aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE.md40
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml32
-rw-r--r--CONTRIBUTING.md22
-rw-r--r--COPYING1
-rw-r--r--Makefile.am10
-rw-r--r--configure.ac30
-rw-r--r--contrib/README.md2
-rw-r--r--contrib/debian/bitcoin-qt.desktop3
-rw-r--r--contrib/debian/examples/bitcoin.conf4
-rwxr-xr-xcontrib/devtools/check-doc.py2
-rwxr-xr-xcontrib/devtools/check-rpc-mappings.py158
-rwxr-xr-xcontrib/devtools/gen-manpages.sh2
-rwxr-xr-xcontrib/devtools/git-subtree-check.sh37
-rwxr-xr-xcontrib/devtools/lint-all.sh22
-rwxr-xr-xcontrib/devtools/lint-python.sh10
-rwxr-xr-xcontrib/devtools/lint-whitespace.sh88
-rwxr-xr-xcontrib/gitian-build.sh7
-rw-r--r--contrib/gitian-descriptors/README.md65
-rw-r--r--contrib/gitian-keys/sjors-key.pgp76
-rw-r--r--contrib/gitian-keys/willyko-key.pgp85
-rw-r--r--contrib/init/bitcoind.openrc2
-rw-r--r--contrib/init/bitcoind.service27
-rw-r--r--contrib/init/org.bitcoin.bitcoind.plist1
-rwxr-xr-xcontrib/install_db4.sh87
-rwxr-xr-xcontrib/macdeploy/detached-sig-create.sh2
-rwxr-xr-xcontrib/tidy_datadir.sh2
-rw-r--r--contrib/valgrind.supp43
-rwxr-xr-xcontrib/verify-commits/gpg.sh15
-rwxr-xr-xcontrib/verify-commits/verify-commits.sh11
-rwxr-xr-xcontrib/verifybinaries/verify.sh6
-rwxr-xr-xcontrib/zmq/zmq_sub.py2
-rwxr-xr-xcontrib/zmq/zmq_sub3.4.py2
-rw-r--r--depends/packages/libevent.mk2
-rw-r--r--depends/packages/native_ds_store.mk9
-rw-r--r--depends/packages/native_mac_alias.mk11
-rw-r--r--depends/packages/zeromq.mk15
-rw-r--r--depends/patches/native_mac_alias/python3.patch28
-rw-r--r--depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch30
-rw-r--r--depends/patches/zeromq/9114d3957725acd34aa8b8d011585812f3369411.patch22
-rw-r--r--depends/patches/zeromq/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch22
-rw-r--r--doc/README.md1
-rw-r--r--doc/bips.md1
-rw-r--r--doc/build-openbsd.md130
-rw-r--r--doc/build-osx.md18
-rw-r--r--doc/build-unix.md33
-rw-r--r--doc/build-windows.md131
-rw-r--r--doc/dependencies.md31
-rw-r--r--doc/developer-notes.md81
-rw-r--r--doc/files.md1
-rw-r--r--doc/gitian-building.md482
-rw-r--r--doc/gitian-building/all_files_in_one_partition.pngbin3350 -> 0 bytes
-rw-r--r--doc/gitian-building/create_new_vm.pngbin119839 -> 0 bytes
-rw-r--r--doc/gitian-building/create_vm_file_location_size.pngbin111942 -> 0 bytes
-rw-r--r--doc/gitian-building/create_vm_hard_disk.pngbin123400 -> 0 bytes
-rw-r--r--doc/gitian-building/create_vm_hard_disk_file_type.pngbin170503 -> 0 bytes
-rw-r--r--doc/gitian-building/create_vm_memsize.pngbin22158 -> 0 bytes
-rw-r--r--doc/gitian-building/create_vm_storage_physical_hard_disk.pngbin181681 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_10_configure_clock.pngbin7892 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_11_partition_disks.pngbin9511 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_12_choose_disk.pngbin6613 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_14_finish.pngbin10794 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_15_write_changes.pngbin8790 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_16_choose_a_mirror.pngbin11134 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_18_proxy_settings.pngbin7582 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_19_software_selection.pngbin8767 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_1_boot_menu.pngbin110818 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_20_install_grub.pngbin9784 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_21_install_grub_bootloader.pngbin8878 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_22_finish_installation.pngbin6964 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_2_select_a_language.pngbin13131 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_3_select_location.pngbin10388 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_4_configure_keyboard.pngbin10224 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_5_configure_the_network.pngbin7612 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_6_domain_name.pngbin6526 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_6a_set_up_root_password.pngbin11876 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_7_set_up_user_fullname.pngbin8407 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_8_set_up_username.pngbin7058 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_install_9_user_password.pngbin6322 -> 0 bytes
-rw-r--r--doc/gitian-building/debian_root_login.pngbin7028 -> 0 bytes
-rw-r--r--doc/gitian-building/network_settings.pngbin72185 -> 0 bytes
-rw-r--r--doc/gitian-building/port_forwarding_rules.pngbin44052 -> 0 bytes
-rw-r--r--doc/gitian-building/select_startup_disk.pngbin72785 -> 0 bytes
-rw-r--r--doc/gitian-building/system_settings.pngbin76448 -> 0 bytes
-rw-r--r--doc/man/bitcoin-cli.127
-rw-r--r--doc/man/bitcoin-qt.1115
-rw-r--r--doc/man/bitcoin-tx.111
-rw-r--r--doc/man/bitcoind.1115
-rw-r--r--doc/release-notes.md50
-rw-r--r--doc/release-notes/release-notes-0.15.0.1.md87
-rw-r--r--doc/release-notes/release-notes-0.15.0.md878
-rw-r--r--doc/release-notes/release-notes-0.15.1.md277
-rw-r--r--share/certs/BitcoinFoundation_Apple_Cert.pem37
-rw-r--r--share/certs/BitcoinFoundation_Comodo_Cert.pem37
-rw-r--r--share/certs/PrivateKeyNotes.md46
-rwxr-xr-xshare/genbuild.sh2
-rw-r--r--share/rpcauth/README.md10
-rwxr-xr-xshare/rpcauth/rpcauth.py (renamed from share/rpcuser/rpcuser.py)2
-rw-r--r--share/rpcuser/README.md10
-rw-r--r--src/Makefile.am13
-rw-r--r--src/Makefile.bench.include9
-rw-r--r--src/Makefile.qt.include6
-rw-r--r--src/Makefile.test.include8
-rw-r--r--src/addrdb.cpp21
-rw-r--r--src/addrdb.h4
-rw-r--r--src/addrman.cpp14
-rw-r--r--src/addrman.h25
-rw-r--r--src/arith_uint256.cpp8
-rw-r--r--src/arith_uint256.h2
-rw-r--r--src/base58.cpp195
-rw-r--r--src/base58.h40
-rw-r--r--src/bech32.cpp191
-rw-r--r--src/bech32.h25
-rw-r--r--src/bench/Examples.cpp6
-rw-r--r--src/bench/base58.cpp8
-rw-r--r--src/bench/bench.cpp45
-rw-r--r--src/bench/bench.h36
-rw-r--r--src/bench/bench_bitcoin.cpp12
-rw-r--r--src/bench/ccoins_caching.cpp8
-rw-r--r--src/bench/checkblock.cpp12
-rw-r--r--src/bench/checkqueue.cpp14
-rw-r--r--src/bench/coin_selection.cpp4
-rw-r--r--src/bench/crypto_hash.cpp22
-rw-r--r--src/bench/lockedpool.cpp4
-rw-r--r--src/bench/mempool_eviction.cpp6
-rw-r--r--src/bench/perf.cpp2
-rw-r--r--src/bench/prevector_destructor.cpp4
-rw-r--r--src/bench/rollingbloom.cpp11
-rw-r--r--src/bench/verify_script.cpp12
-rw-r--r--src/bitcoin-cli.cpp145
-rw-r--r--src/bitcoin-tx.cpp82
-rw-r--r--src/bitcoind.cpp36
-rw-r--r--src/blockencodings.cpp20
-rw-r--r--src/blockencodings.h2
-rw-r--r--src/bloom.cpp16
-rw-r--r--src/bloom.h2
-rw-r--r--src/chain.cpp15
-rw-r--r--src/chain.h22
-rw-r--r--src/chainparams.cpp36
-rw-r--r--src/chainparams.h10
-rw-r--r--src/chainparamsbase.cpp6
-rw-r--r--src/checkpoints.cpp11
-rw-r--r--src/checkpoints.h2
-rw-r--r--src/checkqueue.h2
-rw-r--r--src/clientversion.cpp9
-rw-r--r--src/clientversion.h2
-rw-r--r--src/coins.cpp103
-rw-r--r--src/coins.h24
-rw-r--r--src/compat.h14
-rw-r--r--src/compat/byteswap.h8
-rw-r--r--src/compat/endian.h4
-rw-r--r--src/compat/glibc_compat.cpp2
-rw-r--r--src/compat/glibc_sanity.cpp2
-rw-r--r--src/compat/strnlen.cpp2
-rw-r--r--src/compressor.cpp8
-rw-r--r--src/compressor.h6
-rw-r--r--src/consensus/consensus.h11
-rw-r--r--src/consensus/merkle.cpp6
-rw-r--r--src/consensus/merkle.h6
-rw-r--r--src/consensus/params.h14
-rw-r--r--src/consensus/tx_verify.cpp92
-rw-r--r--src/consensus/tx_verify.h5
-rw-r--r--src/consensus/validation.h19
-rw-r--r--src/core_io.h2
-rw-r--r--src/core_memusage.h6
-rw-r--r--src/core_read.cpp18
-rw-r--r--src/core_write.cpp29
-rw-r--r--src/crypto/aes.cpp6
-rw-r--r--src/crypto/aes.h2
-rw-r--r--src/crypto/chacha20.cpp4
-rw-r--r--src/crypto/common.h4
-rw-r--r--src/crypto/hmac_sha256.cpp2
-rw-r--r--src/crypto/hmac_sha256.h2
-rw-r--r--src/crypto/hmac_sha512.cpp2
-rw-r--r--src/crypto/hmac_sha512.h2
-rw-r--r--src/crypto/ripemd160.cpp4
-rw-r--r--src/crypto/sha1.cpp4
-rw-r--r--src/crypto/sha256.cpp4
-rw-r--r--src/crypto/sha512.cpp4
-rw-r--r--src/dbwrapper.cpp8
-rw-r--r--src/dbwrapper.h14
-rw-r--r--src/fs.cpp2
-rw-r--r--src/hash.cpp7
-rw-r--r--src/hash.h26
-rw-r--r--src/httprpc.cpp39
-rw-r--r--src/httpserver.cpp50
-rw-r--r--src/init.cpp267
-rw-r--r--src/key.cpp11
-rw-r--r--src/key.h13
-rw-r--r--src/keystore.cpp33
-rw-r--r--src/keystore.h50
-rw-r--r--src/memusage.h2
-rw-r--r--src/merkleblock.cpp43
-rw-r--r--src/merkleblock.h24
-rw-r--r--src/miner.cpp84
-rw-r--r--src/miner.h13
-rw-r--r--src/net.cpp475
-rw-r--r--src/net.h131
-rw-r--r--src/net_processing.cpp1060
-rw-r--r--src/net_processing.h61
-rw-r--r--src/netaddress.cpp12
-rw-r--r--src/netaddress.h6
-rw-r--r--src/netbase.cpp254
-rw-r--r--src/netbase.h14
-rw-r--r--src/netmessagemaker.h4
-rw-r--r--src/noui.cpp6
-rw-r--r--src/policy/feerate.cpp4
-rw-r--r--src/policy/feerate.h11
-rw-r--r--src/policy/fees.cpp51
-rw-r--r--src/policy/fees.h16
-rw-r--r--src/policy/policy.cpp49
-rw-r--r--src/policy/policy.h12
-rw-r--r--src/policy/rbf.cpp2
-rw-r--r--src/policy/rbf.h2
-rw-r--r--src/pow.cpp10
-rw-r--r--src/pow.h2
-rw-r--r--src/primitives/block.cpp10
-rw-r--r--src/primitives/block.h6
-rw-r--r--src/primitives/transaction.cpp16
-rw-r--r--src/primitives/transaction.h12
-rw-r--r--src/protocol.cpp6
-rw-r--r--src/protocol.h71
-rw-r--r--src/pubkey.cpp3
-rw-r--r--src/pubkey.h6
-rw-r--r--src/qt/addressbookpage.cpp18
-rw-r--r--src/qt/addresstablemodel.cpp32
-rw-r--r--src/qt/askpassphrasedialog.cpp22
-rw-r--r--src/qt/askpassphrasedialog.h1
-rw-r--r--src/qt/bantablemodel.cpp18
-rw-r--r--src/qt/bantablemodel.h2
-rw-r--r--src/qt/bitcoin.cpp50
-rw-r--r--src/qt/bitcoinaddressvalidator.cpp10
-rw-r--r--src/qt/bitcoinamountfield.cpp10
-rw-r--r--src/qt/bitcoinamountfield.h2
-rw-r--r--src/qt/bitcoingui.cpp57
-rw-r--r--src/qt/bitcoingui.h4
-rw-r--r--src/qt/bitcoinunits.cpp4
-rw-r--r--src/qt/bitcoinunits.h2
-rw-r--r--src/qt/clientmodel.cpp34
-rw-r--r--src/qt/coincontroldialog.cpp42
-rw-r--r--src/qt/coincontroldialog.h2
-rw-r--r--src/qt/coincontroltreewidget.cpp4
-rw-r--r--src/qt/csvmodelwriter.cpp2
-rw-r--r--src/qt/editaddressdialog.cpp8
-rw-r--r--src/qt/forms/askpassphrasedialog.ui7
-rw-r--r--src/qt/forms/coincontroldialog.ui2
-rw-r--r--src/qt/forms/debugwindow.ui2
-rw-r--r--src/qt/forms/editaddressdialog.ui2
-rw-r--r--src/qt/forms/modaloverlay.ui2
-rw-r--r--src/qt/forms/openuridialog.ui2
-rw-r--r--src/qt/forms/optionsdialog.ui16
-rw-r--r--src/qt/forms/receivecoinsdialog.ui24
-rw-r--r--src/qt/forms/receiverequestdialog.ui2
-rw-r--r--src/qt/forms/sendcoinsdialog.ui17
-rw-r--r--src/qt/forms/sendcoinsentry.ui13
-rw-r--r--src/qt/forms/signverifymessagedialog.ui2
-rw-r--r--src/qt/guiutil.cpp124
-rw-r--r--src/qt/guiutil.h11
-rw-r--r--src/qt/intro.cpp19
-rw-r--r--src/qt/macnotificationhandler.h7
-rw-r--r--src/qt/macnotificationhandler.mm14
-rw-r--r--src/qt/modaloverlay.cpp34
-rw-r--r--src/qt/networkstyle.cpp4
-rw-r--r--src/qt/notificator.cpp71
-rw-r--r--src/qt/notificator.h5
-rw-r--r--src/qt/openuridialog.cpp8
-rw-r--r--src/qt/optionsdialog.cpp20
-rw-r--r--src/qt/optionsmodel.cpp152
-rw-r--r--src/qt/optionsmodel.h5
-rw-r--r--src/qt/overviewpage.cpp26
-rw-r--r--src/qt/overviewpage.h2
-rw-r--r--src/qt/paymentrequestplus.cpp7
-rw-r--r--src/qt/paymentrequestplus.h4
-rw-r--r--src/qt/paymentserver.cpp38
-rw-r--r--src/qt/paymentserver.h4
-rw-r--r--src/qt/peertablemodel.cpp32
-rw-r--r--src/qt/peertablemodel.h10
-rw-r--r--src/qt/platformstyle.cpp6
-rw-r--r--src/qt/qvalidatedlineedit.cpp6
-rw-r--r--src/qt/qvaluecombobox.cpp2
-rw-r--r--src/qt/receivecoinsdialog.cpp46
-rw-r--r--src/qt/receivecoinsdialog.h2
-rw-r--r--src/qt/receiverequestdialog.cpp15
-rw-r--r--src/qt/receiverequestdialog.h2
-rw-r--r--src/qt/recentrequeststablemodel.cpp12
-rw-r--r--src/qt/recentrequeststablemodel.h2
-rw-r--r--src/qt/rpcconsole.cpp91
-rw-r--r--src/qt/rpcconsole.h7
-rw-r--r--src/qt/sendcoinsdialog.cpp92
-rw-r--r--src/qt/sendcoinsdialog.h3
-rw-r--r--src/qt/sendcoinsentry.cpp31
-rw-r--r--src/qt/sendcoinsentry.h6
-rw-r--r--src/qt/signverifymessagedialog.cpp46
-rw-r--r--src/qt/splashscreen.cpp50
-rw-r--r--src/qt/splashscreen.h4
-rw-r--r--src/qt/test/compattests.cpp6
-rw-r--r--src/qt/test/paymentservertests.cpp32
-rw-r--r--src/qt/test/paymentservertests.h2
-rw-r--r--src/qt/test/rpcnestedtests.cpp31
-rw-r--r--src/qt/test/rpcnestedtests.h7
-rw-r--r--src/qt/test/test_main.cpp22
-rw-r--r--src/qt/test/uritests.cpp6
-rw-r--r--src/qt/test/wallettests.cpp116
-rw-r--r--src/qt/trafficgraphwidget.cpp4
-rw-r--r--src/qt/transactiondesc.cpp43
-rw-r--r--src/qt/transactiondescdialog.cpp6
-rw-r--r--src/qt/transactionfilterproxy.cpp21
-rw-r--r--src/qt/transactionfilterproxy.h6
-rw-r--r--src/qt/transactionrecord.cpp18
-rw-r--r--src/qt/transactionrecord.h4
-rw-r--r--src/qt/transactiontablemodel.cpp42
-rw-r--r--src/qt/transactiontablemodel.h2
-rw-r--r--src/qt/transactionview.cpp66
-rw-r--r--src/qt/transactionview.h8
-rw-r--r--src/qt/utilitydialog.cpp24
-rw-r--r--src/qt/walletframe.cpp8
-rw-r--r--src/qt/walletmodel.cpp111
-rw-r--r--src/qt/walletmodel.h6
-rw-r--r--src/qt/walletmodeltransaction.cpp14
-rw-r--r--src/qt/walletmodeltransaction.h4
-rw-r--r--src/qt/walletview.cpp40
-rw-r--r--src/qt/walletview.h2
-rw-r--r--src/qt/winshutdownmonitor.cpp6
-rw-r--r--src/random.cpp12
-rw-r--r--src/random.h8
-rw-r--r--src/rest.cpp66
-rw-r--r--src/rpc/blockchain.cpp203
-rw-r--r--src/rpc/client.cpp15
-rw-r--r--src/rpc/mining.cpp82
-rw-r--r--src/rpc/mining.h2
-rw-r--r--src/rpc/misc.cpp279
-rw-r--r--src/rpc/net.cpp54
-rw-r--r--src/rpc/protocol.cpp36
-rw-r--r--src/rpc/protocol.h5
-rw-r--r--src/rpc/rawtransaction.cpp174
-rw-r--r--src/rpc/safemode.cpp8
-rw-r--r--src/rpc/server.cpp38
-rw-r--r--src/rpc/server.h10
-rw-r--r--src/scheduler.cpp6
-rw-r--r--src/scheduler.h2
-rw-r--r--src/script/bitcoinconsensus.cpp10
-rw-r--r--src/script/bitcoinconsensus.h2
-rw-r--r--src/script/interpreter.cpp46
-rw-r--r--src/script/interpreter.h28
-rw-r--r--src/script/ismine.cpp14
-rw-r--r--src/script/ismine.h2
-rw-r--r--src/script/script.cpp6
-rw-r--r--src/script/script.h7
-rw-r--r--src/script/script_error.cpp4
-rw-r--r--src/script/sigcache.cpp14
-rw-r--r--src/script/sigcache.h2
-rw-r--r--src/script/sign.cpp16
-rw-r--r--src/script/sign.h2
-rw-r--r--src/script/standard.cpp59
-rw-r--r--src/script/standard.h46
-rw-r--r--src/secp256k1/contrib/lax_der_parsing.h10
-rw-r--r--src/secp256k1/contrib/lax_der_privatekey_parsing.h10
-rw-r--r--src/secp256k1/include/secp256k1.h23
-rw-r--r--src/secp256k1/include/secp256k1_ecdh.h16
-rw-r--r--src/secp256k1/include/secp256k1_recovery.h16
-rw-r--r--src/secp256k1/sage/group_prover.sage2
-rw-r--r--src/secp256k1/src/asm/field_10x26_arm.s2
-rw-r--r--src/secp256k1/src/basic-config.h9
-rw-r--r--src/secp256k1/src/bench.h6
-rw-r--r--src/secp256k1/src/ecdsa.h6
-rw-r--r--src/secp256k1/src/ecdsa_impl.h8
-rw-r--r--src/secp256k1/src/eckey.h6
-rw-r--r--src/secp256k1/src/eckey_impl.h17
-rw-r--r--src/secp256k1/src/ecmult.h6
-rw-r--r--src/secp256k1/src/ecmult_const.h6
-rw-r--r--src/secp256k1/src/ecmult_const_impl.h13
-rw-r--r--src/secp256k1/src/ecmult_gen.h6
-rw-r--r--src/secp256k1/src/ecmult_gen_impl.h6
-rw-r--r--src/secp256k1/src/ecmult_impl.h6
-rw-r--r--src/secp256k1/src/field.h6
-rw-r--r--src/secp256k1/src/field_10x26.h7
-rw-r--r--src/secp256k1/src/field_10x26_impl.h6
-rw-r--r--src/secp256k1/src/field_5x52.h6
-rw-r--r--src/secp256k1/src/field_5x52_asm_impl.h6
-rw-r--r--src/secp256k1/src/field_5x52_impl.h6
-rw-r--r--src/secp256k1/src/field_5x52_int128_impl.h6
-rw-r--r--src/secp256k1/src/field_impl.h6
-rw-r--r--src/secp256k1/src/group.h6
-rw-r--r--src/secp256k1/src/group_impl.h6
-rw-r--r--src/secp256k1/src/hash.h6
-rw-r--r--src/secp256k1/src/hash_impl.h6
-rw-r--r--src/secp256k1/src/modules/ecdh/main_impl.h6
-rw-r--r--src/secp256k1/src/modules/ecdh/tests_impl.h6
-rwxr-xr-xsrc/secp256k1/src/modules/recovery/main_impl.h6
-rw-r--r--src/secp256k1/src/modules/recovery/tests_impl.h6
-rw-r--r--src/secp256k1/src/num.h6
-rw-r--r--src/secp256k1/src/num_gmp.h6
-rw-r--r--src/secp256k1/src/num_gmp_impl.h6
-rw-r--r--src/secp256k1/src/num_impl.h6
-rw-r--r--src/secp256k1/src/scalar.h6
-rw-r--r--src/secp256k1/src/scalar_4x64.h6
-rw-r--r--src/secp256k1/src/scalar_4x64_impl.h6
-rw-r--r--src/secp256k1/src/scalar_8x32.h6
-rw-r--r--src/secp256k1/src/scalar_8x32_impl.h6
-rw-r--r--src/secp256k1/src/scalar_impl.h6
-rw-r--r--src/secp256k1/src/scalar_low.h6
-rw-r--r--src/secp256k1/src/scalar_low_impl.h6
-rw-r--r--src/secp256k1/src/testrand.h6
-rw-r--r--src/secp256k1/src/testrand_impl.h6
-rw-r--r--src/secp256k1/src/util.h6
-rw-r--r--src/serialize.h8
-rw-r--r--src/streams.h36
-rw-r--r--src/support/allocators/secure.h4
-rw-r--r--src/support/allocators/zeroafterfree.h2
-rw-r--r--src/support/cleanse.cpp36
-rw-r--r--src/support/lockedpool.cpp6
-rw-r--r--src/support/lockedpool.h12
-rw-r--r--src/sync.cpp40
-rw-r--r--src/sync.h67
-rw-r--r--src/test/DoS_tests.cpp228
-rw-r--r--src/test/README.md7
-rw-r--r--src/test/addrman_tests.cpp10
-rw-r--r--src/test/allocator_tests.cpp6
-rw-r--r--src/test/amount_tests.cpp6
-rw-r--r--src/test/arith_uint256_tests.cpp8
-rw-r--r--src/test/base32_tests.cpp4
-rw-r--r--src/test/base58_tests.cpp175
-rw-r--r--src/test/base64_tests.cpp4
-rw-r--r--src/test/bech32_tests.cpp67
-rw-r--r--src/test/bip32_tests.cpp14
-rw-r--r--src/test/blockencodings_tests.cpp13
-rw-r--r--src/test/bloom_tests.cpp42
-rw-r--r--src/test/bswap_tests.cpp4
-rw-r--r--src/test/checkqueue_tests.cpp47
-rw-r--r--src/test/coins_tests.cpp38
-rw-r--r--src/test/compress_tests.cpp6
-rw-r--r--src/test/crypto_tests.cpp107
-rw-r--r--src/test/cuckoocache_tests.cpp8
-rw-r--r--src/test/data/base58_keys_invalid.json30
-rw-r--r--src/test/data/base58_keys_valid.json679
-rw-r--r--src/test/data/script_tests.json2
-rw-r--r--src/test/dbwrapper_tests.cpp34
-rw-r--r--src/test/getarg_tests.cpp6
-rw-r--r--src/test/hash_tests.cpp6
-rw-r--r--src/test/key_tests.cpp41
-rw-r--r--src/test/limitedmap_tests.cpp4
-rw-r--r--src/test/main_tests.cpp8
-rw-r--r--src/test/mempool_tests.cpp16
-rw-r--r--src/test/merkle_tests.cpp4
-rw-r--r--src/test/merkleblock_tests.cpp78
-rw-r--r--src/test/miner_tests.cpp70
-rw-r--r--src/test/multisig_tests.cpp111
-rw-r--r--src/test/net_tests.cpp18
-rw-r--r--src/test/netbase_tests.cpp6
-rw-r--r--src/test/pmt_tests.cpp16
-rw-r--r--src/test/policyestimator_tests.cpp12
-rw-r--r--src/test/pow_tests.cpp12
-rw-r--r--src/test/prevector_tests.cpp10
-rw-r--r--src/test/raii_event_tests.cpp4
-rw-r--r--src/test/random_tests.cpp4
-rw-r--r--src/test/reverselock_tests.cpp4
-rw-r--r--src/test/rpc_tests.cpp12
-rw-r--r--src/test/sanity_tests.cpp6
-rw-r--r--src/test/scheduler_tests.cpp6
-rw-r--r--src/test/script_P2SH_tests.cpp25
-rw-r--r--src/test/script_standard_tests.cpp741
-rw-r--r--src/test/script_tests.cpp54
-rw-r--r--src/test/scriptnum10.h2
-rw-r--r--src/test/scriptnum_tests.cpp6
-rw-r--r--src/test/serialize_tests.cpp8
-rw-r--r--src/test/sighash_tests.cpp24
-rw-r--r--src/test/sigopcount_tests.cpp16
-rw-r--r--src/test/skiplist_tests.cpp10
-rw-r--r--src/test/streams_tests.cpp6
-rw-r--r--src/test/test_bitcoin.cpp83
-rw-r--r--src/test/test_bitcoin.h30
-rw-r--r--src/test/test_bitcoin_fuzzy.cpp103
-rw-r--r--src/test/test_bitcoin_main.cpp2
-rw-r--r--src/test/timedata_tests.cpp4
-rw-r--r--src/test/torcontrol_tests.cpp4
-rw-r--r--src/test/transaction_tests.cpp39
-rw-r--r--src/test/txvalidation_tests.cpp61
-rw-r--r--src/test/txvalidationcache_tests.cpp77
-rw-r--r--src/test/uint256_tests.cpp8
-rw-r--r--src/test/univalue_tests.cpp2
-rw-r--r--src/test/util_tests.cpp39
-rw-r--r--src/test/versionbits_tests.cpp27
-rw-r--r--src/threadinterrupt.cpp2
-rw-r--r--src/timedata.cpp16
-rw-r--r--src/tinyformat.h14
-rw-r--r--src/torcontrol.cpp12
-rw-r--r--src/torcontrol.h2
-rw-r--r--src/txdb.cpp24
-rw-r--r--src/txdb.h22
-rw-r--r--src/txmempool.cpp56
-rw-r--r--src/txmempool.h31
-rw-r--r--src/ui_interface.cpp4
-rw-r--r--src/ui_interface.h10
-rw-r--r--src/uint256.cpp6
-rw-r--r--src/uint256.h6
-rw-r--r--src/undo.h8
-rw-r--r--src/univalue/Makefile.am29
-rw-r--r--src/univalue/README7
-rw-r--r--src/univalue/README.md32
-rw-r--r--src/univalue/configure.ac6
-rw-r--r--src/univalue/include/univalue.h34
-rw-r--r--src/univalue/lib/univalue.cpp193
-rw-r--r--src/univalue/lib/univalue_get.cpp147
-rw-r--r--src/univalue/lib/univalue_read.cpp72
-rw-r--r--src/univalue/lib/univalue_utffilter.h42
-rw-r--r--src/univalue/lib/univalue_write.cpp2
-rw-r--r--src/univalue/test/.gitignore4
-rw-r--r--src/univalue/test/fail1.json2
-rw-r--r--src/univalue/test/fail42.jsonbin0 -> 37 bytes
-rw-r--r--src/univalue/test/fail44.json1
-rw-r--r--src/univalue/test/no_nul.cpp8
-rw-r--r--src/univalue/test/object.cpp395
-rw-r--r--src/univalue/test/round3.json1
-rw-r--r--src/univalue/test/round4.json1
-rw-r--r--src/univalue/test/round5.json1
-rw-r--r--src/univalue/test/round6.json1
-rw-r--r--src/univalue/test/round7.json1
-rw-r--r--src/univalue/test/test_json.cpp24
-rw-r--r--src/univalue/test/unitester.cpp7
-rw-r--r--src/util.cpp63
-rw-r--r--src/util.h28
-rw-r--r--src/utilmoneystr.cpp8
-rw-r--r--src/utilmoneystr.h2
-rw-r--r--src/utilstrencodings.cpp17
-rw-r--r--src/utilstrencodings.h30
-rw-r--r--src/utiltime.cpp4
-rw-r--r--src/validation.cpp907
-rw-r--r--src/validation.h77
-rw-r--r--src/validationinterface.cpp70
-rw-r--r--src/validationinterface.h70
-rw-r--r--src/versionbits.cpp14
-rw-r--r--src/versionbits.h2
-rw-r--r--src/wallet/coincontrol.h8
-rw-r--r--src/wallet/crypter.cpp162
-rw-r--r--src/wallet/crypter.h60
-rw-r--r--src/wallet/db.cpp134
-rw-r--r--src/wallet/db.h25
-rw-r--r--src/wallet/feebumper.cpp244
-rw-r--r--src/wallet/feebumper.h67
-rw-r--r--src/wallet/fees.cpp14
-rw-r--r--src/wallet/fees.h2
-rw-r--r--src/wallet/init.cpp80
-rw-r--r--src/wallet/init.h24
-rw-r--r--src/wallet/rpcdump.cpp156
-rw-r--r--src/wallet/rpcwallet.cpp616
-rw-r--r--src/wallet/rpcwallet.h3
-rw-r--r--src/wallet/test/accounting_tests.cpp24
-rw-r--r--src/wallet/test/crypto_tests.cpp8
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp17
-rw-r--r--src/wallet/test/wallet_test_fixture.h6
-rw-r--r--src/wallet/test/wallet_tests.cpp27
-rw-r--r--src/wallet/wallet.cpp372
-rw-r--r--src/wallet/wallet.h81
-rw-r--r--src/wallet/walletdb.cpp74
-rw-r--r--src/wallet/walletdb.h17
-rw-r--r--src/wallet/walletutil.cpp27
-rw-r--r--src/wallet/walletutil.h13
-rw-r--r--src/warnings.cpp8
-rw-r--r--src/zmq/zmqabstractnotifier.cpp4
-rw-r--r--src/zmq/zmqabstractnotifier.h2
-rw-r--r--src/zmq/zmqconfig.h6
-rw-r--r--src/zmq/zmqnotificationinterface.cpp20
-rw-r--r--src/zmq/zmqnotificationinterface.h2
-rw-r--r--src/zmq/zmqpublishnotifier.cpp14
-rw-r--r--src/zmq/zmqpublishnotifier.h2
-rw-r--r--test/functional/README.md10
-rwxr-xr-xtest/functional/assumevalid.py66
-rwxr-xr-xtest/functional/bip65-cltv-p2p.py41
-rwxr-xr-xtest/functional/bip68-112-113-p2p.py4
-rwxr-xr-xtest/functional/bip68-sequence.py19
-rwxr-xr-xtest/functional/bip9-softforks.py8
-rwxr-xr-xtest/functional/bipdersig-p2p.py42
-rwxr-xr-xtest/functional/bitcoin_cli.py50
-rwxr-xr-xtest/functional/blockchain.py87
-rwxr-xr-xtest/functional/bumpfee.py14
-rwxr-xr-xtest/functional/dbcrash.py2
-rwxr-xr-xtest/functional/deprecated_rpc.py23
-rwxr-xr-xtest/functional/disablewallet.py4
-rwxr-xr-xtest/functional/disconnect_ban.py12
-rwxr-xr-xtest/functional/example_test.py61
-rwxr-xr-xtest/functional/feature_logging.py59
-rwxr-xr-xtest/functional/forknotify.py59
-rwxr-xr-xtest/functional/fundrawtransaction.py14
-rwxr-xr-xtest/functional/import-rescan.py30
-rwxr-xr-xtest/functional/importmulti.py16
-rwxr-xr-xtest/functional/importprunedfunds.py6
-rwxr-xr-xtest/functional/invalidblockrequest.py3
-rwxr-xr-xtest/functional/invalidtxrequest.py2
-rwxr-xr-xtest/functional/keypool-topup.py6
-rwxr-xr-xtest/functional/keypool.py8
-rwxr-xr-xtest/functional/listsinceblock.py35
-rwxr-xr-xtest/functional/maxuploadtarget.py82
-rwxr-xr-xtest/functional/mempool_packages.py4
-rwxr-xr-xtest/functional/mempool_persist.py36
-rwxr-xr-xtest/functional/mempool_reorg.py4
-rwxr-xr-xtest/functional/mempool_spendcoinbase.py2
-rwxr-xr-xtest/functional/merkle_blocks.py8
-rwxr-xr-xtest/functional/minchainwork.py89
-rwxr-xr-xtest/functional/mining.py11
-rwxr-xr-xtest/functional/multiwallet.py53
-rwxr-xr-xtest/functional/net.py4
-rwxr-xr-xtest/functional/node_network_limited.py81
-rwxr-xr-xtest/functional/notifications.py86
-rwxr-xr-xtest/functional/nulldummy.py14
-rwxr-xr-xtest/functional/p2p-acceptblock.py308
-rwxr-xr-xtest/functional/p2p-compactblocks.py41
-rwxr-xr-xtest/functional/p2p-feefilter.py32
-rwxr-xr-xtest/functional/p2p-fingerprint.py152
-rwxr-xr-xtest/functional/p2p-fullblocktest.py5
-rwxr-xr-xtest/functional/p2p-leaktests.py117
-rwxr-xr-xtest/functional/p2p-mempool.py14
-rwxr-xr-xtest/functional/p2p-segwit.py307
-rwxr-xr-xtest/functional/p2p-timeouts.py49
-rwxr-xr-xtest/functional/p2p-versionbits-warning.py27
-rwxr-xr-xtest/functional/prioritise_transaction.py2
-rwxr-xr-xtest/functional/pruning.py8
-rwxr-xr-xtest/functional/rawtransactions.py96
-rwxr-xr-xtest/functional/receivedby.py124
-rwxr-xr-xtest/functional/replace-by-fee.py54
-rwxr-xr-xtest/functional/resendwallettransactions.py4
-rwxr-xr-xtest/functional/rpcbind_test.py2
-rwxr-xr-xtest/functional/rpcnamedargs.py8
-rwxr-xr-xtest/functional/segwit.py87
-rwxr-xr-xtest/functional/sendheaders.py313
-rwxr-xr-xtest/functional/signrawtransactions.py2
-rwxr-xr-xtest/functional/smartfees.py2
-rw-r--r--test/functional/test_framework/address.py28
-rw-r--r--test/functional/test_framework/authproxy.py74
-rw-r--r--test/functional/test_framework/blockstore.py8
-rwxr-xr-xtest/functional/test_framework/comptool.py147
-rw-r--r--test/functional/test_framework/coverage.py28
-rw-r--r--test/functional/test_framework/key.py2
-rw-r--r--test/functional/test_framework/messages.py1304
-rwxr-xr-xtest/functional/test_framework/mininode.py1988
-rw-r--r--test/functional/test_framework/script.py2
-rw-r--r--test/functional/test_framework/segwit_addr.py107
-rw-r--r--test/functional/test_framework/socks5.py8
-rwxr-xr-xtest/functional/test_framework/test_framework.py47
-rwxr-xr-xtest/functional/test_framework/test_node.py96
-rw-r--r--test/functional/test_framework/util.py51
-rwxr-xr-xtest/functional/test_runner.py44
-rwxr-xr-xtest/functional/uacomment.py35
-rwxr-xr-xtest/functional/wallet-accounts.py137
-rwxr-xr-xtest/functional/wallet-dump.py5
-rwxr-xr-xtest/functional/wallet-encryption.py14
-rwxr-xr-xtest/functional/wallet-hd.py25
-rwxr-xr-xtest/functional/wallet.py23
-rwxr-xr-xtest/functional/walletbackup.py22
-rwxr-xr-xtest/functional/zapwallettxes.py12
-rwxr-xr-xtest/functional/zmq_test.py139
-rwxr-xr-xtest/util/bitcoin-util-test.py13
-rw-r--r--test/util/data/bitcoin-util-test.json18
-rw-r--r--test/util/data/tt-delin1-out.json40
-rw-r--r--test/util/data/tt-delout1-out.json40
-rw-r--r--test/util/data/tt-locktime317000-out.json42
-rw-r--r--test/util/data/txcreate1.json6
-rw-r--r--test/util/data/txcreatedata1.json2
-rw-r--r--test/util/data/txcreatedata2.json2
-rw-r--r--test/util/data/txcreatedata_seq1.json2
-rw-r--r--test/util/data/txcreatemultisig1.json4
-rw-r--r--test/util/data/txcreatemultisig3.json6
-rw-r--r--test/util/data/txcreatemultisig5.json26
-rw-r--r--test/util/data/txcreateoutpubkey2.json6
-rw-r--r--test/util/data/txcreatescript3.json6
664 files changed, 17194 insertions, 10647 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 28c1814998..55cebc008f 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,37 +1,21 @@
-<!--- Remove sections that do not apply -->
+<!-- This issue tracker is only for technical issues related to Bitcoin Core.
-This issue tracker is only for technical issues related to bitcoin-core.
+General bitcoin questions and/or support requests are best directed to the Bitcoin StackExchange at https://bitcoin.stackexchange.com.
-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/.
-For reporting security issues, please read instructions at [https://bitcoincore.org/en/contact/](https://bitcoincore.org/en/contact/).
+If the node is "stuck" during sync or giving "block checksum mismatch" errors, please ensure your hardware is stable by running memtest and observe CPU temperature with a load-test tool such as linpack before creating an issue! -->
-### Describe the issue
+<!-- Describe the issue -->
+<!--- What behavior did you expect? -->
-### Can you reliably reproduce the issue?
-#### If so, please list the steps to reproduce below:
-1.
-2.
-3.
+<!--- What was the actual behavior (provide screenshots if the issue is GUI-related)? -->
-### Expected behaviour
-Tell us what should happen
+<!--- How reliably can you reproduce the issue, what are the steps to do so? -->
-### Actual behaviour
-Tell us what happens instead
+<!-- What version of Bitcoin Core are you using, where did you get it (website, self-compiled, etc)? -->
-### Screenshots.
-If the issue is related to the GUI, screenshots can be added to this issue via drag & drop.
+<!-- What type of machine are you observing the error on (OS/CPU and disk type)? -->
-### 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:
-- 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.
+<!-- 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 60c26dae8b..ff297fbeca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,6 +76,7 @@ src/qt/test/moc*.cpp
Makefile
bitcoin-qt
Bitcoin-Qt.app
+background.tiff*
# Unit-tests
Makefile.test
diff --git a/.travis.yml b/.travis.yml
index dadac3165e..ab002acc53 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
sudo: required
dist: trusty
os: linux
-language: generic
+language: minimal
cache:
directories:
- depends/built
@@ -18,17 +18,18 @@ env:
- CCACHE_COMPRESS=1
- BASE_OUTDIR=$TRAVIS_BUILD_DIR/out
- SDK_URL=https://bitcoincore.org/depends-sources/sdks
- - PYTHON_DEBUG=1
- WINEDEBUG=fixme-all
matrix:
# ARM
- - HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf" DEP_OPTS="NO_QT=1" CHECK_DOC=1 GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports"
+ - HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf python3-pip" DEP_OPTS="NO_QT=1" CHECK_DOC=1 GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports"
# Win32
- - HOST=i686-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-i686 wine1.6 bc" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports"
+ - HOST=i686-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-i686 wine1.6" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports"
+# Qt4 & system libs
+ - HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qt4-dev-tools libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev libdb5.1++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev xvfb" NO_DEPENDS=1 NEED_XVFB=1 RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --enable-glibc-back-compat --enable-reduce-exports --with-gui=qt4 CPPFLAGS=-DDEBUG_LOCKORDER"
# 32-bit + dash
- - HOST=i686-pc-linux-gnu PACKAGES="g++-multilib bc python3-zmq" DEP_OPTS="NO_QT=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/dash"
+ - HOST=i686-pc-linux-gnu PACKAGES="g++-multilib python3-zmq" DEP_OPTS="NO_QT=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/dash"
# Win64
- - HOST=x86_64-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine1.6 bc" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports"
+ - HOST=x86_64-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine1.6" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports"
# x86_64 Linux (uses qt5 dev package instead of depends Qt to speed up build and avoid timeout)
- HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools protobuf-compiler libdbus-1-dev libharfbuzz-dev" DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports CPPFLAGS=-DDEBUG_LOCKORDER"
# x86_64 Linux, No wallet
@@ -42,14 +43,23 @@ install:
- if [ -n "$DPKG_ADD_ARCH" ]; then sudo dpkg --add-architecture "$DPKG_ADD_ARCH" ; fi
- if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi
- if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi
+ - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then travis_retry pip3 install flake8 --user; fi
before_script:
- - if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi
- - unset CC; unset CXX
+ - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi
+ - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/crypto/ctaes; fi
+ - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/secp256k1; fi
+ - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/univalue; fi
+ - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/leveldb; fi
- if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-doc.py; fi
+ - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-rpc-mappings.py .; fi
+ - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/lint-all.sh; fi
+ - unset CC; unset CXX
- mkdir -p depends/SDKs depends/sdk-sources
- if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi
- if [ -n "$OSX_SDK" -a -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then tar -C depends/SDKs -xf depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi
- - make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS
+ - if [ -z "$NO_DEPENDS" ]; then make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS; fi
+ # Start xvfb if needed, as documented at https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-a-GUI
+ - if [ "$NEED_XVFB" = 1 ]; then export DISPLAY=:99.0; /sbin/start-stop-daemon --start --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac; fi
script:
- if [ "$CHECK_DOC" = 1 -a "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then while read LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys; fi
- if [ "$CHECK_DOC" = 1 -a "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then git fetch --unshallow; fi
@@ -58,7 +68,7 @@ script:
- if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi
- OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST
- BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib"
- - depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE
+ - if [ -z "$NO_DEPENDS" ]; then depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE; fi
- test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh
- mkdir build && cd build
- ../configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false)
@@ -69,7 +79,7 @@ script:
- export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib
- if [ "$RUN_TESTS" = "true" ]; then travis_wait 30 make $MAKEJOBS check VERBOSE=1; fi
- if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then extended="--extended --exclude pruning,dbcrash"; fi
- - if [ "$RUN_TESTS" = "true" ]; then test/functional/test_runner.py --coverage --quiet ${extended}; fi
+ - if [ "$RUN_TESTS" = "true" ]; then test/functional/test_runner.py --combinedlogslen=4000 --coverage --quiet ${extended}; fi
after_script:
- echo $TRAVIS_COMMIT_RANGE
- echo $TRAVIS_COMMIT_LOG
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 56521a5f7d..8381bd2448 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -24,9 +24,9 @@ facilitates social contribution, easy testing and peer review.
To contribute a patch, the workflow is as follows:
- - Fork repository
- - Create topic branch
- - Commit patches
+ 1. Fork repository
+ 1. Create topic branch
+ 1. Commit patches
The project coding conventions in the [developer notes](doc/developer-notes.md)
must be adhered to.
@@ -42,8 +42,8 @@ in init.cpp") in which case a single title line is sufficient. Commit messages s
helpful to people reading your code in the future, so explain the reasoning for
your decisions. Further explanation [here](http://chris.beams.io/posts/git-commit/).
-If a particular commit references another issue, please add the reference, for
-example `refs #1234`, or `fixes #4321`. Using the `fixes` or `closes` keywords
+If a particular commit references another issue, please add the reference. For
+example: `refs #1234` or `fixes #4321`. Using the `fixes` or `closes` keywords
will cause the corresponding issue to be closed when the pull request is merged.
Please refer to the [Git manual](https://git-scm.com/doc) for more information
@@ -85,7 +85,7 @@ Note that translations should not be submitted as pull requests, please see
[Translation Process](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md)
for more information on helping with translations.
-If a pull request is specifically not to be considered for merging (yet) please
+If a pull request is not to be considered for merging (yet), please
prefix the title with [WIP] or use [Tasks Lists](https://help.github.com/articles/basic-writing-and-formatting-syntax/#task-lists)
in the body of the pull request to indicate tasks are pending.
@@ -157,6 +157,14 @@ behaviour of code within the pull request (bugs must be preserved as is).
Project maintainers aim for a quick turnaround on refactoring pull requests, so
where possible keep them short, uncomplex and easy to verify.
+Pull requests that refactor the code should not be made by new contributors. It
+requires a certain level of experience to know where the code belongs to and to
+understand the full ramification (including rebase effort of open pull requests).
+
+Trivial pull requests or pull requests that refactor the code with no clear
+benefits may be immediately closed by the maintainers to reduce unnecessary
+workload on reviewing.
+
"Decision Making" Process
-------------------------
@@ -178,7 +186,7 @@ In general, all pull requests must:
the project (for example refactoring for modularisation);
- Be well peer reviewed;
- Have unit tests and functional tests where appropriate;
- - Follow code style guidelines;
+ - Follow code style guidelines ([C++](doc/developer-notes.md), [functional tests](test/functional/README.md));
- Not break the existing test suite;
- Where bugs are fixed, where possible, there should be unit tests
demonstrating the bug and also proving the fix. This helps prevent regression.
diff --git a/COPYING b/COPYING
index c6203c0f76..45d51c3c78 100644
--- a/COPYING
+++ b/COPYING
@@ -1,6 +1,7 @@
The MIT License (MIT)
Copyright (c) 2009-2017 The Bitcoin Core developers
+Copyright (c) 2009-2017 Bitcoin Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Makefile.am b/Makefile.am
index 8216b7d608..b24daf9905 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,7 +9,6 @@ SUBDIRS += doc/man
endif
.PHONY: deploy FORCE
-GZIP_ENV="-9n"
export PYTHONPATH
if BUILD_BITCOIN_LIBS
@@ -43,7 +42,11 @@ 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/install_db4.sh \
$(top_srcdir)/contrib/rpm
+DIST_SHARE = \
+ $(top_srcdir)/share/genbuild.sh \
+ $(top_srcdir)/share/rpcauth
BIN_CHECKS=$(top_srcdir)/contrib/devtools/symbol-check.py \
$(top_srcdir)/contrib/devtools/security-check.py
@@ -213,7 +216,7 @@ endif
dist_noinst_SCRIPTS = autogen.sh
-EXTRA_DIST = $(top_srcdir)/share/genbuild.sh test/functional/test_runner.py test/functional $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS)
+EXTRA_DIST = $(DIST_SHARE) test/functional/test_runner.py test/functional $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS)
EXTRA_DIST += \
test/util/bitcoin-util-test.py \
@@ -249,6 +252,7 @@ EXTRA_DIST += \
test/util/data/txcreatemultisig3.json \
test/util/data/txcreatemultisig4.hex \
test/util/data/txcreatemultisig4.json \
+ test/util/data/txcreatemultisig5.json \
test/util/data/txcreateoutpubkey1.hex \
test/util/data/txcreateoutpubkey1.json \
test/util/data/txcreateoutpubkey2.hex \
@@ -275,4 +279,4 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-man
clean-local:
rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ test/tmp/ cache/ $(OSX_APP)
- rm -rf test/functional/__pycache__
+ rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache
diff --git a/configure.ac b/configure.ac
index e3e71044da..f7de4c9f9a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -162,7 +162,7 @@ AC_ARG_ENABLE([ccache],
AC_ARG_ENABLE([lcov],
[AS_HELP_STRING([--enable-lcov],
[enable lcov testing (default is no)])],
- [use_lcov=yes],
+ [use_lcov=$enableval],
[use_lcov=no])
AC_ARG_ENABLE([lcov-branch-coverage],
@@ -241,6 +241,7 @@ if test "x$enable_werror" = "xyes"; then
AC_MSG_ERROR("enable-werror set but -Werror is not usable")
fi
AX_CHECK_COMPILE_FLAG([-Werror=vla],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=vla"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Werror=thread-safety-analysis],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=thread-safety-analysis"],,[[$CXXFLAG_WERROR]])
fi
if test "x$CXXFLAGS_overridden" = "xno"; then
@@ -249,6 +250,7 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
AX_CHECK_COMPILE_FLAG([-Wformat],[CXXFLAGS="$CXXFLAGS -Wformat"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wvla],[CXXFLAGS="$CXXFLAGS -Wvla"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wformat-security],[CXXFLAGS="$CXXFLAGS -Wformat-security"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Wthread-safety-analysis],[CXXFLAGS="$CXXFLAGS -Wthread-safety-analysis"],,[[$CXXFLAG_WERROR]])
## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all
## unknown options if any other warning is produced. Test the -Wfoo case, and
@@ -657,6 +659,28 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
]
)
+TEMP_LDFLAGS="$LDFLAGS"
+LDFLAGS="$TEMP_LDFLAGS $PTHREAD_CFLAGS"
+AC_MSG_CHECKING([for thread_local support])
+AC_LINK_IFELSE([AC_LANG_SOURCE([
+ #include <thread>
+ static thread_local int foo = 0;
+ static void run_thread() { foo++;}
+ int main(){
+ for(int i = 0; i < 10; i++) { std::thread(run_thread).detach();}
+ return foo;
+ }
+ ])],
+ [
+ AC_DEFINE(HAVE_THREAD_LOCAL,1,[Define if thread_local is supported.])
+ AC_MSG_RESULT(yes)
+ ],
+ [
+ AC_MSG_RESULT(no)
+ ]
+)
+LDFLAGS="$TEMP_LDFLAGS"
+
# Check for different ways of gathering OS randomness
AC_MSG_CHECKING(for Linux getrandom syscall)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>
@@ -737,6 +761,9 @@ define(MINIMUM_REQUIRED_BOOST, 1.47.0)
dnl Check for boost libs
AX_BOOST_BASE([MINIMUM_REQUIRED_BOOST])
+if test x$want_boost = xno; then
+ AC_MSG_ERROR([[only libbitcoinconsensus can be built without boost]])
+fi
AX_BOOST_SYSTEM
AX_BOOST_FILESYSTEM
AX_BOOST_PROGRAM_OPTIONS
@@ -1231,6 +1258,7 @@ AC_SUBST(QR_LIBS)
AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/config.ini])
AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh])
AC_CONFIG_FILES([doc/Doxyfile])
+AC_CONFIG_LINKS([contrib/filter-lcov.py:contrib/filter-lcov.py])
AC_CONFIG_LINKS([test/functional/test_runner.py:test/functional/test_runner.py])
AC_CONFIG_LINKS([test/util/bitcoin-util-test.py:test/util/bitcoin-util-test.py])
diff --git a/contrib/README.md b/contrib/README.md
index 34c4cfc55a..a582a724f7 100644
--- a/contrib/README.md
+++ b/contrib/README.md
@@ -26,7 +26,7 @@ Contains files used to package bitcoind/bitcoin-qt
for Debian-based Linux systems. If you compile bitcoind/bitcoin-qt yourself, there are some useful files here.
### [Gitian-descriptors](/contrib/gitian-descriptors) ###
-Notes on getting Gitian builds up and running using KVM.
+Files used during the gitian build process. For more information about gitian, see the [the Bitcoin Core documentation repository](https://github.com/bitcoin-core/docs).
### [Gitian-keys](/contrib/gitian-keys)
PGP keys used for signing Bitcoin Core [Gitian release](/doc/release-process.md) results.
diff --git a/contrib/debian/bitcoin-qt.desktop b/contrib/debian/bitcoin-qt.desktop
index 593d7584ab..204cdf99d0 100644
--- a/contrib/debian/bitcoin-qt.desktop
+++ b/contrib/debian/bitcoin-qt.desktop
@@ -1,5 +1,5 @@
[Desktop Entry]
-Encoding=UTF-8
+Version=1.0
Name=Bitcoin Core
Comment=Connect to the Bitcoin P2P Network
Comment[de]=Verbinde mit dem Bitcoin peer-to-peer Netzwerk
@@ -11,3 +11,4 @@ Type=Application
Icon=bitcoin128
MimeType=x-scheme-handler/bitcoin;
Categories=Office;Finance;
+StartupWMClass=Bitcoin-qt
diff --git a/contrib/debian/examples/bitcoin.conf b/contrib/debian/examples/bitcoin.conf
index 14a59fdf6b..4dd73162a2 100644
--- a/contrib/debian/examples/bitcoin.conf
+++ b/contrib/debian/examples/bitcoin.conf
@@ -77,9 +77,9 @@
#rpcpassword=YourSuperGreatPasswordNumber_DO_NOT_USE_THIS_OR_YOU_WILL_GET_ROBBED_385593
#
# The second method `rpcauth` can be added to server startup argument. It is set at initialization time
-# using the output from the script in share/rpcuser/rpcuser.py after providing a username:
+# using the output from the script in share/rpcauth/rpcauth.py after providing a username:
#
-# ./share/rpcuser/rpcuser.py alice
+# ./share/rpcauth/rpcauth.py alice
# String to be appended to bitcoin.conf:
# rpcauth=alice:f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae
# Your password:
diff --git a/contrib/devtools/check-doc.py b/contrib/devtools/check-doc.py
index 3279dc3612..10c3bab03b 100755
--- a/contrib/devtools/check-doc.py
+++ b/contrib/devtools/check-doc.py
@@ -22,7 +22,7 @@ CMD_GREP_DOCS = r"egrep -r -I 'HelpMessageOpt\(\"\-[^\"=]+?(=|\")' %s" % (CMD_RO
REGEX_ARG = re.compile(r'(?:map(?:Multi)?Args(?:\.count\(|\[)|Get(?:Bool)?Arg\()\"(\-[^\"]+?)\"')
REGEX_DOC = re.compile(r'HelpMessageOpt\(\"(\-[^\"=]+?)(?:=|\")')
# list unsupported, deprecated and duplicate args as they need no documentation
-SET_DOC_OPTIONAL = set(['-rpcssl', '-benchmark', '-h', '-help', '-socks', '-tor', '-debugnet', '-whitelistalwaysrelay', '-prematurewitness', '-walletprematurewitness', '-promiscuousmempoolflags', '-blockminsize', '-dbcrashratio', '-forcecompactdb'])
+SET_DOC_OPTIONAL = set(['-rpcssl', '-benchmark', '-h', '-help', '-socks', '-tor', '-debugnet', '-whitelistalwaysrelay', '-prematurewitness', '-walletprematurewitness', '-promiscuousmempoolflags', '-blockminsize', '-dbcrashratio', '-forcecompactdb', '-usehd'])
def main():
used = check_output(CMD_GREP_ARGS, shell=True)
diff --git a/contrib/devtools/check-rpc-mappings.py b/contrib/devtools/check-rpc-mappings.py
new file mode 100755
index 0000000000..7e96852c5c
--- /dev/null
+++ b/contrib/devtools/check-rpc-mappings.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Check RPC argument consistency."""
+
+from collections import defaultdict
+import os
+import re
+import sys
+
+# Source files (relative to root) to scan for dispatch tables
+SOURCES = [
+ "src/rpc/server.cpp",
+ "src/rpc/blockchain.cpp",
+ "src/rpc/mining.cpp",
+ "src/rpc/misc.cpp",
+ "src/rpc/net.cpp",
+ "src/rpc/rawtransaction.cpp",
+ "src/wallet/rpcwallet.cpp",
+]
+# Source file (relative to root) containing conversion mapping
+SOURCE_CLIENT = 'src/rpc/client.cpp'
+# Argument names that should be ignored in consistency checks
+IGNORE_DUMMY_ARGS = {'dummy', 'arg0', 'arg1', 'arg2', 'arg3', 'arg4', 'arg5', 'arg6', 'arg7', 'arg8', 'arg9'}
+
+class RPCCommand:
+ def __init__(self, name, args):
+ self.name = name
+ self.args = args
+
+class RPCArgument:
+ def __init__(self, names, idx):
+ self.names = names
+ self.idx = idx
+ self.convert = False
+
+def parse_string(s):
+ assert s[0] == '"'
+ assert s[-1] == '"'
+ return s[1:-1]
+
+def process_commands(fname):
+ """Find and parse dispatch table in implementation file `fname`."""
+ cmds = []
+ in_rpcs = False
+ with open(fname, "r") as f:
+ for line in f:
+ line = line.rstrip()
+ if not in_rpcs:
+ if re.match("static const CRPCCommand .*\[\] =", line):
+ in_rpcs = True
+ else:
+ if line.startswith('};'):
+ in_rpcs = False
+ elif '{' in line and '"' in line:
+ m = re.search('{ *("[^"]*"), *("[^"]*"), *&([^,]*), *{([^}]*)} *},', line)
+ assert m, 'No match to table expression: %s' % line
+ name = parse_string(m.group(2))
+ args_str = m.group(4).strip()
+ if args_str:
+ args = [RPCArgument(parse_string(x.strip()).split('|'), idx) for idx, x in enumerate(args_str.split(','))]
+ else:
+ args = []
+ cmds.append(RPCCommand(name, args))
+ assert not in_rpcs and cmds, "Something went wrong with parsing the C++ file: update the regexps"
+ return cmds
+
+def process_mapping(fname):
+ """Find and parse conversion table in implementation file `fname`."""
+ cmds = []
+ in_rpcs = False
+ with open(fname, "r") as f:
+ for line in f:
+ line = line.rstrip()
+ if not in_rpcs:
+ if line == 'static const CRPCConvertParam vRPCConvertParams[] =':
+ in_rpcs = True
+ else:
+ if line.startswith('};'):
+ in_rpcs = False
+ elif '{' in line and '"' in line:
+ m = re.search('{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line)
+ assert m, 'No match to table expression: %s' % line
+ name = parse_string(m.group(1))
+ idx = int(m.group(2))
+ argname = parse_string(m.group(3))
+ cmds.append((name, idx, argname))
+ assert not in_rpcs and cmds
+ return cmds
+
+def main():
+ root = sys.argv[1]
+
+ # Get all commands from dispatch tables
+ cmds = []
+ for fname in SOURCES:
+ cmds += process_commands(os.path.join(root, fname))
+
+ cmds_by_name = {}
+ for cmd in cmds:
+ cmds_by_name[cmd.name] = cmd
+
+ # Get current convert mapping for client
+ client = SOURCE_CLIENT
+ mapping = set(process_mapping(os.path.join(root, client)))
+
+ print('* Checking consistency between dispatch tables and vRPCConvertParams')
+
+ # Check mapping consistency
+ errors = 0
+ for (cmdname, argidx, argname) in mapping:
+ try:
+ rargnames = cmds_by_name[cmdname].args[argidx].names
+ except IndexError:
+ print('ERROR: %s argument %i (named %s in vRPCConvertParams) is not defined in dispatch table' % (cmdname, argidx, argname))
+ errors += 1
+ continue
+ if argname not in rargnames:
+ print('ERROR: %s argument %i is named %s in vRPCConvertParams but %s in dispatch table' % (cmdname, argidx, argname, rargnames), file=sys.stderr)
+ errors += 1
+
+ # Check for conflicts in vRPCConvertParams conversion
+ # All aliases for an argument must either be present in the
+ # conversion table, or not. Anything in between means an oversight
+ # and some aliases won't work.
+ for cmd in cmds:
+ for arg in cmd.args:
+ convert = [((cmd.name, arg.idx, argname) in mapping) for argname in arg.names]
+ if any(convert) != all(convert):
+ print('ERROR: %s argument %s has conflicts in vRPCConvertParams conversion specifier %s' % (cmd.name, arg.names, convert))
+ errors += 1
+ arg.convert = all(convert)
+
+ # Check for conversion difference by argument name.
+ # It is preferable for API consistency that arguments with the same name
+ # have the same conversion, so bin by argument name.
+ all_methods_by_argname = defaultdict(list)
+ converts_by_argname = defaultdict(list)
+ for cmd in cmds:
+ for arg in cmd.args:
+ for argname in arg.names:
+ all_methods_by_argname[argname].append(cmd.name)
+ converts_by_argname[argname].append(arg.convert)
+
+ for argname, convert in converts_by_argname.items():
+ if all(convert) != any(convert):
+ if argname in IGNORE_DUMMY_ARGS:
+ # these are testing or dummy, don't warn for them
+ continue
+ print('WARNING: conversion mismatch for argument named %s (%s)' %
+ (argname, list(zip(all_methods_by_argname[argname], converts_by_argname[argname]))))
+
+ sys.exit(errors > 0)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/devtools/gen-manpages.sh b/contrib/devtools/gen-manpages.sh
index 967717e1e0..925d6a6252 100755
--- a/contrib/devtools/gen-manpages.sh
+++ b/contrib/devtools/gen-manpages.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
TOPDIR=${TOPDIR:-$(git rev-parse --show-toplevel)}
SRCDIR=${SRCDIR:-$TOPDIR/src}
diff --git a/contrib/devtools/git-subtree-check.sh b/contrib/devtools/git-subtree-check.sh
index 2384d66cad..184951715e 100755
--- a/contrib/devtools/git-subtree-check.sh
+++ b/contrib/devtools/git-subtree-check.sh
@@ -18,7 +18,7 @@ find_latest_squash()
sub=
git log --grep="^git-subtree-dir: $dir/*\$" \
--pretty=format:'START %H%n%s%n%n%b%nEND%n' "$COMMIT" |
- while read a b junk; do
+ while read a b _; do
case "$a" in
START) sq="$b" ;;
git-subtree-mainline:) main="$b" ;;
@@ -41,21 +41,17 @@ find_latest_squash()
done
}
+# find latest subtree update
latest_squash="$(find_latest_squash "$DIR")"
if [ -z "$latest_squash" ]; then
echo "ERROR: $DIR is not a subtree" >&2
exit 2
fi
-
set $latest_squash
old=$1
rev=$2
-if [ "d$(git cat-file -t $rev 2>/dev/null)" != dcommit ]; then
- echo "ERROR: subtree commit $rev unavailable. Fetch/update the subtree repository" >&2
- exit 2
-fi
-tree_subtree=$(git show -s --format="%T" $rev)
-echo "$DIR in $COMMIT was last updated to upstream commit $rev (tree $tree_subtree)"
+
+# get the tree in the current commit
tree_actual=$(git ls-tree -d "$COMMIT" "$DIR" | head -n 1)
if [ -z "$tree_actual" ]; then
echo "FAIL: subtree directory $DIR not found in $COMMIT" >&2
@@ -69,9 +65,30 @@ if [ "d$tree_actual_type" != "dtree" ]; then
echo "FAIL: subtree directory $DIR is not a tree in $COMMIT" >&2
exit 1
fi
+
+# get the tree at the time of the last subtree update
+tree_commit=$(git show -s --format="%T" $old)
+echo "$DIR in $COMMIT was last updated in commit $old (tree $tree_commit)"
+
+# ... and compare the actual tree with it
+if [ "$tree_actual_tree" != "$tree_commit" ]; then
+ git diff $tree_commit $tree_actual_tree >&2
+ echo "FAIL: subtree directory was touched without subtree merge" >&2
+ exit 1
+fi
+
+# get the tree in the subtree commit referred to
+if [ "d$(git cat-file -t $rev 2>/dev/null)" != dcommit ]; then
+ echo "subtree commit $rev unavailable: cannot compare" >&2
+ exit
+fi
+tree_subtree=$(git show -s --format="%T" $rev)
+echo "$DIR in $COMMIT was last updated to upstream commit $rev (tree $tree_subtree)"
+
+# ... and compare the actual tree with it
if [ "$tree_actual_tree" != "$tree_subtree" ]; then
- git diff-tree $tree_actual_tree $tree_subtree >&2
- echo "FAIL: subtree directory tree doesn't match subtree commit tree" >&2
+ echo "FAIL: subtree update commit differs from upstream tree!" >&2
exit 1
fi
+
echo "GOOD"
diff --git a/contrib/devtools/lint-all.sh b/contrib/devtools/lint-all.sh
new file mode 100755
index 0000000000..b6d86959c6
--- /dev/null
+++ b/contrib/devtools/lint-all.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (c) 2017 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 runs all contrib/devtools/lint-*.sh files, and fails if any exit
+# with a non-zero status code.
+
+set -u
+
+SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
+LINTALL=$(basename "${BASH_SOURCE[0]}")
+
+for f in "${SCRIPTDIR}"/lint-*.sh; do
+ if [ "$(basename "$f")" != "$LINTALL" ]; then
+ if ! "$f"; then
+ echo "^---- failure generated from $f"
+ exit 1
+ fi
+ fi
+done
diff --git a/contrib/devtools/lint-python.sh b/contrib/devtools/lint-python.sh
new file mode 100755
index 0000000000..9303fcc8ef
--- /dev/null
+++ b/contrib/devtools/lint-python.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Copyright (c) 2017 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Check for specified flake8 warnings in python files.
+
+# F401: module imported but unused
+flake8 --ignore=B,C,E,F,I,N,W --select=F401 .
diff --git a/contrib/devtools/lint-whitespace.sh b/contrib/devtools/lint-whitespace.sh
new file mode 100755
index 0000000000..989923f31a
--- /dev/null
+++ b/contrib/devtools/lint-whitespace.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+#
+# Copyright (c) 2017 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Check for new lines in diff that introduce trailing whitespace.
+
+# We can't run this check unless we know the commit range for the PR.
+if [ -z "${TRAVIS_COMMIT_RANGE}" ]; then
+ echo "Cannot run lint-whitespace.sh without commit range. To run locally, use:"
+ echo "TRAVIS_COMMIT_RANGE='<commit range>' .lint-whitespace.sh"
+ echo "For example:"
+ echo "TRAVIS_COMMIT_RANGE='47ba2c3...ee50c9e' .lint-whitespace.sh"
+ exit 1
+fi
+
+showdiff() {
+ if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/"; then
+ echo "Failed to get a diff"
+ exit 1
+ fi
+}
+
+showcodediff() {
+ if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/"; then
+ echo "Failed to get a diff"
+ exit 1
+ fi
+}
+
+RET=0
+
+# Check if trailing whitespace was found in the diff.
+if showdiff | grep -E -q '^\+.*\s+$'; then
+ echo "This diff appears to have added new lines with trailing whitespace."
+ echo "The following changes were suspected:"
+ FILENAME=""
+ SEEN=0
+ while read -r line; do
+ if [[ "$line" =~ ^diff ]]; then
+ FILENAME="$line"
+ SEEN=0
+ elif [[ "$line" =~ ^@@ ]]; then
+ LINENUMBER="$line"
+ else
+ if [ "$SEEN" -eq 0 ]; then
+ # The first time a file is seen with trailing whitespace, we print the
+ # filename (preceded by a newline).
+ echo
+ echo "$FILENAME"
+ echo "$LINENUMBER"
+ SEEN=1
+ fi
+ echo "$line"
+ fi
+ done < <(showdiff | grep -E '^(diff --git |@@|\+.*\s+$)')
+ RET=1
+fi
+
+# Check if tab characters were found in the diff.
+if showcodediff | grep -P -q '^\+.*\t'; then
+ echo "This diff appears to have added new lines with tab characters instead of spaces."
+ echo "The following changes were suspected:"
+ FILENAME=""
+ SEEN=0
+ while read -r line; do
+ if [[ "$line" =~ ^diff ]]; then
+ FILENAME="$line"
+ SEEN=0
+ elif [[ "$line" =~ ^@@ ]]; then
+ LINENUMBER="$line"
+ else
+ if [ "$SEEN" -eq 0 ]; then
+ # The first time a file is seen with a tab character, we print the
+ # filename (preceded by a newline).
+ echo
+ echo "$FILENAME"
+ echo "$LINENUMBER"
+ SEEN=1
+ fi
+ echo "$line"
+ fi
+ done < <(showcodediff | grep -P '^(diff --git |@@|\+.*\t)')
+ RET=1
+fi
+
+exit $RET
diff --git a/contrib/gitian-build.sh b/contrib/gitian-build.sh
index d94c7f4f8e..631fba9089 100755
--- a/contrib/gitian-build.sh
+++ b/contrib/gitian-build.sh
@@ -6,7 +6,6 @@
sign=false
verify=false
build=false
-setupenv=false
# Systems to build
linux=true
@@ -106,7 +105,7 @@ while :; do
fi
shift
else
- echo 'Error: "--os" requires an argument containing an l (for linux), w (for windows), or x (for Mac OSX)\n'
+ echo 'Error: "--os" requires an argument containing an l (for linux), w (for windows), or x (for Mac OSX)'
exit 1
fi
;;
@@ -179,8 +178,6 @@ done
if [[ $lxc = true ]]
then
export USE_LXC=1
- export LXC_BRIDGE=lxcbr0
- sudo ifconfig lxcbr0 up 10.0.2.2
fi
# Check for OSX SDK
@@ -191,7 +188,7 @@ then
fi
# Get signer
-if [[ -n"$1" ]]
+if [[ -n "$1" ]]
then
SIGNER=$1
shift
diff --git a/contrib/gitian-descriptors/README.md b/contrib/gitian-descriptors/README.md
deleted file mode 100644
index d9dbfd3cb3..0000000000
--- a/contrib/gitian-descriptors/README.md
+++ /dev/null
@@ -1,65 +0,0 @@
-### Gavin's notes on getting Gitian builds up and running using KVM
-
-These instructions distilled from
-[https://help.ubuntu.com/community/KVM/Installation](https://help.ubuntu.com/community/KVM/Installation).
-
-You need the right hardware: you need a 64-bit-capable CPU with hardware virtualization support (Intel VT-x or AMD-V). Not all modern CPUs support hardware virtualization.
-
-You probably need to enable hardware virtualization in your machine's BIOS.
-
-You need to be running a recent version of 64-bit-Ubuntu, and you need to install several prerequisites:
-
- sudo apt-get install ruby apache2 git apt-cacher-ng python-vm-builder qemu-kvm
-
-Sanity checks:
-
- sudo service apt-cacher-ng status # Should return apt-cacher-ng is running
- ls -l /dev/kvm # Should show a /dev/kvm device
-
-
-Once you've got the right hardware and software:
-
- git clone git://github.com/bitcoin/bitcoin.git
- git clone git://github.com/devrandom/gitian-builder.git
- mkdir gitian-builder/inputs
- cd gitian-builder/inputs
-
- # Create base images
- cd gitian-builder
- bin/make-base-vm --suite trusty --arch amd64
- cd ..
-
- # Get inputs (see doc/release-process.md for exact inputs needed and where to get them)
- ...
-
- # For further build instructions see doc/release-process.md
- ...
-
----------------------
-
-`gitian-builder` now also supports building using LXC. See
-[help.ubuntu.com](https://help.ubuntu.com/14.04/serverguide/lxc.html)
-for how to get LXC up and running under Ubuntu.
-
-If your main machine is a 64-bit Mac or PC with a few gigabytes of memory
-and at least 10 gigabytes of free disk space, you can `gitian-build` using
-LXC running inside a virtual machine.
-
-Here's a description of Gavin's setup on OSX 10.6:
-
-1. Download and install VirtualBox from [https://www.virtualbox.org/](https://www.virtualbox.org/)
-
-2. Download the 64-bit Ubuntu Desktop 12.04 LTS .iso CD image from
- [http://www.ubuntu.com/](http://www.ubuntu.com/)
-
-3. Run VirtualBox and create a new virtual machine, using the Ubuntu .iso (see the [VirtualBox documentation](https://www.virtualbox.org/wiki/Documentation) for details). Create it with at least 2 gigabytes of memory and a disk that is at least 20 gigabytes big.
-
-4. Inside the running Ubuntu desktop, install:
-
- sudo apt-get install debootstrap lxc ruby apache2 git apt-cacher-ng python-vm-builder
-
-5. Still inside Ubuntu, tell gitian-builder to use LXC, then follow the "Once you've got the right hardware and software" instructions above:
-
- export USE_LXC=1
- git clone git://github.com/bitcoin/bitcoin.git
- ... etc
diff --git a/contrib/gitian-keys/sjors-key.pgp b/contrib/gitian-keys/sjors-key.pgp
new file mode 100644
index 0000000000..2b5acc82aa
--- /dev/null
+++ b/contrib/gitian-keys/sjors-key.pgp
@@ -0,0 +1,76 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBFWSwMoBEADG31O8+ex+xpgzVKQgF4iVRE5uBPT0+GM6FnwqIIhXVKiBLQh8
+YDhhgk6joh+vsLrFzKZ9kXwoiHN8y/AiNCQ0xjAUdpznD5xvHAaGIAlT/sodRNT+
+869WgT9G1uiVp0P4ucEeilmhCn9o51LqkS3roXkj0ec52b1pslUl2WKdu1ZD+Bj4
+3/oVZm7mmjkDwl0RHJQmqlK0bunq0jlVlgH5sdQfmLbCZaq3LhVPf73zt5qHH+J6
+ZbU7A4cqm2eN5SyH+Nno+cq3+vXmvVI+x/jPe/dPDCXaGWf5fWI/Lbk/mMP7JAl1
+6X44CN+hZHUnNuzeZt2/ROWZ0s0JJcjQkSe9noUQedjBAHX82s886vsFzOHvDtul
+EuV/XAjUlkhMbhZkZaIq9ucqHmUBI4+OcFEIbbKc9TrKtJe+CYuWTNlomVk/iFr8
+zSm/S64NiqKi/BeQGgcsDZIaJDYfDP83esOOaaxFswHnJNtHnU1PwntrJtXft0dK
+ydtlQZ6r96SYxLDTeGfC2SNk0zbnKAGvjj04vzQeN+JSRZ75tNKmgdbJdNL8wvPh
+879TpCwMhNDvSRG+YqCe6whaJV76a+Doxg48HCJYaj6bnRn41/QGJEyL31I8l/7S
+YsLLmAEbqwG7erYi7WZS3cRrGJI8RwohGMZf7yraqoaOgMKmtE/Sq0tLtwARAQAB
+tCNTam9ycyBQcm92b29zdCA8c2pvcnNAc3Byb3Zvb3N0Lm5sPokCQAQTAQoAKgIb
+AwUJB4YfgAULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAUCWWXcoAIZAQAKCRBX/5vb
+zDAQCeYJD/47XDMfEMg4g4spo7k92XsNkvjlAhWvvxd+kxow/V8c64WQXody32FZ
+HRSmK8dVjf9mIJMKkX4lpKpim7cQxsdTcorcdu+yk4TK+Wah61vsMhbSSllfHs1U
++q8jYMGnXTD+CY0aeTMrTfJcR2yN98jmNSWIL1qWmJ51RSTL6BQKb6eYtR7pWRkW
+uMR6oFC09Db4fiKa4zhH81+/t0g+6pMY391gSluaS+OfNqGORCo+/IdG5IDzh5Vp
+f19qXjd5oMsZQf6/P4b4XUktgl8RVRcNzdYGoXpcd8LpeHtEOh5I93ODmCwqd67b
+YDlhDNN7iGhPndPEF6P4CNO/rXLPCZyMhRyt1dflu0KPCr+0AgR31cdhH/p7eCyj
+FTE9gUgUHOG9OHdRoVXrwHYXwAiDBr2pp2giLpBsAwa4d2hXNDJ6wfMMCSOXKQlS
+lHq06y/v/049DammkqW0XnEsU4qvsdteZ0jQu7Ob3LyGoytBIj8fn1OioT21W7wc
+ns3/Tt4cQsn2ICBYB4PzqwkvGUp7fDwwHYw7rq6kvCEVDUDWMtVgQ8kjsh2OoU75
+eeteM1Q1fV06Wfn2Qct9bn0NKRGrA8mm3lrCWYCeGqJeBvC6kna1QgV53vYRLJod
+w3Ql4+M9tUIi9uiGLvVaGZWO9wU1EwL+EAO+6D85h6QiJN7H8gcwUokCPQQTAQoA
+JwUCVZLAygIbAwUJB4YfgAULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRBX/5vb
+zDAQCauuD/9IDWhf/fTseA1Rt5i4gwK+8dCQjTlRS2cZtGc2aMX8w5XruDWnna1P
+Mj/aVUncDrprRx9rxgEqIDyPheuJ6r7v6D8GjrpAjcG/BPNFtPaxQccbZbAYdzoj
+Rrs+ttVIqS+wO7qLmQkKA4oGRMmgYh3VX8EBZNcvxaGCcJx0PfoqS8cPXTnCRHcg
+Wx6kaFyuWtrTX+kCpDraB1KGtxedR4rzuOtUOLoqFOOfsQuOxPlKNNr9Zjc8x2o4
+5TtwbuoEog8FIEttY6NOywpsSsvYvNB4gq1fxO49H0pQopmJlOMatMH6IRT7BJJZ
+cOoHOh4X/zItOJZtuCOT4u+Y2XOuyLcW83X5ymIR3ZCxedsLzjyiCWm61/znJVON
+Ws8I+gShbvauahBCB9rOHqwM0QioJMc36hUPB21KghQS8RJpGwmtk1WhFFMtAsSJ
+w+wRfy2d6u+lSGdlA+2hEyKVm/DNQMDCQVFx3lQ6YBwAwkSiLMylrPKvs56fUjRr
+74qoPyDxuRMC+q+TThHsy5O9r31G+Dc3+H5k4iTk354Jshjltx/k2O732e9Vxyar
+/U5P7UZqHHuJKXDihUFrcJZq+gk8sGEWzGG/wocce7ezrTnHqR8YA04BTA4PXQqZ
+4N42f422YYGIH/3Nm6drQkbigekLw6wx+NrxtTsYg4eCtSsaUd/RjLQhU2pvcnMg
+UHJvdm9vc3QgPHNqb3JzQGZyZWVkb20ubmw+iQI9BBMBCgAnBQJZZdyfAhsDBQkH
+hh+ABQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEFf/m9vMMBAJEsIQAK4ihgRB
+05QqETpWNeV/XSGBHQINuwwEDz/k8dAJ5Uo6OoSpDULa16fs/EgAV46wTSxfWuci
+n2Fc1AWLeLDWOax/NlycL00VDHEwT2PCjcc5uMuwR4RUTciKyByT1u7BFToZ6PyL
+mbU6u6whcQejl6Ci2kw0Mu4n4bKTS7OL4/w/EbdfMSpRi8wWmTPMB/aMjtS2Mxi/
+N+yQhJ9pReHADeCBoAjq0cUy+QbzvBwDCK4XWRzF7kiFuA7UW2r7/dX6l31mPfi/
+GLA5+ftPxJ6EH8cxToF70OWiSfhOTleaqZaHUOG0V7wV2lr/bwAYzpVlxeZSCIta
+lAA9ZLzUD2hiHYcei6kc/YjIhmlml7O0FK1eBk7+bt5wr0nvWt4Lbha4y5LxBX8C
+d7InvB3xUYHz+S5Ul4vp0Rzx97MBL4oX2ltBEDpc1CcOgzv4dcWMG9bbh9/SaI/G
+RehAzwkbpVUl9AEUNKO0dNlZUdu8CkehHdPdz5sJyS/9zE0A7yIECDFP9Nrht0nK
+MahBijm4K+jOiLOZ2xyfOX1pVWLqIXGQHKjfcD3oI3qvGrQYtxB5Dffb9ACFMpZO
+z3jM8h2UAa2/KqA4MZiZG9N6uWHKkIAMMuXWs1s439WePvbQ+5aw/qPUAMyqA3XZ
+dkfn8QWaJPR4nRM+McYBYuS4fKK9HRJWQgcQuQINBFWSwMoBEACzmkabZ8oHWJUE
+beU7rJF/TMbwV1IFtFxJ/QlY8rE4VnHekPMvkLi/gjx3WY5nmMe+d4JYoK/uPNdt
+y5u0QYgH2MB/jebk4gYXCAHIPpU38h9UgHRb6qV8OaqHhmoXvKwyz+1QPzyJpmgg
+oCUN+OAroNjl7zhunE7w7EEddFQftfPoGKEUnTjv84QOCuAb46JsYyiNAc3h6okq
+74hY7PKCv8IRGclMPjemhBT2LEenn1t4yi7a8W/hjIe44PmQiqQEXR17keqcP/ls
+EH9xSST1v/70ieiPqb6zbHGWzjQxqpFUJxRU6OluBCy5pHVd8wfFGYrrbTpoxaUC
+jyA2SLr1oZZ9gaGprt6X7FC5gpE5LV9essq3O5wwvoPbyMe1F5uFaxIPhlt55oEu
+rwVWecFJ8tSjniF/WSkTcILrOmiQZ4mylXfOP9Wk38seZReCs799KEfKFlXHk89a
+Sj3ZvaJQxwVCnvsAsbVKmmHZ5wPt+G2KfhOkkv2A1I/UyeTT7aXvt2vxDqGuG0su
+Eo6QknM/2Sr5Uv7BwBeSIQ6llH5ZnqKz34+HjriP8YPWzvsC959GXsxS01dCSvUM
+92j5PvTZzf5dt1CWHMeufAY5XIH+nftkRniuScRhJ7xK3tJ7wngg7UvdeZwJWqmK
+lJ7GI38V8HIMnd2x28yiGpj1ue6T+QARAQABiQIlBBgBCgAPBQJVksDKAhsMBQkH
+hh+AAAoJEFf/m9vMMBAJjeIP/1UBCi6gSXzpGJBLD2u4PcZJjXBJAImZdf1aCqfS
+YZBCaA65UrM3uaVa7h8MGAJc9kDjpqHurjDmG3YWf33KvHWYmReQvX43pZmfF12s
+X7FZgcCfgZJKKj+ri6oHQonZzUMrecEcAJLLaQoD3Du3iZpETiyRLL7sJ1lZSaCJ
+gYKnN4WV5GypvdFvb8vSUBST2h0D6AewGKMNh8ruRlkIxI+YSlywgYIH+O0qNKqW
+wBlZc/5f+JZ3hu+cjx/+Zn+w+saIb6SgySg0UzN35b2WM2YzrfQep4ah3NIxuC7e
+qzmfV6GnRtuUrBLVJ8qyjif1JSM9tZfinnmAB4/U5Qfc+YYViIXMTljmHWvbokas
+tTBfVAw74yWnkv4ZuXf5SkTmGwEMJUOat0TSr085Ck5y394bRepdI1Y+1cdqpwMQ
+QmkKyvcBlREQ7Xk1UnDDR3o/2ieVuGGHRp8jmoWBWGq4Cm43fYOlVe+PcaX0tDns
+Tmmh2uwEU/TXe5qGil51OlSM7qhAMqhWUIYphSOcdvApNXuiWMfnTdjsNygE4HVh
+Jq4efJ/nlx5N+PNAK2GpzeUJQGyxiVsXybq+h8UlvytBsdz1X6ZYzBv1yYwANThU
+rMB1s4tMaEugX0aNByLcsxuS4ixd2qzwkYVz25Aeko/U1v2/j2cIRtrTNgja3BKE
+N5Ug
+=80Es
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/contrib/gitian-keys/willyko-key.pgp b/contrib/gitian-keys/willyko-key.pgp
new file mode 100644
index 0000000000..a3b58ad994
--- /dev/null
+++ b/contrib/gitian-keys/willyko-key.pgp
@@ -0,0 +1,85 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1
+
+mQINBFgs/RoBEADFxycJTUvwqzBZZ0aBZXbmr8Ppd3EPrgBRd47k7uwanf7UFmvY
+Xt4gMEI+EdV0GuoQ0SeoAmQqc5Fxu3AQe2XFbiF+ZNNYT3+V/5GAzWsAH22ncQr0
+AuK95pPi+PZ+M2h669cq/RzFUXZDew0NobR2oBS5h6g3rgmmejVLRqnUpWkkSrqi
+aNgD2GSn8g820wM6LpdxcjTqmMpHHT5owAbv0UP3IcdtpBaS5McoUXK+OAdKK/Zw
+JQ0J1kx4vIyNwuPD3klziGQw8Izb/gFpWg8XaJmMhD5BxNuXJC58Bj9/sFTc0GDQ
+VKMFpYpNi8a6hLPFb4hMjYF77awoz57HtyOOsS03KO/57QE1htx+2NeDm4XkZSBk
++wrU3zgbtmOBcfzEHS/HrROksYDi+Qw3HZL98nfDEWNfsDzfhMZ9wHdM3NsR2xk6
+oNtX0CprS1n2Xr2AY9X1oNgiZCJaSftU67j3lr+9gHOH61ktxt3cUCDodUFjkpKn
+r1CQ2LB63AoUbwGMAeozdXZWzbXJAJbcH9G77zEi9rW0WA2yMSxTXHlpE9MS0UcE
+BVkIMv2b9iQzlhiS8jh8AiKFO1PuT26Cw52N/lSPhA81zw79pZfSYwKFICGHYfvw
+ozZeN9Q+PPl5tqi/3SExxlZKe8EmaveTrUfKMBS4lQO2gWe0bCFgLOIzIwARAQAB
+tB1XaWxseSBLbyA8d2lsbHlrQHN5c2NvaW4ub3JnPokCOAQTAQIAIgUCWCz9GgIb
+AwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQjjqPMkfby7+0wA//cX7Tc3Nz
+19ApwSbGfC8pJA/nSybcVivroJRftpzeOmYrVM084T9REvYwugl89djvxn6m96iQ
+kqoUGWhBVBtDReVCL7z53G42lHjemaFcxBhIazKxO0qvcc/UXUVOs2OdUbzObDFL
+dHO5xBVqEnW3sq+r4blsXR8U79B9IIri4+2hy4OoEjYv9DzBaaoaqU+F3mudXbmo
+R+hsWc+mklV++TX/kuw6EWT8tusFjXrfqqKcKPRPhbn48OSGWsEPc7yELf7pYFR8
+uDU40faJqkvQ83h5WMTDAhLxd/918ZitqBhjSP+7Humf2YhSto7YmtEWlbeAW+qy
+TcBYkK6SJh8Do3xZd/prFBKEu395n5VQKuLjXaOjqMc1oDHQyPJJjXSN5thLHvan
+z7nNLt2QZO/kxXITDdbWlktVe/WSoive7TuY4dGuX4Si2z9wyhFYxtZDsqE0qmqN
+jIDAZ7u8Qq/LGqpdjOmYr2fEwHe1yVIS+BtVGvtShkX+J+QPb8qBl1d7Ii5i5Afl
+GJoLLIUFkPcIRTYPZpppGSuqfyWdNnaasbLH44lxJisSMMw+fxZabt2bykYN/ZXa
+RP/ItDj81vklg+n6r4f/nZTF1r0UUy4LbSbBY15B4Xm0Tdvh1PMfj/w2q10l7bZB
+XLi9Z/QPaW7TyzaBuLkVckbVFn2nYnXfzHG5Ag0EWCz9GgEQALCgTibFnw+Q3PEL
+G5/peQcQqHxrPAB37HV39B1DedGhVUa6aGSUaLoNMyUjUX1HWN3mWFKTYVB4CH5Y
+xjaXUwxdwCZgBNe4TDglKFPuc+frlSTZxDVE9/fjArmrUP6TPU447ujspyngGLa5
+et5Uig/LxIX/+Mm0ZiYJxb1rMJwK998U1Ev1aHxgNjwTI2ehcKu8CAGOyflzh6a2
+iTBUmLfnQMv5248P2d4P8WDiPq61CWTYTMCFqHqkYKy7h9BYIuMajw3KsgOUNfL2
+1e9Ue8yv5UchZ+GDlBjidIkoK1nd2uJ0kPJkafLGWbcliJfvXxKliZnSbz1Cd4A0
+HDKKCwiuwSLy2aYbs7IRtAOyDER4+fjBcqtf0QTIvoAdNZ9gL64DKVaB58vuSixj
+K1i83XbTOt3q821HxxBrX9u6HP2E5kFdxT2KHDbisAWNP0rFnHVpjugehKFfZb6q
+jbDt3nQL5uCQ8gTNCd4fsoSK6KhCDjamDXlKmaGlxqwOV4W8ZwihoeGt690h7NIH
+h4eiSmMOej3or32lcDETEwrjA2PxvcFsikFc56hRkTaSyyBEH2xhkRrjXMqiQfH0
+j7iOY2PWpFEuu2HVzqe5dBXzn9sMIwxeNCxR/P+xHMqPUlgD1SXEYCNLvvzD6p0+
+kqSe7PiJoEIv351T3hnBhQ6rK0ChABEBAAGJAh8EGAECAAkFAlgs/RoCGwwACgkQ
+jjqPMkfby7/mQA//YsAOdDBl0GscB1PBNXi8VMlI7yG9cqiGrYnZX7h4wUoGEbPI
+jap/PixIsxBCf1BqBRDJdFyvzH9amLlcaVNdCyh6Yt1Pi8kassmz/kbIYgpbFkIL
+ES9N24N7BZ94P77OQy5wic+B4WqJnVrtKr9JBalgBSOMqtccYCma5Ew00mqp+FXM
+suDyBk2HXyl+u6/rRmqZ+BoU8iRpus9F80LFKGEsAgjLjKv68KmApzjunzsBotKk
+g9AsBk4ygbp+nECAtsxpbLMo4hPr4qWm7G4mU5g4xOK2chpAPeqyf0857RWgsXaO
+kjrUu/M5Hme2eIlXwBF14ac4QPnY1rlAIaulvXzmQnMYQFZiw9vaTOdqBFHjkh7T
+XYRAr589Woo25PfMJCbC+Rop6ku6sCFMorbBwojyRhFJnk9xsy5kP5D9IhkPAKu/
+/ABlei0xPOl/gCUUJP7aIikZgS5lAk1TSe/R+yV6ExNwudtLw1G0K2/sY3B4Xo3X
+Q1lTAQPlnAIeK/vlbttLZNIBWquw4cPAkPpIyjmE1dd6jGQdUyZE22uPBx+gpq1w
+AacmVLwvPMe1De0ilJOzj6KpXWBCwt0DWXWztovpBVcAC+qbTrZF9H5dllpqyzKt
+OvxzGssjrX4rDkOx7MyVa2tnXmeCuSN/RvlOUwPvf5zYM8Wh9g7fc6jcDQu5Ag0E
+WfkOfAEQAMNkzAQqSenpXtHsnuCqM1oMMF2kRzny/Jqh3q3BxZ8MHLDhoRRaTENu
+lA4APRXMNM/wlZJUSLX8wWBhufnsPtMf6OOVMZ4AVbXHjUgyJ7lO1zHdj0u6PpYP
+9gmHthIz7FF+cxHj4ziC4CmtRctrn+/U4MwYtNPhxkTnS26oOZes/HXMYSvQBMgT
+AP27GNOBiJRthjIEITvSvS0YZOxgLtWgGiks/pGUw5wm1rguuQVyZ1/LfXBooYJo
+u/v21AEjpuTg7JlwbqXr2k5LojAGq7AxDyWy21IW0E45Gog38zg/hwNll+hjRbSu
+pipf74WXR6xMMlW6A+XWUvElkicfDx8e9LJUnqWbZ+FL7X4SB54ZHNCvfo/8Ug1V
+2tiY9WbUZL9n5ZQHNlk3J+UK/KDvwey1VzKPFjpQNlfahhnppDGiCey+mERjI+75
+gPbk0ctOAEYXgLJjoonGX+iByAfY0YyJF281CtaK/sXQU+TzLLT15WET+gYGsJdY
+xh1PdPscNdSgYudvbKZoFnqUwEGEfD8dT5bjOphfY5+LvGUR2GuLNZpMidcduTYf
+SWAY/vQHQIJArXu29BKscm3tg6tzXu3l9p/bGIQUQB7obN91y3xD3BLICIPRGhKE
+924wxxCuH1vLKmxWDdAAxKo+rEdLJ/rbZnjWQENEFiJ114fBk2NVABEBAAGJBEQE
+GAEIAA8FAln5DnwCGwIFCQPCZwACKQkQjjqPMkfby7/BXSAEGQEIAAYFAln5DnwA
+CgkQYFGSo/6YSmANoQ//SbcKxkop2zA2HrWS4THcEJQwSJ0KGAN/VB83JQhoWThX
+CWxsFNJjBy7+rsoXd3wQG1/aN42nTuj+eh+R6WJJaqqnMqd52l4Kc1kJA6z4DGsy
+3azCDvyzibM0AkJyMJyYi6HRKjzA4M+xKR1HoT/NdQUP5CBUVfvMblSaOWiw4rja
+IhWcbgbQ+Zam/VaV5l1O90eaD9tL3twSfPLYZ/wkeO63jJKHBpI8fpMql/bLg9WD
+Au3h/lU63NWe5lZO1z/jIdfiTSvg8nu162vcOgmUCWo9spkybjJd0Mx6ZId79rVo
+58lwZ4QoaMgPGoVP67LyLOxJTIXeyG5xr1LxhMPMGbnBhlnMQrboLV9kPEL3raHE
+EEKDTtZimVK3ZxmfyBd6MDmwcL/K73xu/R8be9TgdwD8/BZJSOTkO87qZ82G9T7E
+oY5IHU+qd41/Yjbut7AVtAlCr5Lor31EYvZh3gI/H8uZFddOu37Ij7e9Fw2ywv3A
+wPks89tfOvahkfCOJ29znB+uQYpJ461jjhdkB7EHG4ae07M5rRtkNbIc3dqbnMhz
+VA3JpRJN77xPXV7uITHo1s+b50RvWmfYW91zvipaSZxbMLuGBMhn/1QaM1djLOYN
+JordDBwEr2bi5a063yUbZrk6ddECuyxndDHWDNr+Tqx6o7lmAT48UJ199zA4scbf
+2g/9EiRPGcRovsn1tUdjzfmWDxhrRV6F3rYJB1+i6Mqeg2iHHYxxiNDXcuWYXHQ/
+WPWLk5+lgh0rQbrE7InzEejoM0FIHzLTm0lSQpau50/PT2FiH6sOEEDyT2IhBtXX
+eOnKAi1IfGNMzEaPEY8PXH78dEGv0iXIgy4l8Bc57q09Z9R/OUi+Yb1p+S5F/aOi
+7Jd53GGE1bfBIlsMos092XoiMdvKmAczyCUIempKHUBPoqfJge77qk7zJKkyM3Dk
+VX0lXLdhj0PfslFrNf2uRF4uZkmfUV7peeD023c0/SVp3ILUAVds52yawi6Exv4a
+bbvhIw72fc31frCRBqc9HVsBraoozzE9bksG1MdNI3GgKxecOu9lldedlIqi4lO4
+7kTVDLEmcsQO+sSxkXQz2sMSD01CQndpPuhFNlqvVnfK+Kv8pSG37VzSSQz1nt5K
+w/fJBo4T/ztR7D9RzbSDxBP8Jjaa+UYabjab5HcE0JI4CpgmzIOB7qPVbYCn+LNX
+c8Xw5/9iTw+ayawl7PCGRfd14/OPRzI8vS0I9bF8AG84XM46yxAtYieH/9RI3b6/
+GiQYDkBNi6Kb1LfSzx8oKAkbMgiy4y3vWxLQnE34bAoXjGiYdAMliOsyGcvmnObD
+GmSTIlIqunq60CyhaUSIkl2VRhjzz0igfS9751XEvnjeXDc=
+=PVBi
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/contrib/init/bitcoind.openrc b/contrib/init/bitcoind.openrc
index 50377c0995..86222295db 100644
--- a/contrib/init/bitcoind.openrc
+++ b/contrib/init/bitcoind.openrc
@@ -1,4 +1,4 @@
-#!/sbin/runscript
+#!/sbin/openrc-run
# backward compatibility for existing gentoo layout
#
diff --git a/contrib/init/bitcoind.service b/contrib/init/bitcoind.service
index 9132957c38..ee113d7615 100644
--- a/contrib/init/bitcoind.service
+++ b/contrib/init/bitcoind.service
@@ -1,22 +1,25 @@
+# It is not recommended to modify this file in-place, because it will
+# be overwritten during package upgrades. If you want to add further
+# options or overwrite existing ones then use
+# $ systemctl edit bitcoind.service
+# See "man systemd.service" for details.
+
+# Note that almost all daemon options could be specified in
+# /etc/bitcoin/bitcoin.conf
+
[Unit]
-Description=Bitcoin's distributed currency daemon
+Description=Bitcoin daemon
After=network.target
[Service]
+ExecStart=/usr/bin/bitcoind -daemon -conf=/etc/bitcoin/bitcoin.conf -pid=/run/bitcoind/bitcoind.pid
+# Creates /run/bitcoind owned by bitcoin
+RuntimeDirectory=bitcoind
User=bitcoin
-Group=bitcoin
-
Type=forking
-PIDFile=/var/lib/bitcoind/bitcoind.pid
-ExecStart=/usr/bin/bitcoind -daemon -pid=/var/lib/bitcoind/bitcoind.pid \
--conf=/etc/bitcoin/bitcoin.conf -datadir=/var/lib/bitcoind -disablewallet
-
-Restart=always
+PIDFile=/run/bitcoind/bitcoind.pid
+Restart=on-failure
PrivateTmp=true
-TimeoutStopSec=60s
-TimeoutStartSec=2s
-StartLimitInterval=120s
-StartLimitBurst=5
[Install]
WantedBy=multi-user.target
diff --git a/contrib/init/org.bitcoin.bitcoind.plist b/contrib/init/org.bitcoin.bitcoind.plist
index e94cd4466d..95b5342f1e 100644
--- a/contrib/init/org.bitcoin.bitcoind.plist
+++ b/contrib/init/org.bitcoin.bitcoind.plist
@@ -7,7 +7,6 @@
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/bitcoind</string>
- <string>-daemon</string>
</array>
<key>RunAtLoad</key>
<true/>
diff --git a/contrib/install_db4.sh b/contrib/install_db4.sh
new file mode 100755
index 0000000000..1d33e8d3ba
--- /dev/null
+++ b/contrib/install_db4.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+# Install libdb4.8 (Berkeley DB).
+
+set -e
+
+if [ -z "${1}" ]; then
+ echo "Usage: ./install_db4.sh <base-dir> [<extra-bdb-configure-flag> ...]"
+ echo
+ echo "Must specify a single argument: the directory in which db5 will be built."
+ echo "This is probably \`pwd\` if you're at the root of the bitcoin repository."
+ exit 1
+fi
+
+expand_path() {
+ echo "$(cd "${1}" && pwd -P)"
+}
+
+BDB_PREFIX="$(expand_path ${1})/db4"; shift;
+BDB_EXTRA_CONFIGURE_FLAGS="${@}"
+BDB_VERSION='db-4.8.30.NC'
+BDB_HASH='12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef'
+BDB_URL="https://download.oracle.com/berkeley-db/${BDB_VERSION}.tar.gz"
+
+check_exists() {
+ which "$1" >/dev/null 2>&1
+}
+
+sha256_check() {
+ # Args: <sha256_hash> <filename>
+ #
+ if check_exists sha256sum; then
+ echo "${1} ${2}" | sha256sum -c
+ elif check_exists sha256; then
+ echo "${1} ${2}" | sha256 -c
+ else
+ echo "${1} ${2}" | shasum -a 256 -c
+ fi
+}
+
+http_get() {
+ # Args: <url> <filename> <sha256_hash>
+ #
+ # It's acceptable that we don't require SSL here because we manually verify
+ # content hashes below.
+ #
+ if [ -f "${2}" ]; then
+ echo "File ${2} already exists; not downloading again"
+ elif check_exists curl; then
+ curl --insecure "${1}" -o "${2}"
+ else
+ wget --no-check-certificate "${1}" -O "${2}"
+ fi
+
+ sha256_check "${3}" "${2}"
+}
+
+mkdir -p "${BDB_PREFIX}"
+http_get "${BDB_URL}" "${BDB_VERSION}.tar.gz" "${BDB_HASH}"
+tar -xzvf ${BDB_VERSION}.tar.gz -C "$BDB_PREFIX"
+cd "${BDB_PREFIX}/${BDB_VERSION}/"
+
+# Apply a patch when building on OS X to make the build work with Xcode.
+#
+if [ "$(uname)" = "Darwin" ]; then
+ BDB_OSX_ATOMIC_PATCH_URL='https://raw.githubusercontent.com/narkoleptik/os-x-berkeleydb-patch/0007e2846ae3fc9757849f5277018f4179ad17ef/atomic.patch'
+ BDB_OSX_ATOMIC_PATCH_HASH='ba0e2b4f53e9cb0ec58f60a979b53b8567b4565f0384886196f1fc1ef111d151'
+
+ http_get "${BDB_OSX_ATOMIC_PATCH_URL}" atomic.patch "${BDB_OSX_ATOMIC_PATCH_HASH}"
+ patch -p1 < atomic.patch
+fi
+
+cd build_unix/
+
+"${BDB_PREFIX}/${BDB_VERSION}/dist/configure" \
+ --enable-cxx --disable-shared --with-pic --prefix="${BDB_PREFIX}" \
+ "${BDB_EXTRA_CONFIGURE_FLAGS}"
+
+make install
+
+echo
+echo "db4 build complete."
+echo
+echo 'When compiling bitcoind, run `./configure` in the following way:'
+echo
+echo " export BDB_PREFIX='${BDB_PREFIX}'"
+echo ' ./configure LDFLAGS="-L${BDB_PREFIX}/lib/" CPPFLAGS="-I${BDB_PREFIX}/include/" ...'
diff --git a/contrib/macdeploy/detached-sig-create.sh b/contrib/macdeploy/detached-sig-create.sh
index 7f017bb4f1..3379a4599c 100755
--- a/contrib/macdeploy/detached-sig-create.sh
+++ b/contrib/macdeploy/detached-sig-create.sh
@@ -40,7 +40,7 @@ grep CodeResources < "${TEMPLIST}" | while read i; do
RESOURCE="${TEMPDIR}/${OUTROOT}/${TARGETFILE}"
DIRNAME="`dirname "${RESOURCE}"`"
mkdir -p "${DIRNAME}"
- echo "Adding resource for: "${TARGETFILE}""
+ echo "Adding resource for: \"${TARGETFILE}\""
cp "${i}" "${RESOURCE}"
done
diff --git a/contrib/tidy_datadir.sh b/contrib/tidy_datadir.sh
index 8960f8811d..b845b34e41 100755
--- a/contrib/tidy_datadir.sh
+++ b/contrib/tidy_datadir.sh
@@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
if [ -d "$1" ]; then
- cd "$1"
+ cd "$1" || exit 1
else
echo "Usage: $0 <datadir>" >&2
echo "Removes obsolete Bitcoin database files" >&2
diff --git a/contrib/valgrind.supp b/contrib/valgrind.supp
new file mode 100644
index 0000000000..0f6d993fd2
--- /dev/null
+++ b/contrib/valgrind.supp
@@ -0,0 +1,43 @@
+# Valgrind suppressions file for Bitcoin.
+#
+# Includes known Valgrind warnings in our dependencies that cannot be fixed
+# in-tree.
+#
+# Example use:
+# $ valgrind --suppressions=contrib/valgrind.supp src/test/test_bitcoin
+# $ valgrind --suppressions=contrib/valgrind.supp --leak-check=full \
+# --show-leak-kinds=all src/test/test_bitcoin --log_level=test_suite
+{
+ Suppress libstdc++ warning - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65434
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ fun:malloc
+ obj:*/libstdc++.*
+ fun:call_init.part.0
+ fun:call_init
+ fun:_dl_init
+ obj:*/ld-*.so
+}
+{
+ Suppress libdb warning - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=662917
+ Memcheck:Cond
+ obj:*/libdb_cxx-*.so
+ fun:__log_put
+ obj:*/libdb_cxx-*.so
+ fun:__log_put_record
+}
+{
+ Suppress leveldb warning (leveldb::InitModule()) - https://github.com/google/leveldb/issues/113
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ fun:_Znwm
+ fun:_ZN7leveldbL10InitModuleEv
+}
+{
+ Suppress leveldb warning (leveldb::Env::Default()) - https://github.com/google/leveldb/issues/113
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ fun:_Znwm
+ ...
+ fun:_ZN7leveldbL14InitDefaultEnvEv
+}
diff --git a/contrib/verify-commits/gpg.sh b/contrib/verify-commits/gpg.sh
index b01e2a6d39..8f3e4b8063 100755
--- a/contrib/verify-commits/gpg.sh
+++ b/contrib/verify-commits/gpg.sh
@@ -9,7 +9,7 @@ REVSIG=false
IFS='
'
if [ "$BITCOIN_VERIFY_COMMITS_ALLOW_SHA1" = 1 ]; then
- GPG_RES="$(echo "$INPUT" | gpg --trust-model always "$@" 2>/dev/null)"
+ GPG_RES="$(printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null)"
else
# Note how we've disabled SHA1 with the --weak-digest option, disabling
# signatures - including selfsigs - that use SHA1. While you might think that
@@ -24,7 +24,7 @@ else
case "$LINE" in
"gpg (GnuPG) 1.4.1"*|"gpg (GnuPG) 2.0."*)
echo "Please upgrade to at least gpg 2.1.10 to check for weak signatures" > /dev/stderr
- GPG_RES="$(echo "$INPUT" | gpg --trust-model always "$@" 2>/dev/null)"
+ GPG_RES="$(printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null)"
;;
# We assume if you're running 2.1+, you're probably running 2.1.10+
# gpg will fail otherwise
@@ -32,7 +32,7 @@ else
# gpg will fail otherwise
esac
done
- [ "$GPG_RES" = "" ] && GPG_RES="$(echo "$INPUT" | gpg --trust-model always --weak-digest sha1 "$@" 2>/dev/null)"
+ [ "$GPG_RES" = "" ] && GPG_RES="$(printf '%s\n' "$INPUT" | gpg --trust-model always --weak-digest sha1 "$@" 2>/dev/null)"
fi
for LINE in $(echo "$GPG_RES"); do
case "$LINE" in
@@ -46,14 +46,19 @@ for LINE in $(echo "$GPG_RES"); do
REVSIG=true
GOODREVSIG="[GNUPG:] GOODSIG ${LINE#* * *}"
;;
+ "[GNUPG:] EXPKEYSIG "*)
+ [ "$BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG" != 1 ] && exit 1
+ REVSIG=true
+ GOODREVSIG="[GNUPG:] GOODSIG ${LINE#* * *}"
+ ;;
esac
done
if ! $VALID; then
exit 1
fi
if $VALID && $REVSIG; then
- echo "$INPUT" | gpg --trust-model always "$@" 2>/dev/null | grep "\[GNUPG:\] \(NEWSIG\|SIG_ID\|VALIDSIG\)"
+ printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null | grep "\[GNUPG:\] \(NEWSIG\|SIG_ID\|VALIDSIG\)"
echo "$GOODREVSIG"
else
- echo "$INPUT" | gpg --trust-model always "$@" 2>/dev/null
+ printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null
fi
diff --git a/contrib/verify-commits/verify-commits.sh b/contrib/verify-commits/verify-commits.sh
index 74b7f38375..532b97a438 100755
--- a/contrib/verify-commits/verify-commits.sh
+++ b/contrib/verify-commits/verify-commits.sh
@@ -12,8 +12,6 @@ VERIFIED_ROOT=$(cat "${DIR}/trusted-git-root")
VERIFIED_SHA512_ROOT=$(cat "${DIR}/trusted-sha512-root-commit")
REVSIG_ALLOWED=$(cat "${DIR}/allow-revsig-commits")
-HAVE_FAILED=false
-
HAVE_GNU_SHA512=1
[ ! -x "$(which sha512sum)" ] && HAVE_GNU_SHA512=0
@@ -35,11 +33,12 @@ fi
NO_SHA1=1
PREV_COMMIT=""
+INITIAL_COMMIT="${CURRENT_COMMIT}"
while true; do
if [ "$CURRENT_COMMIT" = $VERIFIED_ROOT ]; then
- echo "There is a valid path from "$CURRENT_COMMIT" to $VERIFIED_ROOT where all commits are signed!"
- exit 0;
+ echo "There is a valid path from \"$INITIAL_COMMIT\" to $VERIFIED_ROOT where all commits are signed!"
+ exit 0
fi
if [ "$CURRENT_COMMIT" = $VERIFIED_SHA512_ROOT ]; then
@@ -95,9 +94,9 @@ while true; do
FILE_HASHES=""
for FILE in $(git ls-tree --full-tree -r --name-only "$CURRENT_COMMIT" | LC_ALL=C sort); do
if [ "$HAVE_GNU_SHA512" = 1 ]; then
- HASH=$(git cat-file blob "$CURRENT_COMMIT":"$FILE" | sha512sum | { read FIRST OTHER; echo $FIRST; } )
+ HASH=$(git cat-file blob "$CURRENT_COMMIT":"$FILE" | sha512sum | { read FIRST _; echo $FIRST; } )
else
- HASH=$(git cat-file blob "$CURRENT_COMMIT":"$FILE" | shasum -a 512 | { read FIRST OTHER; echo $FIRST; } )
+ HASH=$(git cat-file blob "$CURRENT_COMMIT":"$FILE" | shasum -a 512 | { read FIRST _; echo $FIRST; } )
fi
[ "$FILE_HASHES" != "" ] && FILE_HASHES="$FILE_HASHES"'
'
diff --git a/contrib/verifybinaries/verify.sh b/contrib/verifybinaries/verify.sh
index 409f517c9f..e0266bf08a 100755
--- a/contrib/verifybinaries/verify.sh
+++ b/contrib/verifybinaries/verify.sh
@@ -33,7 +33,7 @@ if [ ! -d "$WORKINGDIR" ]; then
mkdir "$WORKINGDIR"
fi
-cd "$WORKINGDIR"
+cd "$WORKINGDIR" || exit 1
#test if a version number has been passed as an argument
if [ -n "$1" ]; then
@@ -76,8 +76,6 @@ if [ -n "$1" ]; then
BASEDIR="$BASEDIR$RCSUBDIR.$RCVERSION/"
fi
fi
-
- SIGNATUREFILE="$BASEDIR$SIGNATUREFILENAME"
else
echo "Error: need to specify a version on the command line"
exit 2
@@ -89,7 +87,7 @@ WGETOUT=$(wget -N "$HOST1$BASEDIR$SIGNATUREFILENAME" 2>&1)
#and then see if wget completed successfully
if [ $? -ne 0 ]; then
echo "Error: couldn't fetch signature file. Have you specified the version number in the following format?"
- echo "[$VERSIONPREFIX]<version>-[$RCVERSIONSTRING[0-9]] (example: "$VERSIONPREFIX"0.10.4-"$RCVERSIONSTRING"1)"
+ echo "[$VERSIONPREFIX]<version>-[$RCVERSIONSTRING[0-9]] (example: ${VERSIONPREFIX}0.10.4-${RCVERSIONSTRING}1)"
echo "wget output:"
echo "$WGETOUT"|sed 's/^/\t/g'
exit 2
diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py
index 5cc19761d4..5efd3d2187 100755
--- a/contrib/zmq/zmq_sub.py
+++ b/contrib/zmq/zmq_sub.py
@@ -8,8 +8,8 @@
Bitcoin should be started with the command line arguments:
bitcoind -testnet -daemon \
- -zmqpubhashblock=tcp://127.0.0.1:28332 \
-zmqpubrawtx=tcp://127.0.0.1:28332 \
+ -zmqpubrawblock=tcp://127.0.0.1:28332 \
-zmqpubhashtx=tcp://127.0.0.1:28332 \
-zmqpubhashblock=tcp://127.0.0.1:28332
diff --git a/contrib/zmq/zmq_sub3.4.py b/contrib/zmq/zmq_sub3.4.py
index bfb7ea9eae..7032d475e8 100755
--- a/contrib/zmq/zmq_sub3.4.py
+++ b/contrib/zmq/zmq_sub3.4.py
@@ -8,8 +8,8 @@
Bitcoin should be started with the command line arguments:
bitcoind -testnet -daemon \
- -zmqpubhashblock=tcp://127.0.0.1:28332 \
-zmqpubrawtx=tcp://127.0.0.1:28332 \
+ -zmqpubrawblock=tcp://127.0.0.1:28332 \
-zmqpubhashtx=tcp://127.0.0.1:28332 \
-zmqpubhashblock=tcp://127.0.0.1:28332
diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk
index 00231d75d5..5f622f8e6e 100644
--- a/depends/packages/libevent.mk
+++ b/depends/packages/libevent.mk
@@ -9,7 +9,7 @@ define $(package)_preprocess_cmds
endef
define $(package)_set_vars
- $(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress
+ $(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress --disable-samples
$(package)_config_opts_release=--disable-debug-mode
$(package)_config_opts_linux=--with-pic
endef
diff --git a/depends/packages/native_ds_store.mk b/depends/packages/native_ds_store.mk
index 49f5829ac1..116fa25d38 100644
--- a/depends/packages/native_ds_store.mk
+++ b/depends/packages/native_ds_store.mk
@@ -1,9 +1,8 @@
package=native_ds_store
-$(package)_version=1.1.0
-$(package)_download_path=https://bitbucket.org/al45tair/ds_store/get
-$(package)_download_file=v$($(package)_version).tar.bz2
-$(package)_file_name=$(package)-$($(package)_version).tar.bz2
-$(package)_sha256_hash=921596764d71d1bbd3297a90ef6d286f718794d667e4f81d91d14053525d64c1
+$(package)_version=1.1.2
+$(package)_download_path=https://github.com/al45tair/ds_store/archive/
+$(package)_file_name=v$($(package)_version).tar.gz
+$(package)_sha256_hash=3b3ecb7bf0a5157f5b6010bc3af7c141fb0ad3527084e63336220d22744bc20c
$(package)_install_libdir=$(build_prefix)/lib/python/dist-packages
$(package)_dependencies=native_biplist
diff --git a/depends/packages/native_mac_alias.mk b/depends/packages/native_mac_alias.mk
index 85a8a402bf..488ec8b59c 100644
--- a/depends/packages/native_mac_alias.mk
+++ b/depends/packages/native_mac_alias.mk
@@ -1,14 +1,13 @@
package=native_mac_alias
-$(package)_version=1.1.0
-$(package)_download_path=https://bitbucket.org/al45tair/mac_alias/get
-$(package)_download_file=v$($(package)_version).tar.bz2
-$(package)_file_name=$(package)-$($(package)_version).tar.bz2
-$(package)_sha256_hash=87ad827e66790028361e43fc754f68ed041a9bdb214cca03c853f079b04fb120
+$(package)_version=2.0.6
+$(package)_download_path=https://github.com/al45tair/mac_alias/archive/
+$(package)_file_name=v$($(package)_version).tar.gz
+$(package)_sha256_hash=78a3332d9a597eebf09ae652d38ad1e263b28db5c2e6dd725fad357b03b77371
$(package)_install_libdir=$(build_prefix)/lib/python/dist-packages
$(package)_patches=python3.patch
define $(package)_preprocess_cmds
- patch -p1 < $($(package)_patch_dir)/python3.patch
+ patch -p1 < $($(package)_patch_dir)/python3.patch
endef
define $(package)_build_cmds
diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk
index 01146c26f6..74bf4e9e11 100644
--- a/depends/packages/zeromq.mk
+++ b/depends/packages/zeromq.mk
@@ -1,19 +1,18 @@
package=zeromq
-$(package)_version=4.1.5
-$(package)_download_path=https://github.com/zeromq/zeromq4-1/releases/download/v$($(package)_version)/
+$(package)_version=4.2.2
+$(package)_download_path=https://github.com/zeromq/libzmq/releases/download/v$($(package)_version)/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
-$(package)_sha256_hash=04aac57f081ffa3a2ee5ed04887be9e205df3a7ddade0027460b8042432bdbcf
-$(package)_patches=9114d3957725acd34aa8b8d011585812f3369411.patch 9e6745c12e0b100cd38acecc16ce7db02905e27c.patch
+$(package)_sha256_hash=5b23f4ca9ef545d5bd3af55d305765e3ee06b986263b31967435d285a3e6df6b
+$(package)_patches=0001-fix-build-with-older-mingw64.patch
define $(package)_set_vars
- $(package)_config_opts=--without-documentation --disable-shared --without-libsodium --disable-curve
+ $(package)_config_opts=--without-docs --disable-shared --without-libsodium --disable-curve --disable-curve-keygen --disable-perf
$(package)_config_opts_linux=--with-pic
$(package)_cxxflags=-std=c++11
endef
define $(package)_preprocess_cmds
- patch -p1 < $($(package)_patch_dir)/9114d3957725acd34aa8b8d011585812f3369411.patch && \
- patch -p1 < $($(package)_patch_dir)/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch && \
+ patch -p1 < $($(package)_patch_dir)/0001-fix-build-with-older-mingw64.patch && \
./autogen.sh
endef
@@ -22,7 +21,7 @@ define $(package)_config_cmds
endef
define $(package)_build_cmds
- $(MAKE) libzmq.la
+ $(MAKE) src/libzmq.la
endef
define $(package)_stage_cmds
diff --git a/depends/patches/native_mac_alias/python3.patch b/depends/patches/native_mac_alias/python3.patch
index 1a32340be5..6f2f5534a2 100644
--- a/depends/patches/native_mac_alias/python3.patch
+++ b/depends/patches/native_mac_alias/python3.patch
@@ -1,7 +1,7 @@
diff -dur a/mac_alias/alias.py b/mac_alias/alias.py
---- a/mac_alias/alias.py 2015-10-19 12:12:48.000000000 +0200
-+++ b/mac_alias/alias.py 2016-04-03 12:13:12.037159417 +0200
-@@ -243,10 +243,10 @@
+--- a/mac_alias/alias.py
++++ b/mac_alias/alias.py
+@@ -258,10 +258,10 @@
alias = Alias()
alias.appinfo = appinfo
@@ -14,7 +14,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py
folder_cnid, cnid,
crdate, creator_code, type_code)
alias.target.levels_from = levels_from
-@@ -261,9 +261,9 @@
+@@ -276,9 +276,9 @@
b.read(1)
if tag == TAG_CARBON_FOLDER_NAME:
@@ -26,7 +26,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py
value)
elif tag == TAG_CARBON_PATH:
alias.target.carbon_path = value
-@@ -298,9 +298,9 @@
+@@ -313,9 +313,9 @@
alias.target.creation_date \
= mac_epoch + datetime.timedelta(seconds=seconds)
elif tag == TAG_POSIX_PATH:
@@ -38,23 +38,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py
elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE:
alias.volume.disk_image_alias = Alias.from_bytes(value)
elif tag == TAG_USER_HOME_LENGTH_PREFIX:
-@@ -422,13 +422,13 @@
- # (so doing so is ridiculous, and nothing could rely on it).
- b.write(struct.pack(b'>h28pI2shI64pII4s4shhI2s10s',
- self.target.kind,
-- carbon_volname, voldate,
-+ carbon_volname, int(voldate),
- self.volume.fs_type,
- self.volume.disk_type,
- self.target.folder_cnid,
- carbon_filename,
- self.target.cnid,
-- crdate,
-+ int(crdate),
- self.target.creator_code,
- self.target.type_code,
- self.target.levels_from,
-@@ -449,12 +449,12 @@
+@@ -467,12 +467,12 @@
b.write(struct.pack(b'>hhQhhQ',
TAG_HIGH_RES_VOLUME_CREATION_DATE,
diff --git a/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch b/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch
new file mode 100644
index 0000000000..a6c508fb8a
--- /dev/null
+++ b/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch
@@ -0,0 +1,30 @@
+From 1a159c128c69a42d90819375c06a39994f3fbfc1 Mon Sep 17 00:00:00 2001
+From: Cory Fields <cory-nospam-@coryfields.com>
+Date: Tue, 28 Nov 2017 20:33:25 -0500
+Subject: [PATCH] fix build with older mingw64
+
+---
+ src/windows.hpp | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/windows.hpp b/src/windows.hpp
+index 99e889d..e69038e 100644
+--- a/src/windows.hpp
++++ b/src/windows.hpp
+@@ -55,6 +55,13 @@
+ #include <winsock2.h>
+ #include <windows.h>
+ #include <mswsock.h>
++
++#if defined __MINGW64_VERSION_MAJOR && __MINGW64_VERSION_MAJOR < 4
++// Workaround for mingw-w64 < v4.0 which did not include ws2ipdef.h in iphlpapi.h.
++// Fixed in mingw-w64 by 9bd8fe9148924840d315b4c915dd099955ea89d1.
++#include <ws2def.h>
++#include <ws2ipdef.h>
++#endif
+ #include <iphlpapi.h>
+
+ #if !defined __MINGW32__
+--
+2.7.4
+
diff --git a/depends/patches/zeromq/9114d3957725acd34aa8b8d011585812f3369411.patch b/depends/patches/zeromq/9114d3957725acd34aa8b8d011585812f3369411.patch
deleted file mode 100644
index f704b3d94f..0000000000
--- a/depends/patches/zeromq/9114d3957725acd34aa8b8d011585812f3369411.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-From 9114d3957725acd34aa8b8d011585812f3369411 Mon Sep 17 00:00:00 2001
-From: Jeroen Ooms <jeroenooms@gmail.com>
-Date: Tue, 20 Oct 2015 13:10:38 +0200
-Subject: [PATCH] enable static libraries on mingw
-
----
- configure.ac | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/configure.ac b/configure.ac
-index 393505b..e92131a 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -265,7 +265,7 @@ case "${host_os}" in
- libzmq_dso_visibility="no"
-
- if test "x$enable_static" = "xyes"; then
-- AC_MSG_ERROR([Building static libraries is not supported under MinGW32])
-+ CPPFLAGS="-DZMQ_STATIC"
- fi
-
- # Set FD_SETSIZE to 1024 \ No newline at end of file
diff --git a/depends/patches/zeromq/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch b/depends/patches/zeromq/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch
deleted file mode 100644
index 9aff2c179a..0000000000
--- a/depends/patches/zeromq/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-From 9e6745c12e0b100cd38acecc16ce7db02905e27c Mon Sep 17 00:00:00 2001
-From: David Millard <dmillard10@gmail.com>
-Date: Tue, 10 May 2016 13:53:53 -0700
-Subject: [PATCH] Fix autotools for static MinGW builds
-
----
- configure.ac | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/configure.ac b/configure.ac
-index 5a0fa14..def6ea7 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -259,7 +259,7 @@ case "${host_os}" in
- libzmq_dso_visibility="no"
-
- if test "x$enable_static" = "xyes"; then
-- CPPFLAGS="-DZMQ_STATIC"
-+ CPPFLAGS="-DZMQ_STATIC $CPPFLAGS"
- fi
-
- # Set FD_SETSIZE to 1024 \ No newline at end of file
diff --git a/doc/README.md b/doc/README.md
index 275ae67e54..988019869e 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -37,6 +37,7 @@ Building
---------------------
The following are developer notes on how to build Bitcoin on your native platform. They are not complete guides, but include notes on the necessary libraries, compile flags, etc.
+- [Dependencies](dependencies.md)
- [OS X Build Notes](build-osx.md)
- [Unix Build Notes](build-unix.md)
- [Windows Build Notes](build-windows.md)
diff --git a/doc/bips.md b/doc/bips.md
index bc8dcb6fb3..fbff94a329 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -33,3 +33,4 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.13.0**):
* [`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)).
+* [`BIP 159`](https://github.com/bitcoin/bips/blob/master/bip-0159.mediawiki): NODE_NETWORK_LIMITED service bit [signaling only] is supported as of **v0.16.0** ([PR 10740](https://github.com/bitcoin/bitcoin/pull/10740)).
diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md
index cd800464b4..cd1d217b47 100644
--- a/doc/build-openbsd.md
+++ b/doc/build-openbsd.md
@@ -1,10 +1,10 @@
OpenBSD build guide
======================
-(updated for OpenBSD 6.1)
+(updated for OpenBSD 6.2)
This guide describes how to build bitcoind and command-line utilities on OpenBSD.
-As OpenBSD is most common as a server OS, we will not bother with the GUI.
+OpenBSD is most commonly used as a server OS, so this guide does not contain instructions for building the GUI.
Preparation
-------------
@@ -12,94 +12,51 @@ Preparation
Run the following as root to install the base dependencies for building:
```bash
-pkg_add gmake libtool libevent
+pkg_add git gmake libevent libtool
pkg_add autoconf # (select highest version, e.g. 2.69)
pkg_add automake # (select highest version, e.g. 1.15)
-pkg_add python # (select highest version, e.g. 3.5)
+pkg_add python # (select highest version, e.g. 3.6)
+pkg_add boost
+
+git clone https://github.com/bitcoin/bitcoin.git
```
-The default C++ compiler that comes with OpenBSD 5.9 is g++ 4.2. This version is old (from 2007), and is not able to compile the current version of Bitcoin Core, primarily as it has no C++11 support, but even before there were issues. So here we will be installing a newer compiler.
+See [dependencies.md](dependencies.md) for a complete overview.
GCC
-------
-You can install a newer version of gcc with:
+The default C++ compiler that comes with OpenBSD 6.2 is g++ 4.2.1. This version is old (from 2007), and is not able to compile the current version of Bitcoin Core because it has no C++11 support. We'll install a newer version of GCC:
```bash
-pkg_add g++ # (select newest 4.x version, e.g. 4.9.3)
-```
-
-This compiler will not overwrite the system compiler, it will be installed as `egcc` and `eg++` in `/usr/local/bin`.
-
-### Building boost
-
-Do not use `pkg_add boost`! The boost version installed thus is compiled using the `g++` compiler not `eg++`, which will result in a conflict between `/usr/local/lib/libestdc++.so.XX.0` and `/usr/lib/libstdc++.so.XX.0`, resulting in a test crash:
-
- test_bitcoin:/usr/lib/libstdc++.so.57.0: /usr/local/lib/libestdc++.so.17.0 : WARNING: symbol(_ZN11__gnu_debug17_S_debug_me ssagesE) size mismatch, relink your program
- ...
- Segmentation fault (core dumped)
+ pkg_add g++
+ ```
-This makes it necessary to build boost, or at least the parts used by Bitcoin Core, manually:
-
-```
-# Pick some path to install boost to, here we create a directory within the bitcoin directory
-BITCOIN_ROOT=$(pwd)
-BOOST_PREFIX="${BITCOIN_ROOT}/boost"
-mkdir -p $BOOST_PREFIX
-
-# Fetch the source and verify that it is not tampered with
-curl -o boost_1_64_0.tar.bz2 https://netcologne.dl.sourceforge.net/project/boost/boost/1.64.0/boost_1_64_0.tar.bz2
-echo '7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332 boost_1_64_0.tar.bz2' | sha256 -c
-# MUST output: (SHA256) boost_1_64_0.tar.bz2: OK
-tar -xjf boost_1_64_0.tar.bz2
-
-# Boost 1.64 needs one small patch for OpenBSD
-cd boost_1_64_0
-# Also here: https://gist.githubusercontent.com/laanwj/bf359281dc319b8ff2e1/raw/92250de8404b97bb99d72ab898f4a8cb35ae1ea3/patch-boost_test_impl_execution_monitor_ipp.patch
-patch -p0 < /usr/ports/devel/boost/patches/patch-boost_test_impl_execution_monitor_ipp
-
-# Build w/ minimum configuration necessary for bitcoin
-echo 'using gcc : : eg++ : <cxxflags>"-fvisibility=hidden -fPIC" <linkflags>"" <archiver>"ar" <striper>"strip" <ranlib>"ranlib" <rc>"" : ;' > user-config.jam
-config_opts="runtime-link=shared threadapi=pthread threading=multi link=static variant=release --layout=tagged --build-type=complete --user-config=user-config.jam -sNO_BZIP2=1"
-./bootstrap.sh --without-icu --with-libraries=chrono,filesystem,program_options,system,thread,test
-./b2 -d2 -j2 -d1 ${config_opts} --prefix=${BOOST_PREFIX} stage
-./b2 -d0 -j4 ${config_opts} --prefix=${BOOST_PREFIX} install
-```
+ This compiler will not overwrite the system compiler, it will be installed as `egcc` and `eg++` in `/usr/local/bin`.
### Building BerkeleyDB
BerkeleyDB is only necessary for the wallet functionality. To skip this, pass `--disable-wallet` to `./configure`.
-See "Berkeley DB" in [build_unix.md](build_unix.md) for instructions on how to build BerkeleyDB 4.8.
-You cannot use the BerkeleyDB library from ports, for the same reason as boost above (g++/libstd++ incompatibility).
+It is recommended to use Berkeley DB 4.8. You cannot use the BerkeleyDB library
+from ports, for the same reason as boost above (g++/libstd++ incompatibility).
+If you have to build it yourself, you can use [the installation script included
+in contrib/](contrib/install_db4.sh) like so
-```bash
-# Pick some path to install BDB to, here we create a directory within the bitcoin directory
-BITCOIN_ROOT=$(pwd)
-BDB_PREFIX="${BITCOIN_ROOT}/db4"
-mkdir -p $BDB_PREFIX
-
-# Fetch the source and verify that it is not tampered with
-curl -o db-4.8.30.NC.tar.gz 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz'
-echo '12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef db-4.8.30.NC.tar.gz' | sha256 -c
-# MUST output: (SHA256) db-4.8.30.NC.tar.gz: OK
-tar -xzf db-4.8.30.NC.tar.gz
-
-# Build the library and install to specified prefix
-cd db-4.8.30.NC/build_unix/
-# Note: Do a static build so that it can be embedded into the executable, instead of having to find a .so at runtime
-../dist/configure --enable-cxx --disable-shared --with-pic --prefix=$BDB_PREFIX CC=egcc CXX=eg++ CPP=ecpp
-make install # do NOT use -jX, this is broken
+```shell
+./contrib/install_db4.sh `pwd` CC=egcc CXX=eg++ CPP=ecpp
```
+from the root of the repository.
+
### Resource limits
The standard ulimit restrictions in OpenBSD are very strict:
data(kbytes) 1572864
-This is, unfortunately, no longer enough to compile some `.cpp` files in the project,
-at least with gcc 4.9.3 (see issue https://github.com/bitcoin/bitcoin/issues/6658).
+This, unfortunately, may no longer be enough to compile some `.cpp` files in the project,
+at least with GCC 4.9.4 (see issue [#6658](https://github.com/bitcoin/bitcoin/issues/6658)).
If your user is in the `staff` group the limit can be raised with:
ulimit -d 3000000
@@ -118,59 +75,32 @@ export AUTOCONF_VERSION=2.69 # replace this with the autoconf version that you i
export AUTOMAKE_VERSION=1.15 # replace this with the automake version that you installed
./autogen.sh
```
-Make sure `BDB_PREFIX` and `BOOST_PREFIX` are set to the appropriate paths from the above steps.
+Make sure `BDB_PREFIX` is set to the appropriate path from the above steps.
To configure with wallet:
```bash
-./configure --with-gui=no --with-boost=$BOOST_PREFIX \
- CC=egcc CXX=eg++ CPP=ecpp \
+./configure --with-gui=no CC=egcc CXX=eg++ CPP=ecpp \
BDB_LIBS="-L${BDB_PREFIX}/lib -ldb_cxx-4.8" BDB_CFLAGS="-I${BDB_PREFIX}/include"
```
To configure without wallet:
```bash
-./configure --disable-wallet --with-gui=no --with-boost=$BOOST_PREFIX \
- CC=egcc CXX=eg++ CPP=ecpp
+./configure --disable-wallet --with-gui=no CC=egcc CXX=eg++ CPP=ecpp
```
Build and run the tests:
```bash
-gmake # can use -jX here for parallelism
+gmake # use -jX here for parallelism
gmake check
```
-Clang (not currently working)
+Clang
------------------------------
-WARNING: This is outdated, needs to be updated for OpenBSD 6.0 and re-tried.
-
-Using a newer g++ results in linking the new code to a new libstdc++.
-Libraries built with the old g++, will still import the old library.
-This gives conflicts, necessitating rebuild of all C++ dependencies of the application.
-
-With clang this can - at least theoretically - be avoided because it uses the
-base system's libstdc++.
-
```bash
-pkg_add llvm boost
-```
+pkg_add llvm
-```bash
./configure --disable-wallet --with-gui=no CC=clang CXX=clang++
-gmake
+gmake # use -jX here for parallelism
+gmake check
```
-
-However, this does not appear to work. Compilation succeeds, but link fails
-with many 'local symbol discarded' errors:
-
- local symbol 150: discarded in section `.text._ZN10tinyformat6detail14FormatIterator6finishEv' from libbitcoin_util.a(libbitcoin_util_a-random.o)
- local symbol 151: discarded in section `.text._ZN10tinyformat6detail14FormatIterator21streamStateFromFormatERSoRjPKcii' from libbitcoin_util.a(libbitcoin_util_a-random.o)
- local symbol 152: discarded in section `.text._ZN10tinyformat6detail12convertToIntIA13_cLb0EE6invokeERA13_Kc' from libbitcoin_util.a(libbitcoin_util_a-random.o)
-
-According to similar reported errors this is a binutils (ld) issue in 2.15, the
-version installed by OpenBSD 5.7:
-
-- http://openbsd-archive.7691.n7.nabble.com/UPDATE-cppcheck-1-65-td248900.html
-- https://llvm.org/bugs/show_bug.cgi?id=9758
-
-There is no known workaround for this.
diff --git a/doc/build-osx.md b/doc/build-osx.md
index 1320d309c4..6663016ed8 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -18,12 +18,28 @@ Dependencies
brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf python3 qt libevent
+See [dependencies.md](dependencies.md) for a complete overview.
+
If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG
brew install librsvg
NOTE: Building with Qt4 is still supported, however, could result in a broken UI. Building with Qt5 is recommended.
+Berkeley DB
+-----------
+It is recommended to use Berkeley DB 4.8. If you have to build it yourself,
+you can use [the installation script included in contrib/](contrib/install_db4.sh)
+like so
+
+```shell
+./contrib/install_db4.sh .
+```
+
+from the root of the repository.
+
+**Note**: You only need Berkeley DB if the wallet is enabled (see the section *Disable-Wallet mode* below).
+
Build Bitcoin Core
------------------------
@@ -94,6 +110,6 @@ Uncheck everything except Qt Creator during the installation process.
Notes
-----
-* Tested on OS X 10.8 through 10.12 on 64-bit Intel processors only.
+* Tested on OS X 10.8 through 10.13 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 2e82934cd1..5d3329e2cf 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -49,7 +49,7 @@ Optional dependencies:
univalue | Utility | JSON parsing and encoding (bundled version will be used unless --with-system-univalue passed to configure)
libzmq3 | ZMQ notification | Optional, allows generating ZMQ notifications (requires ZMQ version >= 4.x)
-For the versions used in the release, see [release-process.md](release-process.md) under *Fetch and build inputs*.
+For the versions used, see [dependencies.md](dependencies.md)
Memory Requirements
--------------------
@@ -165,33 +165,16 @@ turned off by default. See the configure options for upnp behavior desired:
Berkeley DB
-----------
-It is recommended to use Berkeley DB 4.8. If you have to build it yourself:
+It is recommended to use Berkeley DB 4.8. If you have to build it yourself,
+you can use [the installation script included in contrib/](contrib/install_db4.sh)
+like so
-```bash
-BITCOIN_ROOT=$(pwd)
-
-# Pick some path to install BDB to, here we create a directory within the bitcoin directory
-BDB_PREFIX="${BITCOIN_ROOT}/db4"
-mkdir -p $BDB_PREFIX
-
-# Fetch the source and verify that it is not tampered with
-wget 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz'
-echo '12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef db-4.8.30.NC.tar.gz' | sha256sum -c
-# -> db-4.8.30.NC.tar.gz: OK
-tar -xzvf db-4.8.30.NC.tar.gz
-
-# Build the library and install to our prefix
-cd db-4.8.30.NC/build_unix/
-# Note: Do a static build so that it can be embedded into the executable, instead of having to find a .so at runtime
-../dist/configure --enable-cxx --disable-shared --with-pic --prefix=$BDB_PREFIX
-make install
-
-# Configure Bitcoin Core to use our own-built instance of BDB
-cd $BITCOIN_ROOT
-./autogen.sh
-./configure LDFLAGS="-L${BDB_PREFIX}/lib/" CPPFLAGS="-I${BDB_PREFIX}/include/" # (other args...)
+```shell
+./contrib/install_db4.sh `pwd`
```
+from the root of the repository.
+
**Note**: You only need Berkeley DB if the wallet is enabled (see the section *Disable-Wallet mode* below).
Boost
diff --git a/doc/build-windows.md b/doc/build-windows.md
index ca1a05dfb4..9e0e66e522 100644
--- a/doc/build-windows.md
+++ b/doc/build-windows.md
@@ -3,73 +3,114 @@ WINDOWS BUILD NOTES
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.
+The options known to work for building Bitcoin Core on Windows are:
-Currently only building on Ubuntu Trusty 14.04 is supported.
-Other versions are unsupported or known to be broken (e.g. Ubuntu Xenial 16.04).
+* On Linux using the [Mingw-w64](https://mingw-w64.org/doku.php) cross compiler tool chain. Ubuntu Trusty 14.04 is recommended
+and is the platform used to build the Bitcoin Core Windows release binaries.
+* On Windows using [Windows
+Subsystem for Linux (WSL)](https://msdn.microsoft.com/commandline/wsl/about) and the Mingw-w64 cross compiler tool chain.
-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.
+Other options which may work but which have not been extensively tested are (please contribute instructions):
-Compiling with Windows Subsystem For Linux
--------------------------------------------
+* On Windows using a POSIX compatibility layer application such as [cygwin](http://www.cygwin.com/) or [msys2](http://www.msys2.org/).
+* On Windows using a native compiler tool chain such as [Visual Studio](https://www.visualstudio.com).
+
+Installing 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
+Subsystem for Linux (WSL)](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.
+the need for a separate Linux VM or server. Note that while WSL can be installed with
+other Linux variants, such as OpenSUSE, the following instructions have only been
+tested with Ubuntu.
This feature is not supported in versions of Windows prior to Windows 10 or on
Windows Server SKUs. In addition, it is available [only for 64-bit versions of
Windows](https://msdn.microsoft.com/en-us/commandline/wsl/install_guide).
-To get the bash shell, you must first activate the feature in Windows.
+Full instructions to install WSL are available on the above link.
+To install WSL on Windows 10 with Fall Creators Update installed (version >= 16215.0) do the following:
-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
+1. 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)
+ * Select Windows Subsystem for Linux
* Click OK
* Restart if necessary
+2. Install Ubuntu
+ * Open Microsoft Store and search for Ubuntu or use [this link](https://www.microsoft.com/store/productId/9NBLGGH4MSV6)
+ * Click Install
3. Complete Installation
- * Open a cmd prompt and type "bash"
- * Accept the license
+ * Open a cmd prompt and type "Ubuntu"
* 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, starting
with the "Cross-compilation" section. Compiling the 64-bit version is
recommended but it is possible to compile the 32-bit version.
-Cross-compilation
--------------------
+Cross-compilation for Ubuntu and Windows Subsystem for Linux
+------------------------------------------------------------
+
+At the time of writing the Windows Subsystem for Linux installs Ubuntu Xenial 16.04. The Mingw-w64 package
+for Ubuntu Xenial does not produce working executables for some of the Bitcoin Core applications.
+It is possible to build on Ubuntu Xenial by installing the cross compiler packages from Ubuntu Zesty, see the steps below.
+Building on Ubuntu Zesty 17.04 up to 17.10 has been verified to work.
-These steps can be performed on, for example, an Ubuntu VM. The depends system
+The steps below can be performed on Ubuntu (including in a VM) or WSL. The depends system
will also work on other Linux distributions, however the commands for
installing the toolchain will be different.
First, install the general dependencies:
- sudo apt-get install build-essential libtool autotools-dev automake pkg-config bsdmainutils curl
+ sudo apt install build-essential libtool autotools-dev automake pkg-config bsdmainutils curl git
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.
+See also: [dependencies.md](dependencies.md).
+
## Building for 64-bit Windows
-To build executables for Windows 64-bit, install the following dependencies:
+The first step is to install the mingw-w64 cross-compilation tool chain. Due to different Ubuntu
+packages for each distribution and problems with the Xenial packages the steps for each are different.
- sudo apt-get install g++-mingw-w64-x86-64 mingw-w64-x86-64-dev
+Common steps to install mingw32 cross compiler tool chain:
-Then build using:
+ sudo apt install g++-mingw-w64-x86-64
+
+Ubuntu Trusty 14.04:
+
+ No further steps required
+
+Ubuntu Xenial 16.04 and Windows Subsystem for Linux <sup>[1](#footnote1),[2](#footnote2)</sup>:
+
+ sudo apt install software-properties-common
+ sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu zesty universe"
+ sudo apt update
+ sudo apt upgrade
+ sudo update-alternatives --config x86_64-w64-mingw32-g++ # Set the default mingw32 g++ compiler option to posix.
+
+Ubuntu Zesty 17.04 <sup>[2](#footnote2)</sup>:
+
+ sudo update-alternatives --config x86_64-w64-mingw32-g++ # Set the default mingw32 g++ compiler option to posix.
+Once the tool chain is installed the build steps are common:
+
+Note that for WSL the Bitcoin Core source path MUST be somewhere in the default mount file system, for
+example /usr/src/bitcoin, AND not under /mnt/d/. If this is not the case the dependency autoconf scripts will fail.
+This means you cannot use a directory that located directly on the host Windows file system to perform the build.
+
+The next three steps are an example of how to acquire the source in an appropriate way.
+
+ cd /usr/src
+ sudo git clone https://github.com/bitcoin/bitcoin.git
+ sudo chmod -R a+rw bitcoin
+
+Once the source code is ready the build steps are below.
+
+ PATH=$(echo "$PATH" | sed -e 's/:\/mnt.*//g') # strip out problematic Windows %PATH% imported var
cd depends
make HOST=x86_64-w64-mingw32
cd ..
@@ -81,10 +122,25 @@ Then build using:
To build executables for Windows 32-bit, install the following dependencies:
- sudo apt-get install g++-mingw-w64-i686 mingw-w64-i686-dev
+ sudo apt install g++-mingw-w64-i686 mingw-w64-i686-dev
+
+For Ubuntu Xenial 16.04, Ubuntu Zesty 17.04 and Windows Subsystem for Linux <sup>[2](#footnote2)</sup>:
+
+ sudo update-alternatives --config i686-w64-mingw32-g++ # Set the default mingw32 g++ compiler option to posix.
+
+Note that for WSL the Bitcoin Core source path MUST be somewhere in the default mount file system, for
+example /usr/src/bitcoin, AND not under /mnt/d/. If this is not the case the dependency autoconf scripts will fail.
+This means you cannot use a directory that located directly on the host Windows file system to perform the build.
+
+The next three steps are an example of how to acquire the source in an appropriate way.
+
+ cd /usr/src
+ sudo git clone https://github.com/bitcoin/bitcoin.git
+ sudo chmod -R a+rw bitcoin
Then build using:
+ PATH=$(echo "$PATH" | sed -e 's/:\/mnt.*//g') # strip out problematic Windows %PATH% imported var
cd depends
make HOST=i686-w64-mingw32
cd ..
@@ -105,3 +161,20 @@ 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
+
+Footnotes
+---------
+
+<a name="footnote1">1</a>: There is currently a bug in the 64 bit Mingw-w64 cross compiler packaged for WSL/Ubuntu Xenial 16.04 that
+causes two of the bitcoin executables to crash shortly after start up. The bug is related to the
+-fstack-protector-all g++ compiler flag which is used to mitigate buffer overflows.
+Installing the Mingw-w64 packages from the Ubuntu 17 distribution solves the issue, however, this is not
+an officially supported approach and it's only recommended if you are prepared to reinstall WSL/Ubuntu should
+something break.
+
+<a name="footnote2">2</a>: Starting from Ubuntu Xenial 16.04 both the 32 and 64 bit Mingw-w64 packages install two different
+compiler options to allow a choice between either posix or win32 threads. The default option is win32 threads which is the more
+efficient since it will result in binary code that links directly with the Windows kernel32.lib. Unfortunately, the headers
+required to support win32 threads conflict with some of the classes in the C++11 standard library in particular std::mutex.
+It's not possible to build the bitcoin code using the win32 version of the Mingw-w64 cross compilers (at least not without
+modifying headers in the bitcoin source code).
diff --git a/doc/dependencies.md b/doc/dependencies.md
new file mode 100644
index 0000000000..5c5645de97
--- /dev/null
+++ b/doc/dependencies.md
@@ -0,0 +1,31 @@
+Dependencies
+============
+
+These are the dependencies currently used by Bitcoin Core. You can find instructions for installing them in the `build-*.md` file for your platform.
+
+| Dependency | Version used | Minimum required | CVEs | Shared | [Bundled Qt library](https://doc.qt.io/qt-5/configure-options.html) |
+| --- | --- | --- | --- | --- | --- |
+| Berkeley DB | [4.8.30](http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.x | No | | |
+| Boost | [1.64.0](http://www.boost.org/users/download/) | [1.47.0](https://github.com/bitcoin/bitcoin/pull/8920) | No | | |
+| ccache | [3.3.4](https://ccache.samba.org/download.html) | | No | | |
+| Clang | | [3.3+](http://llvm.org/releases/download.html) (C++11 support) | | | |
+| D-Bus | [1.10.18](https://cgit.freedesktop.org/dbus/dbus/tree/NEWS?h=dbus-1.10) | | No | Yes | |
+| Expat | [2.2.1](https://libexpat.github.io/) | | No | Yes | |
+| fontconfig | [2.12.1](https://www.freedesktop.org/software/fontconfig/release/) | | No | Yes | |
+| FreeType | [2.7.1](http://download.savannah.gnu.org/releases/freetype) | | No | | |
+| GCC | | [4.8+](https://gcc.gnu.org/) | | | |
+| HarfBuzz-NG | | | | | |
+| libevent | [2.1.8-stable](https://github.com/libevent/libevent/releases) | 2.0.22 | No | | |
+| libjpeg | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L75) |
+| libpng | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L74) |
+| MiniUPnPc | [2.0.20170509](http://miniupnp.free.fr/files) | | No | | |
+| OpenSSL | [1.0.1k](https://www.openssl.org/source) | | Yes | | |
+| PCRE | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L76) |
+| protobuf | [2.6.3](https://github.com/google/protobuf/releases) | | No | | |
+| Python (tests) | | [3.4](https://www.python.org/downloads) | | | |
+| qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | |
+| Qt | [5.7.1](https://download.qt.io/official_releases/qt/) | 4.7+ | No | | |
+| XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L94) (Linux only) |
+| xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L93) (Linux only) |
+| ZeroMQ | [4.2.2](https://github.com/zeromq/libzmq/releases) | | No | | |
+| zlib | [1.2.11](http://zlib.net/) | | | | No |
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 2f04d6ee6c..9dc63a1e4b 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -167,6 +167,37 @@ can be very difficult to track down. Compiling with -DDEBUG_LOCKORDER (configure
CXXFLAGS="-DDEBUG_LOCKORDER -g") inserts run-time checks to keep track of which locks
are held, and adds warnings to the debug.log file if inconsistencies are detected.
+**Valgrind suppressions file**
+
+Valgrind is a programming tool for memory debugging, memory leak detection, and
+profiling. The repo contains a Valgrind suppressions file
+([`valgrind.supp`](https://github.com/bitcoin/bitcoin/blob/master/contrib/valgrind.supp))
+which includes known Valgrind warnings in our dependencies that cannot be fixed
+in-tree. Example use:
+
+```shell
+$ valgrind --suppressions=contrib/valgrind.supp src/test/test_bitcoin
+$ valgrind --suppressions=contrib/valgrind.supp --leak-check=full \
+ --show-leak-kinds=all src/test/test_bitcoin --log_level=test_suite
+$ valgrind -v --leak-check=full src/bitcoind -printtoconsole
+```
+
+**compiling for test coverage**
+
+LCOV can be used to generate a test coverage report based upon `make check`
+execution. LCOV must be installed on your system (e.g. the `lcov` package
+on Debian/Ubuntu).
+
+To enable LCOV report generation during test runs:
+
+```shell
+./configure --enable-lcov
+make
+make cov
+
+# A coverage report will now be accessible at `./test_bitcoin.coverage/index.html`.
+```
+
Locking/mutex usage notes
-------------------------
@@ -461,6 +492,14 @@ namespace {
- *Rationale*: Avoids confusion about the namespace context
+- Prefer `#include <primitives/transaction.h>` bracket syntax instead of
+ `#include "primitives/transactions.h"` quote syntax when possible.
+
+ - *Rationale*: Bracket syntax is less ambiguous because the preprocessor
+ searches a fixed list of include directories without taking location of the
+ source file into account. This allows quoted includes to stand out more when
+ the location of the source file actually is relevant.
+
GUI
-----
@@ -549,6 +588,26 @@ Git and GitHub tips
or `git fetch upstream-pull`. Afterwards, you can use `upstream-pull/NUMBER/head` in arguments to `git show`,
`git checkout` and anywhere a commit id would be acceptable to see the changes from pull request NUMBER.
+Scripted diffs
+--------------
+
+For reformatting and refactoring commits where the changes can be easily automated using a bash script, we use
+scripted-diff commits. The bash script is included in the commit message and our Travis CI job checks that
+the result of the script is identical to the commit. This aids reviewers since they can verify that the script
+does exactly what it's supposed to do. It is also helpful for rebasing (since the same script can just be re-run
+on the new master commit).
+
+To create a scripted-diff:
+
+- start the commit message with `scripted-diff:` (and then a description of the diff on the same line)
+- in the commit message include the bash script between lines containing just the following text:
+ - `-BEGIN VERIFY SCRIPT-`
+ - `-END VERIFY SCRIPT-`
+
+The scripted-diff is verified by the tool `contrib/devtools/commit-script-check.sh`
+
+Commit `bb81e173` is an example of a scripted-diff.
+
RPC interface guidelines
--------------------------
@@ -613,9 +672,27 @@ A few guidelines for introducing and reviewing new RPC interfaces:
from there.
- A RPC method must either be a wallet method or a non-wallet method. Do not
- introduce new methods such as `getinfo` and `signrawtransaction` that differ
- in behavior based on presence of a wallet.
+ introduce new methods such as `signrawtransaction` that differ in behavior
+ based on presence of a wallet.
- *Rationale*: as well as complicating the implementation and interfering
with the introduction of multi-wallet, wallet and non-wallet code should be
separated to avoid introducing circular dependencies between code units.
+
+- Try to make the RPC response a JSON object.
+
+ - *Rationale*: If a RPC response is not a JSON object then it is harder to avoid API breakage if
+ new data in the response is needed.
+
+- Wallet RPCs call BlockUntilSyncedToCurrentChain to maintain consistency with
+ `getblockchaininfo`'s state immediately prior to the call's execution. Wallet
+ RPCs whose behavior does *not* depend on the current chainstate may omit this
+ call.
+
+ - *Rationale*: In previous versions of Bitcoin Core, the wallet was always
+ in-sync with the chainstate (by virtue of them all being updated in the
+ same cs_main lock). In order to maintain the behavior that wallet RPCs
+ return results as of at least the highest best-known block an RPC
+ client may be aware of prior to entering a wallet RPC call, we must block
+ until the wallet is caught up to the chainstate as of the RPC call's entry.
+ This also makes the API much easier for RPC clients to reason about.
diff --git a/doc/files.md b/doc/files.md
index 928977143b..3d603445fb 100644
--- a/doc/files.md
+++ b/doc/files.md
@@ -15,6 +15,7 @@
* 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
* onion_private_key: cached Tor hidden service private key for `-listenonion`: since 0.12.0
+* guisettings.ini.bak: backup of former GUI settings after `-resetguisettings` is used
Only used in pre-0.8.0
---------------------
diff --git a/doc/gitian-building.md b/doc/gitian-building.md
index 636686b391..3a48f4a0b3 100644
--- a/doc/gitian-building.md
+++ b/doc/gitian-building.md
@@ -1,484 +1,4 @@
Gitian building
================
-*Setup instructions for a Gitian build of Bitcoin Core using a Debian VM or physical system.*
-
-Gitian is the deterministic build process that is used to build the Bitcoin
-Core executables. It provides a way to be reasonably sure that the
-executables are really built from the source on GitHub. It also makes sure that
-the same, tested dependencies are used and statically built into the executable.
-
-Multiple developers build the source code by following a specific descriptor
-("recipe"), cryptographically sign the result, and upload the resulting signature.
-These results are compared and only if they match, the build is accepted and uploaded
-to bitcoin.org.
-
-More independent Gitian builders are needed, which is why this guide exists.
-It is preferred you follow these steps yourself instead of using someone else's
-VM image to avoid 'contaminating' the build.
-
-Table of Contents
-------------------
-
-- [Create a new VirtualBox VM](#create-a-new-virtualbox-vm)
-- [Connecting to the VM](#connecting-to-the-vm)
-- [Setting up Debian for Gitian building](#setting-up-debian-for-gitian-building)
-- [Installing Gitian](#installing-gitian)
-- [Setting up the Gitian image](#setting-up-the-gitian-image)
-- [Getting and building the inputs](#getting-and-building-the-inputs)
-- [Building Bitcoin Core](#building-bitcoin-core)
-- [Building an alternative repository](#building-an-alternative-repository)
-- [Signing externally](#signing-externally)
-- [Uploading signatures](#uploading-signatures)
-
-Preparing the Gitian builder host
----------------------------------
-
-The first step is to prepare the host environment that will be used to perform the Gitian builds.
-This guide explains how to set up the environment, and how to start the builds.
-
-Debian Linux was chosen as the host distribution because it has a lightweight install (in contrast to Ubuntu) and is readily available.
-Any kind of virtualization can be used, for example:
-- [VirtualBox](https://www.virtualbox.org/) (covered by this guide)
-- [KVM](http://www.linux-kvm.org/page/Main_Page)
-- [LXC](https://linuxcontainers.org/), see also [Gitian host docker container](https://github.com/gdm85/tenku/tree/master/docker/gitian-bitcoin-host/README.md).
-
-You can also install Gitian on actual hardware instead of using virtualization.
-
-Create a new VirtualBox VM
----------------------------
-In the VirtualBox GUI click "New" and choose the following parameters in the wizard:
-
-![](gitian-building/create_new_vm.png)
-
-- Type: Linux, Debian (64-bit)
-
-![](gitian-building/create_vm_memsize.png)
-
-- Memory Size: at least 3000MB, anything less and the build might not complete.
-
-![](gitian-building/create_vm_hard_disk.png)
-
-- Hard Disk: Create a virtual hard disk now
-
-![](gitian-building/create_vm_hard_disk_file_type.png)
-
-- Hard Disk file type: Use the default, VDI (VirtualBox Disk Image)
-
-![](gitian-building/create_vm_storage_physical_hard_disk.png)
-
-- Storage on physical hard disk: Dynamically Allocated
-
-![](gitian-building/create_vm_file_location_size.png)
-
-- File location and size: at least 40GB; as low as 20GB *may* be possible, but better to err on the safe side
-- Click `Create`
-
-After creating the VM, we need to configure it.
-
-- Click the `Settings` button, then go to `System` tab and `Processor` sub-tab. Increase the number of processors to the number of cores on your machine if you want builds to be faster.
-
-![](gitian-building/system_settings.png)
-
-- Go to the `Network` tab. Adapter 1 should be attached to `NAT`.
-
-![](gitian-building/network_settings.png)
-
-- Click `Advanced`, then `Port Forwarding`. We want to set up a port through which we can reach the VM to get files in and out.
-- Create a new rule by clicking the plus icon.
-
-![](gitian-building/port_forwarding_rules.png)
-
-- Set up the new rule the following way:
- - Name: `SSH`
- - Protocol: `TCP`
- - Leave Host IP empty
- - Host Port: `22222`
- - Leave Guest IP empty
- - Guest Port: `22`
-
-- Click `Ok` twice to save.
-
-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.
-
-![](gitian-building/select_startup_disk.png)
-
-Installing Debian
-------------------
-
-This section will explain how to install Debian on the newly created VM.
-
-- Choose the non-graphical installer. We do not need the graphical environment; it will only increase installation time and disk usage.
-
-![](gitian-building/debian_install_1_boot_menu.png)
-
-**Note**: Navigating in the Debian installer:
-To keep a setting at the default and proceed, just press `Enter`.
-To select a different button, press `Tab`.
-
-- Choose locale and keyboard settings (doesn't matter, you can just go with the defaults or select your own information)
-
-![](gitian-building/debian_install_2_select_a_language.png)
-![](gitian-building/debian_install_3_select_location.png)
-![](gitian-building/debian_install_4_configure_keyboard.png)
-
-- The VM will detect network settings using DHCP, this should all proceed automatically
-- Configure the network:
- - Hostname `debian`.
- - Leave domain name empty.
-
-![](gitian-building/debian_install_5_configure_the_network.png)
-![](gitian-building/debian_install_6_domain_name.png)
-
-- Choose a root password and enter it twice (remember it for later)
-
-![](gitian-building/debian_install_6a_set_up_root_password.png)
-
-- Name the new user `debian` (the full name doesn't matter, you can leave it empty)
-- Set the account username as `debian`
-
-![](gitian-building/debian_install_7_set_up_user_fullname.png)
-![](gitian-building/debian_install_8_set_up_username.png)
-
-- Choose a user password and enter it twice (remember it for later)
-
-![](gitian-building/debian_install_9_user_password.png)
-
-- The installer will set up the clock using a time server; this process should be automatic
-- Set up the clock: choose a time zone (depends on the locale settings that you picked earlier; specifics don't matter)
-
-![](gitian-building/debian_install_10_configure_clock.png)
-
-- Disk setup
- - Partitioning method: Guided - Use the entire disk
-
-![](gitian-building/debian_install_11_partition_disks.png)
-
- - Select disk to partition: SCSI1 (0,0,0)
-
-![](gitian-building/debian_install_12_choose_disk.png)
-
- - Partition Disks -> *All files in one partition*
-
-![](gitian-building/all_files_in_one_partition.png)
-
- - Finish partitioning and write changes to disk -> *Yes* (`Tab`, `Enter` to select the `Yes` button)
-
-![](gitian-building/debian_install_14_finish.png)
-![](gitian-building/debian_install_15_write_changes.png)
-
-- The base system will be installed, this will take a minute or so
-- Choose a mirror (any will do)
-
-![](gitian-building/debian_install_16_choose_a_mirror.png)
-
-- Enter proxy information (unless you are on an intranet, leave this empty)
-
-![](gitian-building/debian_install_18_proxy_settings.png)
-
-- Wait a bit while 'Select and install software' runs
-- Participate in popularity contest -> *No*
-- Choose software to install. We need just the base system.
-- Make sure only 'SSH server' and 'Standard System Utilities' are checked
-- Uncheck 'Debian Desktop Environment' and 'Print Server'
-
-![](gitian-building/debian_install_19_software_selection.png)
-
-- Install the GRUB boot loader to the master boot record? -> Yes
-
-![](gitian-building/debian_install_20_install_grub.png)
-
-- Device for boot loader installation -> ata-VBOX_HARDDISK
-
-![](gitian-building/debian_install_21_install_grub_bootloader.png)
-
-- Installation Complete -> *Continue*
-- After installation, the VM will reboot and you will have a working Debian VM. Congratulations!
-
-![](gitian-building/debian_install_22_finish_installation.png)
-
-
-After Installation
--------------------
-The next step in the guide involves logging in as root via SSH.
-SSH login for root users is disabled by default, so we'll enable that now.
-
-Login to the VM using username `root` and the root password you chose earlier.
-You'll be presented with a screen similar to this.
-
-![](gitian-building/debian_root_login.png)
-
-Type:
-
-```
-sed -i 's/^PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
-```
-and press enter. Then,
-```
-/etc/init.d/ssh restart
-```
-and enter to restart SSH. Logout by typing 'logout' and pressing 'enter'.
-
-Connecting to the VM
-----------------------
-
-After the VM has booted you can connect to it using SSH, and files can be copied from and to the VM using a SFTP utility.
-Connect to `localhost`, port `22222` (or the port configured when installing the VM).
-On Windows you can use [putty](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html) and [WinSCP](http://winscp.net/eng/index.php).
-
-For example, to connect as `root` from a Linux command prompt use
-
- $ ssh root@localhost -p 22222
- The authenticity of host '[localhost]:22222 ([127.0.0.1]:22222)' can't be established.
- RSA key fingerprint is ae:f5:c8:9f:17:c6:c7:1b:c2:1b:12:31:1d:bb:d0:c7.
- Are you sure you want to continue connecting (yes/no)? yes
- Warning: Permanently added '[localhost]:22222' (RSA) to the list of known hosts.
- root@localhost's password: (enter root password configured during install)
-
- The programs included with the Debian GNU/Linux system are free software;
- the exact distribution terms for each program are described in the
- individual files in /usr/share/doc/*/copyright.
-
- Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
- permitted by applicable law.
- root@debian:~#
-
-Replace `root` with `debian` to log in as user.
-
-Setting up Debian for Gitian building
---------------------------------------
-
-In this section we will be setting up the Debian installation for Gitian building.
-
-First we need to log in as `root` to set up dependencies and make sure that our
-user can use the sudo command. Type/paste the following in the terminal:
-
-```bash
-apt-get install git ruby sudo apt-cacher-ng qemu-utils debootstrap lxc python-cheetah parted kpartx bridge-utils make ubuntu-archive-keyring curl
-adduser debian sudo
-```
-
-Then set up LXC and the rest with the following, which is a complex jumble of settings and workarounds:
-
-```bash
-# the version of lxc-start in Debian needs to run as root, so make sure
-# that the build script can execute it without providing a password
-echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-start" > /etc/sudoers.d/gitian-lxc
-echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-execute" >> /etc/sudoers.d/gitian-lxc
-# make /etc/rc.local script that sets up bridge between guest and host
-echo '#!/bin/sh -e' > /etc/rc.local
-echo 'brctl addbr br0' >> /etc/rc.local
-echo 'ifconfig br0 10.0.3.2/24 up' >> /etc/rc.local
-echo 'iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE' >> /etc/rc.local
-echo 'echo 1 > /proc/sys/net/ipv4/ip_forward' >> /etc/rc.local
-echo 'exit 0' >> /etc/rc.local
-# make sure that USE_LXC is always set when logging in as debian,
-# and configure LXC IP addresses
-echo 'export USE_LXC=1' >> /home/debian/.profile
-echo 'export GITIAN_HOST_IP=10.0.3.2' >> /home/debian/.profile
-echo 'export LXC_GUEST_IP=10.0.3.5' >> /home/debian/.profile
-reboot
-```
-
-At the end the VM is rebooted to make sure that the changes take effect. The steps in this
-section only need to be performed once.
-
-Installing Gitian
-------------------
-
-Re-login as the user `debian` that was created during installation.
-The rest of the steps in this guide will be performed as that user.
-
-There is no `python-vm-builder` package in Debian, so we need to install it from source ourselves,
-
-```bash
-wget http://archive.ubuntu.com/ubuntu/pool/universe/v/vm-builder/vm-builder_0.12.4+bzr494.orig.tar.gz
-echo "76cbf8c52c391160b2641e7120dbade5afded713afaa6032f733a261f13e6a8e vm-builder_0.12.4+bzr494.orig.tar.gz" | sha256sum -c
-# (verification -- must return OK)
-tar -zxvf vm-builder_0.12.4+bzr494.orig.tar.gz
-cd vm-builder-0.12.4+bzr494
-sudo python setup.py install
-cd ..
-```
-
-**Note**: When sudo asks for a password, enter the password for the user *debian* not for *root*.
-
-Clone the git repositories for bitcoin and Gitian.
-
-```bash
-git clone https://github.com/devrandom/gitian-builder.git
-git clone https://github.com/bitcoin/bitcoin
-git clone https://github.com/bitcoin-core/gitian.sigs.git
-```
-
-Setting up the Gitian image
--------------------------
-
-Gitian needs a virtual image of the operating system to build in.
-Currently this is Ubuntu Trusty x86_64.
-This image will be copied and used every time that a build is started to
-make sure that the build is deterministic.
-Creating the image will take a while, but only has to be done once.
-
-Execute the following as user `debian`:
-
-```bash
-cd gitian-builder
-bin/make-base-vm --lxc --arch amd64 --suite trusty
-```
-
-There will be a lot of warnings printed during the build of the image. These can be ignored.
-
-**Note**: When sudo asks for a password, enter the password for the user *debian* not for *root*.
-
-Getting and building the inputs
---------------------------------
-
-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-build.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.
-
-Building Bitcoin Core
-----------------
-
-To build Bitcoin Core (for Linux, OS X and Windows) just follow the steps under 'perform
-Gitian builds' in [doc/release-process.md](release-process.md#perform-gitian-builds) in the bitcoin repository.
-
-This may take some time as it will build all the dependencies needed for each descriptor.
-These dependencies will be cached after a successful build to avoid rebuilding them when possible.
-
-At any time you can check the package installation and build progress with
-
-```bash
-tail -f var/install.log
-tail -f var/build.log
-```
-
-Output from `gbuild` will look something like
-
- Initialized empty Git repository in /home/debian/gitian-builder/inputs/bitcoin/.git/
- remote: Counting objects: 57959, done.
- remote: Total 57959 (delta 0), reused 0 (delta 0), pack-reused 57958
- Receiving objects: 100% (57959/57959), 53.76 MiB | 484.00 KiB/s, done.
- Resolving deltas: 100% (41590/41590), done.
- From https://github.com/bitcoin/bitcoin
- ... (new tags, new branch etc)
- --- Building for trusty amd64 ---
- Stopping target if it is up
- Making a new image copy
- stdin: is not a tty
- Starting target
- Checking if target is up
- Preparing build environment
- Updating apt-get repository (log in var/install.log)
- Installing additional packages (log in var/install.log)
- Grabbing package manifest
- stdin: is not a tty
- Creating build script (var/build-script)
- lxc-start: Connection refused - inotify event with no name (mask 32768)
- Running build script (log in var/build.log)
-
-Building an alternative repository
------------------------------------
-
-If you want to do a test build of a pull on GitHub it can be useful to point
-the Gitian builder at an alternative repository, using the same descriptors
-and inputs.
-
-For example:
-```bash
-URL=https://github.com/laanwj/bitcoin.git
-COMMIT=2014_03_windows_unicode_path
-./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml
-./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml
-./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml
-```
-
-Building fully offline
------------------------
-
-For building fully offline including attaching signatures to unsigned builds, the detached-sigs repository
-and the bitcoin git repository with the desired tag must both be available locally, and then gbuild must be
-told where to find them. It also requires an apt-cacher-ng which is fully-populated but set to offline mode, or
-manually disabling gitian-builder's use of apt-get to update the VM build environment.
-
-To configure apt-cacher-ng as an offline cacher, you will need to first populate its cache with the relevant
-files. You must additionally patch target-bin/bootstrap-fixup to set its apt sources to something other than
-plain archive.ubuntu.com: us.archive.ubuntu.com works.
-
-So, if you use LXC:
-
-```bash
-export PATH="$PATH":/path/to/gitian-builder/libexec
-export USE_LXC=1
-cd /path/to/gitian-builder
-./libexec/make-clean-vm --suite trusty --arch amd64
-
-LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root apt-get update
-LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root \
- -e DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends -y install \
- $( sed -ne '/^packages:/,/[^-] .*/ {/^- .*/{s/"//g;s/- //;p}}' ../bitcoin/contrib/gitian-descriptors/*|sort|uniq )
-LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root apt-get -q -y purge grub
-LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root -e DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade
-```
-
-And then set offline mode for apt-cacher-ng:
-
-```
-/etc/apt-cacher-ng/acng.conf
-[...]
-Offlinemode: 1
-[...]
-
-service apt-cacher-ng restart
-```
-
-Then when building, override the remote URLs that gbuild would otherwise pull from the Gitian descriptors::
-```bash
-
-cd /some/root/path/
-git clone https://github.com/bitcoin-core/bitcoin-detached-sigs.git
-
-BTCPATH=/some/root/path/bitcoin
-SIGPATH=/some/root/path/bitcoin-detached-sigs
-
-./bin/gbuild --url bitcoin=${BTCPATH},signature=${SIGPATH} ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml
-```
-
-Signing externally
--------------------
-
-If you want to do the PGP signing on another device, that's also possible; just define `SIGNER` as mentioned
-and follow the steps in the build process as normal.
-
- gpg: skipped "laanwj": secret key not available
-
-When you execute `gsign` you will get an error from GPG, which can be ignored. Copy the resulting `.assert` files
-in `gitian.sigs` to your signing machine and do
-
-```bash
- gpg --detach-sign ${VERSION}-linux/${SIGNER}/bitcoin-linux-build.assert
- gpg --detach-sign ${VERSION}-win/${SIGNER}/bitcoin-win-build.assert
- gpg --detach-sign ${VERSION}-osx-unsigned/${SIGNER}/bitcoin-osx-build.assert
-```
-
-This will create the `.sig` files that can be committed together with the `.assert` files to assert your
-Gitian build.
-
-Uploading signatures
----------------------
-
-After building and signing you can push your signatures (both the `.assert` and `.assert.sig` files) to the
-[bitcoin-core/gitian.sigs](https://github.com/bitcoin-core/gitian.sigs/) repository, or if that's not possible create a pull
-request. You can also mail the files to Wladimir (laanwj@gmail.com) and he will commit them.
+This file was moved to [the Bitcoin Core documentation repository](https://github.com/bitcoin-core/docs/blob/master/gitian-building.md) at [https://github.com/bitcoin-core/docs](https://github.com/bitcoin-core/docs).
diff --git a/doc/gitian-building/all_files_in_one_partition.png b/doc/gitian-building/all_files_in_one_partition.png
deleted file mode 100644
index 8cbb0d8adc..0000000000
--- a/doc/gitian-building/all_files_in_one_partition.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/create_new_vm.png b/doc/gitian-building/create_new_vm.png
deleted file mode 100644
index dd22428e17..0000000000
--- a/doc/gitian-building/create_new_vm.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/create_vm_file_location_size.png b/doc/gitian-building/create_vm_file_location_size.png
deleted file mode 100644
index 5f77206b6f..0000000000
--- a/doc/gitian-building/create_vm_file_location_size.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/create_vm_hard_disk.png b/doc/gitian-building/create_vm_hard_disk.png
deleted file mode 100644
index 8e29816fab..0000000000
--- a/doc/gitian-building/create_vm_hard_disk.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/create_vm_hard_disk_file_type.png b/doc/gitian-building/create_vm_hard_disk_file_type.png
deleted file mode 100644
index a157211cf5..0000000000
--- a/doc/gitian-building/create_vm_hard_disk_file_type.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/create_vm_memsize.png b/doc/gitian-building/create_vm_memsize.png
deleted file mode 100644
index 6f42cda73f..0000000000
--- a/doc/gitian-building/create_vm_memsize.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/create_vm_storage_physical_hard_disk.png b/doc/gitian-building/create_vm_storage_physical_hard_disk.png
deleted file mode 100644
index cee16a6c63..0000000000
--- a/doc/gitian-building/create_vm_storage_physical_hard_disk.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_10_configure_clock.png b/doc/gitian-building/debian_install_10_configure_clock.png
deleted file mode 100644
index 7cda038ae4..0000000000
--- a/doc/gitian-building/debian_install_10_configure_clock.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_11_partition_disks.png b/doc/gitian-building/debian_install_11_partition_disks.png
deleted file mode 100644
index 2a648c517f..0000000000
--- a/doc/gitian-building/debian_install_11_partition_disks.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_12_choose_disk.png b/doc/gitian-building/debian_install_12_choose_disk.png
deleted file mode 100644
index 0f3acc498e..0000000000
--- a/doc/gitian-building/debian_install_12_choose_disk.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_14_finish.png b/doc/gitian-building/debian_install_14_finish.png
deleted file mode 100644
index c8ef0b37ad..0000000000
--- a/doc/gitian-building/debian_install_14_finish.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_15_write_changes.png b/doc/gitian-building/debian_install_15_write_changes.png
deleted file mode 100644
index d8de00dec6..0000000000
--- a/doc/gitian-building/debian_install_15_write_changes.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_16_choose_a_mirror.png b/doc/gitian-building/debian_install_16_choose_a_mirror.png
deleted file mode 100644
index 0bd985b38c..0000000000
--- a/doc/gitian-building/debian_install_16_choose_a_mirror.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_18_proxy_settings.png b/doc/gitian-building/debian_install_18_proxy_settings.png
deleted file mode 100644
index 2c19919f64..0000000000
--- a/doc/gitian-building/debian_install_18_proxy_settings.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_19_software_selection.png b/doc/gitian-building/debian_install_19_software_selection.png
deleted file mode 100644
index 5430456b14..0000000000
--- a/doc/gitian-building/debian_install_19_software_selection.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_1_boot_menu.png b/doc/gitian-building/debian_install_1_boot_menu.png
deleted file mode 100644
index 216502e1c6..0000000000
--- a/doc/gitian-building/debian_install_1_boot_menu.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_20_install_grub.png b/doc/gitian-building/debian_install_20_install_grub.png
deleted file mode 100644
index d853c15871..0000000000
--- a/doc/gitian-building/debian_install_20_install_grub.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_21_install_grub_bootloader.png b/doc/gitian-building/debian_install_21_install_grub_bootloader.png
deleted file mode 100644
index 493ab806a6..0000000000
--- a/doc/gitian-building/debian_install_21_install_grub_bootloader.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_22_finish_installation.png b/doc/gitian-building/debian_install_22_finish_installation.png
deleted file mode 100644
index 7c4445585b..0000000000
--- a/doc/gitian-building/debian_install_22_finish_installation.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_2_select_a_language.png b/doc/gitian-building/debian_install_2_select_a_language.png
deleted file mode 100644
index 0228ae2c01..0000000000
--- a/doc/gitian-building/debian_install_2_select_a_language.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_3_select_location.png b/doc/gitian-building/debian_install_3_select_location.png
deleted file mode 100644
index 7b18fba975..0000000000
--- a/doc/gitian-building/debian_install_3_select_location.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_4_configure_keyboard.png b/doc/gitian-building/debian_install_4_configure_keyboard.png
deleted file mode 100644
index 8e46117de4..0000000000
--- a/doc/gitian-building/debian_install_4_configure_keyboard.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_5_configure_the_network.png b/doc/gitian-building/debian_install_5_configure_the_network.png
deleted file mode 100644
index 8e3720f243..0000000000
--- a/doc/gitian-building/debian_install_5_configure_the_network.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_6_domain_name.png b/doc/gitian-building/debian_install_6_domain_name.png
deleted file mode 100644
index 7a986d92f4..0000000000
--- a/doc/gitian-building/debian_install_6_domain_name.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_6a_set_up_root_password.png b/doc/gitian-building/debian_install_6a_set_up_root_password.png
deleted file mode 100644
index dcade11967..0000000000
--- a/doc/gitian-building/debian_install_6a_set_up_root_password.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_7_set_up_user_fullname.png b/doc/gitian-building/debian_install_7_set_up_user_fullname.png
deleted file mode 100644
index 6763c6e08a..0000000000
--- a/doc/gitian-building/debian_install_7_set_up_user_fullname.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_8_set_up_username.png b/doc/gitian-building/debian_install_8_set_up_username.png
deleted file mode 100644
index bb04de96d2..0000000000
--- a/doc/gitian-building/debian_install_8_set_up_username.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_install_9_user_password.png b/doc/gitian-building/debian_install_9_user_password.png
deleted file mode 100644
index 981f1181d7..0000000000
--- a/doc/gitian-building/debian_install_9_user_password.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/debian_root_login.png b/doc/gitian-building/debian_root_login.png
deleted file mode 100644
index 14cdd5ba5b..0000000000
--- a/doc/gitian-building/debian_root_login.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/network_settings.png b/doc/gitian-building/network_settings.png
deleted file mode 100644
index 9e714fd154..0000000000
--- a/doc/gitian-building/network_settings.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/port_forwarding_rules.png b/doc/gitian-building/port_forwarding_rules.png
deleted file mode 100644
index 9e1fa2af20..0000000000
--- a/doc/gitian-building/port_forwarding_rules.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/select_startup_disk.png b/doc/gitian-building/select_startup_disk.png
deleted file mode 100644
index 59bc093e2c..0000000000
--- a/doc/gitian-building/select_startup_disk.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitian-building/system_settings.png b/doc/gitian-building/system_settings.png
deleted file mode 100644
index a5720ef3a3..0000000000
--- a/doc/gitian-building/system_settings.png
+++ /dev/null
Binary files differ
diff --git a/doc/man/bitcoin-cli.1 b/doc/man/bitcoin-cli.1
index 0493241b1e..6787638443 100644
--- a/doc/man/bitcoin-cli.1
+++ b/doc/man/bitcoin-cli.1
@@ -1,9 +1,9 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4.
-.TH BITCOIN-CLI "1" "February 2017" "bitcoin-cli v0.14.99.0" "User Commands"
+.TH BITCOIN-CLI "1" "September 2017" "bitcoin-cli v0.15.99.0" "User Commands"
.SH NAME
-bitcoin-cli \- manual page for bitcoin-cli v0.14.99.0
+bitcoin-cli \- manual page for bitcoin-cli v0.15.99.0
.SH DESCRIPTION
-Bitcoin Core RPC client version v0.14.99.0
+Bitcoin Core RPC client version v0.15.99.0
.SS "Usage:"
.TP
bitcoin\-cli [options] <command> [params]
@@ -64,12 +64,29 @@ Password for JSON\-RPC connections
.HP
\fB\-rpcclienttimeout=\fR<n>
.IP
-Timeout during HTTP requests (default: 900)
+Timeout in seconds during HTTP requests, or 0 for no timeout. (default:
+900)
+.HP
+\fB\-stdinrpcpass\fR
+.TP
+Read RPC password from standard input as a single line.
+When combined
+.IP
+with \fB\-stdin\fR, the first line from standard input is used for the
+RPC password.
.HP
\fB\-stdin\fR
.IP
Read extra arguments from standard input, one per line until EOF/Ctrl\-D
-(recommended for sensitive information such as passphrases)
+(recommended for sensitive information such as passphrases).
+When combined with \fB\-stdinrpcpass\fR, the first line from standard
+input is used for the RPC password.
+.HP
+\fB\-rpcwallet=\fR<walletname>
+.IP
+Send RPC for non\-default wallet on RPC server (argument is wallet
+filename in bitcoind directory, required if bitcoind/\-Qt runs
+with multiple wallets)
.SH COPYRIGHT
Copyright (C) 2009-2017 The Bitcoin Core developers
diff --git a/doc/man/bitcoin-qt.1 b/doc/man/bitcoin-qt.1
index ce252612e5..ae35d50ac3 100644
--- a/doc/man/bitcoin-qt.1
+++ b/doc/man/bitcoin-qt.1
@@ -1,9 +1,9 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4.
-.TH BITCOIN-QT "1" "February 2017" "bitcoin-qt v0.14.99.0" "User Commands"
+.TH BITCOIN-QT "1" "September 2017" "bitcoin-qt v0.15.99.0" "User Commands"
.SH NAME
-bitcoin-qt \- manual page for bitcoin-qt v0.14.99.0
+bitcoin-qt \- manual page for bitcoin-qt v0.15.99.0
.SH DESCRIPTION
-Bitcoin Core version v0.14.99.0 (64\-bit)
+Bitcoin Core version v0.15.99.0 (64\-bit)
Usage:
.IP
bitcoin\-qt [command\-line options]
@@ -32,9 +32,9 @@ block hash)
If this block is in the chain assume that it and its ancestors are valid
and potentially skip their script verification (0 to verify all,
default:
-00000000000000000013176bf8d7dfeab4e1db31dc93bc311b436e82ab226b90,
+0000000000000000003b9ce759c2a087d52abc4266f8f4ebd6d768b89defa50a,
testnet:
-00000000000128796ee387cf110ccb9d2f36cffaf7f73079c995377c65ac0dcc)
+0000000002e9e7b00e1f6dc5123a04aad68dd0f0968d8c7aa45f6640795c37b1)
.HP
\fB\-conf=\fR<file>
.IP
@@ -46,7 +46,7 @@ Specify data directory
.HP
\fB\-dbcache=\fR<n>
.IP
-Set database cache size in megabytes (4 to 16384, default: 300)
+Set database cache size in megabytes (4 to 16384, default: 450)
.HP
\fB\-loadblock=\fR<file>
.IP
@@ -65,6 +65,10 @@ Keep the transaction memory pool below <n> megabytes (default: 300)
Do not keep transactions in the mempool longer than <n> hours (default:
336)
.HP
+\fB\-persistmempool\fR
+.IP
+Whether to save the mempool on shutdown and load on restart (default: 1)
+.HP
\fB\-blockreconstructionextratxn=\fR<n>
.IP
Extra transactions to keep in memory for compact block reconstructions
@@ -131,8 +135,8 @@ for IPv6
.HP
\fB\-connect=\fR<ip>
.IP
-Connect only to the specified node(s); \fB\-noconnect\fR or \fB\-connect\fR=\fI\,0\/\fR alone to
-disable automatic connections
+Connect only to the specified node(s); \fB\-connect\fR=\fI\,0\/\fR disables automatic
+connections
.HP
\fB\-discover\fR
.IP
@@ -146,7 +150,7 @@ Allow DNS lookups for \fB\-addnode\fR, \fB\-seednode\fR and \fB\-connect\fR (def
\fB\-dnsseed\fR
.IP
Query for peer addresses via DNS lookup, if low on addresses (default: 1
-unless \fB\-connect\fR/\-noconnect)
+unless \fB\-connect\fR used)
.HP
\fB\-externalip=\fR<ip>
.IP
@@ -158,8 +162,7 @@ Always query for peer addresses via DNS lookup (default: 0)
.HP
\fB\-listen\fR
.IP
-Accept connections from outside (default: 1 if no \fB\-proxy\fR or
-\fB\-connect\fR/\-noconnect)
+Accept connections from outside (default: 1 if no \fB\-proxy\fR or \fB\-connect\fR)
.HP
\fB\-listenonion\fR
.IP
@@ -214,11 +217,6 @@ Connect through SOCKS5 proxy
Randomize credentials for every proxy connection. This enables Tor
stream isolation (default: 1)
.HP
-\fB\-rpcserialversion\fR
-.IP
-Sets the serialization of raw transaction or block hex returned in
-non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1)
-.HP
\fB\-seednode=\fR<ip>
.IP
Connect to a node to retrieve peer addresses, and disconnect
@@ -253,16 +251,6 @@ 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
.HP
-\fB\-whitelistrelay\fR
-.IP
-Accept relayed transactions received from whitelisted peers even when
-not relaying transactions (default: 1)
-.HP
-\fB\-whitelistforcerelay\fR
-.IP
-Force relay of transactions from whitelisted peers even if they violate
-local relay policy (default: 1)
-.HP
\fB\-maxuploadtarget=\fR<n>
.IP
Tries to keep outbound traffic under the given target (in MiB per 24h),
@@ -276,13 +264,21 @@ Do not load the wallet and disable wallet RPC calls
.HP
\fB\-keypool=\fR<n>
.IP
-Set key pool size to <n> (default: 100)
+Set key pool size to <n> (default: 1000)
.HP
\fB\-fallbackfee=\fR<amt>
.IP
A fee rate (in BTC/kB) that will be used when fee estimation has
insufficient data (default: 0.0002)
.HP
+\fB\-discardfee=\fR<amt>
+.IP
+The fee rate (in BTC/kB) that indicates your tolerance for discarding
+change by adding it to the fee (default: 0.0001). Note: An output
+is discarded if it is dust at this rate, but we will always
+discard up to the dust relay fee and a discard fee above that is
+limited by the fee estimate for the longest target
+.HP
\fB\-mintxfee=\fR<amt>
.IP
Fees (in BTC/kB) smaller than this are considered zero fee for
@@ -309,11 +305,6 @@ Spend unconfirmed change when sending transactions (default: 1)
If paytxfee is not set, include enough fee so transactions begin
confirmation on average within n blocks (default: 6)
.HP
-\fB\-usehd\fR
-.IP
-Use hierarchical deterministic key generation (HD) after BIP32. Only has
-effect during wallet creation/first start (default: 1)
-.HP
\fB\-walletrbf\fR
.IP
Send transactions with full\-RBF opt\-in enabled (default: 0)
@@ -370,10 +361,16 @@ Append comment to the user agent string
.IP
Output debugging information (default: 0, supplying <category> is
optional). If <category> is not supplied or if <category> = 1,
-output all debugging information.<category> can be: addrman,
-alert, bench, cmpctblock, coindb, db, http, libevent, lock,
-mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc,
-selectcoins, tor, zmq, qt.
+output all debugging information. <category> can be: net, tor,
+mempool, http, bench, zmq, db, rpc, estimatefee, addrman,
+selectcoins, reindex, cmpctblock, rand, prune, proxy, mempoolrej,
+libevent, coindb, qt, leveldb.
+.HP
+\fB\-debugexclude=\fR<category>
+.IP
+Exclude debugging information for a category. Can be used in conjunction
+with \fB\-debug\fR=\fI\,1\/\fR to output debug logs for all categories except one
+or more specified categories.
.HP
\fB\-help\-debug\fR
.IP
@@ -387,11 +384,6 @@ Include IP addresses in debug output (default: 0)
.IP
Prepend debug output with timestamp (default: 1)
.HP
-\fB\-minrelaytxfee=\fR<amt>
-.IP
-Fees (in BTC/kB) smaller than this are considered zero fee for relaying,
-mining and transaction creation (default: 0.00001)
-.HP
\fB\-maxtxfee=\fR<amt>
.IP
Maximum total fees (in BTC) to use in a single wallet transaction or raw
@@ -431,21 +423,32 @@ Maximum size of data in data carrier transactions we relay and mine
\fB\-mempoolreplacement\fR
.IP
Enable transaction replacement in the memory pool (default: 1)
+.HP
+\fB\-minrelaytxfee=\fR<amt>
+.IP
+Fees (in BTC/kB) smaller than this are considered zero fee for relaying,
+mining and transaction creation (default: 0.00001)
+.HP
+\fB\-whitelistrelay\fR
+.IP
+Accept relayed transactions received from whitelisted peers even when
+not relaying transactions (default: 1)
+.HP
+\fB\-whitelistforcerelay\fR
+.IP
+Force relay of transactions from whitelisted peers even if they violate
+local relay policy (default: 1)
.PP
Block creation options:
.HP
\fB\-blockmaxweight=\fR<n>
.IP
-Set maximum BIP141 block weight (default: 3000000)
+Set maximum BIP141 block weight (default: 3996000)
.HP
\fB\-blockmaxsize=\fR<n>
.IP
-Set maximum block size in bytes (default: 750000)
-.HP
-\fB\-blockprioritysize=\fR<n>
-.IP
-Set maximum size of high\-priority/low\-fee transactions in bytes
-(default: 0)
+Set maximum BIP141 block weight to this * 4. Deprecated, use
+blockmaxweight
.HP
\fB\-blockmintxfee=\fR<amt>
.IP
@@ -462,11 +465,14 @@ Accept command line and JSON\-RPC commands
.IP
Accept public REST requests (default: 0)
.HP
-\fB\-rpcbind=\fR<addr>
+\fB\-rpcbind=\fR<addr>[:port]
.IP
-Bind to given address to listen for JSON\-RPC connections. Use
-[host]:port notation for IPv6. This option can be specified
-multiple times (default: bind to all interfaces)
+Bind to given address to listen for JSON\-RPC connections. This option is
+ignored unless \fB\-rpcallowip\fR is also passed. Port is optional and
+overrides \fB\-rpcport\fR. Use [host]:port notation for IPv6. This
+option can be specified multiple times (default: 127.0.0.1 and
+::1 i.e., localhost, or if \fB\-rpcallowip\fR has been specified,
+0.0.0.0 and :: i.e., all addresses)
.HP
\fB\-rpccookiefile=\fR<loc>
.IP
@@ -501,6 +507,11 @@ single IP (e.g. 1.2.3.4), a network/netmask (e.g.
1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This
option can be specified multiple times
.HP
+\fB\-rpcserialversion\fR
+.IP
+Sets the serialization of raw transaction or block hex returned in
+non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1)
+.HP
\fB\-rpcthreads=\fR<n>
.IP
Set the number of threads to service RPC calls (default: 4)
diff --git a/doc/man/bitcoin-tx.1 b/doc/man/bitcoin-tx.1
index 98adf2f5b1..8b72fbde05 100644
--- a/doc/man/bitcoin-tx.1
+++ b/doc/man/bitcoin-tx.1
@@ -1,9 +1,9 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4.
-.TH BITCOIN-TX "1" "February 2017" "bitcoin-tx v0.14.99.0" "User Commands"
+.TH BITCOIN-TX "1" "September 2017" "bitcoin-tx v0.15.99.0" "User Commands"
.SH NAME
-bitcoin-tx \- manual page for bitcoin-tx v0.14.99.0
+bitcoin-tx \- manual page for bitcoin-tx v0.15.99.0
.SH DESCRIPTION
-Bitcoin Core bitcoin\-tx utility version v0.14.99.0
+Bitcoin Core bitcoin\-tx utility version v0.15.99.0
.SS "Usage:"
.TP
bitcoin\-tx [options] <hex\-tx> [commands]
@@ -63,6 +63,11 @@ nversion=N
.IP
Set TX version to N
.IP
+replaceable(=N)
+.IP
+Set RBF opt\-in sequence number for input N (if not provided, opt\-in all
+available inputs)
+.IP
outaddr=VALUE:ADDRESS
.IP
Add address\-based output to TX
diff --git a/doc/man/bitcoind.1 b/doc/man/bitcoind.1
index fb066e0c6f..baf747436f 100644
--- a/doc/man/bitcoind.1
+++ b/doc/man/bitcoind.1
@@ -1,9 +1,9 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4.
-.TH BITCOIND "1" "February 2017" "bitcoind v0.14.99.0" "User Commands"
+.TH BITCOIND "1" "September 2017" "bitcoind v0.15.99.0" "User Commands"
.SH NAME
-bitcoind \- manual page for bitcoind v0.14.99.0
+bitcoind \- manual page for bitcoind v0.15.99.0
.SH DESCRIPTION
-Bitcoin Core Daemon version v0.14.99.0
+Bitcoin Core Daemon version v0.15.99.0
.SS "Usage:"
.TP
bitcoind [options]
@@ -33,9 +33,9 @@ block hash)
If this block is in the chain assume that it and its ancestors are valid
and potentially skip their script verification (0 to verify all,
default:
-00000000000000000013176bf8d7dfeab4e1db31dc93bc311b436e82ab226b90,
+0000000000000000003b9ce759c2a087d52abc4266f8f4ebd6d768b89defa50a,
testnet:
-00000000000128796ee387cf110ccb9d2f36cffaf7f73079c995377c65ac0dcc)
+0000000002e9e7b00e1f6dc5123a04aad68dd0f0968d8c7aa45f6640795c37b1)
.HP
\fB\-conf=\fR<file>
.IP
@@ -51,7 +51,7 @@ Specify data directory
.HP
\fB\-dbcache=\fR<n>
.IP
-Set database cache size in megabytes (4 to 16384, default: 300)
+Set database cache size in megabytes (4 to 16384, default: 450)
.HP
\fB\-loadblock=\fR<file>
.IP
@@ -70,6 +70,10 @@ Keep the transaction memory pool below <n> megabytes (default: 300)
Do not keep transactions in the mempool longer than <n> hours (default:
336)
.HP
+\fB\-persistmempool\fR
+.IP
+Whether to save the mempool on shutdown and load on restart (default: 1)
+.HP
\fB\-blockreconstructionextratxn=\fR<n>
.IP
Extra transactions to keep in memory for compact block reconstructions
@@ -136,8 +140,8 @@ for IPv6
.HP
\fB\-connect=\fR<ip>
.IP
-Connect only to the specified node(s); \fB\-noconnect\fR or \fB\-connect\fR=\fI\,0\/\fR alone to
-disable automatic connections
+Connect only to the specified node(s); \fB\-connect\fR=\fI\,0\/\fR disables automatic
+connections
.HP
\fB\-discover\fR
.IP
@@ -151,7 +155,7 @@ Allow DNS lookups for \fB\-addnode\fR, \fB\-seednode\fR and \fB\-connect\fR (def
\fB\-dnsseed\fR
.IP
Query for peer addresses via DNS lookup, if low on addresses (default: 1
-unless \fB\-connect\fR/\-noconnect)
+unless \fB\-connect\fR used)
.HP
\fB\-externalip=\fR<ip>
.IP
@@ -163,8 +167,7 @@ Always query for peer addresses via DNS lookup (default: 0)
.HP
\fB\-listen\fR
.IP
-Accept connections from outside (default: 1 if no \fB\-proxy\fR or
-\fB\-connect\fR/\-noconnect)
+Accept connections from outside (default: 1 if no \fB\-proxy\fR or \fB\-connect\fR)
.HP
\fB\-listenonion\fR
.IP
@@ -219,11 +222,6 @@ Connect through SOCKS5 proxy
Randomize credentials for every proxy connection. This enables Tor
stream isolation (default: 1)
.HP
-\fB\-rpcserialversion\fR
-.IP
-Sets the serialization of raw transaction or block hex returned in
-non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1)
-.HP
\fB\-seednode=\fR<ip>
.IP
Connect to a node to retrieve peer addresses, and disconnect
@@ -258,16 +256,6 @@ 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
.HP
-\fB\-whitelistrelay\fR
-.IP
-Accept relayed transactions received from whitelisted peers even when
-not relaying transactions (default: 1)
-.HP
-\fB\-whitelistforcerelay\fR
-.IP
-Force relay of transactions from whitelisted peers even if they violate
-local relay policy (default: 1)
-.HP
\fB\-maxuploadtarget=\fR<n>
.IP
Tries to keep outbound traffic under the given target (in MiB per 24h),
@@ -281,13 +269,21 @@ Do not load the wallet and disable wallet RPC calls
.HP
\fB\-keypool=\fR<n>
.IP
-Set key pool size to <n> (default: 100)
+Set key pool size to <n> (default: 1000)
.HP
\fB\-fallbackfee=\fR<amt>
.IP
A fee rate (in BTC/kB) that will be used when fee estimation has
insufficient data (default: 0.0002)
.HP
+\fB\-discardfee=\fR<amt>
+.IP
+The fee rate (in BTC/kB) that indicates your tolerance for discarding
+change by adding it to the fee (default: 0.0001). Note: An output
+is discarded if it is dust at this rate, but we will always
+discard up to the dust relay fee and a discard fee above that is
+limited by the fee estimate for the longest target
+.HP
\fB\-mintxfee=\fR<amt>
.IP
Fees (in BTC/kB) smaller than this are considered zero fee for
@@ -314,11 +310,6 @@ Spend unconfirmed change when sending transactions (default: 1)
If paytxfee is not set, include enough fee so transactions begin
confirmation on average within n blocks (default: 6)
.HP
-\fB\-usehd\fR
-.IP
-Use hierarchical deterministic key generation (HD) after BIP32. Only has
-effect during wallet creation/first start (default: 1)
-.HP
\fB\-walletrbf\fR
.IP
Send transactions with full\-RBF opt\-in enabled (default: 0)
@@ -375,10 +366,16 @@ Append comment to the user agent string
.IP
Output debugging information (default: 0, supplying <category> is
optional). If <category> is not supplied or if <category> = 1,
-output all debugging information.<category> can be: addrman,
-alert, bench, cmpctblock, coindb, db, http, libevent, lock,
-mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc,
-selectcoins, tor, zmq.
+output all debugging information. <category> can be: net, tor,
+mempool, http, bench, zmq, db, rpc, estimatefee, addrman,
+selectcoins, reindex, cmpctblock, rand, prune, proxy, mempoolrej,
+libevent, coindb, qt, leveldb.
+.HP
+\fB\-debugexclude=\fR<category>
+.IP
+Exclude debugging information for a category. Can be used in conjunction
+with \fB\-debug\fR=\fI\,1\/\fR to output debug logs for all categories except one
+or more specified categories.
.HP
\fB\-help\-debug\fR
.IP
@@ -392,11 +389,6 @@ Include IP addresses in debug output (default: 0)
.IP
Prepend debug output with timestamp (default: 1)
.HP
-\fB\-minrelaytxfee=\fR<amt>
-.IP
-Fees (in BTC/kB) smaller than this are considered zero fee for relaying,
-mining and transaction creation (default: 0.00001)
-.HP
\fB\-maxtxfee=\fR<amt>
.IP
Maximum total fees (in BTC) to use in a single wallet transaction or raw
@@ -436,21 +428,32 @@ Maximum size of data in data carrier transactions we relay and mine
\fB\-mempoolreplacement\fR
.IP
Enable transaction replacement in the memory pool (default: 1)
+.HP
+\fB\-minrelaytxfee=\fR<amt>
+.IP
+Fees (in BTC/kB) smaller than this are considered zero fee for relaying,
+mining and transaction creation (default: 0.00001)
+.HP
+\fB\-whitelistrelay\fR
+.IP
+Accept relayed transactions received from whitelisted peers even when
+not relaying transactions (default: 1)
+.HP
+\fB\-whitelistforcerelay\fR
+.IP
+Force relay of transactions from whitelisted peers even if they violate
+local relay policy (default: 1)
.PP
Block creation options:
.HP
\fB\-blockmaxweight=\fR<n>
.IP
-Set maximum BIP141 block weight (default: 3000000)
+Set maximum BIP141 block weight (default: 3996000)
.HP
\fB\-blockmaxsize=\fR<n>
.IP
-Set maximum block size in bytes (default: 750000)
-.HP
-\fB\-blockprioritysize=\fR<n>
-.IP
-Set maximum size of high\-priority/low\-fee transactions in bytes
-(default: 0)
+Set maximum BIP141 block weight to this * 4. Deprecated, use
+blockmaxweight
.HP
\fB\-blockmintxfee=\fR<amt>
.IP
@@ -467,11 +470,14 @@ Accept command line and JSON\-RPC commands
.IP
Accept public REST requests (default: 0)
.HP
-\fB\-rpcbind=\fR<addr>
+\fB\-rpcbind=\fR<addr>[:port]
.IP
-Bind to given address to listen for JSON\-RPC connections. Use
-[host]:port notation for IPv6. This option can be specified
-multiple times (default: bind to all interfaces)
+Bind to given address to listen for JSON\-RPC connections. This option is
+ignored unless \fB\-rpcallowip\fR is also passed. Port is optional and
+overrides \fB\-rpcport\fR. Use [host]:port notation for IPv6. This
+option can be specified multiple times (default: 127.0.0.1 and
+::1 i.e., localhost, or if \fB\-rpcallowip\fR has been specified,
+0.0.0.0 and :: i.e., all addresses)
.HP
\fB\-rpccookiefile=\fR<loc>
.IP
@@ -506,6 +512,11 @@ single IP (e.g. 1.2.3.4), a network/netmask (e.g.
1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This
option can be specified multiple times
.HP
+\fB\-rpcserialversion\fR
+.IP
+Sets the serialization of raw transaction or block hex returned in
+non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1)
+.HP
\fB\-rpcthreads=\fR<n>
.IP
Set the number of threads to service RPC calls (default: 4)
diff --git a/doc/release-notes.md b/doc/release-notes.md
index aa1d1bea14..78caddc8f0 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -8,7 +8,7 @@ Bitcoin Core version *version* is now available from:
This is a new major version release, including new features, various bugfixes
and performance improvements, as well as updated translations.
-Please report bugs using the issue tracker at github:
+Please report bugs using the issue tracker at GitHub:
<https://github.com/bitcoin/bitcoin/issues>
@@ -20,8 +20,8 @@ How to Upgrade
==============
If you are running an older version, shut it down. Wait until it has completely
-shut down (which might take a few minutes for older versions), then run the
-installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac)
+shut down (which might take a few minutes for older versions), then run the
+installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac)
or `bitcoind`/`bitcoin-qt` (on Linux).
The first time you run version 0.15.0, your chainstate database will be converted to a
@@ -56,6 +56,50 @@ frequently tested on them.
Notable changes
===============
+GCC 4.8.x
+--------------
+The minimum version of GCC required to compile Bitcoin Core is now 4.8. No effort will be
+made to support older versions of GCC. See discussion in issue #11732 for more information.
+
+HD-wallets by default
+---------------------
+Due to a backward-incompatible change in the wallet database, wallets created
+with version 0.16.0 will be rejected by previous versions. Also, version 0.16.0
+will only create hierarchical deterministic (HD) wallets.
+
+Custom wallet directories
+---------------------
+The ability to specify a directory other than the default data directory in which to store
+wallets has been added. An existing directory can be specified using the `-walletdir=<dir>`
+argument. Wallets loaded via `-wallet` arguments must be in this wallet directory. Care should be taken
+when choosing a wallet directory location, as if it becomes unavailable during operation,
+funds may be lost.
+
+Default wallet directory change
+--------------------------
+On new installations (if the data directory doesn't exist), wallets will now be stored in a
+new `wallets/` subdirectory inside the data directory. If this `wallets/` subdirectory
+doesn't exist (i.e. on existing nodes), the current datadir root is used instead, as it was.
+
+Low-level RPC changes
+----------------------
+- The deprecated RPC `getinfo` was removed. It is recommended that the more specific RPCs are used:
+ * `getblockchaininfo`
+ * `getnetworkinfo`
+ * `getwalletinfo`
+ * `getmininginfo`
+- The wallet RPC `getreceivedbyaddress` will return an error if called with an address not in the wallet.
+
+Changed command-line options
+-----------------------------
+- `-debuglogfile=<file>` can be used to specify an alternative debug logging file.
+
+Renamed script for creating JSON-RPC credentials
+-----------------------------
+The `share/rpcuser/rpcuser.py` script was renamed to `share/rpcauth/rpcauth.py`. This script can be
+used to create `rpcauth` credentials for a JSON-RPC user.
+
+
Credits
=======
diff --git a/doc/release-notes/release-notes-0.15.0.1.md b/doc/release-notes/release-notes-0.15.0.1.md
new file mode 100644
index 0000000000..53ab02cbb0
--- /dev/null
+++ b/doc/release-notes/release-notes-0.15.0.1.md
@@ -0,0 +1,87 @@
+Bitcoin Core version *0.15.0.1* is now available from:
+
+ <https://bitcoin.org/bin/bitcoin-core-0.15.0.1/>
+
+and
+
+ <https://bitcoincore.org/bin/bitcoin-core-0.15.0.1/>
+
+This is a minor bug fix for 0.15.0.
+
+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/>
+
+How to Upgrade
+==============
+
+If you are running an older version, shut it down. Wait until it has completely
+shut down (which might take a few minutes for older versions), then run the
+installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac)
+or `bitcoind`/`bitcoin-qt` (on Linux).
+
+The first time you run version 0.15.0 or higher, your chainstate database will
+be converted to a new format, which will take anywhere from a few minutes to
+half an hour, depending on the speed of your machine.
+
+The file format of `fee_estimates.dat` changed in version 0.15.0. Hence, a
+downgrade from version 0.15.0 or upgrade to version 0.15.0 will cause all fee
+estimates to be discarded.
+
+Note that the block database format also changed in version 0.8.0 and there is no
+automatic upgrade code from before version 0.8 to version 0.15.0. Upgrading
+directly from 0.7.x and earlier without redownloading the blockchain is not supported.
+However, as usual, old wallet versions are still supported.
+
+Downgrading warning
+-------------------
+
+The chainstate database for this release is not compatible with previous
+releases, so if you run 0.15 and then decide to switch back to any
+older version, you will need to run the old release with the `-reindex-chainstate`
+option to rebuild the chainstate data structures in the old format.
+
+If your node has pruning enabled, this will entail re-downloading and
+processing the entire blockchain.
+
+Compatibility
+==============
+
+Bitcoin Core is extensively tested on multiple operating systems using
+the Linux kernel, macOS 10.8+, and Windows Vista and later. Windows XP is not supported.
+
+Bitcoin Core should also work on most other Unix-like systems but is not
+frequently tested on them.
+
+Notable changes
+===============
+
+GUI startup crash issue
+-------------------------
+
+After upgrade to 0.15.0, some clients would crash at startup because a custom
+fee setting was configured that no longer exists in the GUI. This is a minimal
+patch to avoid this issue from occuring.
+
+0.15.0.1 Change log
+====================
+
+- #11332 `46c8d23` Fix possible crash with invalid nCustomFeeRadio in QSettings (achow101, TheBlueMatt)
+
+Also the manpages were updated, as this was forgotten for 0.15.0.
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- Andrew Chow
+- Matt Corallo
+- Jonas Schnelli
+- 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-notes/release-notes-0.15.0.md b/doc/release-notes/release-notes-0.15.0.md
new file mode 100644
index 0000000000..29816cacf7
--- /dev/null
+++ b/doc/release-notes/release-notes-0.15.0.md
@@ -0,0 +1,878 @@
+Bitcoin Core version *0.15.0* is now available from:
+
+ <https://bitcoin.org/bin/bitcoin-core-0.15.0/>
+
+This is a new major version release, including new features, 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/>
+
+How to Upgrade
+==============
+
+If you are running an older version, shut it down. Wait until it has completely
+shut down (which might take a few minutes for older versions), then run the
+installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac)
+or `bitcoind`/`bitcoin-qt` (on Linux).
+
+The first time you run version 0.15.0, your chainstate database will be converted to a
+new format, which will take anywhere from a few minutes to half an hour,
+depending on the speed of your machine.
+
+The file format of `fee_estimates.dat` changed in version 0.15.0. Hence, a
+downgrade from version 0.15.0 or upgrade to version 0.15.0 will cause all fee
+estimates to be discarded.
+
+Note that the block database format also changed in version 0.8.0 and there is no
+automatic upgrade code from before version 0.8 to version 0.15.0. Upgrading
+directly from 0.7.x and earlier without redownloading the blockchain is not supported.
+However, as usual, old wallet versions are still supported.
+
+Downgrading warning
+-------------------
+
+The chainstate database for this release is not compatible with previous
+releases, so if you run 0.15 and then decide to switch back to any
+older version, you will need to run the old release with the `-reindex-chainstate`
+option to rebuild the chainstate data structures in the old format.
+
+If your node has pruning enabled, this will entail re-downloading and
+processing the entire blockchain.
+
+Compatibility
+==============
+
+Bitcoin Core is extensively tested on multiple operating systems using
+the Linux kernel, macOS 10.8+, and Windows Vista and later. Windows XP is not supported.
+
+Bitcoin Core should also work on most other Unix-like systems but is not
+frequently tested on them.
+
+Notes for 0.15.0
+================
+
+Current SegWit support
+----------------------
+
+Version 0.15.0 supports adding a segregated witness address via the `addwitnessaddress` RPC, but
+please note that this is a testing/expert RPC, which does not guarantee recovery from backup. Only use
+this RPC if you know what you are doing. More complete wallet support for segregated witness is coming
+in a next version.
+
+Rescanning with encrypted wallets
+---------------------------------
+
+As in previous versions, when using an encrypted HD wallet, the keypool cannot be topped up without unlocking
+the wallet. This means that currently, in order to recover from a backup of an encrypted HD wallet, the user
+must unlock the wallet with a really long timeout and manually trigger a rescan, otherwise they risk missing
+some keys when auto-topup cannot run. Unfortunately there is no `rescan` RPC in this version, that will be
+included in a future version, so for now a rescan can be triggered using one of the `import*` commands, using
+a dummy address generated by another (trusted) wallet.
+
+Notable changes
+===============
+
+Performance Improvements
+------------------------
+
+Version 0.15 contains a number of significant performance improvements, which make
+Initial Block Download, startup, transaction and block validation much faster:
+
+- The chainstate database (which is used for tracking UTXOs) has been changed
+ from a per-transaction model to a per-output model (See [PR 10195](https://github.com/bitcoin/bitcoin/pull/10195)). Advantages of this model
+ are that it:
+ - avoids the CPU overhead of deserializing and serializing the unused outputs;
+ - has more predictable memory usage;
+ - uses simpler code;
+ - is adaptable to various future cache flushing strategies.
+
+ As a result, validating the blockchain during Initial Block Download (IBD) and reindex
+ is ~30-40% faster, uses 10-20% less memory, and flushes to disk far less frequently.
+ The only downside is that the on-disk database is 15% larger. During the conversion from the previous format
+ a few extra gigabytes may be used.
+- Earlier versions experienced a spike in memory usage while flushing UTXO updates to disk.
+ As a result, only half of the available memory was actually used as cache, and the other half was
+ reserved to accommodate flushing. This is no longer the case (See [PR 10148](https://github.com/bitcoin/bitcoin/pull/10148)), and the entirety of
+ the available cache (see `-dbcache`) is now actually used as cache. This reduces the flushing
+ frequency by a factor 2 or more.
+- In previous versions, signature validation for transactions has been cached when the
+ transaction is accepted to the mempool. Version 0.15 extends this to cache the entire script
+ validity (See [PR 10192](https://github.com/bitcoin/bitcoin/pull/10192)). This means that if a transaction in a block has already been accepted to the
+ mempool, the scriptSig does not need to be re-evaluated. Empirical tests show that
+ this results in new block validation being 40-50% faster.
+- LevelDB has been upgraded to version 1.20 (See [PR 10544](https://github.com/bitcoin/bitcoin/pull/10544)). This version contains hardware acceleration for CRC
+ on architectures supporting SSE 4.2. As a result, synchronization and block validation are now faster.
+- SHA256 hashing has been optimized for architectures supporting SSE 4 (See [PR 10821](https://github.com/bitcoin/bitcoin/pull/10821)). SHA256 is around
+ 50% faster on supported hardware, which results in around 5% faster IBD and block
+ validation. In version 0.15, SHA256 hardware optimization is disabled in release builds by
+ default, but can be enabled by using `--enable-experimental-asm` when building.
+- Refill of the keypool no longer flushes the wallet between each key which resulted in a ~20x speedup in creating a new wallet. Part of this speedup was used to increase the default keypool to 1000 keys to make recovery more robust. (See [PR 10831](https://github.com/bitcoin/bitcoin/pull/10831)).
+
+Fee Estimation Improvements
+---------------------------
+
+Fee estimation has been significantly improved in version 0.15, with more accurate fee estimates used by the wallet and a wider range of options for advanced users of the `estimatesmartfee` and `estimaterawfee` RPCs (See [PR 10199](https://github.com/bitcoin/bitcoin/pull/10199)).
+
+### Changes to internal logic and wallet behavior
+
+- Internally, estimates are now tracked on 3 different time horizons. This allows for longer targets and means estimates adjust more quickly to changes in conditions.
+- Estimates can now be *conservative* or *economical*. *Conservative* estimates use longer time horizons to produce an estimate which is less susceptible to rapid changes in fee conditions. *Economical* estimates use shorter time horizons and will be more affected by short-term changes in fee conditions. Economical estimates may be considerably lower during periods of low transaction activity (for example over weekends), but may result in transactions being unconfirmed if prevailing fees increase rapidly.
+- By default, the wallet will use conservative fee estimates to increase the reliability of transactions being confirmed within the desired target. For transactions that are marked as replaceable, the wallet will use an economical estimate by default, since the fee can be 'bumped' if the fee conditions change rapidly (See [PR 10589](https://github.com/bitcoin/bitcoin/pull/10589)).
+- Estimates can now be made for confirmation targets up to 1008 blocks (one week).
+- More data on historical fee rates is stored, leading to more precise fee estimates.
+- Transactions which leave the mempool due to eviction or other non-confirmed reasons are now taken into account by the fee estimation logic, leading to more accurate fee estimates.
+- The fee estimation logic will make sure enough data has been gathered to return a meaningful estimate. If there is insufficient data, a fallback default fee is used.
+
+### Changes to fee estimate RPCs
+
+- The `estimatefee` RPC is now deprecated in favor of using only `estimatesmartfee` (which is the implementation used by the GUI)
+- The `estimatesmartfee` RPC interface has been changed (See [PR 10707](https://github.com/bitcoin/bitcoin/pull/10707)):
+ - The `nblocks` argument has been renamed to `conf_target` (to be consistent with other RPC methods).
+ - An `estimate_mode` argument has been added. This argument takes one of the following strings: `CONSERVATIVE`, `ECONOMICAL` or `UNSET` (which defaults to `CONSERVATIVE`).
+ - The RPC return object now contains an `errors` member, which returns errors encountered during processing.
+ - If Bitcoin Core has not been running for long enough and has not seen enough blocks or transactions to produce an accurate fee estimation, an error will be returned (previously a value of -1 was used to indicate an error, which could be confused for a feerate).
+- A new `estimaterawfee` RPC is added to provide raw fee data. External clients can query and use this data in their own fee estimation logic.
+
+Multi-wallet support
+--------------------
+
+Bitcoin Core now supports loading multiple, separate wallets (See [PR 8694](https://github.com/bitcoin/bitcoin/pull/8694), [PR 10849](https://github.com/bitcoin/bitcoin/pull/10849)). The wallets are completely separated, with individual balances, keys and received transactions.
+
+Multi-wallet is enabled by using more than one `-wallet` argument when starting Bitcoin, either on the command line or in the Bitcoin config file.
+
+**In Bitcoin-Qt, only the first wallet will be displayed and accessible for creating and signing transactions.** GUI selectable multiple wallets will be supported in a future version. However, even in 0.15 other loaded wallets will remain synchronized to the node's current tip in the background. This can be useful if running a pruned node, since loading a wallet where the most recent sync is beyond the pruned height results in having to download and revalidate the whole blockchain. Continuing to synchronize all wallets in the background avoids this problem.
+
+Bitcoin Core 0.15.0 contains the following changes to the RPC interface and `bitcoin-cli` for multi-wallet:
+
+* When running Bitcoin Core with a single wallet, there are **no** changes to the RPC interface or `bitcoin-cli`. All RPC calls and `bitcoin-cli` commands continue to work as before.
+* When running Bitcoin Core with multi-wallet, all *node-level* RPC methods continue to work as before. HTTP RPC requests should be send to the normal `<RPC IP address>:<RPC port>/` endpoint, and `bitcoin-cli` commands should be run as before. A *node-level* RPC method is any method which does not require access to the wallet.
+* When running Bitcoin Core with multi-wallet, *wallet-level* RPC methods must specify the wallet for which they're intended in every request. HTTP RPC requests should be send to the `<RPC IP address>:<RPC port>/wallet/<wallet name>/` endpoint, for example `127.0.0.1:8332/wallet/wallet1.dat/`. `bitcoin-cli` commands should be run with a `-rpcwallet` option, for example `bitcoin-cli -rpcwallet=wallet1.dat getbalance`.
+* A new *node-level* `listwallets` RPC method is added to display which wallets are currently loaded. The names returned by this method are the same as those used in the HTTP endpoint and for the `rpcwallet` argument.
+
+Note that while multi-wallet is now fully supported, the RPC multi-wallet interface should be considered unstable for version 0.15.0, and there may backwards-incompatible changes in future versions.
+
+Replace-by-fee control in the GUI
+---------------------------------
+
+Bitcoin Core has supported creating opt-in replace-by-fee (RBF) transactions
+since version 0.12.0, and since version 0.14.0 has included a `bumpfee` RPC method to
+replace unconfirmed opt-in RBF transactions with a new transaction that pays
+a higher fee.
+
+In version 0.15, creating an opt-in RBF transaction and replacing the unconfirmed
+transaction with a higher-fee transaction are both supported in the GUI (See [PR 9592](https://github.com/bitcoin/bitcoin/pull/9592)).
+
+Removal of Coin Age Priority
+----------------------------
+
+In previous versions of Bitcoin Core, a portion of each block could be reserved for transactions based on the age and value of UTXOs they spent. This concept (Coin Age Priority) is a policy choice by miners, and there are no consensus rules around the inclusion of Coin Age Priority transactions in blocks. In practice, only a few miners continue to use Coin Age Priority for transaction selection in blocks. Bitcoin Core 0.15 removes all remaining support for Coin Age Priority (See [PR 9602](https://github.com/bitcoin/bitcoin/pull/9602)). This has the following implications:
+
+- The concept of *free transactions* has been removed. High Coin Age Priority transactions would previously be allowed to be relayed even if they didn't attach a miner fee. This is no longer possible since there is no concept of Coin Age Priority. The `-limitfreerelay` and `-relaypriority` options which controlled relay of free transactions have therefore been removed.
+- The `-sendfreetransactions` option has been removed, since almost all miners do not include transactions which do not attach a transaction fee.
+- The `-blockprioritysize` option has been removed.
+- The `estimatepriority` and `estimatesmartpriority` RPCs have been removed.
+- The `getmempoolancestors`, `getmempooldescendants`, `getmempoolentry` and `getrawmempool` RPCs no longer return `startingpriority` and `currentpriority`.
+- The `prioritisetransaction` RPC no longer takes a `priority_delta` argument, which is replaced by a `dummy` argument for backwards compatibility with clients using positional arguments. The RPC is still used to change the apparent fee-rate of the transaction by using the `fee_delta` argument.
+- `-minrelaytxfee` can now be set to 0. If `minrelaytxfee` is set, then fees smaller than `minrelaytxfee` (per kB) are rejected from relaying, mining and transaction creation. This defaults to 1000 satoshi/kB.
+- The `-printpriority` option has been updated to only output the fee rate and hash of transactions included in a block by the mining code.
+
+Mempool Persistence Across Restarts
+-----------------------------------
+
+Version 0.14 introduced mempool persistence across restarts (the mempool is saved to a `mempool.dat` file in the data directory prior to shutdown and restores the mempool when the node is restarted). Version 0.15 allows this feature to be switched on or off using the `-persistmempool` command-line option (See [PR 9966](https://github.com/bitcoin/bitcoin/pull/9966)). By default, the option is set to true, and the mempool is saved on shutdown and reloaded on startup. If set to false, the `mempool.dat` file will not be loaded on startup or saved on shutdown.
+
+New RPC methods
+---------------
+
+Version 0.15 introduces several new RPC methods:
+
+- `abortrescan` stops current wallet rescan, e.g. when triggered by an `importprivkey` call (See [PR 10208](https://github.com/bitcoin/bitcoin/pull/10208)).
+- `combinerawtransaction` accepts a JSON array of raw transactions and combines them into a single raw transaction (See [PR 10571](https://github.com/bitcoin/bitcoin/pull/10571)).
+- `estimaterawfee` returns raw fee data so that customized logic can be implemented to analyze the data and calculate estimates. See [Fee Estimation Improvements](#fee-estimation-improvements) for full details on changes to the fee estimation logic and interface.
+- `getchaintxstats` returns statistics about the total number and rate of transactions
+ in the chain (See [PR 9733](https://github.com/bitcoin/bitcoin/pull/9733)).
+- `listwallets` lists wallets which are currently loaded. See the *Multi-wallet* section
+ of these release notes for full details (See [Multi-wallet support](#multi-wallet-support)).
+- `uptime` returns the total runtime of the `bitcoind` server since its last start (See [PR 10400](https://github.com/bitcoin/bitcoin/pull/10400)).
+
+Low-level RPC changes
+---------------------
+
+- When using Bitcoin Core in multi-wallet mode, RPC requests for wallet methods must specify
+ the wallet that they're intended for. See [Multi-wallet support](#multi-wallet-support) for full details.
+
+- The new database model no longer stores information about transaction
+ versions of unspent outputs (See [Performance improvements](#performance-improvements)). This means that:
+ - The `gettxout` RPC no longer has a `version` field in the response.
+ - The `gettxoutsetinfo` RPC reports `hash_serialized_2` instead of `hash_serialized`,
+ which does not commit to the transaction versions of unspent outputs, but does
+ commit to the height and coinbase information.
+ - The `getutxos` REST path no longer reports the `txvers` field in JSON format,
+ and always reports 0 for transaction versions in the binary format
+
+- The `estimatefee` RPC is deprecated. Clients should switch to using the `estimatesmartfee` RPC, which returns better fee estimates. See [Fee Estimation Improvements](#fee-estimation-improvements) for full details on changes to the fee estimation logic and interface.
+
+- The `gettxoutsetinfo` response now contains `disk_size` and `bogosize` instead of
+ `bytes_serialized`. The first is a more accurate estimate of actual disk usage, but
+ is not deterministic. The second is unrelated to disk usage, but is a
+ database-independent metric of UTXO set size: it counts every UTXO entry as 50 + the
+ length of its scriptPubKey (See [PR 10426](https://github.com/bitcoin/bitcoin/pull/10426)).
+
+- `signrawtransaction` can no longer be used to combine multiple transactions into a single transaction. Instead, use the new `combinerawtransaction` RPC (See [PR 10571](https://github.com/bitcoin/bitcoin/pull/10571)).
+
+- `fundrawtransaction` no longer accepts a `reserveChangeKey` option. This option used to allow RPC users to fund a raw transaction using an key from the keypool for the change address without removing it from the available keys in the keypool. The key could then be re-used for a `getnewaddress` call, which could potentially result in confusing or dangerous behaviour (See [PR 10784](https://github.com/bitcoin/bitcoin/pull/10784)).
+
+- `estimatepriority` and `estimatesmartpriority` have been removed. See [Removal of Coin Age Priority](#removal-of-coin-age-priority).
+
+- The `listunspent` RPC now takes a `query_options` argument (see [PR 8952](https://github.com/bitcoin/bitcoin/pull/8952)), which is a JSON object
+ containing one or more of the following members:
+ - `minimumAmount` - a number specifying the minimum value of each UTXO
+ - `maximumAmount` - a number specifying the maximum value of each UTXO
+ - `maximumCount` - a number specifying the minimum number of UTXOs
+ - `minimumSumAmount` - a number specifying the minimum sum value of all UTXOs
+
+- The `getmempoolancestors`, `getmempooldescendants`, `getmempoolentry` and `getrawmempool` RPCs no longer return `startingpriority` and `currentpriority`. See [Removal of Coin Age Priority](#removal-of-coin-age-priority).
+
+- The `dumpwallet` RPC now returns the full absolute path to the dumped wallet. It
+ used to return no value, even if successful (See [PR 9740](https://github.com/bitcoin/bitcoin/pull/9740)).
+
+- In the `getpeerinfo` RPC, the return object for each peer now returns an `addrbind` member, which contains the ip address and port of the connection to the peer. This is in addition to the `addrlocal` member which contains the ip address and port of the local node as reported by the peer (See [PR 10478](https://github.com/bitcoin/bitcoin/pull/10478)).
+
+- The `disconnectnode` RPC can now disconnect a node specified by node ID (as well as by IP address/port). To disconnect a node based on node ID, call the RPC with the new `nodeid` argument (See [PR 10143](https://github.com/bitcoin/bitcoin/pull/10143)).
+
+- The second argument in `prioritisetransaction` has been renamed from `priority_delta` to `dummy` since Bitcoin Core no longer has a concept of coin age priority. The `dummy` argument has no functional effect, but is retained for positional argument compatibility. See [Removal of Coin Age Priority](#removal-of-coin-age-priority).
+
+- The `resendwallettransactions` RPC throws an error if the `-walletbroadcast` option is set to false (See [PR 10995](https://github.com/bitcoin/bitcoin/pull/10995)).
+
+- The second argument in the `submitblock` RPC argument has been renamed from `parameters` to `dummy`. This argument never had any effect, and the renaming is simply to communicate this fact to the user (See [PR 10191](https://github.com/bitcoin/bitcoin/pull/10191))
+ (Clients should, however, use positional arguments for `submitblock` in order to be compatible with BIP 22.)
+
+- The `verbose` argument of `getblock` has been renamed to `verbosity` and now takes an integer from 0 to 2. Verbose level 0 is equivalent to `verbose=false`. Verbose level 1 is equivalent to `verbose=true`. Verbose level 2 will give the full transaction details of each transaction in the output as given by `getrawtransaction`. The old behavior of using the `verbose` named argument and a boolean value is still maintained for compatibility.
+
+- Error codes have been updated to be more accurate for the following error cases (See [PR 9853](https://github.com/bitcoin/bitcoin/pull/9853)):
+ - `getblock` now returns RPC_MISC_ERROR if the block can't be found on disk (for
+ example if the block has been pruned). Previously returned RPC_INTERNAL_ERROR.
+ - `pruneblockchain` now returns RPC_MISC_ERROR if the blocks cannot be pruned
+ because the node is not in pruned mode. Previously returned RPC_METHOD_NOT_FOUND.
+ - `pruneblockchain` now returns RPC_INVALID_PARAMETER if the blocks cannot be pruned
+ because the supplied timestamp is too late. Previously returned RPC_INTERNAL_ERROR.
+ - `pruneblockchain` now returns RPC_MISC_ERROR if the blocks cannot be pruned
+ because the blockchain is too short. Previously returned RPC_INTERNAL_ERROR.
+ - `setban` now returns RPC_CLIENT_INVALID_IP_OR_SUBNET if the supplied IP address
+ or subnet is invalid. Previously returned RPC_CLIENT_NODE_ALREADY_ADDED.
+ - `setban` now returns RPC_CLIENT_INVALID_IP_OR_SUBNET if the user tries to unban
+ a node that has not previously been banned. Previously returned RPC_MISC_ERROR.
+ - `removeprunedfunds` now returns RPC_WALLET_ERROR if `bitcoind` is unable to remove
+ the transaction. Previously returned RPC_INTERNAL_ERROR.
+ - `removeprunedfunds` now returns RPC_INVALID_PARAMETER if the transaction does not
+ exist in the wallet. Previously returned RPC_INTERNAL_ERROR.
+ - `fundrawtransaction` now returns RPC_INVALID_ADDRESS_OR_KEY if an invalid change
+ address is provided. Previously returned RPC_INVALID_PARAMETER.
+ - `fundrawtransaction` now returns RPC_WALLET_ERROR if `bitcoind` is unable to create
+ the transaction. The error message provides further details. Previously returned
+ RPC_INTERNAL_ERROR.
+ - `bumpfee` now returns RPC_INVALID_PARAMETER if the provided transaction has
+ descendants in the wallet. Previously returned RPC_MISC_ERROR.
+ - `bumpfee` now returns RPC_INVALID_PARAMETER if the provided transaction has
+ descendants in the mempool. Previously returned RPC_MISC_ERROR.
+ - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has
+ has been mined or conflicts with a mined transaction. Previously returned
+ RPC_INVALID_ADDRESS_OR_KEY.
+ - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction is not
+ BIP 125 replaceable. Previously returned RPC_INVALID_ADDRESS_OR_KEY.
+ - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has already
+ been bumped by a different transaction. Previously returned RPC_INVALID_REQUEST.
+ - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction contains
+ inputs which don't belong to this wallet. Previously returned RPC_INVALID_ADDRESS_OR_KEY.
+ - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has multiple change
+ outputs. Previously returned RPC_MISC_ERROR.
+ - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has no change
+ output. Previously returned RPC_MISC_ERROR.
+ - `bumpfee` now returns RPC_WALLET_ERROR if the fee is too high. Previously returned
+ RPC_MISC_ERROR.
+ - `bumpfee` now returns RPC_WALLET_ERROR if the fee is too low. Previously returned
+ RPC_MISC_ERROR.
+ - `bumpfee` now returns RPC_WALLET_ERROR if the change output is too small to bump the
+ fee. Previously returned RPC_MISC_ERROR.
+
+0.15.0 Change log
+=================
+
+### RPC and other APIs
+- #9485 `61a640e` ZMQ example using python3 and asyncio (mcelrath)
+- #9894 `0496e15` remove 'label' filter for rpc command help (instagibbs)
+- #9853 `02bd6e9` Fix error codes from various RPCs (jnewbery)
+- #9842 `598ef9c` Fix RPC failure testing (continuation of #9707) (jnewbery)
+- #10038 `d34995a` Add mallocinfo mode to `getmemoryinfo` RPC (laanwj)
+- #9500 `3568b30` [Qt][RPC] Autocomplete commands for 'help' command in debug console (achow101)
+- #10056 `e6156a0` [zmq] Call va_end() on va_start()ed args (kallewoof)
+- #10086 `7438cea` Trivial: move rpcserialversion into RPC option group (jlopp)
+- #10150 `350b224` [rpc] Add logging rpc (jnewbery)
+- #10208 `393160c` [wallet] Rescan abortability (kallewoof)
+- #10143 `a987def` [net] Allow disconnectnode RPC to be called with node id (jnewbery)
+- #10281 `0e8499c` doc: Add RPC interface guidelines (laanwj)
+- #9733 `d4732f3` Add getchaintxstats RPC (sipa)
+- #10310 `f4b15e2` [doc] Add hint about getmempoolentry to getrawmempool help (kallewoof)
+- #8704 `96c850c` [RPC] Transaction details in getblock (achow101)
+- #8952 `9390845` Add query options to listunspent RPC call (pedrobranco)
+- #10413 `08ac35a` Fix docs (there's no rpc command setpaytxfee) (RHavar)
+- #8384 `e317c0d` Add witness data output to TxInError messages (instagibbs)
+- #9571 `4677151` RPC: getblockchaininfo returns BIP signaling statistics (pinheadmz)
+- #10450 `ef2d062` Fix bumpfee rpc "errors" return value (ryanofsky)
+- #10475 `39039b1` [RPC] getmempoolinfo mempoolminfee is a BTC/KB feerate (instagibbs)
+- #10478 `296928e` rpc: Add listen address to incoming connections in `getpeerinfo` (laanwj)
+- #10403 `08d0390` Fix importmulti failure to return rescan errors (ryanofsky)
+- #9740 `9fec4da` Add friendly output to dumpwallet (aideca)
+- #10426 `16f6c98` Replace bytes_serialized with bogosize (sipa)
+- #10252 `980deaf` RPC/Mining: Restore API compatibility for prioritisetransaction (luke-jr)
+- #9672 `46311e7` Opt-into-RBF for RPC & bitcoin-tx (luke-jr)
+- #10481 `9c248e3` Decodehextx scripts sanity check (achow101)
+- #10488 `fa1f106` Note that the prioritizetransaction dummy value is deprecated, and has no meaning (TheBlueMatt)
+- #9738 `c94b89e` gettxoutproof() should return consistent result (jnewbery)
+- #10191 `00350bd` [trivial] Rename unused RPC arguments 'dummy' (jnewbery)
+- #10627 `b62b4c8` fixed listunspent rpc convert parameter (tnakagawa)
+- #10412 `bef02fb` Improve wallet rescan API (ryanofsky)
+- #10400 `1680ee0` [RPC] Add an uptime command that displays the amount of time (in seconds) bitcoind has been running (rvelhote)
+- #10683 `d81bec7` rpc: Move the `generate` RPC call to rpcwallet (laanwj)
+- #10710 `30bc0f6` REST/RPC example update (Mirobit)
+- #10747 `9edda0c` [rpc] fix verbose argument for getblock in bitcoin-cli (jnewbery)
+- #10589 `104f5f2` More economical fee estimates for RBF and RPC options to control (morcos)
+- #10543 `b27b004` Change API to estimaterawfee (morcos)
+- #10807 `afd2fca` getbalance example covers at least 6 confirms (instagibbs)
+- #10707 `75b5643` Better API for estimatesmartfee RPC (morcos)
+- #10784 `9e8d6a3` Do not allow users to get keys from keypool without reserving them (TheBlueMatt)
+- #10857 `d445a2c` [RPC] Add a deprecation warning to getinfo's output (achow101)
+- #10571 `adf170d` [RPC]Move transaction combining from signrawtransaction to new RPC (achow101)
+- #10783 `041dad9` [RPC] Various rpc argument fixes (instagibbs)
+- #9622 `6ef3c7e` [rpc] listsinceblock should include lost transactions when parameter is a reorg'd block (kallewoof)
+- #10799 `8537187` Prevent user from specifying conflicting parameters to fundrawtx (TheBlueMatt)
+- #10931 `0b11a07` Fix misleading "Method not found" multiwallet errors (ryanofsky)
+- #10788 `f66c596` [RPC] Fix addwitnessaddress by replacing ismine with producesignature (achow101)
+- #10999 `627c3c0` Fix amounts formatting in `decoderawtransaction` (laanwj)
+- #11002 `4268426` [wallet] return correct error code from resendwallettransaction (jnewbery)
+- #11029 `96a63a3` [RPC] trivial: gettxout no longer shows version of tx (FelixWeis)
+- #11083 `6c2b008` Fix combinerawtransaction RPC help result section (jonasnick)
+- #11027 `07164bb` [RPC] Only return hex field once in getrawtransaction (achow101)
+- #10698 `5af6572` Be consistent in calling transactions "replaceable" for Opt-In RBF (TheBlueMatt)
+
+### Block and transaction handling
+- #9801 `a8c5751` Removed redundant parameter from mempool.PrioritiseTransaction (gubatron)
+- #9819 `1efc99c` Remove harmless read of unusued priority estimates (morcos)
+- #9822 `b7547fa` Remove block file location upgrade code (benma)
+- #9602 `30ff3a2` Remove coin age priority and free transactions - implementation (morcos)
+- #9548 `47510ad` Remove min reasonable fee (morcos)
+- #10249 `c73af54` Switch CCoinsMap from boost to std unordered_map (sipa)
+- #9966 `2a183de` Control mempool persistence using a command line parameter (jnewbery)
+- #10199 `318ea50` Better fee estimates (morcos)
+- #10196 `bee3529` Bugfix: PrioritiseTransaction updates the mempool tx counter (sdaftuar)
+- #10195 `1088b02` Switch chainstate db and cache to per-txout model (sipa)
+- #10284 `c2ab38b` Always log debug information for fee calculation in CreateTransaction (morcos)
+- #10503 `efbcf2b` Use REJECT_DUPLICATE for already known and conflicted txn (sipa)
+- #10537 `b3eb0d6` Few Minor per-utxo assert-semantics re-adds and tweak (TheBlueMatt)
+- #10626 `8c841a3` doc: Remove outdated minrelaytxfee comment (MarcoFalke)
+- #10559 `234ffc6` Change semantics of HaveCoinInCache to match HaveCoin (morcos)
+- #10581 `7878353` Simplify return values of GetCoin/HaveCoin(InCache) (sipa)
+- #10684 `a381f6a` Remove no longer used mempool.exists(outpoint) (morcos)
+- #10148 `d4e551a` Use non-atomic flushing with block replay (sipa)
+- #10685 `30c2130` Clarify CCoinsViewMemPool documentation (TheBlueMatt)
+- #10558 `90a002e` Address nits from per-utxo change (morcos)
+- #10706 `6859ad2` Improve wallet fee logic and fix GUI bugs (morcos)
+- #10526 `754aa02` Force on-the-fly compaction during pertxout upgrade (sipa)
+- #10985 `d896d5c` Add undocumented -forcecompactdb to force LevelDB compactions (sipa)
+- #10292 `e4bbd3d` Improved efficiency in COutPoint constructors (mm-s)
+- #10290 `8d6d43e` Add -stopatheight for benchmarking (sipa)
+
+### P2P protocol and network code
+- #9726 `7639d38` netbase: Do not print an error on connection timeouts through proxy (laanwj)
+- #9805 `5b583ef` Add seed.btc.petertodd.org to mainnet DNS seeds (petertodd)
+- #9861 `22f609f` Trivial: Debug log ambiguity fix for peer addrs (keystrike)
+- #9774 `90cb2a2` Enable host lookups for -proxy and -onion parameters (jmcorgan)
+- #9558 `7b585cf` Clarify assumptions made about when BlockCheck is called (TheBlueMatt)
+- #10135 `e19586a` [p2p] Send the correct error code in reject messages (jnewbery)
+- #9665 `eab00d9` Use cached [compact] blocks to respond to getdata messages (TheBlueMatt)
+- #10215 `a077a90` Check interruptNet during dnsseed lookups (TheBlueMatt)
+- #10234 `faf2dea` [net] listbanned RPC and QT should show correct banned subnets (jnewbery)
+- #10134 `314ebdf` [qa] Fixes segwit block relay test after inv-direct-fetch was disabled (sdaftuar)
+- #10351 `3f57c55` removed unused code in INV message (Greg-Griffith)
+- #10061 `ae78609` [net] Added SetSocketNoDelay() utility function (tjps)
+- #10408 `28c6e8d` Net: Improvements to Tor control port parser (str4d)
+- #10460 `5c63d66` Broadcast address every day, not 9 hours (sipa)
+- #10471 `400fdd0` Denote functions CNode::GetRecvVersion() and CNode::GetRefCount() as const (pavlosantoniou)
+- #10345 `67700b3` [P2P] Timeout for headers sync (sdaftuar)
+- #10564 `8d9f45e` Return early in IsBanned (gmaxwell)
+- #10587 `de8db47` Net: Fix resource leak in ReadBinaryFile(...) (practicalswift)
+- #9549 `b33ca14` [net] Avoid possibility of NULL pointer dereference in MarkBlockAsInFlight(...) (practicalswift)
+- #10446 `2772dc9` net: avoid extra dns query per seed (theuni)
+- #10824 `9dd6a2b` Avoid unnecessary work in SetNetworkActive (promag)
+- #10948 `df3a6f4` p2p: Hardcoded seeds update pre-0.15 branch (laanwj)
+- #10977 `02f4c4a` [net] Fix use of uninitialized value in getnetworkinfo(const JSONRPCRequest&) (practicalswift)
+- #10982 `c8b62c7` Disconnect network service bits 6 and 8 until Aug 1, 2018 (TheBlueMatt)
+- #11012 `0e5cff6` Make sure to clean up mapBlockSource if we've already seen the block (theuni)
+
+### Validation
+- #9725 `67023e9` CValidationInterface Cleanups (TheBlueMatt)
+- #10178 `2584925` Remove CValidationInterface::UpdatedTransaction (TheBlueMatt)
+- #10201 `a6548a4` pass Consensus::Params& to functions in validation.cpp and make them static (mariodian)
+- #10297 `431a548` Simplify DisconnectBlock arguments/return value (sipa)
+- #10464 `f94b7d5` Introduce static DoWarning (simplify UpdateTip) (jtimon)
+- #10569 `2e7d8f8` Fix stopatheight (achow101)
+- #10192 `2935b46` Cache full script execution results in addition to signatures (TheBlueMatt)
+- #10179 `21ed30a` Give CValidationInterface Support for calling notifications on the CScheduler Thread (TheBlueMatt)
+- #10557 `66270a4` Make check to distinguish between orphan txs and old txs more efficient (morcos)
+- #10775 `7c2400c` nCheckDepth chain height fix (romanornr)
+- #10821 `16240f4` Add SSE4 optimized SHA256 (sipa)
+- #10854 `04d395e` Avoid using sizes on non-fixed-width types to derive protocol constants (gmaxwell)
+- #10945 `2a50b11` Update defaultAssumeValid according to release-process.md (gmaxwell)
+- #10986 `2361208` Update chain transaction statistics (sipa)
+- #11028 `6bdf4b3` Avoid masking of difficulty adjustment errors by checkpoints (sipa)
+- #9533 `cb598cf` Allow non-power-of-2 signature cache sizes (sipa)
+- #9208 `acd9957` Improve DisconnectTip performance (sdaftuar)
+- #10618 `f90603a` Remove confusing MAX_BLOCK_BASE_SIZE (gmaxwell)
+- #10758 `bd92424` Fix some chainstate-init-order bugs (TheBlueMatt)
+- #10550 `b7296bc` Don't return stale data from CCoinsViewCache::Cursor() (ryanofsky)
+- #10998 `2507fd5` Fix upgrade cancel warnings (TheBlueMatt)
+- #9868 `cbdb473` Abstract out the command line options for block assembly (sipa)
+
+### Build system
+- #9727 `5f0556d` Remove fallbacks for boost_filesystem < v3 (laanwj)
+- #9788 `50a2265` gitian: bump descriptors for master (theuni)
+- #9794 `7ca2f54` Minor update to qrencode package builder (mitchellcash)
+- #9514 `2cc0df1` release: Windows signing script (theuni)
+- #9921 `8b789d8` build: Probe MSG_DONTWAIT in the same way as MSG_NOSIGNAL (laanwj)
+- #10011 `32d1b34` build: Fix typo s/HAVE_DONTWAIT/HAVE_MSG_DONTWAIT (laanwj)
+- #9946 `90dd9e6` Fix build errors if spaces in path or parent directory (pinheadmz)
+- #10136 `81da4c7` build: Disable Wshadow warning (laanwj)
+- #10166 `64962ae` Ignore Doxyfile generated from Doxyfile.in template (paveljanik)
+- #10239 `0416ea9` Make Boost use std::atomic internally (sipa)
+- #10228 `27faa6c` build: regenerate bitcoin-config.h as necessary (theuni)
+- #10273 `8979f45` [scripts] Minor improvements to `macdeployqtplus` script (chrisgavin)
+- #10325 `a26280b` 0.15.0 Depends Updates (fanquake)
+- #10328 `79aeff6` Update contrib/debian to latest Ubuntu PPA upload (TheBlueMatt)
+- #7522 `d25449f` Bugfix: Only use git for build info if the repository is actually the right one (luke-jr)
+- #10489 `e654d61` build: silence gcc7's implicit fallthrough warning (theuni)
+- #10549 `ad1a13e` Avoid printing generic and duplicated "checking for QT" during ./configure (drizzt)
+- #10628 `8465b68` [depends] expat 2.2.1 (fanquake)
+- #10806 `db825d2` build: verify that the assembler can handle crc32 functions (theuni)
+- #10766 `b4d03be` Building Environment: Set ARFLAGS to cr (ReneNyffenegger)
+- #10803 `91edda8` Explicitly search for bdb5.3 (pstratem)
+- #10855 `81560b0` random: only use getentropy on openbsd (theuni)
+- #10508 `1caafa6` Run Qt wallet tests on travis (ryanofsky)
+- #10851 `e222618` depends: fix fontconfig with newer glibc (theuni)
+- #10971 `88b1e4b` build: fix missing sse42 in depends builds (theuni)
+- #11097 `129b03f` gitian: quick hack to fix version string in releases (theuni)
+- #10039 `919aaf6` Fix compile errors with Qt 5.3.2 and Boost 1.55.0 (ryanofsky)
+- #10168 `7032021` Fix build warning from #error text (jnewbery)
+- #10301 `318392c` Check if sys/random.h is required for getentropy (jameshilliard)
+
+### GUI
+- #9724 `1a9fd5c` Qt/Intro: Add explanation of IBD process (luke-jr)
+- #9834 `b00ba62` qt: clean up initialize/shutdown signals (benma)
+- #9481 `ce01e62` [Qt] Show more significant warning if we fall back to the default fee (jonasschnelli)
+- #9974 `b9f930b` Add basic Qt wallet test (ryanofsky)
+- #9690 `a387d3a` Change 'Clear' button string to 'Reset' (da2x)
+- #9592 `9c7b7cf` [Qt] Add checkbox in the GUI to opt-in to RBF when creating a transaction (ryanofsky)
+- #10098 `2b477e6` Make qt wallet test compatible with qt4 (ryanofsky)
+- #9890 `1fa4ae6` Add a button to open the config file in a text editor (ericshawlinux)
+- #10156 `51833a1` Fix for issues with startup and multiple monitors on windows (AllanDoensen)
+- #10177 `de01da7` Changed "Send" button default status from true to false (KibbledJiveElkZoo)
+- #10221 `e96486c` Stop treating coinbase outputs differently in GUI: show them at 1conf (TheBlueMatt)
+- #10231 `987a6c0` [Qt] Reduce a significant cs_main lock freeze (jonasschnelli)
+- #10242 `f6f3b58` [qt] Don't call method on null WalletModel object (ryanofsky)
+- #10093 `a3e756b` [Qt] Don't add arguments of sensitive command to console window (jonasschnelli)
+- #10362 `95546c8` [GUI] Add OSX keystroke to RPCConsole info (spencerlievens)
+- #9697 `962cd3f` [Qt] simple fee bumper with user verification (jonasschnelli)
+- #10390 `e477516` [wallet] remove minimum total fee option (instagibbs)
+- #10420 `4314544` Add Qt tests for wallet spends & bumpfee (ryanofsky)
+- #10454 `c1c9a95` Fix broken q4 test build (ryanofsky)
+- #10449 `64beb13` Overhaul Qt fee bumper (jonasschnelli)
+- #10582 `7c72fb9` Pass in smart fee slider value to coin control dialog (morcos)
+- #10673 `4c72cc3` [qt] Avoid potential null pointer dereference in TransactionView::exportClicked() (practicalswift)
+- #10769 `8fdd23a` [Qt] replace fee slider with a Dropdown, extend conf. targets (jonasschnelli)
+- #10870 `412b466` [Qt] Use wallet 0 in rpc console if running with multiple wallets (jonasschnelli)
+- #10988 `a9dd111` qt: Increase BLOCK_CHAIN_SIZE constants (laanwj)
+- #10644 `e292140` Slightly overhaul NSI pixmaps (jonasschnelli)
+- #10660 `0c3542e` Allow to cancel the txdb upgrade via splashscreen keypress 'q' (jonasschnelli)
+
+### Wallet
+- #9359 `f7ec7cf` Add test for CWalletTx::GetImmatureCredit() returning stale values (ryanofsky)
+- #9576 `56ab672` [wallet] Remove redundant initialization (practicalswift)
+- #9333 `fa625b0` Document CWalletTx::mapValue entries and remove erase of nonexistent "version" entry (ryanofsky)
+- #9906 `72fb515` Disallow copy constructor CReserveKeys (instagibbs)
+- #9369 `3178b2c` Factor out CWallet::nTimeSmart computation into a method (ryanofsky)
+- #9830 `afcd7c0` Add safe flag to listunspent result (NicolasDorier)
+- #9993 `c49355c` Initialize nRelockTime (pstratem)
+- #9818 `3d857f3` Save watch only key timestamps when reimporting keys (ryanofsky)
+- #9294 `f34cdcb` Use internal HD chain for change outputs (hd split) (jonasschnelli)
+- #10164 `e183ea2` Wallet: reduce excess logic InMempool() (kewde)
+- #10186 `c9ff4f8` Remove SYNC_TRANSACTION_NOT_IN_BLOCK magic number (jnewbery)
+- #10226 `64c45aa` wallet: Use boost to more portably ensure -wallet specifies only a filename (luke-jr)
+- #9827 `c91ca0a` Improve ScanForWalletTransactions return value (ryanofsky)
+- #9951 `fa1ac28` Wallet database handling abstractions/simplifications (laanwj)
+- #10265 `c29a0d4` [wallet] [moveonly] Check non-null pindex before potentially referencing (kallewoof)
+- #10283 `a550f6e` Cleanup: reduce to one GetMinimumFee call signature (morcos)
+- #10294 `e2b99b1` [Wallet] unset change position when there is no change (instagibbs)
+- #10115 `d3dce0e` Avoid reading the old hd master key during wallet encryption (TheBlueMatt)
+- #10341 `18c9deb` rpc/wallet: Workaround older UniValue which returns a std::string temporary for get_str (luke-jr)
+- #10308 `94e5227` [wallet] Securely erase potentially sensitive keys/values (tjps)
+- #10257 `ea1fd43` [test] Add test for getmemoryinfo (jimmysong)
+- #10295 `ce8176d` [qt] Move some WalletModel functions into CWallet (ryanofsky)
+- #10506 `7cc2c67` Fix bumpfee test after #10449 (ryanofsky)
+- #10500 `098b01d` Avoid CWalletTx copies in GetAddressBalances and GetAddressGroupings (ryanofsky)
+- #10455 `0747d33` Simplify feebumper minimum fee code slightly (ryanofsky)
+- #10522 `2805d60` [wallet] Remove unused variables (practicalswift)
+- #8694 `177433a` Basic multiwallet support (luke-jr)
+- #10598 `7a74f88` Supress struct/class mismatch warnings introduced in #10284 (paveljanik)
+- #9343 `209eef6` Don't create change at dust limit (morcos)
+- #10744 `ed88e31` Use method name via __func__ macro (darksh1ne)
+- #10712 `e8b9523` Add change output if necessary to reduce excess fee (morcos)
+- #10816 `1c011ff` Properly forbid -salvagewallet and -zapwallettxes for multi wallet (morcos)
+- #10235 `5cfdda2` Track keypool entries as internal vs external in memory (TheBlueMatt)
+- #10330 `bf0a08b` [wallet] fix zapwallettxes interaction with persistent mempool (jnewbery)
+- #10831 `0b01935` Batch flushing operations to the walletdb during top up and increase keypool size (gmaxwell)
+- #10795 `7b6e8bc` No longer ever reuse keypool indexes (TheBlueMatt)
+- #10849 `bde4f93` Multiwallet: simplest endpoint support (jonasschnelli)
+- #10817 `9022aa3` Redefine Dust and add a discard_rate (morcos)
+- #10883 `bf3b742` Rename -usewallet to -rpcwallet (morcos)
+- #10604 `420238d` [wallet] [tests] Add listwallets RPC, include wallet name in `getwalletinfo` and add multiwallet test (jnewbery)
+- #10885 `70888a3` Reject invalid wallets (promag)
+- #10949 `af56397` Clarify help message for -discardfee (morcos)
+- #10942 `2e857bb` Eliminate fee overpaying edge case when subtracting fee from recipients (morcos)
+- #10995 `fa64636` Fix resendwallettransactions assert failure if -walletbroadcast=0 (TheBlueMatt)
+- #11022 `653a46d` Basic keypool topup (jnewbery)
+- #11081 `9fe1f6b` Add length check for CExtKey deserialization (jonasschnelli, guidovranken)
+- #11044 `4ef8374` [wallet] Keypool topup cleanups (jnewbery)
+- #11145 `e51bb71` Fix rounding bug in calculation of minimum change (morcos)
+- #9605 `779f2f9` Use CScheduler for wallet flushing, remove ThreadFlushWalletDB (TheBlueMatt)
+- #10108 `4e3efd4` ApproximateBestSubset should take inputs by reference, not value (RHavar)
+
+### Tests and QA
+- #9744 `8efd1c8` Remove unused module from rpc-tests (34ro)
+- #9657 `7ff4a53` Improve rpc-tests.py (jnewbery)
+- #9766 `7146d96` Add --exclude option to rpc-tests.py (jnewbery)
+- #9577 `d6064a8` Fix docstrings in qa tests (jnewbery)
+- #9823 `a13a417` qa: Set correct path for binaries in rpc tests (MarcoFalke)
+- #9847 `6206252` Extra test vector for BIP32 (sipa)
+- #9350 `88c2ae3` [Trivial] Adding label for amount inside of tx_valid/tx_invalid.json (Christewart)
+- #9888 `36afd4d` travis: Verify commits only for one target (MarcoFalke)
+- #9904 `58861ad` test: Fail if InitBlockIndex fails (laanwj)
+- #9828 `67c5cc1` Avoid -Wshadow warnings in wallet_tests (ryanofsky)
+- #9832 `48c3429` [qa] assert_start_raises_init_error (NicolasDorier)
+- #9739 `9d5fcbf` Fix BIP68 activation test (jnewbery)
+- #9547 `d32581c` bench: Assert that division by zero is unreachable (practicalswift)
+- #9843 `c78adbf` Fix segwit getblocktemplate test (jnewbery)
+- #9929 `d5ce14e` tests: Delete unused function _rpchost_to_args (laanwj)
+- #9555 `19be26a` [test] Avoid reading a potentially uninitialized variable in tx_invalid-test (transaction_tests.cpp) (practicalswift)
+- #9945 `ac23a7c` Improve logging in bctest.py if there is a formatting mismatch (jnewbery)
+- #9768 `8910b47` [qa] Add logging to test_framework.py (jnewbery)
+- #9972 `21833f9` Fix extended rpc tests broken by #9768 (jnewbery)
+- #9977 `857d1e1` QA: getblocktemplate_longpoll.py should always use >0 fee tx (sdaftuar)
+- #9970 `3cc13ea` Improve readability of segwit.py, smartfees.py (sdaftuar)
+- #9497 `2c781fb` CCheckQueue Unit Tests (JeremyRubin)
+- #10024 `9225de2` [trivial] Use log.info() instead of print() in remaining functional test cases (jnewbery)
+- #9956 `3192e52` Reorganise qa directory (jnewbery)
+- #10017 `02d64bd` combine_logs.py - aggregates log files from multiple bitcoinds during functional tests (jnewbery)
+- #10047 `dfef6b6` [tests] Remove unused variables and imports (practicalswift)
+- #9701 `a230b05` Make bumpfee tests less fragile (ryanofsky)
+- #10053 `ca20923` [test] Allow functional test cases to be skipped (jnewbery)
+- #10052 `a0b1e57` [test] Run extended tests once daily in Travis (jnewbery)
+- #10069 `1118493` [QA] Fix typo in fundrawtransaction test (NicolasDorier)
+- #10083 `c044f03` [QA] Renaming rawTx into rawtx (NicolasDorier)
+- #10073 `b1a4f27` Actually run assumevalid.py (jnewbery)
+- #9780 `c412fd8` Suppress noisy output from qa tests in Travis (jnewbery)
+- #10096 `79af9fb` Check that all test scripts in test/functional are being run (jnewbery)
+- #10076 `5b029aa` [qa] combine_logs: Use ordered list for logfiles (MarcoFalke)
+- #10107 `f2734c2` Remove unused variable. Remove accidental trailing semicolons in Python code (practicalswift)
+- #10109 `8ac8041` Remove SingleNodeConnCB (jnewbery)
+- #10114 `edc62c9` [tests] sync_with_ping should assert that ping hasn't timed out (jnewbery)
+- #10128 `427d2fd` Speed Up CuckooCache tests (JeremyRubin)
+- #10072 `12af74b` Remove sources of unreliablility in extended functional tests (jnewbery)
+- #10077 `ebfd653` [qa] Add setnetworkactive smoke test (MarcoFalke)
+- #10152 `080d7c7` [trivial] remove unused line in Travis config (jnewbery)
+- #10159 `df1ca9e` [tests] color test results and sort alphabetically (jnewbery)
+- #10124 `88799ea` [test] Suppress test logging spam (jnewbery)
+- #10142 `ed09dd3` Run bitcoin_test-qt under minimal QPA platform (ryanofsky)
+- #9949 `a27dbc5` [bench] Avoid function call arguments which are pointers to uninitialized values (practicalswift)
+- #10187 `b44adf9` tests: Fix test_runner return value in case of skipped test (laanwj)
+- #10197 `d86bb07` [tests] Functional test warnings (jnewbery)
+- #10219 `9111df9` Tests: Order Python Tests Differently (jimmysong)
+- #10229 `f3db4c6` Tests: Add test for getdifficulty (jimmysong)
+- #10224 `2723bcd` [test] Add test for getaddednodeinfo (jimmysong)
+- #10023 `c530c15` [tests] remove maxblocksinflight.py (functionality covered by other test) (jnewbery)
+- #10097 `1b25b6d` Move zmq test skipping logic into individual test case (jnewbery)
+- #10272 `54e2d87` [Tests] Prevent warning: variable 'x' is uninitialized (paveljanik)
+- #10225 `e0a7e19` [test] Add aborttrescan tests (kallewoof)
+- #10278 `8254a8a` [test] Add Unit Test for GetListenPort (jimmysong)
+- #10280 `47535d7` [test] Unit test amount.h/amount.cpp (jimmysong)
+- #10256 `80c3a73` [test] Add test for gettxout to wallet.py (jimmysong)
+- #10264 `492d22f` [test] Add tests for getconnectioncount, getnettotals and ping (jimmysong)
+- #10169 `8f3e384` [tests] Remove func test code duplication (jnewbery)
+- #10198 `dc8fc0c` [tests] Remove is_network_split from functional test framework (jnewbery)
+- #10255 `3c5e6c9` [test] Add test for listaddressgroupings (jimmysong)
+- #10137 `75171f0` Remove unused import. Remove accidental trailing semicolons (practicalswift)
+- #10307 `83073de` [tests] allow zmq test to be run in out-of-tree builds (jnewbery)
+- #10344 `e927483` [tests] Fix abandonconflict.py intermittency (jnewbery)
+- #10318 `170bc2c` [tests] fix wait_for_inv() (jnewbery)
+- #10171 `fff72de` [tests] Add node methods to test framework (jnewbery)
+- #10352 `23d78c4` test: Add elapsed time to RPC tracing (laanwj)
+- #10342 `6a796b2` [tests] Improve mempool_persist test (jnewbery)
+- #10287 `776ba23` [tests] Update Unit Test for addrman.h/addrman.cpp (jimmysong)
+- #10365 `7ee5236` [tests] increase timeouts in sendheaders test (jnewbery)
+- #10361 `f6241b3` qa: disablewallet: Check that wallet is really disabled (MarcoFalke)
+- #10371 `4b766fc` [tests] Clean up addrman_tests.cpp (jimmysong)
+- #10253 `87abe20` [test] Add test for getnetworkhashps (jimmysong)
+- #10376 `8bd16ee` [tests] fix disconnect_ban intermittency (jnewbery)
+- #10374 `5411997` qa: Warn when specified test is not found (MarcoFalke)
+- #10405 `0542978` tests: Correct testcase in script_tests.json for large number OP_EQUAL (laanwj)
+- #10429 `6b99daf` tests: fix spurious addrman test failure (theuni)
+- #10433 `8e57256` [tests] improve tmpdir structure (jnewbery)
+- #10415 `217b416` [tests] Speed up fuzzing by ~200x when using afl-fuzz (practicalswift)
+- #10445 `b4b057a` Add test for empty chain and reorg consistency for gettxoutsetinfo (gmaxwell)
+- #10423 `1aefc94` [tests] skipped tests should clean up after themselves (jnewbery)
+- #10359 `329fc1d` [tests] functional tests should call BitcoinTestFramework start/stop node methods (jnewbery)
+- #10514 `e103b3f` Bugfix: missing == 0 after randrange (sipa)
+- #10515 `c871f32` [test] Add test for getchaintxstats (jimmysong)
+- #10509 `bea5b00` Remove xvfb configuration from travis (ryanofsky)
+- #10535 `30853e1` [qa] fundrawtx: Fix shutdown race (MarcoFalke)
+- #9909 `300f8e7` tests: Add FindEarliestAtLeast test for edge cases (ryanofsky)
+- #10331 `75e898c` Share config between util and functional tests (jnewbery)
+- #10321 `e801084` Use FastRandomContext for all tests (sipa)
+- #10524 `6c2d81f` [tests] Remove printf(...) (practicalswift)
+- #10547 `71ab6e5` [tests] Use FastRandomContext instead of boost::random::{mt19937,uniform_int_distribution} (practicalswift)
+- #10551 `6702617` [Tests] Wallet encryption functional tests (achow101)
+- #10555 `643fa0b` [tests] various improvements to zmq_test.py (jnewbery)
+- #10533 `d083bd9` [tests] Use cookie auth instead of rpcuser and rpcpassword (achow101)
+- #10632 `c68a9a6` qa: Add stopatheight test (MarcoFalke)
+- #10636 `4bc853b` [qa] util: Check return code after closing bitcoind proc (MarcoFalke)
+- #10662 `e0a7801` Initialize randomness in benchmarks (achow101)
+- #10612 `7c87a9c` The young person's guide to the test_framework (jnewbery)
+- #10659 `acb1153` [qa] blockchain: Pass on closed connection during generate call (MarcoFalke)
+- #10690 `416af3e` [qa] Bugfix: allow overriding extra_args in ComparisonTestFramework (sdaftuar)
+- #10556 `65cc7aa` Move stop/start functions from utils.py into BitcoinTestFramework (jnewbery)
+- #10704 `dd07f47` [tests] nits in dbcrash.py (jnewbery)
+- #10743 `be82498` [test] don't run dbcrash.py on Travis (jnewbery)
+- #10761 `d3b5870` [tests] fix replace_by_fee.py (jnewbery)
+- #10759 `1d4805c` Fix multi_rpc test for hosts that dont default to utf8 (TheBlueMatt)
+- #10190 `e4f226a` [tests] mining functional tests (including regression test for submitblock) (jnewbery)
+- #10739 `1fc783f` test: Move variable `state` down where it is used (paveljanik)
+- #9980 `fee0d80` Fix mem access violation merkleblock (Christewart)
+- #10893 `0c173a1` [QA] Avoid running multiwallet.py twice (jonasschnelli)
+- #10927 `9d5e8f9` test: Make sure wallet.backup is created in temp path (laanwj)
+- #10899 `f29d5db` [test] Qt: Use _putenv_s instead of setenv on Windows builds (brianmcmichael)
+- #10912 `5c8eb79` [tests] Fix incorrect memory_cleanse(…) call in crypto_tests.cpp (practicalswift)
+- #11001 `fa8a063` [tests] Test disconnecting unsupported service bits logic (jnewbery)
+- #10695 `929fd72` [qa] Rewrite BIP65/BIP66 functional tests (sdaftuar)
+- #10963 `ecd2135` [bench] Restore format state of cout after printing with std::fixed/setprecision (practicalswift)
+- #11025 `e5d26e4` qa: Fix inv race in example_test (MarcoFalke)
+- #10765 `2c811e0` Tests: address placement should be deterministic by default (ReneNyffenegger)
+- #11000 `ac016e1` test: Add resendwallettransactions functional tests (promag)
+- #11032 `aeb3175` [qa] Fix block message processing error in sendheaders.py (sdaftuar)
+- #10105 `0b9fb68` [tests] fixup - make all Travis test runs quiet, non just cron job runs (jnewbery)
+- #10222 `6ce7337` [tests] test_runner - check unicode (jnewbery)
+- #10327 `35da2ae` [tests] remove import-abort-rescan.py (jnewbery)
+- #11023 `bf74d37` [tests] Add option to attach a python debugger if functional test fails (jnewbery)
+- #10565 `8c2098a` [coverage] Remove subtrees and benchmarks from coverage report (achow101)
+
+### Miscellaneous
+- #9871 `be8ba2c` Add a tree sha512 hash to merge commits (sipa)
+- #9821 `d19d45a` util: Specific GetOSRandom for Linux/FreeBSD/OpenBSD (laanwj)
+- #9903 `ba80a68` Docs: add details to -rpcclienttimeout doc (ian-kelling)
+- #9910 `53c300f` Docs: correct and elaborate -rpcbind doc (ian-kelling)
+- #9905 `01b7cda` [contrib] gh-merge: Move second sha512 check to the end (MarcoFalke)
+- #9880 `4df8213` Verify Tree-SHA512s in merge commits, enforce sigs are not SHA1 (TheBlueMatt)
+- #9932 `00c13ea` Fix verify-commits on travis and always check top commit's tree (TheBlueMatt)
+- #9952 `6996e06` Add historical release notes for 0.14.0 (laanwj)
+- #9940 `fa99663` Fix verify-commits on OSX, update for new bad Tree-SHA512, point travis to different keyservers (TheBlueMatt)
+- #9963 `8040ae6` util: Properly handle errors during log message formatting (laanwj)
+- #9984 `cce056d` devtools: Make github-merge compute SHA512 from git, instead of worktree (laanwj)
+- #9995 `8bcf934` [doc] clarify blockchain size and pruning (askmike)
+- #9734 `0c17afc` Add updating of chainTxData to release process (sipa)
+- #10063 `530fcbd` add missing spaces so that markdown recognizes headline (flack)
+- #10085 `db1ae54` Docs: remove 'noconnect' option (jlopp)
+- #10090 `8e4f7e7` Update bitcoin.conf with example for pruning (coinables)
+- #9424 `1a5aaab` Change LogAcceptCategory to use uint32_t rather than sets of strings (gmaxwell)
+- #10036 `fbf36ca` Fix init README format to render correctly on github (jlopp)
+- #10058 `a2cd0b0` No need to use OpenSSL malloc/free (tjps)
+- #10123 `471ed00` Allow debug logs to be excluded from specified component (jnewbery)
+- #10104 `fadf078` linearize script: Option to use RPC cookie (achow101)
+- #10162 `a3a2160` [trivial] Log calls to getblocktemplate (jnewbery)
+- #10155 `928695b` build: Deduplicate version numbers (laanwj)
+- #10211 `a86255b` [doc] Contributor fixes & new "finding reviewers" section (kallewoof)
+- #10250 `1428f30` Fix some empty vector references (sipa)
+- #10270 `95f5e44` Remove Clang workaround for Boost 1.46 (fanquake)
+- #10263 `cb007e4` Trivial: fix fee estimate write error log message (CryptAxe)
+- #9670 `bd9ec0e` contrib: github-merge improvements (laanwj)
+- #10260 `1d75597` [doc] Minor corrections to osx dependencies (fanquake)
+- #10189 `750c5a5` devtools/net: add a verifier for scriptable changes. Use it to make CNode::id private (theuni)
+- #10322 `bc64b5a` Use hardware timestamps in RNG seeding (sipa)
+- #10381 `7f2b9e0` Shadowing warnings are not enabled by default, update doc accordingly (paveljanik)
+- #10380 `b6ee855` [doc] Removing comments about dirty entries on txmempool (madeo)
+- #10383 `d0c37ee` [logging] log system time and mock time (jnewbery)
+- #10404 `b45a52a` doc: Add logging to FinalizeNode() (sdaftuar)
+- #10388 `526e839` Output line to debug.log when IsInitialBlockDownload latches to false (morcos)
+- #10372 `15254e9` Add perf counter data to GetStrongRandBytes state in scheduler (TheBlueMatt)
+- #10461 `55b72f3` Update style guide (sipa)
+- #10486 `10e8c0a` devtools: Retry after signing fails in github-merge (laanwj)
+- #10447 `f259263` Make bitcoind invalid argument error message specific (laanwj)
+- #10495 `6a38b79` contrib: Update location of seeds.txt (laanwj)
+- #10469 `b6b150b` Fixing typo in rpcdump.cpp help message (keystrike)
+- #10451 `27b9931` contrib/init/bitcoind.openrcconf: Don't disable wallet by default (luke-jr)
+- #10323 `00d3692` Update to latest libsecp256k1 master (sipa)
+- #10422 `cec9e1e` Fix timestamp in fee estimate debug message (morcos)
+- #10566 `5d034ee` [docs] Use the "domain name setup" image (previously unused) in the gitian docs (practicalswift)
+- #10534 `a514ac3` Clarify prevector::erase and avoid swap-to-clear (sipa)
+- #10575 `22ec768` Header include guideline (sipa)
+- #10480 `fbf5d3b` Improve commit-check-script.sh (sipa)
+- #10502 `1ad3d4e` scripted-diff: Remove BOOST_FOREACH, Q_FOREACH and PAIRTYPE (jtimon)
+- #10377 `b63be2c` Use rdrand as entropy source on supported platforms (sipa)
+- #9895 `228c319` Turn TryCreateDirectory() into TryCreateDirectories() (benma)
+- #10602 `d76e84a` Make clang-format use C++11 features (e.g. A<A<int>> instead of A<A<int> >) (practicalswift)
+- #10623 `c38f540` doc: Add 0.14.2 release notes (MarcoFalke)
+- #10276 `b750b33` contrib/verifybinaries: allow filtering by platform (knocte)
+- #10248 `01c4b14` Rewrite addrdb with less duplication using CHashVerifier (sipa)
+- #10577 `232508f` Add an explanation of quickly hashing onto a non-power of two range (gmaxwell)
+- #10608 `eee398f` Add a comment explaining the use of MAX_BLOCK_BASE_SIZE (gmaxwell)
+- #10728 `7397af9` fix typo in help text for removeprunedfunds (AkioNak)
+- #10193 `6dbcc74` scripted-diff: Remove #include <boost/foreach.hpp> (jtimon)
+- #10676 `379aed0` document script-based return fields for validateaddress (instagibbs)
+- #10651 `cef4b5c` Verify binaries from bitcoincore.org and bitcoin.org (TheBlueMatt)
+- #10786 `ca4c545` Add PR description to merge commit in github-merge.py (sipa)
+- #10812 `c5904e8` [utils] Allow bitcoin-cli's -rpcconnect option to be used with square brackets (jnewbery)
+- #10842 `3895e25` Fix incorrect Doxygen tag (@ince → @since). Doxygen parameter name matching (practicalswift)
+- #10681 `df0793f` add gdb attach process to test README (instagibbs)
+- #10789 `1124328` Punctuation/grammer fixes in rpcwallet.cpp (stevendlander)
+- #10655 `78f307b` Properly document target_confirmations in listsinceblock (RHavar)
+- #10917 `5c003cb` developer-notes: add reference to snake_case and PascalCase (benma)
+- #11003 `4b5a7ce` Docs: Capitalize bullet points in CONTRIBUTING guide (eklitzke)
+- #10968 `98aa3f6` Add instructions for parallel gitian builds (coblee)
+- #11076 `1c4b9b3` 0.15 release-notes nits: fix redundancy, remove accidental parenthesis & fix range style (practicalswift)
+- #11090 `8f0121c` Update contributor names in release-notes.md (Derek701)
+- #11056 `cbdd338` disable jni in builds (instagibbs)
+- #11080 `2b59cfb` doc: Update build-openbsd for 6.1 (laanwj)
+- #11119 `0a6af47` [doc] build-windows: Mention that only trusty works (MarcoFalke)
+- #11108 `e8ad101` Changing -txindex requires -reindex, not -reindex-chainstate (TheBlueMatt)
+- #9792 `342b9bc` FastRandomContext improvements and switch to ChaCha20 (sipa)
+- #9505 `67ed40e` Prevector Quick Destruct (JeremyRubin)
+- #10820 `ef37f20` Use cpuid intrinsics instead of asm code (sipa)
+- #9999 `a328904` [LevelDB] Plug leveldb logs to bitcoin logs (NicolasDorier)
+- #9693 `c5e9e42` Prevent integer overflow in ReadVarInt (gmaxwell)
+- #10129 `351d0ad` scheduler: fix sub-second precision with boost < 1.50 (theuni)
+- #10153 `fade788` logging: Fix off-by-one for shrinkdebugfile default (MarcoFalke)
+- #10305 `c45da32` Fix potential NPD introduced in b297426c (TheBlueMatt)
+- #10338 `daf3e7d` Maintain state across GetStrongRandBytes calls (sipa)
+- #10544 `a4fe077` Update to LevelDB 1.20 (sipa)
+- #10614 `cafe24f` random: fix crash on some 64bit platforms (theuni)
+- #10714 `2a09a38` Avoid printing incorrect block indexing time due to uninitialized variable (practicalswift)
+- #10837 `8bc6d1f` Fix resource leak on error in GetDevURandom (corebob)
+- #10832 `89bb036` init: Factor out AppInitLockDataDirectory and fix startup core dump issue (laanwj)
+- #10914 `b995a37` Add missing lock in CScheduler::AreThreadsServicingQueue() (TheBlueMatt)
+- #10958 `659c096` Update to latest Bitcoin patches for LevelDB (sipa)
+- #10919 `c1c671f` Fix more init bugs (TheBlueMatt)
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- ロハン ダル
+- Ahmad Kazi
+- aideca
+- Akio Nakamura
+- Alex Morcos
+- Allan Doensen
+- Andres G. Aragoneses
+- Andrew Chow
+- Angel Leon
+- Awemany
+- Bob McElrath
+- Brian McMichael
+- BtcDrak
+- Charlie Lee
+- Chris Gavin
+- Chris Stewart
+- Cory Fields
+- CryptAxe
+- Dag Robole
+- Daniel Aleksandersen
+- Daniel Cousens
+- darksh1ne
+- Dimitris Tsapakidis
+- Eric Shaw
+- Evan Klitzke
+- fanquake
+- Felix Weis
+- flack
+- Guido Vranken
+- Greg Griffith
+- Gregory Maxwell
+- Gregory Sanders
+- Ian Kelling
+- Jack Grigg
+- James Evans
+- James Hilliard
+- Jameson Lopp
+- Jeremy Rubin
+- Jimmy Song
+- João Barbosa
+- Johnathan Corgan
+- John Newbery
+- Jonas Schnelli
+- Jorge Timón
+- Karl-Johan Alm
+- kewde
+- KibbledJiveElkZoo
+- Kirit Thadaka
+- kobake
+- Kyle Honeycutt
+- Lawrence Nahum
+- Luke Dashjr
+- Marco Falke
+- Marcos Mayorga
+- Marijn Stollenga
+- Mario Dian
+- Mark Friedenbach
+- Marko Bencun
+- Masahiko Hyuga
+- Matt Corallo
+- Matthew Zipkin
+- Matthias Grundmann
+- Michael Goldstein
+- Michael Rotarius
+- Mikerah
+- Mike van Rossum
+- Mitchell Cash
+- Nicolas Dorier
+- Patrick Strateman
+- Pavel Janík
+- Pavlos Antoniou
+- Pavol Rusnak
+- Pedro Branco
+- Peter Todd
+- Pieter Wuille
+- practicalswift
+- René Nyffenegger
+- Ricardo Velhote
+- romanornr
+- Russell Yanofsky
+- Rusty Russell
+- Ryan Havar
+- shaolinfry
+- Shigeya Suzuki
+- Simone Madeo
+- Spencer Lievens
+- Steven D. Lander
+- Suhas Daftuar
+- Takashi Mitsuta
+- Thomas Snider
+- Timothy Redaelli
+- tintinweb
+- tnaka
+- Warren Togami
+- 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-notes/release-notes-0.15.1.md b/doc/release-notes/release-notes-0.15.1.md
new file mode 100644
index 0000000000..75d2e09714
--- /dev/null
+++ b/doc/release-notes/release-notes-0.15.1.md
@@ -0,0 +1,277 @@
+Bitcoin Core version *0.15.1* is now available from:
+
+ <https://bitcoincore.org/bin/bitcoin-core-0.15.1/>
+
+or
+
+ <https://bitcoin.org/bin/bitcoin-core-0.15.1/>
+
+This is a new minor version release, including 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/>
+
+How to Upgrade
+==============
+
+If you are running an older version, shut it down. Wait until it has completely
+shut down (which might take a few minutes for older versions), then run the
+installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac)
+or `bitcoind`/`bitcoin-qt` (on Linux).
+
+The first time you run version 0.15.0 or higher, your chainstate database will
+be converted to a new format, which will take anywhere from a few minutes to
+half an hour, depending on the speed of your machine.
+
+The file format of `fee_estimates.dat` changed in version 0.15.0. Hence, a
+downgrade from version 0.15 or upgrade to version 0.15 will cause all fee
+estimates to be discarded.
+
+Note that the block database format also changed in version 0.8.0 and there is no
+automatic upgrade code from before version 0.8 to version 0.15.0. Upgrading
+directly from 0.7.x and earlier without redownloading the blockchain is not supported.
+However, as usual, old wallet versions are still supported.
+
+Downgrading warning
+-------------------
+
+The chainstate database for this release is not compatible with previous
+releases, so if you run 0.15 and then decide to switch back to any
+older version, you will need to run the old release with the `-reindex-chainstate`
+option to rebuild the chainstate data structures in the old format.
+
+If your node has pruning enabled, this will entail re-downloading and
+processing the entire blockchain.
+
+Compatibility
+==============
+
+Bitcoin Core is extensively tested on multiple operating systems using
+the Linux kernel, macOS 10.8+, and Windows Vista and later. Windows XP is not supported.
+
+Bitcoin Core should also work on most other Unix-like systems but is not
+frequently tested on them.
+
+
+Notable changes
+===============
+
+Network fork safety enhancements
+--------------------------------
+
+A number of changes to the way Bitcoin Core deals with peer connections and invalid blocks
+have been made, as a safety precaution against blockchain forks and misbehaving peers.
+
+- Unrequested blocks with less work than the minimum-chain-work are now no longer processed even
+if they have more work than the tip (a potential issue during IBD where the tip may have low-work).
+This prevents peers wasting the resources of a node.
+
+- Peers which provide a chain with less work than the minimum-chain-work during IBD will now be disconnected.
+
+- For a given outbound peer, we now check whether their best known block has at least as much work as our tip. If it
+doesn't, and if we still haven't heard about a block with sufficient work after a 20 minute timeout, then we send
+a single getheaders message, and wait 2 more minutes. If after two minutes their best known block has insufficient
+work, we disconnect that peer. We protect 4 of our outbound peers from being disconnected by this logic to prevent
+excessive network topology changes as a result of this algorithm, while still ensuring that we have a reasonable
+number of nodes not known to be on bogus chains.
+
+- Outbound (non-manual) peers that serve us block headers that are already known to be invalid (other than compact
+block announcements, because BIP 152 explicitly permits nodes to relay compact blocks before fully validating them)
+will now be disconnected.
+
+- If the chain tip has not been advanced for over 30 minutes, we now assume the tip may be stale and will try to connect
+to an additional outbound peer. A periodic check ensures that if this extra peer connection is in use, we will disconnect
+the peer that least recently announced a new block.
+
+- The set of all known invalid-themselves blocks (i.e. blocks which we attempted to connect but which were found to be
+invalid) are now tracked and used to check if new headers build on an invalid chain. This ensures that everything that
+descends from an invalid block is marked as such.
+
+
+Miner block size limiting deprecated
+------------------------------------
+
+Though blockmaxweight has been preferred for limiting the size of blocks returned by
+getblocktemplate since 0.13.0, blockmaxsize remained as an option for those who wished
+to limit their block size directly. Using this option resulted in a few UI issues as
+well as non-optimal fee selection and ever-so-slightly worse performance, and has thus
+now been deprecated. Further, the blockmaxsize option is now used only to calculate an
+implied blockmaxweight, instead of limiting block size directly. Any miners who wish
+to limit their blocks by size, instead of by weight, will have to do so manually by
+removing transactions from their block template directly.
+
+
+GUI settings backed up on reset
+-------------------------------
+
+The GUI settings will now be written to `guisettings.ini.bak` in the data directory before wiping them when
+the `-resetguisettings` argument is used. This can be used to retroactively troubleshoot issues due to the
+GUI settings.
+
+
+Duplicate wallets disallowed
+----------------------------
+
+Previously, it was possible to open the same wallet twice by manually copying the wallet file, causing
+issues when both were opened simultaneously. It is no longer possible to open copies of the same wallet.
+
+
+Debug `-minimumchainwork` argument added
+----------------------------------------
+
+A hidden debug argument `-minimumchainwork` has been added to allow a custom minimum work value to be used
+when validating a chain.
+
+
+Low-level RPC changes
+----------------------
+
+- The "currentblocksize" value in getmininginfo has been removed.
+
+- `dumpwallet` no longer allows overwriting files. This is a security measure
+ as well as prevents dangerous user mistakes.
+
+- `backupwallet` will now fail when attempting to backup to source file, rather than
+ destroying the wallet.
+
+- `listsinceblock` will now throw an error if an unknown `blockhash` argument
+ value is passed, instead of returning a list of all wallet transactions since
+ the genesis block. The behaviour is unchanged when an empty string is provided.
+
+0.15.1 Change log
+=================
+
+### Mining
+- #11100 `7871a7d` Fix confusing blockmax{size,weight} options, dont default to throwing away money (TheBlueMatt)
+
+### RPC and other APIs
+- #10859 `2a5d099` gettxout: Slightly improve doc and tests (jtimon)
+- #11267 `b1a6c94` update cli for estimate\*fee argument rename (laanwj)
+- #11483 `20cdc2b` Fix importmulti bug when importing an already imported key (pedrobranco)
+- #9937 `a43be5b` Prevent `dumpwallet` from overwriting files (laanwj)
+- #11465 `405e069` Update named args documentation for importprivkey (dusty-wil)
+- #11131 `b278a43` Write authcookie atomically (laanwj)
+- #11565 `7d4546f` Make listsinceblock refuse unknown block hash (ryanofsky)
+- #11593 `8195cb0` Work-around an upstream libevent bug (theuni)
+
+### P2P protocol and network code
+- #11397 `27e861a` Improve and document SOCKS code (laanwj)
+- #11252 `0fe2a9a` When clearing addrman clear mapInfo and mapAddr (instagibbs)
+- #11527 `a2bd86a` Remove my testnet DNS seed (schildbach)
+- #10756 `0a5477c` net processing: swap out signals for an interface class (theuni)
+- #11531 `55b7abf` Check that new headers are not a descendant of an invalid block (more effeciently) (TheBlueMatt)
+- #11560 `49bf090` Connect to a new outbound peer if our tip is stale (sdaftuar)
+- #11568 `fc966bb` Disconnect outbound peers on invalid chains (sdaftuar)
+- #11578 `ec8dedf` Add missing lock in ProcessHeadersMessage(...) (practicalswift)
+- #11456 `6f27965` Replace relevant services logic with a function suite (TheBlueMatt)
+- #11490 `bf191a7` Disconnect from outbound peers with bad headers chains (sdaftuar)
+
+### Validation
+- #10357 `da4908c` Allow setting nMinimumChainWork on command line (sdaftuar)
+- #11458 `2df65ee` Don't process unrequested, low-work blocks (sdaftuar)
+
+### Build system
+- #11440 `b6c0209` Fix validationinterface build on super old boost/clang (TheBlueMatt)
+- #11530 `265bb21` Add share/rpcuser to dist. source code archive (MarcoFalke)
+
+### GUI
+- #11334 `19d63e8` Remove custom fee radio group and remove nCustomFeeRadio setting (achow101)
+- #11198 `7310f1f` Fix display of package name on 'open config file' tooltip (esotericnonsense)
+- #11015 `6642558` Add delay before filtering transactions (lclc)
+- #11338 `6a62c74` Backup former GUI settings on `-resetguisettings` (laanwj)
+- #11335 `8d13b42` Replace save|restoreWindowGeometry with Qt functions (MeshCollider)
+- #11237 `2e31b1d` Fixing division by zero in time remaining (MeshCollider)
+- #11247 `47c02a8` Use IsMine to validate custom change address (MarcoFalke)
+
+### Wallet
+- #11017 `9e8aae3` Close DB on error (kallewoof)
+- #11225 `6b4d9f2` Update stored witness in AddToWallet (sdaftuar)
+- #11126 `2cb720a` Acquire cs_main lock before cs_wallet during wallet initialization (ryanofsky)
+- #11476 `9c8006d` Avoid opening copied wallet databases simultaneously (ryanofsky)
+- #11492 `de7053f` Fix leak in CDB constructor (promag)
+- #11376 `fd79ed6` Ensure backupwallet fails when attempting to backup to source file (tomasvdw)
+- #11326 `d570aa4` Fix crash on shutdown with invalid wallet (MeshCollider)
+
+### Tests and QA
+- #11399 `a825d4a` Fix bip68-sequence rpc test (jl2012)
+- #11150 `847c75e` Add getmininginfo test (mess110)
+- #11407 `806c78f` add functional test for mempoolreplacement command line arg (instagibbs)
+- #11433 `e169349` Restore bitcoin-util-test py2 compatibility (MarcoFalke)
+- #11308 `2e1ac70` zapwallettxes: Wait up to 3s for mempool reload (MarcoFalke)
+- #10798 `716066d` test bitcoin-cli (jnewbery)
+- #11443 `019c492` Allow "make cov" out-of-tree; Fix rpc mapping check (MarcoFalke)
+- #11445 `51bad91` 0.15.1 Backports (MarcoFalke)
+- #11319 `2f0b30a` Fix error introduced into p2p-segwit.py, and prevent future similar errors (sdaftuar)
+- #10552 `e4605d9` Tests for zmqpubrawtx and zmqpubrawblock (achow101)
+- #11067 `eeb24a3` TestNode: Add wait_until_stopped helper method (MarcoFalke)
+- #11068 `5398f20` Move wait_until to util (MarcoFalke)
+- #11125 `812c870` Add bitcoin-cli -stdin and -stdinrpcpass functional tests (promag)
+- #11077 `1d80d1e` fix timeout issues from TestNode (jnewbery)
+- #11078 `f1ced0d` Make p2p-leaktests.py more robust (jnewbery)
+- #11210 `f3f7891` Stop test_bitcoin-qt touching ~/.bitcoin (MeshCollider)
+- #11234 `f0b6795` Remove redundant testutil.cpp|h files (MeshCollider)
+- #11215 `cef0319` fixups from set_test_params() (jnewbery)
+- #11345 `f9cf7b5` Check connectivity before sending in assumevalid.py (jnewbery)
+- #11091 `c276c1e` Increase initial RPC timeout to 60 seconds (laanwj)
+- #10711 `fc2aa09` Introduce TestNode (jnewbery)
+- #11230 `d8dd8e7` Fixup dbcrash interaction with add_nodes() (jnewbery)
+- #11241 `4424176` Improve signmessages functional test (mess110)
+- #11116 `2c4ff35` Unit tests for script/standard and IsMine functions (jimpo)
+- #11422 `a36f332` Verify DBWrapper iterators are taking snapshots (TheBlueMatt)
+- #11121 `bb5e7cb` TestNode tidyups (jnewbery)
+- #11521 `ca0f3f7` travis: move back to the minimal image (theuni)
+- #11538 `adbc9d1` Fix race condition failures in replace-by-fee.py, sendheaders.py (sdaftuar)
+- #11472 `4108879` Make tmpdir option an absolute path, misc cleanup (MarcoFalke)
+- #10853 `5b728c8` Fix RPC failure testing (again) (jnewbery)
+- #11310 `b6468d3` Test listwallets RPC (mess110)
+
+### Miscellaneous
+- #11377 `75997c3` Disallow uncompressed pubkeys in bitcoin-tx [multisig] output adds (TheBlueMatt)
+- #11437 `dea3b87` [Docs] Update Windows build instructions for using WSL and Ubuntu 17.04 (fanquake)
+- #11318 `8b61aee` Put back inadvertently removed copyright notices (gmaxwell)
+- #11442 `cf18f42` [Docs] Update OpenBSD Build Instructions for OpenBSD 6.2 (fanquake)
+- #10957 `50bd3f6` Avoid returning a BIP9Stats object with uninitialized values (practicalswift)
+- #11539 `01223a0` [verify-commits] Allow revoked keys to expire (TheBlueMatt)
+
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- Andreas Schildbach
+- Andrew Chow
+- Chris Moore
+- Cory Fields
+- Cristian Mircea Messel
+- Daniel Edgecumbe
+- Donal OConnor
+- Dusty Williams
+- fanquake
+- Gregory Sanders
+- Jim Posen
+- John Newbery
+- Johnson Lau
+- João Barbosa
+- Jorge Timón
+- Karl-Johan Alm
+- Lucas Betschart
+- MarcoFalke
+- Matt Corallo
+- Paul Berg
+- Pedro Branco
+- Pieter Wuille
+- practicalswift
+- Russell Yanofsky
+- Samuel Dobson
+- Suhas Daftuar
+- Tomas van der Wansem
+- Wladimir J. van der Laan
+
+As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/).
diff --git a/share/certs/BitcoinFoundation_Apple_Cert.pem b/share/certs/BitcoinFoundation_Apple_Cert.pem
deleted file mode 100644
index beb0d7073c..0000000000
--- a/share/certs/BitcoinFoundation_Apple_Cert.pem
+++ /dev/null
@@ -1,37 +0,0 @@
-Bag Attributes
- friendlyName: Developer ID Application: BITCOIN FOUNDATION, INC., THE
- localKeyID: 6B 9C 6C A8 A5 73 70 70 E2 57 A3 49 D8 62 FB 97 C7 A5 5D 5E
-subject=/UID=PBV4GLS9J4/CN=Developer ID Application: BITCOIN FOUNDATION, INC., THE/OU=PBV4GLS9J4/O=BITCOIN FOUNDATION, INC., THE/C=US
-issuer=/CN=Developer ID Certification Authority/OU=Apple Certification Authority/O=Apple Inc./C=US
------BEGIN CERTIFICATE-----
-MIIFhzCCBG+gAwIBAgIIJ0r1rumyfZAwDQYJKoZIhvcNAQELBQAweTEtMCsGA1UE
-AwwkRGV2ZWxvcGVyIElEIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSYwJAYDVQQL
-DB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUg
-SW5jLjELMAkGA1UEBhMCVVMwHhcNMTMwMTEwMjIzOTAxWhcNMTgwMTExMjIzOTAx
-WjCBqDEaMBgGCgmSJomT8ixkAQEMClBCVjRHTFM5SjQxQDA+BgNVBAMMN0RldmVs
-b3BlciBJRCBBcHBsaWNhdGlvbjogQklUQ09JTiBGT1VOREFUSU9OLCBJTkMuLCBU
-SEUxEzARBgNVBAsMClBCVjRHTFM5SjQxJjAkBgNVBAoMHUJJVENPSU4gRk9VTkRB
-VElPTiwgSU5DLiwgVEhFMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQAD
-ggEPADCCAQoCggEBALTd5zURuZVoJviusr119aktXksenb9IN9vq6kBbq38vxEk7
-9wkKMES2XfBRh0HxcEizGzhMNy5OCXuTLMaNMihYdfwYSoBoR2foEU+6kjPUnyJ4
-dQBFLJZJr5/QeQmALmYHEgZ6lwXFD2lU8t92340zeJ4y5LZw5pcEHtH9IummYDut
-OGCkCGXDcjL+5nHhNScJiXHhswM+62o6XXsQiP6EWbM1CsgrGTNLtaa0U/UvVDwE
-79YKklSC5Bog2LD0jBcTuveI66mFzqu++L9X9u+ZArtebwCl7BPNQ+uboYy5uV2d
-zf8lpNNZLfXCFjoLe9bLICKfZ7ub9V5aC8+GhckCAwEAAaOCAeEwggHdMD4GCCsG
-AQUFBwEBBDIwMDAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuYXBwbGUuY29tL29j
-c3AtZGV2aWQwMTAdBgNVHQ4EFgQUa5xsqKVzcHDiV6NJ2GL7l8elXV4wDAYDVR0T
-AQH/BAIwADAfBgNVHSMEGDAWgBRXF+2iz9x8mKEQ4Py+hy0s8uMXVDCCAQ4GA1Ud
-IASCAQUwggEBMIH+BgkqhkiG92NkBQEwgfAwKAYIKwYBBQUHAgEWHGh0dHA6Ly93
-d3cuYXBwbGUuY29tL2FwcGxlY2EwgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ug
-b24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRh
-bmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNv
-bmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmlj
-YXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wDgYDVR0PAQH/BAQDAgeAMBYGA1Ud
-JQEB/wQMMAoGCCsGAQUFBwMDMBMGCiqGSIb3Y2QGAQ0BAf8EAgUAMA0GCSqGSIb3
-DQEBCwUAA4IBAQAfJ0BjID/1dS2aEeVyhAzPzCBjG8vm0gDf+/qfwRn3+yWeL9vS
-nMdbilwM48IyQWTagjGGcojbsAd/vE4N7NhQyHInoCllNoeor1I5xx+blTaGRBK+
-dDhJbbdlGCjsLnH/BczGZi5fyEJds9lUIrp1hJidRcUKO76qb/9gc6qNZpl1vH5k
-lDUuJYt7YhAs+L6rTXDyqcK9maeQr0gaOPsRRAQLLwiQCorPeMTUNsbVMdMwZYJs
-R+PxiAnk+nyi7rfiFvPoASAYUuI6OzYL/Fa6QU4/gYyPgic944QYVkaQBnc0vEP1
-nXq6LGKwgVGcqJnkr/E2kui5gJoV5C3qll3e
------END CERTIFICATE-----
diff --git a/share/certs/BitcoinFoundation_Comodo_Cert.pem b/share/certs/BitcoinFoundation_Comodo_Cert.pem
deleted file mode 100644
index dc752d455c..0000000000
--- a/share/certs/BitcoinFoundation_Comodo_Cert.pem
+++ /dev/null
@@ -1,37 +0,0 @@
-Bag Attributes
- friendlyName: The Bitcoin Foundation, Inc.'s COMODO CA Limited ID
- localKeyID: 8C 94 64 E3 B5 B0 41 89 5B 89 B0 57 CC 74 B9 44 E5 B2 92 66
-subject=/C=US/postalCode=98104-1444/ST=WA/L=Seattle/street=Suite 300/street=71 Columbia St/O=The Bitcoin Foundation, Inc./CN=The Bitcoin Foundation, Inc.
-issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Code Signing CA 2
------BEGIN CERTIFICATE-----
-MIIFeDCCBGCgAwIBAgIRAJVYMd+waOER7lUqtiz3M2IwDQYJKoZIhvcNAQEFBQAw
-ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
-A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxITAfBgNV
-BAMTGENPTU9ETyBDb2RlIFNpZ25pbmcgQ0EgMjAeFw0xMzAxMTYwMDAwMDBaFw0x
-NDAxMTYyMzU5NTlaMIG8MQswCQYDVQQGEwJVUzETMBEGA1UEEQwKOTgxMDQtMTQ0
-NDELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxEjAQBgNVBAkMCVN1aXRl
-IDMwMDEXMBUGA1UECQwONzEgQ29sdW1iaWEgU3QxJTAjBgNVBAoMHFRoZSBCaXRj
-b2luIEZvdW5kYXRpb24sIEluYy4xJTAjBgNVBAMMHFRoZSBCaXRjb2luIEZvdW5k
-YXRpb24sIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChUwLD
-u/hu5aFZ/n11B27awONaaDrmHm0pamiWHb01yL4JmTBtaLCrSftF8RhCscQ8jpI0
-UG1Cchmay0e3zH5o5XRs0H9C3x+SM5ozms0TWDmAYiB8aQEghsGovDk0D2nyTQeK
-Q0xqyCh0m8ZPOnMnYrakHEmF6WvhLdJvI6Od4KIwbKxgN17cPFIfLVsZ7GrzmmbU
-Gdi4wSQCHy5rxzvBxho8Qq/SfBl93uOMUrqOHjOUAPhNuTJG3t/MdhU8Zp24s29M
-abHtYkT9W86hMjIiI8RTAR+WHKVglx9SB0cjDabXN8SZ3gME0+H++LyzlySHT8sI
-ykepojZ7UBRgp9w3AgMBAAGjggGzMIIBrzAfBgNVHSMEGDAWgBQexbEsfYfaAmh8
-JbwMB4Q/ts/e8TAdBgNVHQ4EFgQUfPf+ZyDWl/4LH0Y5BuJTelkRd/EwDgYDVR0P
-AQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYJ
-YIZIAYb4QgEBBAQDAgQQMEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQMCMCswKQYI
-KwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5uZXQvQ1BTMEEGA1UdHwQ6
-MDgwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWdu
-aW5nQ0EyLmNybDByBggrBgEFBQcBAQRmMGQwPAYIKwYBBQUHMAKGMGh0dHA6Ly9j
-cnQuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWduaW5nQ0EyLmNydDAkBggrBgEF
-BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMCgGA1UdEQQhMB+BHWxpbmRz
-YXlAYml0Y29pbmZvdW5kYXRpb24ub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAqibjo
-D4HG5XSIIMCmYE5RgQBSEAJfI+EZERk1G9F83ZUWr0yNRZCw4O+RaM7xQhvJhEoD
-G2kpk/q2bNOc71/VyZ6SrE1JRVUON41/Flhz4M6cP0BclTicXvh+efVwqZhIz+ws
-UxF2hvC/1Xx6rqI7NYAlOYXk2MSUq3HREo+gWUPKM8em4MZZV/7XCH4QbsfxOl1J
-xS6EOQmV8hfUN4KRXI5WfGUmedBxq7dM0RSJOSQl8fq2f+JjRLfjQwQucy7LDY+y
-pRTsL2TdQV/DuDuI3s0NHRGznQNddoX5jqpXhSQFAAdgrhN1gGkWaaTPzr9IF2TG
-qgr6PEp9tIYC+MbM
------END CERTIFICATE-----
diff --git a/share/certs/PrivateKeyNotes.md b/share/certs/PrivateKeyNotes.md
deleted file mode 100644
index 8d50144c21..0000000000
--- a/share/certs/PrivateKeyNotes.md
+++ /dev/null
@@ -1,46 +0,0 @@
-Code-signing private key notes
-==
-
-The private keys for these certificates were generated on Gavin's main work machine,
-following the certificate authority's recommendations for generating certificate
-signing requests.
-
-For OSX, the private key was generated by Keychain.app on Gavin's main work machine.
-The key and certificate is in a separate, passphrase-protected keychain file that is
-unlocked to sign the Bitcoin-Qt.app bundle.
-
-For Windows, the private key was generated by Firefox running on Gavin's main work machine.
-The key and certificate were exported into a separate, passphrase-protected PKCS#12 file, and
-then deleted from Firefox's keystore. The exported file is used to sign the Windows setup.exe.
-
-Threat analysis
---
-
-Gavin is a single point of failure. He could be coerced to divulge the secret signing keys,
-allowing somebody to distribute a Bitcoin-Qt.app or bitcoin-qt-setup.exe with a valid
-signature but containing a malicious binary.
-
-Or the machine Gavin uses to sign the binaries could be compromised, either remotely or
-by breaking in to his office, allowing the attacker to get the private key files and then
-install a keylogger to get the passphrase that protects them.
-
-Threat Mitigation
---
-
-"Air gapping" the machine used to do the signing will not work, because the signing
-process needs to access a timestamp server over the network. And it would not
-prevent the "rubber hose cryptography" threat (coercing Gavin to sign a bad binary
-or divulge the private keys).
-
-Windows binaries are reproducibly 'gitian-built', and the setup.exe file created
-by the NSIS installer system is a 7zip archive, so you could check to make sure
-that the bitcoin-qt.exe file inside the installer had not been tampered with.
-However, an attacker could modify the installer's code, so when the setup.exe
-was run it compromised users' systems. A volunteer to write an auditing tool
-that checks the setup.exe for tampering, and checks the files in it against
-the list of gitian signatures, is needed.
-
-The long-term solution is something like the 'gitian downloader' system, which
-uses signatures from multiple developers to determine whether or not a binary
-should be trusted. However, that just pushes the problem to "how will
-non-technical users securely get the gitian downloader code to start?"
diff --git a/share/genbuild.sh b/share/genbuild.sh
index 32ef2a5755..419e0da0fd 100755
--- a/share/genbuild.sh
+++ b/share/genbuild.sh
@@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
if [ $# -gt 1 ]; then
- cd "$2"
+ cd "$2" || exit 1
fi
if [ $# -gt 0 ]; then
FILE="$1"
diff --git a/share/rpcauth/README.md b/share/rpcauth/README.md
new file mode 100644
index 0000000000..389278a125
--- /dev/null
+++ b/share/rpcauth/README.md
@@ -0,0 +1,10 @@
+RPC Tools
+---------------------
+
+### [RPCAuth](/share/rpcauth) ###
+
+Create login credentials for a JSON-RPC user.
+
+Usage:
+
+ ./rpcauth.py <username>
diff --git a/share/rpcuser/rpcuser.py b/share/rpcauth/rpcauth.py
index 63c69e308a..6d9b44f699 100755
--- a/share/rpcuser/rpcuser.py
+++ b/share/rpcauth/rpcauth.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/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.
diff --git a/share/rpcuser/README.md b/share/rpcuser/README.md
deleted file mode 100644
index 12a8e6fb0c..0000000000
--- a/share/rpcuser/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-RPC Tools
----------------------
-
-### [RPCUser](/share/rpcuser) ###
-
-Create an RPC user login credential.
-
-Usage:
-
- ./rpcuser.py <username>
diff --git a/src/Makefile.am b/src/Makefile.am
index ebae53a8c1..4b65774fc6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,7 +18,7 @@ else
LIBUNIVALUE = $(UNIVALUE_LIBS)
endif
-BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS)
+BITCOIN_INCLUDES=-I$(builddir) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS)
BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include
BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS)
@@ -78,6 +78,7 @@ BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
base58.h \
+ bech32.h \
bloom.h \
blockencodings.h \
chain.h \
@@ -167,6 +168,7 @@ BITCOIN_CORE_H = \
wallet/rpcwallet.h \
wallet/wallet.h \
wallet/walletdb.h \
+ wallet/walletutil.h \
warnings.h \
zmq/zmqabstractnotifier.h \
zmq/zmqconfig.h\
@@ -248,6 +250,7 @@ libbitcoin_wallet_a_SOURCES = \
wallet/rpcwallet.cpp \
wallet/wallet.cpp \
wallet/walletdb.cpp \
+ wallet/walletutil.cpp \
$(BITCOIN_CORE_H)
# crypto primitives library
@@ -316,6 +319,7 @@ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_common_a_SOURCES = \
base58.cpp \
+ bech32.cpp \
chainparams.cpp \
coins.cpp \
compressor.cpp \
@@ -474,8 +478,7 @@ CLEANFILES += univalue/*.gcda univalue/*.gcno
CLEANFILES += wallet/*.gcda wallet/*.gcno
CLEANFILES += wallet/test/*.gcda wallet/test/*.gcno
CLEANFILES += zmq/*.gcda zmq/*.gcno
-
-DISTCLEANFILES = obj/build.h
+CLEANFILES += obj/build.h
EXTRA_DIST = $(CTAES_DIST)
@@ -499,10 +502,6 @@ clean-local:
## FIXME: How to get the appropriate modulename_CPPFLAGS in here?
$(AM_V_GEN) $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) -DWINDRES_PREPROC -i $< -o $@
-.mm.o:
- $(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
- $(CPPFLAGS) $(AM_CXXFLAGS) $(QT_INCLUDES) $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) -c -o $@ $<
-
check-symbols: $(bin_PROGRAMS)
if GLIBC_BACK_COMPAT
@echo "Checking glibc back compat..."
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 2b1f70b25b..8e2e587d32 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -6,11 +6,12 @@ bin_PROGRAMS += bench/bench_bitcoin
BENCH_SRCDIR = bench
BENCH_BINARY = bench/bench_bitcoin$(EXEEXT)
-RAW_TEST_FILES = \
+RAW_BENCH_FILES = \
bench/data/block413567.raw
-GENERATED_TEST_FILES = $(RAW_TEST_FILES:.raw=.raw.h)
+GENERATED_BENCH_FILES = $(RAW_BENCH_FILES:.raw=.raw.h)
bench_bench_bitcoin_SOURCES = \
+ $(RAW_BENCH_FILES) \
bench/bench_bitcoin.cpp \
bench/bench.cpp \
bench/bench.h \
@@ -28,7 +29,7 @@ bench_bench_bitcoin_SOURCES = \
bench/perf.h \
bench/prevector_destructor.cpp
-nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
+nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_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)
@@ -56,7 +57,7 @@ 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 $(GENERATED_TEST_FILES)
+CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)
CLEANFILES += $(CLEAN_BITCOIN_BENCH)
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index e4b64c1ca7..0bdde06772 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -362,12 +362,12 @@ RES_MOVIES = $(wildcard $(srcdir)/qt/res/movies/spinner-*.png)
BITCOIN_RC = qt/res/bitcoin-qt-res.rc
-BITCOIN_QT_INCLUDES = -I$(builddir)/qt -I$(srcdir)/qt -I$(srcdir)/qt/forms \
- -I$(builddir)/qt/forms -DQT_NO_KEYWORDS
+BITCOIN_QT_INCLUDES = -DQT_NO_KEYWORDS
qt_libbitcoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \
$(QT_INCLUDES) $(QT_DBUS_INCLUDES) $(PROTOBUF_CFLAGS) $(QR_CFLAGS)
qt_libbitcoinqt_a_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
+qt_libbitcoinqt_a_OBJCXXFLAGS = $(AM_OBJCXXFLAGS) $(QT_PIE_FLAGS)
qt_libbitcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \
$(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(PROTOBUF_PROTO) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES)
@@ -438,7 +438,7 @@ $(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES)
$(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin $< | \
$(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@
-CLEAN_QT = $(nodist_qt_libbitcoinqt_a_SOURCES) $(QT_QM) $(QT_FORMS_H) qt/*.gcda qt/*.gcno
+CLEAN_QT = $(nodist_qt_libbitcoinqt_a_SOURCES) $(QT_QM) $(QT_FORMS_H) qt/*.gcda qt/*.gcno qt/temp_bitcoin_locale.qrc
CLEANFILES += $(CLEAN_QT)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 01ab0134fe..06175be3fc 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -31,6 +31,7 @@ BITCOIN_TESTS =\
test/base32_tests.cpp \
test/base58_tests.cpp \
test/base64_tests.cpp \
+ test/bech32_tests.cpp \
test/bip32_tests.cpp \
test/blockencodings_tests.cpp \
test/bloom_tests.cpp \
@@ -49,6 +50,7 @@ BITCOIN_TESTS =\
test/main_tests.cpp \
test/mempool_tests.cpp \
test/merkle_tests.cpp \
+ test/merkleblock_tests.cpp \
test/miner_tests.cpp \
test/multisig_tests.cpp \
test/net_tests.cpp \
@@ -65,6 +67,7 @@ BITCOIN_TESTS =\
test/scheduler_tests.cpp \
test/script_P2SH_tests.cpp \
test/script_tests.cpp \
+ test/script_standard_tests.cpp \
test/scriptnum_tests.cpp \
test/serialize_tests.cpp \
test/sighash_tests.cpp \
@@ -77,6 +80,7 @@ BITCOIN_TESTS =\
test/timedata_tests.cpp \
test/torcontrol_tests.cpp \
test/transaction_tests.cpp \
+ test/txvalidation_tests.cpp \
test/txvalidationcache_tests.cpp \
test/versionbits_tests.cpp \
test/uint256_tests.cpp \
@@ -93,7 +97,7 @@ BITCOIN_TESTS += \
endif
test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
-test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS)
+test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EVENT_CFLAGS)
test_test_bitcoin_LDADD =
if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
@@ -146,7 +150,7 @@ bitcoin_test_clean : FORCE
check-local:
@echo "Running test/util/bitcoin-util-test.py..."
- $(top_builddir)/test/util/bitcoin-util-test.py
+ $(PYTHON) $(top_builddir)/test/util/bitcoin-util-test.py
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check
if EMBEDDED_UNIVALUE
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 7f85c16585..7d7f34863d 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -3,17 +3,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "addrdb.h"
-
-#include "addrman.h"
-#include "chainparams.h"
-#include "clientversion.h"
-#include "fs.h"
-#include "hash.h"
-#include "random.h"
-#include "streams.h"
-#include "tinyformat.h"
-#include "util.h"
+#include <addrdb.h>
+
+#include <addrman.h>
+#include <chainparams.h>
+#include <clientversion.h>
+#include <hash.h>
+#include <random.h>
+#include <streams.h>
+#include <tinyformat.h>
+#include <util.h>
namespace {
diff --git a/src/addrdb.h b/src/addrdb.h
index d930de204d..098da4240e 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -6,8 +6,8 @@
#ifndef BITCOIN_ADDRDB_H
#define BITCOIN_ADDRDB_H
-#include "fs.h"
-#include "serialize.h"
+#include <fs.h>
+#include <serialize.h>
#include <string>
#include <map>
diff --git a/src/addrman.cpp b/src/addrman.cpp
index a56bb4f9c1..372cac8483 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -3,11 +3,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "addrman.h"
+#include <addrman.h>
-#include "hash.h"
-#include "serialize.h"
-#include "streams.h"
+#include <hash.h>
+#include <serialize.h>
+#include <streams.h>
int CAddrInfo::GetTriedBucket(const uint256& nKey) const
{
@@ -390,9 +390,9 @@ int CAddrMan::Check_()
if (vRandom.size() != nTried + nNew)
return -7;
- for (std::map<int, CAddrInfo>::iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
- int n = (*it).first;
- CAddrInfo& info = (*it).second;
+ for (const auto& entry : mapInfo) {
+ int n = entry.first;
+ const CAddrInfo& info = entry.second;
if (info.fInTried) {
if (!info.nLastSuccess)
return -1;
diff --git a/src/addrman.h b/src/addrman.h
index 547088aedf..ea289d508c 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -6,12 +6,12 @@
#ifndef BITCOIN_ADDRMAN_H
#define BITCOIN_ADDRMAN_H
-#include "netaddress.h"
-#include "protocol.h"
-#include "random.h"
-#include "sync.h"
-#include "timedata.h"
-#include "util.h"
+#include <netaddress.h>
+#include <protocol.h>
+#include <random.h>
+#include <sync.h>
+#include <timedata.h>
+#include <util.h>
#include <map>
#include <set>
@@ -313,9 +313,9 @@ public:
s << nUBuckets;
std::map<int, int> mapUnkIds;
int nIds = 0;
- for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
- mapUnkIds[(*it).first] = nIds;
- const CAddrInfo &info = (*it).second;
+ for (const auto& entry : mapInfo) {
+ mapUnkIds[entry.first] = nIds;
+ const CAddrInfo &info = entry.second;
if (info.nRefCount) {
assert(nIds != nNew); // this means nNew was wrong, oh ow
s << info;
@@ -323,8 +323,8 @@ public:
}
}
nIds = 0;
- for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
- const CAddrInfo &info = (*it).second;
+ for (const auto& entry : mapInfo) {
+ const CAddrInfo &info = entry.second;
if (info.fInTried) {
assert(nIds != nTried); // this means nTried was wrong, oh ow
s << info;
@@ -455,6 +455,7 @@ public:
void Clear()
{
+ LOCK(cs);
std::vector<int>().swap(vRandom);
nKey = GetRandHash();
for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
@@ -472,6 +473,8 @@ public:
nTried = 0;
nNew = 0;
nLastGood = 1; //Initially at 1 so that "never" is strictly worse.
+ mapInfo.clear();
+ mapAddr.clear();
}
CAddrMan()
diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp
index b4952af6f4..738224fb70 100644
--- a/src/arith_uint256.cpp
+++ b/src/arith_uint256.cpp
@@ -3,11 +3,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "arith_uint256.h"
+#include <arith_uint256.h>
-#include "uint256.h"
-#include "utilstrencodings.h"
-#include "crypto/common.h"
+#include <uint256.h>
+#include <utilstrencodings.h>
+#include <crypto/common.h>
#include <stdio.h>
#include <string.h>
diff --git a/src/arith_uint256.h b/src/arith_uint256.h
index 5fd4fe96cf..1009fe1cc8 100644
--- a/src/arith_uint256.h
+++ b/src/arith_uint256.h
@@ -25,7 +25,7 @@ template<unsigned int BITS>
class base_uint
{
protected:
- enum { WIDTH=BITS/32 };
+ static constexpr int WIDTH = BITS / 32;
uint32_t pn[WIDTH];
public:
diff --git a/src/base58.cpp b/src/base58.cpp
index 3802f953f9..cd087e7997 100644
--- a/src/base58.cpp
+++ b/src/base58.cpp
@@ -2,19 +2,22 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "base58.h"
+#include <base58.h>
-#include "hash.h"
-#include "uint256.h"
+#include <bech32.h>
+#include <hash.h>
+#include <script/script.h>
+#include <uint256.h>
+#include <utilstrencodings.h>
-#include <assert.h>
-#include <stdint.h>
-#include <string.h>
-#include <vector>
-#include <string>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
+#include <algorithm>
+#include <assert.h>
+#include <string.h>
+
+
/** All alphanumeric characters except for "0", "I", "O", and "l" */
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
@@ -136,7 +139,7 @@ bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet)
}
// re-calculate the checksum, ensure it matches the included 4-byte checksum
uint256 hash = Hash(vchRet.begin(), vchRet.end() - 4);
- if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) {
+ if (memcmp(&hash, &vchRet[vchRet.size() - 4], 4) != 0) {
vchRet.clear();
return false;
}
@@ -212,79 +215,113 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const
namespace
{
-class CBitcoinAddressVisitor : public boost::static_visitor<bool>
+class DestinationEncoder : public boost::static_visitor<std::string>
{
private:
- CBitcoinAddress* addr;
+ const CChainParams& m_params;
public:
- explicit CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {}
-
- bool operator()(const CKeyID& id) const { return addr->Set(id); }
- bool operator()(const CScriptID& id) const { return addr->Set(id); }
- bool operator()(const CNoDestination& no) const { return false; }
-};
-
-} // namespace
-
-bool CBitcoinAddress::Set(const CKeyID& id)
-{
- SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20);
- return true;
-}
+ DestinationEncoder(const CChainParams& params) : m_params(params) {}
-bool CBitcoinAddress::Set(const CScriptID& id)
-{
- SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20);
- return true;
-}
+ std::string operator()(const CKeyID& id) const
+ {
+ std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
+ data.insert(data.end(), id.begin(), id.end());
+ return EncodeBase58Check(data);
+ }
-bool CBitcoinAddress::Set(const CTxDestination& dest)
-{
- return boost::apply_visitor(CBitcoinAddressVisitor(this), dest);
-}
+ std::string operator()(const CScriptID& id) const
+ {
+ std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
+ data.insert(data.end(), id.begin(), id.end());
+ return EncodeBase58Check(data);
+ }
-bool CBitcoinAddress::IsValid() const
-{
- return IsValid(Params());
-}
+ std::string operator()(const WitnessV0KeyHash& id) const
+ {
+ std::vector<unsigned char> data = {0};
+ ConvertBits<8, 5, true>(data, id.begin(), id.end());
+ return bech32::Encode(m_params.Bech32HRP(), data);
+ }
-bool CBitcoinAddress::IsValid(const CChainParams& params) const
-{
- bool fCorrectSize = vchData.size() == 20;
- bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) ||
- vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
- return fCorrectSize && fKnownVersion;
-}
+ std::string operator()(const WitnessV0ScriptHash& id) const
+ {
+ std::vector<unsigned char> data = {0};
+ ConvertBits<8, 5, true>(data, id.begin(), id.end());
+ return bech32::Encode(m_params.Bech32HRP(), data);
+ }
-CTxDestination CBitcoinAddress::Get() const
-{
- if (!IsValid())
- return CNoDestination();
- uint160 id;
- memcpy(&id, vchData.data(), 20);
- if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
- return CKeyID(id);
- else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS))
- return CScriptID(id);
- else
- return CNoDestination();
-}
+ std::string operator()(const WitnessUnknown& id) const
+ {
+ if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) {
+ return {};
+ }
+ std::vector<unsigned char> data = {(unsigned char)id.version};
+ ConvertBits<8, 5, true>(data, id.program, id.program + id.length);
+ return bech32::Encode(m_params.Bech32HRP(), data);
+ }
-bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const
-{
- if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
- return false;
- uint160 id;
- memcpy(&id, vchData.data(), 20);
- keyID = CKeyID(id);
- return true;
-}
+ std::string operator()(const CNoDestination& no) const { return {}; }
+};
-bool CBitcoinAddress::IsScript() const
+CTxDestination DecodeDestination(const std::string& str, const CChainParams& params)
{
- return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS);
+ std::vector<unsigned char> data;
+ uint160 hash;
+ if (DecodeBase58Check(str, data)) {
+ // base58-encoded Bitcoin addresses.
+ // Public-key-hash-addresses have version 0 (or 111 testnet).
+ // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
+ const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
+ if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
+ std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin());
+ return CKeyID(hash);
+ }
+ // Script-hash-addresses have version 5 (or 196 testnet).
+ // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
+ const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
+ if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) {
+ std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin());
+ return CScriptID(hash);
+ }
+ }
+ data.clear();
+ auto bech = bech32::Decode(str);
+ if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) {
+ // Bech32 decoding
+ int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16)
+ // The rest of the symbols are converted witness program bytes.
+ if (ConvertBits<5, 8, false>(data, bech.second.begin() + 1, bech.second.end())) {
+ if (version == 0) {
+ {
+ WitnessV0KeyHash keyid;
+ if (data.size() == keyid.size()) {
+ std::copy(data.begin(), data.end(), keyid.begin());
+ return keyid;
+ }
+ }
+ {
+ WitnessV0ScriptHash scriptid;
+ if (data.size() == scriptid.size()) {
+ std::copy(data.begin(), data.end(), scriptid.begin());
+ return scriptid;
+ }
+ }
+ return CNoDestination();
+ }
+ if (version > 16 || data.size() < 2 || data.size() > 40) {
+ return CNoDestination();
+ }
+ WitnessUnknown unk;
+ unk.version = version;
+ std::copy(data.begin(), data.end(), unk.program);
+ unk.length = data.size();
+ return unk;
+ }
+ }
+ return CNoDestination();
}
+} // namespace
void CBitcoinSecret::SetKey(const CKey& vchSecret)
{
@@ -318,3 +355,23 @@ bool CBitcoinSecret::SetString(const std::string& strSecret)
{
return SetString(strSecret.c_str());
}
+
+std::string EncodeDestination(const CTxDestination& dest)
+{
+ return boost::apply_visitor(DestinationEncoder(Params()), dest);
+}
+
+CTxDestination DecodeDestination(const std::string& str)
+{
+ return DecodeDestination(str, Params());
+}
+
+bool IsValidDestinationString(const std::string& str, const CChainParams& params)
+{
+ return IsValidDestination(DecodeDestination(str, params));
+}
+
+bool IsValidDestinationString(const std::string& str)
+{
+ return IsValidDestinationString(str, Params());
+}
diff --git a/src/base58.h b/src/base58.h
index db12208f74..fee79f02bf 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -14,12 +14,11 @@
#ifndef BITCOIN_BASE58_H
#define BITCOIN_BASE58_H
-#include "chainparams.h"
-#include "key.h"
-#include "pubkey.h"
-#include "script/script.h"
-#include "script/standard.h"
-#include "support/allocators/zeroafterfree.h"
+#include <chainparams.h>
+#include <key.h>
+#include <pubkey.h>
+#include <script/standard.h>
+#include <support/allocators/zeroafterfree.h>
#include <string>
#include <vector>
@@ -95,30 +94,6 @@ public:
bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; }
};
-/** base58-encoded Bitcoin addresses.
- * Public-key-hash-addresses have version 0 (or 111 testnet).
- * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
- * Script-hash-addresses have version 5 (or 196 testnet).
- * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
- */
-class CBitcoinAddress : public CBase58Data {
-public:
- bool Set(const CKeyID &id);
- bool Set(const CScriptID &id);
- bool Set(const CTxDestination &dest);
- bool IsValid() const;
- bool IsValid(const CChainParams &params) const;
-
- CBitcoinAddress() {}
- CBitcoinAddress(const CTxDestination &dest) { Set(dest); }
- CBitcoinAddress(const std::string& strAddress) { SetString(strAddress); }
- CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); }
-
- CTxDestination Get() const;
- bool GetKeyID(CKeyID &keyID) const;
- bool IsScript() const;
-};
-
/**
* A base58-encoded secret key
*/
@@ -167,4 +142,9 @@ public:
typedef CBitcoinExtKeyBase<CExtKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_SECRET_KEY> CBitcoinExtKey;
typedef CBitcoinExtKeyBase<CExtPubKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_PUBLIC_KEY> CBitcoinExtPubKey;
+std::string EncodeDestination(const CTxDestination& dest);
+CTxDestination DecodeDestination(const std::string& str);
+bool IsValidDestinationString(const std::string& str);
+bool IsValidDestinationString(const std::string& str, const CChainParams& params);
+
#endif // BITCOIN_BASE58_H
diff --git a/src/bech32.cpp b/src/bech32.cpp
new file mode 100644
index 0000000000..274782e467
--- /dev/null
+++ b/src/bech32.cpp
@@ -0,0 +1,191 @@
+// Copyright (c) 2017 Pieter Wuille
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bech32.h>
+
+namespace
+{
+
+typedef std::vector<uint8_t> data;
+
+/** The Bech32 character set for encoding. */
+const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
+
+/** The Bech32 character set for decoding. */
+const int8_t CHARSET_REV[128] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
+ -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
+ 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
+ -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
+ 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
+};
+
+/** Concatenate two byte arrays. */
+data Cat(data x, const data& y)
+{
+ x.insert(x.end(), y.begin(), y.end());
+ return x;
+}
+
+/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to
+ * make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
+ * bits correspond to earlier values. */
+uint32_t PolyMod(const data& v)
+{
+ // The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an
+ // implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) =
+ // 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that
+ // [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...].
+
+ // The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of
+ // v(x) mod g(x), where g(x) is the Bech32 generator,
+ // x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way
+ // that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a
+ // window of 1023 characters. Among the various possible BCH codes, one was selected to in
+ // fact guarantee detection of up to 4 errors within a window of 89 characters.
+
+ // Note that the coefficients are elements of GF(32), here represented as decimal numbers
+ // between {}. In this finite field, addition is just XOR of the corresponding numbers. For
+ // example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires
+ // treating the bits of values themselves as coefficients of a polynomial over a smaller field,
+ // GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} =
+ // (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a
+ // = a^3 + 1 (mod a^5 + a^3 + 1) = {9}.
+
+ // During the course of the loop below, `c` contains the bitpacked coefficients of the
+ // polynomial constructed from just the values of v that were processed so far, mod g(x). In
+ // the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of
+ // v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value
+ // for `c`.
+ uint32_t c = 1;
+ for (auto v_i : v) {
+ // We want to update `c` to correspond to a polynomial with one extra term. If the initial
+ // value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to
+ // correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to
+ // process. Simplifying:
+ // c'(x) = (f(x) * x + v_i) mod g(x)
+ // ((f(x) mod g(x)) * x + v_i) mod g(x)
+ // (c(x) * x + v_i) mod g(x)
+ // If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute
+ // c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x)
+ // = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x)
+ // = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i
+ // If we call (x^6 mod g(x)) = k(x), this can be written as
+ // c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x)
+
+ // First, determine the value of c0:
+ uint8_t c0 = c >> 25;
+
+ // Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i:
+ c = ((c & 0x1ffffff) << 5) ^ v_i;
+
+ // Finally, for each set bit n in c0, conditionally add {2^n}k(x):
+ if (c0 & 1) c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}
+ if (c0 & 2) c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13}
+ if (c0 & 4) c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26}
+ if (c0 & 8) c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29}
+ if (c0 & 16) c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19}
+ }
+ return c;
+}
+
+/** Convert to lower case. */
+inline unsigned char LowerCase(unsigned char c)
+{
+ return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c;
+}
+
+/** Expand a HRP for use in checksum computation. */
+data ExpandHRP(const std::string& hrp)
+{
+ data ret;
+ ret.reserve(hrp.size() + 90);
+ ret.resize(hrp.size() * 2 + 1);
+ for (size_t i = 0; i < hrp.size(); ++i) {
+ unsigned char c = hrp[i];
+ ret[i] = c >> 5;
+ ret[i + hrp.size() + 1] = c & 0x1f;
+ }
+ ret[hrp.size()] = 0;
+ return ret;
+}
+
+/** Verify a checksum. */
+bool VerifyChecksum(const std::string& hrp, const data& values)
+{
+ // PolyMod computes what value to xor into the final values to make the checksum 0. However,
+ // if we required that the checksum was 0, it would be the case that appending a 0 to a valid
+ // list of values would result in a new valid list. For that reason, Bech32 requires the
+ // resulting checksum to be 1 instead.
+ return PolyMod(Cat(ExpandHRP(hrp), values)) == 1;
+}
+
+/** Create a checksum. */
+data CreateChecksum(const std::string& hrp, const data& values)
+{
+ data enc = Cat(ExpandHRP(hrp), values);
+ enc.resize(enc.size() + 6); // Append 6 zeroes
+ uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes.
+ data ret(6);
+ for (size_t i = 0; i < 6; ++i) {
+ // Convert the 5-bit groups in mod to checksum values.
+ ret[i] = (mod >> (5 * (5 - i))) & 31;
+ }
+ return ret;
+}
+
+} // namespace
+
+namespace bech32
+{
+
+/** Encode a Bech32 string. */
+std::string Encode(const std::string& hrp, const data& values) {
+ data checksum = CreateChecksum(hrp, values);
+ data combined = Cat(values, checksum);
+ std::string ret = hrp + '1';
+ ret.reserve(ret.size() + combined.size());
+ for (auto c : combined) {
+ ret += CHARSET[c];
+ }
+ return ret;
+}
+
+/** Decode a Bech32 string. */
+std::pair<std::string, data> Decode(const std::string& str) {
+ bool lower = false, upper = false;
+ for (size_t i = 0; i < str.size(); ++i) {
+ unsigned char c = str[i];
+ if (c < 33 || c > 126) return {};
+ if (c >= 'a' && c <= 'z') lower = true;
+ if (c >= 'A' && c <= 'Z') upper = true;
+ }
+ if (lower && upper) return {};
+ size_t pos = str.rfind('1');
+ if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) {
+ return {};
+ }
+ data values(str.size() - 1 - pos);
+ for (size_t i = 0; i < str.size() - 1 - pos; ++i) {
+ unsigned char c = str[i + pos + 1];
+ int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c];
+ if (rev == -1) {
+ return {};
+ }
+ values[i] = rev;
+ }
+ std::string hrp;
+ for (size_t i = 0; i < pos; ++i) {
+ hrp += LowerCase(str[i]);
+ }
+ if (!VerifyChecksum(hrp, values)) {
+ return {};
+ }
+ return {hrp, data(values.begin(), values.end() - 6)};
+}
+
+} // namespace bech32
diff --git a/src/bech32.h b/src/bech32.h
new file mode 100644
index 0000000000..7ef7b22213
--- /dev/null
+++ b/src/bech32.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2017 Pieter Wuille
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+// Bech32 is a string encoding format used in newer address types.
+// The output consists of a human-readable part (alphanumeric), a
+// separator character (1), and a base32 data section, the last
+// 6 characters of which are a checksum.
+//
+// For more information, see BIP 173.
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace bech32
+{
+
+/** Encode a Bech32 string. Returns the empty string in case of failure. */
+std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values);
+
+/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */
+std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str);
+
+} // namespace bech32
diff --git a/src/bench/Examples.cpp b/src/bench/Examples.cpp
index 314947d48c..536e450940 100644
--- a/src/bench/Examples.cpp
+++ b/src/bench/Examples.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bench.h"
-#include "validation.h"
-#include "utiltime.h"
+#include <bench/bench.h>
+#include <validation.h>
+#include <utiltime.h>
// Sanity test: this should loop ten times, and
// min/max/average should be close to 100ms.
diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp
index 65e27a615d..2d9a9f2908 100644
--- a/src/bench/base58.cpp
+++ b/src/bench/base58.cpp
@@ -2,10 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bench.h"
+#include <bench/bench.h>
-#include "validation.h"
-#include "base58.h"
+#include <validation.h>
+#include <base58.h>
#include <array>
#include <vector>
@@ -22,7 +22,7 @@ static void Base58Encode(benchmark::State& state)
}
};
while (state.KeepRunning()) {
- EncodeBase58(buff.begin(), buff.end());
+ EncodeBase58(buff.data(), buff.data() + buff.size());
}
}
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 849d924af2..1482452814 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -2,35 +2,31 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bench.h"
-#include "perf.h"
+#include <bench/bench.h>
+#include <bench/perf.h>
#include <assert.h>
#include <iostream>
#include <iomanip>
-#include <sys/time.h>
benchmark::BenchRunner::BenchmarkMap &benchmark::BenchRunner::benchmarks() {
static std::map<std::string, benchmark::BenchFunction> benchmarks_map;
return benchmarks_map;
}
-static double gettimedouble(void) {
- struct timeval tv;
- gettimeofday(&tv, nullptr);
- return tv.tv_usec * 0.000001 + tv.tv_sec;
-}
-
benchmark::BenchRunner::BenchRunner(std::string name, benchmark::BenchFunction func)
{
benchmarks().insert(std::make_pair(name, func));
}
void
-benchmark::BenchRunner::RunAll(double elapsedTimeForOne)
+benchmark::BenchRunner::RunAll(benchmark::duration elapsedTimeForOne)
{
perf_init();
- std::cout << "#Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << ","
+ if (std::ratio_less_equal<benchmark::clock::period, std::micro>::value) {
+ std::cerr << "WARNING: Clock precision is worse than microsecond - benchmarks may be less accurate!\n";
+ }
+ std::cout << "#Benchmark" << "," << "count" << "," << "min(ns)" << "," << "max(ns)" << "," << "average(ns)" << ","
<< "min_cycles" << "," << "max_cycles" << "," << "average_cycles" << "\n";
for (const auto &p: benchmarks()) {
@@ -46,22 +42,23 @@ bool benchmark::State::KeepRunning()
++count;
return true;
}
- double now;
+ time_point now;
+
uint64_t nowCycles;
if (count == 0) {
- lastTime = beginTime = now = gettimedouble();
+ lastTime = beginTime = now = clock::now();
lastCycles = beginCycles = nowCycles = perf_cpucycles();
}
else {
- now = gettimedouble();
- double elapsed = now - lastTime;
- double elapsedOne = elapsed * countMaskInv;
+ now = clock::now();
+ auto elapsed = now - lastTime;
+ auto elapsedOne = elapsed / (countMask + 1);
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;
+ uint64_t elapsedOneCycles = (nowCycles - lastCycles) / (countMask + 1);
if (elapsedOneCycles < minCycles) minCycles = elapsedOneCycles;
if (elapsedOneCycles > maxCycles) maxCycles = elapsedOneCycles;
@@ -69,10 +66,9 @@ bool benchmark::State::KeepRunning()
// 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.
countMask = ((countMask<<3)|7) & ((1LL<<60)-1);
- countMaskInv = 1./(countMask+1);
count = 0;
- minTime = std::numeric_limits<double>::max();
- maxTime = std::numeric_limits<double>::min();
+ minTime = duration::max();
+ maxTime = duration::zero();
minCycles = std::numeric_limits<uint64_t>::max();
maxCycles = std::numeric_limits<uint64_t>::min();
return true;
@@ -81,7 +77,6 @@ bool benchmark::State::KeepRunning()
uint64_t newCountMask = ((countMask<<1)|1) & ((1LL<<60)-1);
if ((count & newCountMask)==0) {
countMask = newCountMask;
- countMaskInv = 1./(countMask+1);
}
}
}
@@ -96,9 +91,13 @@ bool benchmark::State::KeepRunning()
assert(count != 0 && "count == 0 => (now == 0 && beginTime == 0) => return above");
// Output results
- double average = (now-beginTime)/count;
+ // Duration casts are only necessary here because hardware with sub-nanosecond clocks
+ // will lose precision.
+ int64_t min_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(minTime).count();
+ int64_t max_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(maxTime).count();
+ int64_t avg_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>((now-beginTime)/count).count();
int64_t averageCycles = (nowCycles-beginCycles)/count;
- std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << minTime << "," << maxTime << "," << average << ","
+ std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << min_elapsed << "," << max_elapsed << "," << avg_elapsed << ","
<< minCycles << "," << maxCycles << "," << averageCycles << "\n";
std::cout.copyfmt(std::ios(nullptr));
diff --git a/src/bench/bench.h b/src/bench/bench.h
index 1f36f2a4bc..071a5dc9c7 100644
--- a/src/bench/bench.h
+++ b/src/bench/bench.h
@@ -9,6 +9,7 @@
#include <limits>
#include <map>
#include <string>
+#include <chrono>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
@@ -36,12 +37,21 @@ BENCHMARK(CODE_TO_TIME);
*/
namespace benchmark {
+ // In case high_resolution_clock is steady, prefer that, otherwise use steady_clock.
+ struct best_clock {
+ using hi_res_clock = std::chrono::high_resolution_clock;
+ using steady_clock = std::chrono::steady_clock;
+ using type = std::conditional<hi_res_clock::is_steady, hi_res_clock, steady_clock>::type;
+ };
+ using clock = best_clock::type;
+ using time_point = clock::time_point;
+ using duration = clock::duration;
class State {
std::string name;
- double maxElapsed;
- double beginTime;
- double lastTime, minTime, maxTime, countMaskInv;
+ duration maxElapsed;
+ time_point beginTime, lastTime;
+ duration minTime, maxTime;
uint64_t count;
uint64_t countMask;
uint64_t beginCycles;
@@ -49,13 +59,17 @@ namespace benchmark {
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);
+ State(std::string _name, duration _maxElapsed) :
+ name(_name),
+ maxElapsed(_maxElapsed),
+ minTime(duration::max()),
+ maxTime(duration::zero()),
+ count(0),
+ countMask(1),
+ beginCycles(0),
+ lastCycles(0),
+ minCycles(std::numeric_limits<uint64_t>::max()),
+ maxCycles(std::numeric_limits<uint64_t>::min()) {
}
bool KeepRunning();
};
@@ -70,7 +84,7 @@ namespace benchmark {
public:
BenchRunner(std::string name, BenchFunction func);
- static void RunAll(double elapsedTimeForOne=1.0);
+ static void RunAll(duration elapsedTimeForOne = std::chrono::seconds(1));
};
}
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 37fd772435..84e51d809a 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -2,13 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bench.h"
+#include <bench/bench.h>
-#include "crypto/sha256.h"
-#include "key.h"
-#include "validation.h"
-#include "util.h"
-#include "random.h"
+#include <crypto/sha256.h>
+#include <key.h>
+#include <validation.h>
+#include <util.h>
+#include <random.h>
int
main(int argc, char** argv)
diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp
index 5aab3381fd..89ba3d3d21 100644
--- a/src/bench/ccoins_caching.cpp
+++ b/src/bench/ccoins_caching.cpp
@@ -2,10 +2,10 @@
// 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 <bench/bench.h>
+#include <coins.h>
+#include <policy/policy.h>
+#include <wallet/crypter.h>
#include <vector>
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index 7bb1b93668..9533b3c711 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -2,15 +2,15 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bench.h"
+#include <bench/bench.h>
-#include "chainparams.h"
-#include "validation.h"
-#include "streams.h"
-#include "consensus/validation.h"
+#include <chainparams.h>
+#include <validation.h>
+#include <streams.h>
+#include <consensus/validation.h>
namespace block_bench {
-#include "bench/data/block413567.raw.h"
+#include <bench/data/block413567.raw.h>
} // namespace block_bench
// These are the two major time-sinks which happen after we have fully received
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
index b7ae5c2d57..35750aa1b6 100644
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -2,14 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bench.h"
-#include "util.h"
-#include "validation.h"
-#include "checkqueue.h"
-#include "prevector.h"
+#include <bench/bench.h>
+#include <util.h>
+#include <validation.h>
+#include <checkqueue.h>
+#include <prevector.h>
#include <vector>
#include <boost/thread/thread.hpp>
-#include "random.h"
+#include <random.h>
// This Benchmark tests the CheckQueue with the lightest
@@ -19,7 +19,7 @@ static const int MIN_CORES = 2;
static const size_t BATCHES = 101;
static const size_t BATCH_SIZE = 30;
static const int PREVECTOR_SIZE = 28;
-static const int QUEUE_BATCH_SIZE = 128;
+static const unsigned int QUEUE_BATCH_SIZE = 128;
static void CCheckQueueSpeed(benchmark::State& state)
{
struct FakeJobNoWork {
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index f8956508f6..ff57f88170 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -2,8 +2,8 @@
// 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 <bench/bench.h>
+#include <wallet/wallet.h>
#include <set>
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index 2914a36c7b..b37b5cad62 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -4,16 +4,16 @@
#include <iostream>
-#include "bench.h"
-#include "bloom.h"
-#include "hash.h"
-#include "random.h"
-#include "uint256.h"
-#include "utiltime.h"
-#include "crypto/ripemd160.h"
-#include "crypto/sha1.h"
-#include "crypto/sha256.h"
-#include "crypto/sha512.h"
+#include <bench/bench.h>
+#include <bloom.h>
+#include <hash.h>
+#include <random.h>
+#include <uint256.h>
+#include <utiltime.h>
+#include <crypto/ripemd160.h>
+#include <crypto/sha1.h>
+#include <crypto/sha256.h>
+#include <crypto/sha512.h>
/* Number of bytes to hash per iteration */
static const uint64_t BUFFER_SIZE = 1000*1000;
@@ -47,7 +47,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(in.data(), in.size()).Finalize(&in[0]);
+ CSHA256().Write(in.data(), in.size()).Finalize(in.data());
}
}
}
diff --git a/src/bench/lockedpool.cpp b/src/bench/lockedpool.cpp
index c6a05567be..b0bfa95144 100644
--- a/src/bench/lockedpool.cpp
+++ b/src/bench/lockedpool.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bench.h"
+#include <bench/bench.h>
-#include "support/lockedpool.h"
+#include <support/lockedpool.h>
#include <iostream>
#include <vector>
diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp
index 073bbde016..eda6edbb23 100644
--- a/src/bench/mempool_eviction.cpp
+++ b/src/bench/mempool_eviction.cpp
@@ -2,9 +2,9 @@
// 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 <bench/bench.h>
+#include <policy/policy.h>
+#include <txmempool.h>
#include <list>
#include <vector>
diff --git a/src/bench/perf.cpp b/src/bench/perf.cpp
index a549ec29ea..4045670a31 100644
--- a/src/bench/perf.cpp
+++ b/src/bench/perf.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 "perf.h"
+#include <bench/perf.h>
#if defined(__i386__) || defined(__x86_64__)
diff --git a/src/bench/prevector_destructor.cpp b/src/bench/prevector_destructor.cpp
index 55af3de4fe..de7ecab737 100644
--- a/src/bench/prevector_destructor.cpp
+++ b/src/bench/prevector_destructor.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bench.h"
-#include "prevector.h"
+#include <bench/bench.h>
+#include <prevector.h>
static void PrevectorDestructor(benchmark::State& state)
{
diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp
index 73c02cf718..452099b800 100644
--- a/src/bench/rollingbloom.cpp
+++ b/src/bench/rollingbloom.cpp
@@ -4,9 +4,8 @@
#include <iostream>
-#include "bench.h"
-#include "bloom.h"
-#include "utiltime.h"
+#include <bench/bench.h>
+#include <bloom.h>
static void RollingBloom(benchmark::State& state)
{
@@ -23,10 +22,10 @@ static void RollingBloom(benchmark::State& state)
data[2] = count >> 16;
data[3] = count >> 24;
if (countnow == nEntriesPerGeneration) {
- int64_t b = GetTimeMicros();
+ auto b = benchmark::clock::now();
filter.insert(data);
- int64_t e = GetTimeMicros();
- std::cout << "RollingBloom-refresh,1," << (e-b)*0.000001 << "," << (e-b)*0.000001 << "," << (e-b)*0.000001 << "\n";
+ auto total = std::chrono::duration_cast<std::chrono::nanoseconds>(benchmark::clock::now() - b).count();
+ std::cout << "RollingBloom-refresh,1," << total << "," << total << "," << total << "\n";
countnow = 0;
} else {
filter.insert(data);
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index ef7381c120..bfa5806c9d 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -2,14 +2,14 @@
// 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"
+#include <bench/bench.h>
+#include <key.h>
#if defined(HAVE_CONSENSUS_LIB)
-#include "script/bitcoinconsensus.h"
+#include <script/bitcoinconsensus.h>
#endif
-#include "script/script.h"
-#include "script/sign.h"
-#include "streams.h"
+#include <script/script.h>
+#include <script/sign.h>
+#include <streams.h>
#include <array>
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index fca6083ea8..136981b709 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -4,22 +4,22 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "chainparamsbase.h"
-#include "clientversion.h"
-#include "fs.h"
-#include "rpc/client.h"
-#include "rpc/protocol.h"
-#include "util.h"
-#include "utilstrencodings.h"
+#include <chainparamsbase.h>
+#include <clientversion.h>
+#include <fs.h>
+#include <rpc/client.h>
+#include <rpc/protocol.h>
+#include <util.h>
+#include <utilstrencodings.h>
#include <stdio.h>
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>
-#include "support/events.h"
+#include <support/events.h>
#include <univalue.h>
@@ -37,6 +37,7 @@ std::string HelpMessageCli()
strUsage += HelpMessageOpt("-?", _("This help message"));
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
+ strUsage += HelpMessageOpt("-getinfo", _("Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)"));
AppendParamsHelpMessages(strUsage);
strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED));
strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));
@@ -191,7 +192,99 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx)
}
#endif
-static UniValue CallRPC(const std::string& strMethod, const UniValue& params)
+/** Class that handles the conversion from a command-line to a JSON-RPC request,
+ * as well as converting back to a JSON object that can be shown as result.
+ */
+class BaseRequestHandler
+{
+public:
+ virtual UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) = 0;
+ virtual UniValue ProcessReply(const UniValue &batch_in) = 0;
+};
+
+/** Process getinfo requests */
+class GetinfoRequestHandler: public BaseRequestHandler
+{
+public:
+ const int ID_NETWORKINFO = 0;
+ const int ID_BLOCKCHAININFO = 1;
+ const int ID_WALLETINFO = 2;
+
+ /** Create a simulated `getinfo` request. */
+ UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
+ {
+ if (!args.empty()) {
+ throw std::runtime_error("-getinfo takes no arguments");
+ }
+ UniValue result(UniValue::VARR);
+ result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO));
+ result.push_back(JSONRPCRequestObj("getblockchaininfo", NullUniValue, ID_BLOCKCHAININFO));
+ result.push_back(JSONRPCRequestObj("getwalletinfo", NullUniValue, ID_WALLETINFO));
+ return result;
+ }
+
+ /** Collect values from the batch and form a simulated `getinfo` reply. */
+ UniValue ProcessReply(const UniValue &batch_in) override
+ {
+ UniValue result(UniValue::VOBJ);
+ std::vector<UniValue> batch = JSONRPCProcessBatchReply(batch_in, 3);
+ // Errors in getnetworkinfo() and getblockchaininfo() are fatal, pass them on
+ // getwalletinfo() is allowed to fail in case there is no wallet.
+ if (!batch[ID_NETWORKINFO]["error"].isNull()) {
+ return batch[ID_NETWORKINFO];
+ }
+ if (!batch[ID_BLOCKCHAININFO]["error"].isNull()) {
+ return batch[ID_BLOCKCHAININFO];
+ }
+ result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]);
+ result.pushKV("protocolversion", batch[ID_NETWORKINFO]["result"]["protocolversion"]);
+ if (!batch[ID_WALLETINFO].isNull()) {
+ result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]);
+ result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]);
+ }
+ result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]);
+ result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]);
+ result.pushKV("connections", batch[ID_NETWORKINFO]["result"]["connections"]);
+ result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]);
+ result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
+ result.pushKV("testnet", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"].get_str() == "test"));
+ if (!batch[ID_WALLETINFO].isNull()) {
+ result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]);
+ result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]);
+ result.pushKV("keypoololdest", batch[ID_WALLETINFO]["result"]["keypoololdest"]);
+ result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]);
+ if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) {
+ result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]);
+ }
+ result.pushKV("paytxfee", batch[ID_WALLETINFO]["result"]["paytxfee"]);
+ }
+ result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]);
+ result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]);
+ return JSONRPCReplyObj(result, NullUniValue, 1);
+ }
+};
+
+/** Process default single requests */
+class DefaultRequestHandler: public BaseRequestHandler {
+public:
+ UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
+ {
+ UniValue params;
+ if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) {
+ params = RPCConvertNamedValues(method, args);
+ } else {
+ params = RPCConvertValues(method, args);
+ }
+ return JSONRPCRequestObj(method, params, 1);
+ }
+
+ UniValue ProcessReply(const UniValue &reply) override
+ {
+ return reply.get_obj();
+ }
+};
+
+static UniValue CallRPC(BaseRequestHandler *rh, const std::string& strMethod, const std::vector<std::string>& args)
{
std::string host;
// In preference order, we choose the following for the port:
@@ -238,7 +331,7 @@ static UniValue CallRPC(const std::string& strMethod, const UniValue& params)
evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
// Attach request data
- std::string strRequest = JSONRPCRequestObj(strMethod, params, 1).write() + "\n";
+ std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n";
struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
assert(output_buffer);
evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
@@ -277,7 +370,7 @@ static UniValue CallRPC(const std::string& strMethod, const UniValue& params)
UniValue valReply(UniValue::VSTR);
if (!valReply.read(response.body))
throw std::runtime_error("couldn't parse reply from server");
- const UniValue& reply = valReply.get_obj();
+ const UniValue reply = rh->ProcessReply(valReply);
if (reply.empty())
throw std::runtime_error("expected reply to have result, error and id properties");
@@ -296,34 +389,38 @@ int CommandLineRPC(int argc, char *argv[])
}
std::string rpcPass;
if (gArgs.GetBoolArg("-stdinrpcpass", false)) {
- if(!std::getline(std::cin,rpcPass))
+ if (!std::getline(std::cin, rpcPass)) {
throw std::runtime_error("-stdinrpcpass specified but failed to read from standard input");
+ }
gArgs.ForceSetArg("-rpcpassword", rpcPass);
}
std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
if (gArgs.GetBoolArg("-stdin", false)) {
// Read one arg per line from stdin and append
std::string line;
- while (std::getline(std::cin,line))
+ while (std::getline(std::cin, line)) {
args.push_back(line);
+ }
}
- if (args.size() < 1)
- throw std::runtime_error("too few parameters (need at least command)");
- std::string strMethod = args[0];
- args.erase(args.begin()); // Remove trailing method name from arguments vector
-
- UniValue params;
- if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) {
- params = RPCConvertNamedValues(strMethod, args);
+ std::unique_ptr<BaseRequestHandler> rh;
+ std::string method;
+ if (gArgs.GetBoolArg("-getinfo", false)) {
+ rh.reset(new GetinfoRequestHandler());
+ method = "";
} else {
- params = RPCConvertValues(strMethod, args);
+ rh.reset(new DefaultRequestHandler());
+ if (args.size() < 1) {
+ throw std::runtime_error("too few parameters (need at least command)");
+ }
+ method = args[0];
+ args.erase(args.begin()); // Remove trailing method name from arguments vector
}
// Execute and handle connection failures with -rpcwait
const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
do {
try {
- const UniValue reply = CallRPC(strMethod, params);
+ const UniValue reply = CallRPC(rh.get(), method, args);
// Parse reply
const UniValue& result = find_value(reply, "result");
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index cff90d13af..c9223dbf10 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -3,24 +3,24 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "base58.h"
-#include "clientversion.h"
-#include "coins.h"
-#include "consensus/consensus.h"
-#include "core_io.h"
-#include "keystore.h"
-#include "policy/policy.h"
-#include "policy/rbf.h"
-#include "primitives/transaction.h"
-#include "script/script.h"
-#include "script/sign.h"
+#include <base58.h>
+#include <clientversion.h>
+#include <coins.h>
+#include <consensus/consensus.h>
+#include <core_io.h>
+#include <keystore.h>
+#include <policy/policy.h>
+#include <policy/rbf.h>
+#include <primitives/transaction.h>
+#include <script/script.h>
+#include <script/sign.h>
#include <univalue.h>
-#include "util.h"
-#include "utilmoneystr.h"
-#include "utilstrencodings.h"
+#include <util.h>
+#include <utilmoneystr.h>
+#include <utilstrencodings.h>
#include <stdio.h>
@@ -271,11 +271,11 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn
// extract and validate ADDRESS
std::string strAddr = vStrInputParts[1];
- CBitcoinAddress addr(strAddr);
- if (!addr.IsValid())
+ CTxDestination destination = DecodeDestination(strAddr);
+ if (!IsValidDestination(destination)) {
throw std::runtime_error("invalid TX output address");
- // build standard output script via GetScriptForDestination()
- CScript scriptPubKey = GetScriptForDestination(addr.Get());
+ }
+ CScript scriptPubKey = GetScriptForDestination(destination);
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
@@ -310,14 +310,15 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str
}
if (bSegWit) {
+ if (!pubkey.IsCompressed()) {
+ throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs");
+ }
// Call GetScriptForWitness() to build a P2WSH scriptPubKey
scriptPubKey = GetScriptForWitness(scriptPubKey);
}
if (bScriptHash) {
- // Get the address for the redeem script, then call
- // GetScriptForDestination() to construct a P2SH scriptPubKey.
- CBitcoinAddress redeemScriptAddr(scriptPubKey);
- scriptPubKey = GetScriptForDestination(redeemScriptAddr.Get());
+ // Get the ID for the script, and then construct a P2SH destination for it.
+ scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
}
// construct TxOut, append to transaction output list
@@ -377,14 +378,21 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s
CScript scriptPubKey = GetScriptForMultisig(required, pubkeys);
if (bSegWit) {
+ for (CPubKey& pubkey : pubkeys) {
+ if (!pubkey.IsCompressed()) {
+ throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs");
+ }
+ }
// Call GetScriptForWitness() to build a P2WSH scriptPubKey
scriptPubKey = GetScriptForWitness(scriptPubKey);
}
if (bScriptHash) {
- // Get the address for the redeem script, then call
- // GetScriptForDestination() to construct a P2SH scriptPubKey.
- CBitcoinAddress addr(scriptPubKey);
- scriptPubKey = GetScriptForDestination(addr.Get());
+ if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
+ throw std::runtime_error(strprintf(
+ "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
+ }
+ // Get the ID for the script, and then construct a P2SH destination for it.
+ scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
}
// construct TxOut, append to transaction output list
@@ -443,12 +451,20 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
bScriptHash = (flags.find("S") != std::string::npos);
}
+ if (scriptPubKey.size() > MAX_SCRIPT_SIZE) {
+ throw std::runtime_error(strprintf(
+ "script exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_SIZE));
+ }
+
if (bSegWit) {
- scriptPubKey = GetScriptForWitness(scriptPubKey);
+ scriptPubKey = GetScriptForWitness(scriptPubKey);
}
if (bScriptHash) {
- CBitcoinAddress addr(scriptPubKey);
- scriptPubKey = GetScriptForDestination(addr.Get());
+ if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
+ throw std::runtime_error(strprintf(
+ "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
+ }
+ scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
}
// construct TxOut, append to transaction output list
@@ -687,10 +703,10 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
else if (command == "outaddr")
MutateTxAddOutAddr(tx, commandVal);
else if (command == "outpubkey") {
- if (!ecc) { ecc.reset(new Secp256k1Init()); }
+ ecc.reset(new Secp256k1Init());
MutateTxAddOutPubKey(tx, commandVal);
} else if (command == "outmultisig") {
- if (!ecc) { ecc.reset(new Secp256k1Init()); }
+ ecc.reset(new Secp256k1Init());
MutateTxAddOutMultiSig(tx, commandVal);
} else if (command == "outscript")
MutateTxAddOutScript(tx, commandVal);
@@ -698,7 +714,7 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
MutateTxAddOutData(tx, commandVal);
else if (command == "sign") {
- if (!ecc) { ecc.reset(new Secp256k1Init()); }
+ ecc.reset(new Secp256k1Init());
MutateTxSign(tx, commandVal);
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 543eba0e69..bc23912d2b 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -4,21 +4,21 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "chainparams.h"
-#include "clientversion.h"
-#include "compat.h"
-#include "fs.h"
-#include "rpc/server.h"
-#include "init.h"
-#include "noui.h"
-#include "scheduler.h"
-#include "util.h"
-#include "httpserver.h"
-#include "httprpc.h"
-#include "utilstrencodings.h"
+#include <chainparams.h>
+#include <clientversion.h>
+#include <compat.h>
+#include <fs.h>
+#include <rpc/server.h>
+#include <init.h>
+#include <noui.h>
+#include <scheduler.h>
+#include <util.h>
+#include <httpserver.h>
+#include <httprpc.h>
+#include <utilstrencodings.h>
#include <boost/thread.hpp>
@@ -120,7 +120,7 @@ bool AppInit(int argc, char* argv[])
for (int i = 1; i < argc; i++) {
if (!IsSwitchChar(argv[i][0])) {
fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
- exit(EXIT_FAILURE);
+ return false;
}
}
@@ -132,17 +132,17 @@ bool AppInit(int argc, char* argv[])
if (!AppInitBasicSetup())
{
// InitError will have been called with detailed error, which ends up on console
- exit(EXIT_FAILURE);
+ return false;
}
if (!AppInitParameterInteraction())
{
// InitError will have been called with detailed error, which ends up on console
- exit(EXIT_FAILURE);
+ return false;
}
if (!AppInitSanityChecks())
{
// InitError will have been called with detailed error, which ends up on console
- exit(EXIT_FAILURE);
+ return false;
}
if (gArgs.GetBoolArg("-daemon", false))
{
@@ -163,7 +163,7 @@ bool AppInit(int argc, char* argv[])
if (!AppInitLockDataDirectory())
{
// If locking the data directory failed, exit immediately
- exit(EXIT_FAILURE);
+ return false;
}
fRet = AppInitMain(threadGroup, scheduler);
}
diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
index 6f27b7b9dc..6ff4d97bf4 100644
--- a/src/blockencodings.cpp
+++ b/src/blockencodings.cpp
@@ -2,16 +2,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "blockencodings.h"
-#include "consensus/consensus.h"
-#include "consensus/validation.h"
-#include "chainparams.h"
-#include "hash.h"
-#include "random.h"
-#include "streams.h"
-#include "txmempool.h"
-#include "validation.h"
-#include "util.h"
+#include <blockencodings.h>
+#include <consensus/consensus.h>
+#include <consensus/validation.h>
+#include <chainparams.h>
+#include <hash.h>
+#include <random.h>
+#include <streams.h>
+#include <txmempool.h>
+#include <validation.h>
+#include <util.h>
#include <unordered_map>
diff --git a/src/blockencodings.h b/src/blockencodings.h
index 50478f9f32..d034bb3080 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_BLOCK_ENCODINGS_H
#define BITCOIN_BLOCK_ENCODINGS_H
-#include "primitives/block.h"
+#include <primitives/block.h>
#include <memory>
diff --git a/src/bloom.cpp b/src/bloom.cpp
index fa884f0bf3..11ea099637 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -2,14 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bloom.h"
-
-#include "primitives/transaction.h"
-#include "hash.h"
-#include "script/script.h"
-#include "script/standard.h"
-#include "random.h"
-#include "streams.h"
+#include <bloom.h>
+
+#include <primitives/transaction.h>
+#include <hash.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <random.h>
+#include <streams.h>
#include <math.h>
#include <stdlib.h>
diff --git a/src/bloom.h b/src/bloom.h
index 7ca9682239..d29a8dcd6f 100644
--- a/src/bloom.h
+++ b/src/bloom.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_BLOOM_H
#define BITCOIN_BLOOM_H
-#include "serialize.h"
+#include <serialize.h>
#include <vector>
diff --git a/src/chain.cpp b/src/chain.cpp
index 47acde882e..7ebc08a50b 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -3,7 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "chain.h"
+#include <chain.h>
/**
* CChain implementation
@@ -80,12 +80,13 @@ int static inline GetSkipHeight(int height) {
return (height & 1) ? InvertLowestOne(InvertLowestOne(height - 1)) + 1 : InvertLowestOne(height);
}
-CBlockIndex* CBlockIndex::GetAncestor(int height)
+const CBlockIndex* CBlockIndex::GetAncestor(int height) const
{
- if (height > nHeight || height < 0)
+ if (height > nHeight || height < 0) {
return nullptr;
+ }
- CBlockIndex* pindexWalk = this;
+ const CBlockIndex* pindexWalk = this;
int heightWalk = nHeight;
while (heightWalk > height) {
int heightSkip = GetSkipHeight(heightWalk);
@@ -106,9 +107,9 @@ CBlockIndex* CBlockIndex::GetAncestor(int height)
return pindexWalk;
}
-const CBlockIndex* CBlockIndex::GetAncestor(int height) const
+CBlockIndex* CBlockIndex::GetAncestor(int height)
{
- return const_cast<CBlockIndex*>(this)->GetAncestor(height);
+ return const_cast<CBlockIndex*>(static_cast<const CBlockIndex*>(this)->GetAncestor(height));
}
void CBlockIndex::BuildSkip()
@@ -128,7 +129,7 @@ arith_uint256 GetBlockProof(const CBlockIndex& block)
// We need to compute 2**256 / (bnTarget+1), but we can't represent 2**256
// as it's too large for an arith_uint256. However, as 2**256 is at least as large
// as bnTarget+1, it is equal to ((2**256 - bnTarget - 1) / (bnTarget+1)) + 1,
- // or ~bnTarget / (nTarget+1) + 1.
+ // or ~bnTarget / (bnTarget+1) + 1.
return (~bnTarget / (bnTarget + 1)) + 1;
}
diff --git a/src/chain.h b/src/chain.h
index ef7e6f9554..ca1900c22b 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -6,11 +6,11 @@
#ifndef BITCOIN_CHAIN_H
#define BITCOIN_CHAIN_H
-#include "arith_uint256.h"
-#include "primitives/block.h"
-#include "pow.h"
-#include "tinyformat.h"
-#include "uint256.h"
+#include <arith_uint256.h>
+#include <primitives/block.h>
+#include <pow.h>
+#include <tinyformat.h>
+#include <uint256.h>
#include <vector>
@@ -204,14 +204,14 @@ public:
unsigned int nChainTx;
//! Verification status of this block. See enum BlockStatus
- unsigned int nStatus;
+ uint32_t nStatus;
//! block header
- int nVersion;
+ int32_t nVersion;
uint256 hashMerkleRoot;
- unsigned int nTime;
- unsigned int nBits;
- unsigned int nNonce;
+ uint32_t nTime;
+ uint32_t nBits;
+ uint32_t nNonce;
//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
int32_t nSequenceId;
@@ -304,7 +304,7 @@ public:
return (int64_t)nTimeMax;
}
- enum { nMedianTimeSpan=11 };
+ static constexpr int nMedianTimeSpan = 11;
int64_t GetMedianTimePast() const
{
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 2021ec51db..e75d3be4c1 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -3,16 +3,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "chainparams.h"
-#include "consensus/merkle.h"
+#include <chainparams.h>
+#include <consensus/merkle.h>
-#include "tinyformat.h"
-#include "util.h"
-#include "utilstrencodings.h"
+#include <tinyformat.h>
+#include <util.h>
+#include <utilstrencodings.h>
#include <assert.h>
-#include "chainparamsseeds.h"
+#include <chainparamsseeds.h>
static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
{
@@ -75,6 +75,7 @@ public:
CMainParams() {
strNetworkID = "main";
consensus.nSubsidyHalvingInterval = 210000;
+ consensus.BIP16Height = 173805; // 00000000000000ce80a7e057163a4db1d5ad7b20fb6f598c9597b9665c8fb0d4 - April 1, 2012
consensus.BIP34Height = 227931;
consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8");
consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
@@ -137,13 +138,15 @@ public:
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4};
+ bech32_hrp = "bc";
+
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
fDefaultConsistencyChecks = false;
fRequireStandard = true;
fMineBlocksOnDemand = false;
- checkpointData = (CCheckpointData) {
+ checkpointData = {
{
{ 11111, uint256S("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")},
{ 33333, uint256S("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")},
@@ -179,6 +182,7 @@ public:
CTestNetParams() {
strNetworkID = "test";
consensus.nSubsidyHalvingInterval = 210000;
+ consensus.BIP16Height = 514; // 00000000040b4e986385315e14bee30ad876d8b47f748025b26683116d21aa65
consensus.BIP34Height = 21111;
consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8");
consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
@@ -228,7 +232,6 @@ public:
vSeeds.emplace_back("testnet-seed.bitcoin.jonasschnelli.ch", true);
vSeeds.emplace_back("seed.tbtc.petertodd.org", true);
vSeeds.emplace_back("testnet-seed.bluematt.me", false);
- vSeeds.emplace_back("testnet-seed.bitcoin.schildbach.de", false);
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
@@ -236,6 +239,8 @@ public:
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
+ bech32_hrp = "tb";
+
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
fDefaultConsistencyChecks = false;
@@ -243,7 +248,7 @@ public:
fMineBlocksOnDemand = false;
- checkpointData = (CCheckpointData) {
+ checkpointData = {
{
{546, uint256S("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")},
}
@@ -267,6 +272,7 @@ public:
CRegTestParams() {
strNetworkID = "regtest";
consensus.nSubsidyHalvingInterval = 150;
+ consensus.BIP16Height = 0; // always enforce P2SH BIP16 on regtest
consensus.BIP34Height = 100000000; // BIP34 has not activated on regtest (far in the future so block v1 are not rejected in tests)
consensus.BIP34Hash = uint256();
consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in rpc activation tests)
@@ -280,13 +286,13 @@ public:
consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016)
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 999999999999ULL;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 999999999999ULL;
+ consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 999999999999ULL;
+ consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
+ consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00");
@@ -313,7 +319,7 @@ public:
fRequireStandard = false;
fMineBlocksOnDemand = true;
- checkpointData = (CCheckpointData) {
+ checkpointData = {
{
{0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")},
}
@@ -330,6 +336,8 @@ public:
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
+
+ bech32_hrp = "bcrt";
}
};
diff --git a/src/chainparams.h b/src/chainparams.h
index f55ae4cf7f..0d82a998b1 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -6,10 +6,10 @@
#ifndef BITCOIN_CHAINPARAMS_H
#define BITCOIN_CHAINPARAMS_H
-#include "chainparamsbase.h"
-#include "consensus/params.h"
-#include "primitives/block.h"
-#include "protocol.h"
+#include <chainparamsbase.h>
+#include <consensus/params.h>
+#include <primitives/block.h>
+#include <protocol.h>
#include <memory>
#include <vector>
@@ -73,6 +73,7 @@ public:
std::string NetworkIDString() const { return strNetworkID; }
const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
+ const std::string& Bech32HRP() const { return bech32_hrp; }
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; }
@@ -86,6 +87,7 @@ protected:
uint64_t nPruneAfterHeight;
std::vector<CDNSSeedData> vSeeds;
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
+ std::string bech32_hrp;
std::string strNetworkID;
CBlock genesis;
std::vector<SeedSpec6> vFixedSeeds;
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index c966683b72..a7e87faee3 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -3,10 +3,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "chainparamsbase.h"
+#include <chainparamsbase.h>
-#include "tinyformat.h"
-#include "util.h"
+#include <tinyformat.h>
+#include <util.h>
#include <assert.h>
diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp
index 5afe3e66b6..929d864588 100644
--- a/src/checkpoints.cpp
+++ b/src/checkpoints.cpp
@@ -2,13 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "checkpoints.h"
+#include <checkpoints.h>
-#include "chain.h"
-#include "chainparams.h"
-#include "reverse_iterator.h"
-#include "validation.h"
-#include "uint256.h"
+#include <chain.h>
+#include <chainparams.h>
+#include <reverse_iterator.h>
+#include <validation.h>
#include <stdint.h>
diff --git a/src/checkpoints.h b/src/checkpoints.h
index 7449d1063b..df97a674a8 100644
--- a/src/checkpoints.h
+++ b/src/checkpoints.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_CHECKPOINTS_H
#define BITCOIN_CHECKPOINTS_H
-#include "uint256.h"
+#include <uint256.h>
#include <map>
diff --git a/src/checkqueue.h b/src/checkqueue.h
index 6377fbe942..08de336079 100644
--- a/src/checkqueue.h
+++ b/src/checkqueue.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_CHECKQUEUE_H
#define BITCOIN_CHECKQUEUE_H
-#include "sync.h"
+#include <sync.h>
#include <algorithm>
#include <vector>
diff --git a/src/clientversion.cpp b/src/clientversion.cpp
index d2344ded09..1934e71bbc 100644
--- a/src/clientversion.cpp
+++ b/src/clientversion.cpp
@@ -2,15 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "clientversion.h"
+#include <clientversion.h>
-#include "tinyformat.h"
+#include <tinyformat.h>
-#include <string>
/**
* Name of client reported in the 'version' message. Report the same name
- * for both bitcoind and bitcoin-core, to make it harder for attackers to
+ * for both bitcoind and bitcoin-qt, to make it harder for attackers to
* target servers or GUI users specifically.
*/
const std::string CLIENT_NAME("Satoshi");
@@ -39,7 +38,7 @@ const std::string CLIENT_NAME("Satoshi");
//! First, include build.h if requested
#ifdef HAVE_BUILD_INFO
-#include "build.h"
+#include <obj/build.h>
#endif
//! git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$
diff --git a/src/clientversion.h b/src/clientversion.h
index 3d5392619b..3a3d117d53 100644
--- a/src/clientversion.h
+++ b/src/clientversion.h
@@ -6,7 +6,7 @@
#define BITCOIN_CLIENTVERSION_H
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif //HAVE_CONFIG_H
// Check that required client information is defined
diff --git a/src/coins.cpp b/src/coins.cpp
index b45dfc3342..afaf5ccc13 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -2,13 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "coins.h"
+#include <coins.h>
-#include "consensus/consensus.h"
-#include "memusage.h"
-#include "random.h"
-
-#include <assert.h>
+#include <consensus/consensus.h>
+#include <random.h>
bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
@@ -146,56 +143,58 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
}
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) {
- for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
- if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
- CCoinsMap::iterator itUs = cacheCoins.find(it->first);
- if (itUs == cacheCoins.end()) {
- // The parent cache does not have an entry, while the child does
- // We can ignore it if it's both FRESH and pruned in the child
- if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coin.IsSpent())) {
- // Otherwise we will need to create it in the parent
- // and move the data up and mark it as dirty
- CCoinsCacheEntry& entry = cacheCoins[it->first];
- entry.coin = std::move(it->second.coin);
- cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
- entry.flags = CCoinsCacheEntry::DIRTY;
- // We can mark it FRESH in the parent if it was FRESH in the child
- // Otherwise it might have just been flushed from the parent's cache
- // and already exist in the grandparent
- if (it->second.flags & CCoinsCacheEntry::FRESH)
- entry.flags |= CCoinsCacheEntry::FRESH;
+ for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) {
+ // Ignore non-dirty entries (optimization).
+ if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
+ continue;
+ }
+ CCoinsMap::iterator itUs = cacheCoins.find(it->first);
+ if (itUs == cacheCoins.end()) {
+ // The parent cache does not have an entry, while the child does
+ // We can ignore it if it's both FRESH and pruned in the child
+ if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coin.IsSpent())) {
+ // Otherwise we will need to create it in the parent
+ // and move the data up and mark it as dirty
+ CCoinsCacheEntry& entry = cacheCoins[it->first];
+ entry.coin = std::move(it->second.coin);
+ cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
+ entry.flags = CCoinsCacheEntry::DIRTY;
+ // We can mark it FRESH in the parent if it was FRESH in the child
+ // Otherwise it might have just been flushed from the parent's cache
+ // and already exist in the grandparent
+ if (it->second.flags & CCoinsCacheEntry::FRESH) {
+ entry.flags |= CCoinsCacheEntry::FRESH;
}
- } else {
- // Assert that the child cache entry was not marked FRESH if the
- // parent cache entry has unspent outputs. If this ever happens,
- // it means the FRESH flag was misapplied and there is a logic
- // error in the calling code.
- if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coin.IsSpent())
- throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs");
+ }
+ } else {
+ // Assert that the child cache entry was not marked FRESH if the
+ // parent cache entry has unspent outputs. If this ever happens,
+ // it means the FRESH flag was misapplied and there is a logic
+ // error in the calling code.
+ if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coin.IsSpent()) {
+ throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs");
+ }
- // Found the entry in the parent cache
- if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsSpent()) {
- // The grandparent does not have an entry, and the child is
- // modified and being pruned. This means we can just delete
- // it from the parent.
- cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
- cacheCoins.erase(itUs);
- } else {
- // A normal modification.
- cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
- itUs->second.coin = std::move(it->second.coin);
- cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
- itUs->second.flags |= CCoinsCacheEntry::DIRTY;
- // NOTE: It is possible the child has a FRESH flag here in
- // the event the entry we found in the parent is pruned. But
- // we must not copy that FRESH flag to the parent as that
- // pruned state likely still needs to be communicated to the
- // grandparent.
- }
+ // Found the entry in the parent cache
+ if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsSpent()) {
+ // The grandparent does not have an entry, and the child is
+ // modified and being pruned. This means we can just delete
+ // it from the parent.
+ cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
+ cacheCoins.erase(itUs);
+ } else {
+ // A normal modification.
+ cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
+ itUs->second.coin = std::move(it->second.coin);
+ cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
+ itUs->second.flags |= CCoinsCacheEntry::DIRTY;
+ // NOTE: It is possible the child has a FRESH flag here in
+ // the event the entry we found in the parent is pruned. But
+ // we must not copy that FRESH flag to the parent as that
+ // pruned state likely still needs to be communicated to the
+ // grandparent.
}
}
- CCoinsMap::iterator itOld = it++;
- mapCoins.erase(itOld);
}
hashBlock = hashBlockIn;
return true;
diff --git a/src/coins.h b/src/coins.h
index efb5ce869c..0280abb56c 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -6,13 +6,13 @@
#ifndef BITCOIN_COINS_H
#define BITCOIN_COINS_H
-#include "primitives/transaction.h"
-#include "compressor.h"
-#include "core_memusage.h"
-#include "hash.h"
-#include "memusage.h"
-#include "serialize.h"
-#include "uint256.h"
+#include <primitives/transaction.h>
+#include <compressor.h>
+#include <core_memusage.h>
+#include <hash.h>
+#include <memusage.h>
+#include <serialize.h>
+#include <uint256.h>
#include <assert.h>
#include <stdint.h>
@@ -214,6 +214,11 @@ protected:
public:
CCoinsViewCache(CCoinsView *baseIn);
+ /**
+ * By deleting the copy constructor, we prevent accidentally using it when one intends to create a cache on top of a base cache.
+ */
+ CCoinsViewCache(const CCoinsViewCache &) = delete;
+
// Standard CCoinsView methods
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
bool HaveCoin(const COutPoint &outpoint) const override;
@@ -290,11 +295,6 @@ public:
private:
CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const;
-
- /**
- * By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on top of a base cache.
- */
- CCoinsViewCache(const CCoinsViewCache &);
};
//! Utility function to add all of a transaction's outputs to a cache.
diff --git a/src/compat.h b/src/compat.h
index e022659c01..89cc4709a4 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -7,7 +7,7 @@
#define BITCOIN_COMPAT_H
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
#ifdef WIN32
@@ -31,6 +31,7 @@
#include <mswsock.h>
#include <windows.h>
#include <ws2tcpip.h>
+#include <stdint.h>
#else
#include <sys/fcntl.h>
#include <sys/mman.h>
@@ -49,7 +50,7 @@
#ifndef WIN32
typedef unsigned int SOCKET;
-#include "errno.h"
+#include <errno.h>
#define WSAGetLastError() errno
#define WSAEINVAL EINVAL
#define WSAEALREADY EALREADY
@@ -71,6 +72,15 @@ typedef unsigned int SOCKET;
#else
#define MAX_PATH 1024
#endif
+#ifdef _MSC_VER
+#if !defined(ssize_t)
+#ifdef _WIN64
+typedef int64_t ssize_t;
+#else
+typedef int32_t ssize_t;
+#endif
+#endif
+#endif
#if HAVE_DECL_STRNLEN == 0
size_t strnlen( const char *start, size_t max_len);
diff --git a/src/compat/byteswap.h b/src/compat/byteswap.h
index d93ff7413a..a6df6ded7a 100644
--- a/src/compat/byteswap.h
+++ b/src/compat/byteswap.h
@@ -6,7 +6,7 @@
#define BITCOIN_COMPAT_BYTESWAP_H
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
#include <stdint.h>
@@ -37,7 +37,7 @@ inline uint16_t bswap_16(uint16_t x)
{
return (x >> 8) | (x << 8);
}
-#endif // HAVE_DECL_BSWAP16
+#endif // HAVE_DECL_BSWAP16 == 0
#if HAVE_DECL_BSWAP_32 == 0
inline uint32_t bswap_32(uint32_t x)
@@ -45,7 +45,7 @@ inline uint32_t bswap_32(uint32_t x)
return (((x & 0xff000000U) >> 24) | ((x & 0x00ff0000U) >> 8) |
((x & 0x0000ff00U) << 8) | ((x & 0x000000ffU) << 24));
}
-#endif // HAVE_DECL_BSWAP32
+#endif // HAVE_DECL_BSWAP32 == 0
#if HAVE_DECL_BSWAP_64 == 0
inline uint64_t bswap_64(uint64_t x)
@@ -59,7 +59,7 @@ inline uint64_t bswap_64(uint64_t x)
| ((x & 0x000000000000ff00ull) << 40)
| ((x & 0x00000000000000ffull) << 56));
}
-#endif // HAVE_DECL_BSWAP64
+#endif // HAVE_DECL_BSWAP64 == 0
#endif // defined(__APPLE__)
diff --git a/src/compat/endian.h b/src/compat/endian.h
index dbf178f53c..9fabbd0bfb 100644
--- a/src/compat/endian.h
+++ b/src/compat/endian.h
@@ -6,10 +6,10 @@
#define BITCOIN_COMPAT_ENDIAN_H
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "compat/byteswap.h"
+#include <compat/byteswap.h>
#include <stdint.h>
diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp
index 3b9c70df7f..6633658c16 100644
--- a/src/compat/glibc_compat.cpp
+++ b/src/compat/glibc_compat.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
#include <cstddef>
diff --git a/src/compat/glibc_sanity.cpp b/src/compat/glibc_sanity.cpp
index b4d1c90992..20d2ad3cb6 100644
--- a/src/compat/glibc_sanity.cpp
+++ b/src/compat/glibc_sanity.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
#include <cstddef>
diff --git a/src/compat/strnlen.cpp b/src/compat/strnlen.cpp
index 1ac266c2d1..313435a222 100644
--- a/src/compat/strnlen.cpp
+++ b/src/compat/strnlen.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
#include <cstring>
diff --git a/src/compressor.cpp b/src/compressor.cpp
index f4c12f38d2..a729e28f07 100644
--- a/src/compressor.cpp
+++ b/src/compressor.cpp
@@ -3,11 +3,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "compressor.h"
+#include <compressor.h>
-#include "hash.h"
-#include "pubkey.h"
-#include "script/standard.h"
+#include <hash.h>
+#include <pubkey.h>
+#include <script/standard.h>
bool CScriptCompressor::IsToKeyID(CKeyID &hash) const
{
diff --git a/src/compressor.h b/src/compressor.h
index 094c1bcfe1..8731eef9f7 100644
--- a/src/compressor.h
+++ b/src/compressor.h
@@ -6,9 +6,9 @@
#ifndef BITCOIN_COMPRESSOR_H
#define BITCOIN_COMPRESSOR_H
-#include "primitives/transaction.h"
-#include "script/script.h"
-#include "serialize.h"
+#include <primitives/transaction.h>
+#include <script/script.h>
+#include <serialize.h>
class CKeyID;
class CPubKey;
diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h
index ddd4ee9fab..6e3bac2d0e 100644
--- a/src/consensus/consensus.h
+++ b/src/consensus/consensus.h
@@ -24,12 +24,9 @@ static const size_t MIN_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 60; // 60 is
static const size_t MIN_SERIALIZABLE_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 10; // 10 is the lower bound for the size of a serialized CTransaction
/** Flags for nSequence and nLockTime locks */
-enum {
- /* Interpret sequence numbers as relative lock-time constraints. */
- LOCKTIME_VERIFY_SEQUENCE = (1 << 0),
-
- /* Use GetMedianTimePast() instead of nTime for end point timestamp. */
- LOCKTIME_MEDIAN_TIME_PAST = (1 << 1),
-};
+/** Interpret sequence numbers as relative lock-time constraints. */
+static constexpr unsigned int LOCKTIME_VERIFY_SEQUENCE = (1 << 0);
+/** Use GetMedianTimePast() instead of nTime for end point timestamp. */
+static constexpr unsigned int LOCKTIME_MEDIAN_TIME_PAST = (1 << 1);
#endif // BITCOIN_CONSENSUS_CONSENSUS_H
diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp
index 798ce4b5fd..fef4a5d560 100644
--- a/src/consensus/merkle.cpp
+++ b/src/consensus/merkle.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "merkle.h"
-#include "hash.h"
-#include "utilstrencodings.h"
+#include <consensus/merkle.h>
+#include <hash.h>
+#include <utilstrencodings.h>
/* WARNING! If you're reading this because you're learning about crypto
and/or designing a new system that will use merkle trees, keep in mind
diff --git a/src/consensus/merkle.h b/src/consensus/merkle.h
index 33764c7460..c1573cc214 100644
--- a/src/consensus/merkle.h
+++ b/src/consensus/merkle.h
@@ -8,9 +8,9 @@
#include <stdint.h>
#include <vector>
-#include "primitives/transaction.h"
-#include "primitives/block.h"
-#include "uint256.h"
+#include <primitives/transaction.h>
+#include <primitives/block.h>
+#include <uint256.h>
uint256 ComputeMerkleRoot(const std::vector<uint256>& leaves, bool* mutated = nullptr);
std::vector<uint256> ComputeMerkleBranch(const std::vector<uint256>& leaves, uint32_t position);
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 6240e82857..c9fbba12a2 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -6,7 +6,8 @@
#ifndef BITCOIN_CONSENSUS_PARAMS_H
#define BITCOIN_CONSENSUS_PARAMS_H
-#include "uint256.h"
+#include <uint256.h>
+#include <limits>
#include <map>
#include <string>
@@ -31,6 +32,15 @@ struct BIP9Deployment {
int64_t nStartTime;
/** Timeout/expiry MedianTime for the deployment attempt. */
int64_t nTimeout;
+
+ /** Constant for nTimeout very far in the future. */
+ static constexpr int64_t NO_TIMEOUT = std::numeric_limits<int64_t>::max();
+
+ /** Special value for nStartTime indicating that the deployment is always active.
+ * This is useful for testing, as it means tests don't need to deal with the activation
+ * process (which takes at least 3 BIP9 intervals). Only tests that specifically test the
+ * behaviour during activation cannot use this. */
+ static constexpr int64_t ALWAYS_ACTIVE = -1;
};
/**
@@ -39,6 +49,8 @@ struct BIP9Deployment {
struct Params {
uint256 hashGenesisBlock;
int nSubsidyHalvingInterval;
+ /** Block height at which BIP16 becomes active */
+ int BIP16Height;
/** Block height and hash at which BIP34 becomes active */
int BIP34Height;
uint256 BIP34Hash;
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
index 0a71915d1d..be73d0a2f9 100644
--- a/src/consensus/tx_verify.cpp
+++ b/src/consensus/tx_verify.cpp
@@ -2,18 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "tx_verify.h"
+#include <consensus/tx_verify.h>
-#include "consensus.h"
-#include "primitives/transaction.h"
-#include "script/interpreter.h"
-#include "validation.h"
+#include <consensus/consensus.h>
+#include <primitives/transaction.h>
+#include <script/interpreter.h>
+#include <consensus/validation.h>
// TODO remove the following dependencies
-#include "chain.h"
-#include "coins.h"
-#include "utilmoneystr.h"
-
+#include <chain.h>
+#include <coins.h>
+#include <utilmoneystr.h>
+
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
{
if (tx.nLockTime == 0)
@@ -205,46 +205,46 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
return true;
}
-bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight)
+bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
{
- // This doesn't trigger the DoS code on purpose; if it did, it would make it easier
- // for an attacker to attempt to split the network.
- if (!inputs.HaveInputs(tx))
- return state.Invalid(false, 0, "", "Inputs unavailable");
-
- CAmount nValueIn = 0;
- CAmount nFees = 0;
- for (unsigned int i = 0; i < tx.vin.size(); i++)
- {
- const COutPoint &prevout = tx.vin[i].prevout;
- const Coin& coin = inputs.AccessCoin(prevout);
- assert(!coin.IsSpent());
-
- // If prev is coinbase, check that it's matured
- if (coin.IsCoinBase()) {
- if (nSpendHeight - coin.nHeight < COINBASE_MATURITY)
- return state.Invalid(false,
- REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
- strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
- }
-
- // Check for negative or overflow input values
- nValueIn += coin.out.nValue;
- if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn))
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
+ // are the actual inputs available?
+ if (!inputs.HaveInputs(tx)) {
+ return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", false,
+ strprintf("%s: inputs missing/spent", __func__));
+ }
+
+ CAmount nValueIn = 0;
+ for (unsigned int i = 0; i < tx.vin.size(); ++i) {
+ const COutPoint &prevout = tx.vin[i].prevout;
+ const Coin& coin = inputs.AccessCoin(prevout);
+ assert(!coin.IsSpent());
+
+ // If prev is coinbase, check that it's matured
+ if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
+ return state.Invalid(false,
+ REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
+ strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
+ }
+ // Check for negative or overflow input values
+ nValueIn += coin.out.nValue;
+ if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
+ return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
}
+ }
+
+ const CAmount value_out = tx.GetValueOut();
+ if (nValueIn < value_out) {
+ return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
+ strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
+ }
+
+ // Tally transaction fees
+ const CAmount txfee_aux = nValueIn - value_out;
+ if (!MoneyRange(txfee_aux)) {
+ return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
+ }
- if (nValueIn < tx.GetValueOut())
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
- strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())));
-
- // Tally transaction fees
- CAmount nTxFee = nValueIn - tx.GetValueOut();
- if (nTxFee < 0)
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative");
- nFees += nTxFee;
- if (!MoneyRange(nFees))
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
+ txfee = txfee_aux;
return true;
}
diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h
index d46d3294ca..a4836ceda4 100644
--- a/src/consensus/tx_verify.h
+++ b/src/consensus/tx_verify.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_CONSENSUS_TX_VERIFY_H
#define BITCOIN_CONSENSUS_TX_VERIFY_H
+#include <amount.h>
+
#include <stdint.h>
#include <vector>
@@ -22,9 +24,10 @@ namespace Consensus {
/**
* Check whether all inputs of this transaction are valid (no double spends and amounts)
* This does not modify the UTXO set. This does not check scripts and sigs.
+ * @param[out] txfee Set to the transaction fee if successful.
* Preconditions: tx.IsCoinBase() is false.
*/
-bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight);
+bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee);
} // namespace Consensus
/** Auxiliary functions for transaction validation (ideally should not be exposed) */
diff --git a/src/consensus/validation.h b/src/consensus/validation.h
index 5494ce40ea..55f6c363fc 100644
--- a/src/consensus/validation.h
+++ b/src/consensus/validation.h
@@ -7,10 +7,10 @@
#define BITCOIN_CONSENSUS_VALIDATION_H
#include <string>
-#include "version.h"
-#include "consensus/consensus.h"
-#include "primitives/transaction.h"
-#include "primitives/block.h"
+#include <version.h>
+#include <consensus/consensus.h>
+#include <primitives/transaction.h>
+#include <primitives/block.h>
/** "reject" message codes */
static const unsigned char REJECT_MALFORMED = 0x01;
@@ -89,17 +89,16 @@ public:
std::string GetDebugMessage() const { return strDebugMessage; }
};
+// These implement the weight = (stripped_size * 4) + witness_size formula,
+// using only serialization with and without witness data. As witness_size
+// is equal to total_size - stripped_size, this formula is identical to:
+// weight = (stripped_size * 3) + total_size.
static inline int64_t GetTransactionWeight(const CTransaction& tx)
{
- return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR -1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
+ return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
}
-
static inline int64_t GetBlockWeight(const CBlock& block)
{
- // This implements the weight = (stripped_size * 4) + witness_size formula,
- // using only serialization with and without witness data. As witness_size
- // is equal to total_size - stripped_size, this formula is identical to:
- // weight = (stripped_size * 3) + total_size.
return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION);
}
diff --git a/src/core_io.h b/src/core_io.h
index 946480cfa4..3cdae06dfb 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_CORE_IO_H
#define BITCOIN_CORE_IO_H
-#include "amount.h"
+#include <amount.h>
#include <string>
#include <vector>
diff --git a/src/core_memusage.h b/src/core_memusage.h
index f038e7b154..d234f0a9d0 100644
--- a/src/core_memusage.h
+++ b/src/core_memusage.h
@@ -5,9 +5,9 @@
#ifndef BITCOIN_CORE_MEMUSAGE_H
#define BITCOIN_CORE_MEMUSAGE_H
-#include "primitives/transaction.h"
-#include "primitives/block.h"
-#include "memusage.h"
+#include <primitives/transaction.h>
+#include <primitives/block.h>
+#include <memusage.h>
static inline size_t RecursiveDynamicUsage(const CScript& script) {
return memusage::DynamicUsage(script);
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 819fd1dd14..4f2dabe10f 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -2,17 +2,17 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "core_io.h"
+#include <core_io.h>
-#include "primitives/block.h"
-#include "primitives/transaction.h"
-#include "script/script.h"
-#include "serialize.h"
-#include "streams.h"
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <script/script.h>
+#include <serialize.h>
+#include <streams.h>
#include <univalue.h>
-#include "util.h"
-#include "utilstrencodings.h"
-#include "version.h"
+#include <util.h>
+#include <utilstrencodings.h>
+#include <version.h>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/predicate.hpp>
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 1431fa0c9b..e97876bef3 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -2,19 +2,19 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "core_io.h"
-
-#include "base58.h"
-#include "consensus/consensus.h"
-#include "consensus/validation.h"
-#include "script/script.h"
-#include "script/standard.h"
-#include "serialize.h"
-#include "streams.h"
+#include <core_io.h>
+
+#include <base58.h>
+#include <consensus/consensus.h>
+#include <consensus/validation.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <serialize.h>
+#include <streams.h>
#include <univalue.h>
-#include "util.h"
-#include "utilmoneystr.h"
-#include "utilstrencodings.h"
+#include <util.h>
+#include <utilmoneystr.h>
+#include <utilstrencodings.h>
UniValue ValueFromAmount(const CAmount& amount)
{
@@ -148,8 +148,9 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
out.pushKV("type", GetTxnOutputType(type));
UniValue a(UniValue::VARR);
- for (const CTxDestination& addr : addresses)
- a.push_back(CBitcoinAddress(addr).ToString());
+ for (const CTxDestination& addr : addresses) {
+ a.push_back(EncodeDestination(addr));
+ }
out.pushKV("addresses", a);
}
diff --git a/src/crypto/aes.cpp b/src/crypto/aes.cpp
index 5e70d25eee..baba8bcad0 100644
--- a/src/crypto/aes.cpp
+++ b/src/crypto/aes.cpp
@@ -2,14 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "aes.h"
-#include "crypto/common.h"
+#include <crypto/aes.h>
+#include <crypto/common.h>
#include <assert.h>
#include <string.h>
extern "C" {
-#include "crypto/ctaes/ctaes.c"
+#include <crypto/ctaes/ctaes.c>
}
AES128Encrypt::AES128Encrypt(const unsigned char key[16])
diff --git a/src/crypto/aes.h b/src/crypto/aes.h
index a7b63b19df..7dda94d291 100644
--- a/src/crypto/aes.h
+++ b/src/crypto/aes.h
@@ -8,7 +8,7 @@
#define BITCOIN_CRYPTO_AES_H
extern "C" {
-#include "crypto/ctaes/ctaes.h"
+#include <crypto/ctaes/ctaes.h>
}
static const int AES_BLOCKSIZE = 16;
diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp
index 4038ae9f86..ac4470f04f 100644
--- a/src/crypto/chacha20.cpp
+++ b/src/crypto/chacha20.cpp
@@ -5,8 +5,8 @@
// Based on the public domain implementation 'merged' by D. J. Bernstein
// See https://cr.yp.to/chacha.html.
-#include "crypto/common.h"
-#include "crypto/chacha20.h"
+#include <crypto/common.h>
+#include <crypto/chacha20.h>
#include <string.h>
diff --git a/src/crypto/common.h b/src/crypto/common.h
index bd9bc9420b..5a193a4e64 100644
--- a/src/crypto/common.h
+++ b/src/crypto/common.h
@@ -6,13 +6,13 @@
#define BITCOIN_CRYPTO_COMMON_H
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
#include <stdint.h>
#include <string.h>
-#include "compat/endian.h"
+#include <compat/endian.h>
uint16_t static inline ReadLE16(const unsigned char* ptr)
{
diff --git a/src/crypto/hmac_sha256.cpp b/src/crypto/hmac_sha256.cpp
index 3c791625d0..9366e216c2 100644
--- a/src/crypto/hmac_sha256.cpp
+++ b/src/crypto/hmac_sha256.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 "crypto/hmac_sha256.h"
+#include <crypto/hmac_sha256.h>
#include <string.h>
diff --git a/src/crypto/hmac_sha256.h b/src/crypto/hmac_sha256.h
index 8c42fcfe14..40d40f9fb2 100644
--- a/src/crypto/hmac_sha256.h
+++ b/src/crypto/hmac_sha256.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_CRYPTO_HMAC_SHA256_H
#define BITCOIN_CRYPTO_HMAC_SHA256_H
-#include "crypto/sha256.h"
+#include <crypto/sha256.h>
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/crypto/hmac_sha512.cpp b/src/crypto/hmac_sha512.cpp
index 5939c6ec47..f984201f6a 100644
--- a/src/crypto/hmac_sha512.cpp
+++ b/src/crypto/hmac_sha512.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 "crypto/hmac_sha512.h"
+#include <crypto/hmac_sha512.h>
#include <string.h>
diff --git a/src/crypto/hmac_sha512.h b/src/crypto/hmac_sha512.h
index 17dee61ea8..6d3fd59f27 100644
--- a/src/crypto/hmac_sha512.h
+++ b/src/crypto/hmac_sha512.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_CRYPTO_HMAC_SHA512_H
#define BITCOIN_CRYPTO_HMAC_SHA512_H
-#include "crypto/sha512.h"
+#include <crypto/sha512.h>
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/crypto/ripemd160.cpp b/src/crypto/ripemd160.cpp
index 77c9acfc26..4afa9abd1e 100644
--- a/src/crypto/ripemd160.cpp
+++ b/src/crypto/ripemd160.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "crypto/ripemd160.h"
+#include <crypto/ripemd160.h>
-#include "crypto/common.h"
+#include <crypto/common.h>
#include <string.h>
diff --git a/src/crypto/sha1.cpp b/src/crypto/sha1.cpp
index 0b895b33a2..8ccc5e112a 100644
--- a/src/crypto/sha1.cpp
+++ b/src/crypto/sha1.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "crypto/sha1.h"
+#include <crypto/sha1.h>
-#include "crypto/common.h"
+#include <crypto/common.h>
#include <string.h>
diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp
index 29afe86ec7..9a21aec20e 100644
--- a/src/crypto/sha256.cpp
+++ b/src/crypto/sha256.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "crypto/sha256.h"
-#include "crypto/common.h"
+#include <crypto/sha256.h>
+#include <crypto/common.h>
#include <assert.h>
#include <string.h>
diff --git a/src/crypto/sha512.cpp b/src/crypto/sha512.cpp
index 564127cc31..aa46e7ba86 100644
--- a/src/crypto/sha512.cpp
+++ b/src/crypto/sha512.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "crypto/sha512.h"
+#include <crypto/sha512.h>
-#include "crypto/common.h"
+#include <crypto/common.h>
#include <string.h>
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index dfc90f3ab9..6ff740f581 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -2,11 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "dbwrapper.h"
+#include <dbwrapper.h>
-#include "fs.h"
-#include "util.h"
-#include "random.h"
+#include <random.h>
#include <leveldb/cache.h>
#include <leveldb/env.h>
@@ -64,7 +62,7 @@ public:
assert(p <= limit);
base[std::min(bufsize - 1, (int)(p - base))] = '\0';
- LogPrintStr(base);
+ LogPrintf("leveldb: %s", base);
if (base != buffer) {
delete[] base;
}
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index e19fde51c1..9f4d4814eb 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -5,13 +5,13 @@
#ifndef BITCOIN_DBWRAPPER_H
#define BITCOIN_DBWRAPPER_H
-#include "clientversion.h"
-#include "fs.h"
-#include "serialize.h"
-#include "streams.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "version.h"
+#include <clientversion.h>
+#include <fs.h>
+#include <serialize.h>
+#include <streams.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <version.h>
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
diff --git a/src/fs.cpp b/src/fs.cpp
index a5e12f1cfc..570ed3e2ee 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -1,4 +1,4 @@
-#include "fs.h"
+#include <fs.h>
namespace fsbridge {
diff --git a/src/hash.cpp b/src/hash.cpp
index 5a15600be5..ea61764ded 100644
--- a/src/hash.cpp
+++ b/src/hash.cpp
@@ -2,10 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "hash.h"
-#include "crypto/common.h"
-#include "crypto/hmac_sha512.h"
-#include "pubkey.h"
+#include <hash.h>
+#include <crypto/common.h>
+#include <crypto/hmac_sha512.h>
inline uint32_t ROTL32(uint32_t x, int8_t r)
diff --git a/src/hash.h b/src/hash.h
index ad59bb1817..55724fb636 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -6,12 +6,12 @@
#ifndef BITCOIN_HASH_H
#define BITCOIN_HASH_H
-#include "crypto/ripemd160.h"
-#include "crypto/sha256.h"
-#include "prevector.h"
-#include "serialize.h"
-#include "uint256.h"
-#include "version.h"
+#include <crypto/ripemd160.h>
+#include <crypto/sha256.h>
+#include <prevector.h>
+#include <serialize.h>
+#include <uint256.h>
+#include <version.h>
#include <vector>
@@ -88,20 +88,6 @@ inline uint256 Hash(const T1 p1begin, const T1 p1end,
return result;
}
-/** Compute the 256-bit hash of the concatenation of three objects. */
-template<typename T1, typename T2, typename T3>
-inline uint256 Hash(const T1 p1begin, const T1 p1end,
- const T2 p2begin, const T2 p2end,
- const T3 p3begin, const T3 p3end) {
- static const unsigned char pblank[1] = {};
- uint256 result;
- CHash256().Write(p1begin == p1end ? pblank : (const unsigned char*)&p1begin[0], (p1end - p1begin) * sizeof(p1begin[0]))
- .Write(p2begin == p2end ? pblank : (const unsigned char*)&p2begin[0], (p2end - p2begin) * sizeof(p2begin[0]))
- .Write(p3begin == p3end ? pblank : (const unsigned char*)&p3begin[0], (p3end - p3begin) * sizeof(p3begin[0]))
- .Finalize((unsigned char*)&result);
- return result;
-}
-
/** Compute the 160-bit hash an object. */
template<typename T1>
inline uint160 Hash160(const T1 pbegin, const T1 pend)
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 91f96ef207..6b6849e59b 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -2,19 +2,19 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "httprpc.h"
-
-#include "base58.h"
-#include "chainparams.h"
-#include "httpserver.h"
-#include "rpc/protocol.h"
-#include "rpc/server.h"
-#include "random.h"
-#include "sync.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "ui_interface.h"
-#include "crypto/hmac_sha256.h"
+#include <httprpc.h>
+
+#include <base58.h>
+#include <chainparams.h>
+#include <httpserver.h>
+#include <rpc/protocol.h>
+#include <rpc/server.h>
+#include <random.h>
+#include <sync.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <ui_interface.h>
+#include <crypto/hmac_sha256.h>
#include <stdio.h>
#include <boost/algorithm/string.hpp> // boost::trim
@@ -62,7 +62,7 @@ private:
/* Pre-base64-encoded authentication token */
static std::string strRPCUserColonPass;
/* Stored RPC timer interface (for unregistration) */
-static HTTPRPCTimerInterface* httpRPCTimerInterface = nullptr;
+static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface;
static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
{
@@ -192,7 +192,7 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
// array of requests
} else if (valRequest.isArray())
- strReply = JSONRPCExecBatch(valRequest.get_array());
+ strReply = JSONRPCExecBatch(jreq, valRequest.get_array());
else
throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
@@ -238,8 +238,8 @@ bool StartHTTPRPC()
RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
#endif
assert(EventBase());
- httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
- RPCSetTimerInterface(httpRPCTimerInterface);
+ httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(EventBase());
+ RPCSetTimerInterface(httpRPCTimerInterface.get());
return true;
}
@@ -253,8 +253,7 @@ void StopHTTPRPC()
LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
UnregisterHTTPHandler("/", true);
if (httpRPCTimerInterface) {
- RPCUnsetTimerInterface(httpRPCTimerInterface);
- delete httpRPCTimerInterface;
- httpRPCTimerInterface = nullptr;
+ RPCUnsetTimerInterface(httpRPCTimerInterface.get());
+ httpRPCTimerInterface.reset();
}
}
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 5923871691..7d590f8e1f 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -2,16 +2,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "httpserver.h"
+#include <httpserver.h>
-#include "chainparamsbase.h"
-#include "compat.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "netbase.h"
-#include "rpc/protocol.h" // For HTTP status codes
-#include "sync.h"
-#include "ui_interface.h"
+#include <chainparamsbase.h>
+#include <compat.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <netbase.h>
+#include <rpc/protocol.h> // For HTTP status codes
+#include <sync.h>
+#include <ui_interface.h>
#include <stdio.h>
#include <stdlib.h>
@@ -24,10 +24,11 @@
#include <event2/thread.h>
#include <event2/buffer.h>
+#include <event2/bufferevent.h>
#include <event2/util.h>
#include <event2/keyvalq_struct.h>
-#include "support/events.h"
+#include <support/events.h>
#ifdef EVENT__HAVE_NETINET_IN_H
#include <netinet/in.h>
@@ -239,6 +240,16 @@ static std::string RequestMethodString(HTTPRequest::RequestMethod m)
/** HTTP request callback */
static void http_request_cb(struct evhttp_request* req, void* arg)
{
+ // Disable reading to work around a libevent bug, fixed in 2.2.0.
+ if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
+ evhttp_connection* conn = evhttp_request_get_connection(req);
+ if (conn) {
+ bufferevent* bev = evhttp_connection_get_bufferevent(conn);
+ if (bev) {
+ bufferevent_disable(bev, EV_READ);
+ }
+ }
+ }
std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));
LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
@@ -481,6 +492,8 @@ void StopHTTPServer()
}
if (eventBase) {
LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n");
+ // Exit the event loop as soon as there are no active events.
+ event_base_loopexit(eventBase, nullptr);
// Give event loop a few seconds to exit (to send back last RPC responses), then break it
// Before this was solved with event_base_loopexit, but that didn't work as expected in
// at least libevent 2.0.21 and always introduced a delay. In libevent
@@ -599,8 +612,21 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
struct evbuffer* evb = evhttp_request_get_output_buffer(req);
assert(evb);
evbuffer_add(evb, strReply.data(), strReply.size());
- HTTPEvent* ev = new HTTPEvent(eventBase, true,
- std::bind(evhttp_send_reply, req, nStatus, (const char*)nullptr, (struct evbuffer *)nullptr));
+ auto req_copy = req;
+ HTTPEvent* ev = new HTTPEvent(eventBase, true, [req_copy, nStatus]{
+ evhttp_send_reply(req_copy, nStatus, nullptr, nullptr);
+ // Re-enable reading from the socket. This is the second part of the libevent
+ // workaround above.
+ if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
+ evhttp_connection* conn = evhttp_request_get_connection(req_copy);
+ if (conn) {
+ bufferevent* bev = evhttp_connection_get_bufferevent(conn);
+ if (bev) {
+ bufferevent_enable(bev, EV_READ | EV_WRITE);
+ }
+ }
+ }
+ });
ev->trigger(nullptr);
replySent = true;
req = nullptr; // transferred back to main thread
diff --git a/src/init.cpp b/src/init.cpp
index 6114bbaa2d..e4cad01b70 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -4,50 +4,49 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "init.h"
-
-#include "addrman.h"
-#include "amount.h"
-#include "chain.h"
-#include "chainparams.h"
-#include "checkpoints.h"
-#include "compat/sanity.h"
-#include "consensus/validation.h"
-#include "fs.h"
-#include "httpserver.h"
-#include "httprpc.h"
-#include "key.h"
-#include "validation.h"
-#include "miner.h"
-#include "netbase.h"
-#include "net.h"
-#include "net_processing.h"
-#include "policy/feerate.h"
-#include "policy/fees.h"
-#include "policy/policy.h"
-#include "rpc/server.h"
-#include "rpc/register.h"
-#include "rpc/safemode.h"
-#include "rpc/blockchain.h"
-#include "script/standard.h"
-#include "script/sigcache.h"
-#include "scheduler.h"
-#include "timedata.h"
-#include "txdb.h"
-#include "txmempool.h"
-#include "torcontrol.h"
-#include "ui_interface.h"
-#include "util.h"
-#include "utilmoneystr.h"
-#include "validationinterface.h"
+#include <init.h>
+
+#include <addrman.h>
+#include <amount.h>
+#include <chain.h>
+#include <chainparams.h>
+#include <checkpoints.h>
+#include <compat/sanity.h>
+#include <consensus/validation.h>
+#include <fs.h>
+#include <httpserver.h>
+#include <httprpc.h>
+#include <key.h>
+#include <validation.h>
+#include <miner.h>
+#include <netbase.h>
+#include <net.h>
+#include <net_processing.h>
+#include <policy/feerate.h>
+#include <policy/fees.h>
+#include <policy/policy.h>
+#include <rpc/server.h>
+#include <rpc/register.h>
+#include <rpc/safemode.h>
+#include <rpc/blockchain.h>
+#include <script/standard.h>
+#include <script/sigcache.h>
+#include <scheduler.h>
+#include <timedata.h>
+#include <txdb.h>
+#include <txmempool.h>
+#include <torcontrol.h>
+#include <ui_interface.h>
+#include <util.h>
+#include <utilmoneystr.h>
+#include <validationinterface.h>
#ifdef ENABLE_WALLET
-#include "wallet/init.h"
-#include "wallet/wallet.h"
+#include <wallet/init.h>
#endif
-#include "warnings.h"
+#include <warnings.h>
#include <stdint.h>
#include <stdio.h>
#include <memory>
@@ -65,7 +64,7 @@
#include <openssl/crypto.h>
#if ENABLE_ZMQ
-#include "zmq/zmqnotificationinterface.h"
+#include <zmq/zmqnotificationinterface.h>
#endif
bool fFeeEstimatesInitialized = false;
@@ -103,12 +102,11 @@ static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
// created by AppInit() or the Qt main() function.
//
// A clean exit happens when StartShutdown() or the SIGTERM
-// signal handler sets fRequestShutdown, which triggers
-// the DetectShutdownThread(), which interrupts the main thread group.
-// DetectShutdownThread() then exits, which causes AppInit() to
-// continue (it .joins the shutdown thread).
-// Shutdown() is then
-// called to clean up database connections, and stop other
+// signal handler sets fRequestShutdown, which makes main thread's
+// WaitForShutdown() interrupts the thread group.
+// And then, WaitForShutdown() makes all other on-going threads
+// in the thread group join the main thread.
+// Shutdown() is then called to clean up database connections, and stop other
// threads that should only be stopped after the main network-processing
// threads have exited.
//
@@ -154,7 +152,7 @@ public:
// Writes do not need similar protection, as failure to write is handled by the caller.
};
-static CCoinsViewErrorCatcher *pcoinscatcher = nullptr;
+static std::unique_ptr<CCoinsViewErrorCatcher> pcoinscatcher;
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
void Interrupt(boost::thread_group& threadGroup)
@@ -189,17 +187,18 @@ void Shutdown()
StopRPC();
StopHTTPServer();
#ifdef ENABLE_WALLET
- for (CWalletRef pwallet : vpwallets) {
- pwallet->Flush(false);
- }
+ FlushWallets();
#endif
MapPort(false);
- UnregisterValidationInterface(peerLogic.get());
+
+ // Because these depend on each-other, we make sure that neither can be
+ // using the other before destroying them.
+ if (peerLogic) UnregisterValidationInterface(peerLogic.get());
+ if (g_connman) g_connman->Stop();
peerLogic.reset();
g_connman.reset();
StopTorControl();
- UnregisterNodeSignals(GetNodeSignals());
if (fDumpMempoolLater && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool();
}
@@ -236,19 +235,13 @@ void Shutdown()
if (pcoinsTip != nullptr) {
FlushStateToDisk();
}
- delete pcoinsTip;
- pcoinsTip = nullptr;
- delete pcoinscatcher;
- pcoinscatcher = nullptr;
- delete pcoinsdbview;
- pcoinsdbview = nullptr;
- delete pblocktree;
- pblocktree = nullptr;
+ pcoinsTip.reset();
+ pcoinscatcher.reset();
+ pcoinsdbview.reset();
+ pblocktree.reset();
}
#ifdef ENABLE_WALLET
- for (CWalletRef pwallet : vpwallets) {
- pwallet->Flush(true);
- }
+ StopWallets();
#endif
#if ENABLE_ZMQ
@@ -268,11 +261,9 @@ void Shutdown()
#endif
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
+ GetMainSignals().UnregisterWithMempoolSignals(mempool);
#ifdef ENABLE_WALLET
- for (CWalletRef pwallet : vpwallets) {
- delete pwallet;
- }
- vpwallets.clear();
+ CloseWallets();
#endif
globalVerifyHandle.reset();
ECC_Stop();
@@ -351,9 +342,13 @@ std::string HelpMessage(HelpMessageMode mode)
if (showDebug)
strUsage += HelpMessageOpt("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER));
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup"));
+ strUsage += HelpMessageOpt("-debuglogfile=<file>", strprintf(_("Specify location of debug log file: this can be an absolute path or a path relative to the data directory (default: %s)"), DEFAULT_DEBUGLOGFILE));
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE));
strUsage += HelpMessageOpt("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY));
+ if (showDebug) {
+ strUsage += HelpMessageOpt("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()));
+ }
strUsage += HelpMessageOpt("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL));
strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN));
strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"),
@@ -372,11 +367,11 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX));
strUsage += HelpMessageGroup(_("Connection options:"));
- strUsage += HelpMessageOpt("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open"));
+ strUsage += HelpMessageOpt("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info)"));
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); -connect=0 disables automatic connections"));
+ strUsage += HelpMessageOpt("-connect=<ip>", _("Connect only to the specified node(s); -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode)"));
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 used)"));
@@ -433,6 +428,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks()));
strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED));
strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", DEFAULT_DISABLE_SAFEMODE));
+ strUsage += HelpMessageOpt("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used");
strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", DEFAULT_TESTSAFEMODE));
strUsage += HelpMessageOpt("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages");
strUsage += HelpMessageOpt("-fuzzmessagestest=<n>", "Randomly fuzz 1 of every <n> network messages");
@@ -486,7 +482,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageGroup(_("Block creation options:"));
strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT));
- strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE));
+ strUsage += HelpMessageOpt("-blockmaxsize=<n>", _("Set maximum BIP141 block weight to this * 4. Deprecated, use blockmaxweight"));
strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)));
if (showDebug)
strUsage += HelpMessageOpt("-blockversion=<n>", "Override block version to test forking scenarios");
@@ -539,20 +535,21 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex
return;
std::string strCmd = gArgs.GetArg("-blocknotify", "");
-
- boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex());
- boost::thread t(runCommand, strCmd); // thread runs free
+ if (!strCmd.empty()) {
+ boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex());
+ boost::thread t(runCommand, strCmd); // thread runs free
+ }
}
static bool fHaveGenesis = false;
-static boost::mutex cs_GenesisWait;
+static CWaitableCriticalSection cs_GenesisWait;
static CConditionVariable condvar_GenesisWait;
static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex)
{
if (pBlockIndex != nullptr) {
{
- boost::unique_lock<boost::mutex> lock_GenesisWait(cs_GenesisWait);
+ WaitableLock lock_GenesisWait(cs_GenesisWait);
fHaveGenesis = true;
}
condvar_GenesisWait.notify_all();
@@ -589,7 +586,7 @@ void CleanupBlockRevFiles()
LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n");
fs::path blocksdir = GetDataDir() / "blocks";
for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) {
- if (is_regular_file(*it) &&
+ if (fs::is_regular_file(*it) &&
it->path().filename().string().length() == 12 &&
it->path().filename().string().substr(8,4) == ".dat")
{
@@ -787,6 +784,15 @@ void InitParameterInteraction()
if (gArgs.SoftSetBoolArg("-whitelistrelay", true))
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__);
}
+
+ if (gArgs.IsArgSet("-blockmaxsize")) {
+ unsigned int max_size = gArgs.GetArg("-blockmaxsize", 0);
+ if (gArgs.SoftSetArg("blockmaxweight", strprintf("%d", max_size * WITNESS_SCALE_FACTOR))) {
+ LogPrintf("%s: parameter interaction: -blockmaxsize=%d -> setting -blockmaxweight=%d (-blockmaxsize is deprecated!)\n", __func__, max_size, max_size * WITNESS_SCALE_FACTOR);
+ } else {
+ LogPrintf("%s: Ignoring blockmaxsize setting which is overridden by blockmaxweight", __func__);
+ }
+ }
}
static std::string ResolveErrMsg(const char * const optname, const std::string& strBind)
@@ -807,11 +813,10 @@ void InitLogging()
namespace { // Variables internal to initialization process only
-ServiceFlags nRelevantServices = NODE_NETWORK;
int nMaxConnections;
int nUserMaxConnections;
int nFD;
-ServiceFlags nLocalServices = NODE_NETWORK;
+ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED);
} // namespace
@@ -915,7 +920,8 @@ bool AppInitParameterInteraction()
// Special-case: if -debug=0/-nodebug is set, turn off debugging messages
const std::vector<std::string> categories = gArgs.GetArgs("-debug");
- if (find(categories.begin(), categories.end(), std::string("0")) == categories.end()) {
+ if (std::none_of(categories.begin(), categories.end(),
+ [](std::string cat){return cat == "0" || cat == "none";})) {
for (const auto& cat : categories) {
uint32_t flag = 0;
if (!GetLogCategory(&flag, &cat)) {
@@ -970,6 +976,20 @@ bool AppInitParameterInteraction()
else
LogPrintf("Validating signatures for all blocks.\n");
+ if (gArgs.IsArgSet("-minimumchainwork")) {
+ const std::string minChainWorkStr = gArgs.GetArg("-minimumchainwork", "");
+ if (!IsHexNumber(minChainWorkStr)) {
+ return InitError(strprintf("Invalid non-hex (%s) minimum chain work value specified", minChainWorkStr));
+ }
+ nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr));
+ } else {
+ nMinimumChainWork = UintToArith256(chainparams.GetConsensus().nMinimumChainWork);
+ }
+ LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex());
+ if (nMinimumChainWork < UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) {
+ LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainparams.GetConsensus().nMinimumChainWork.GetHex());
+ }
+
// mempool limits
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t nMempoolSizeMin = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
@@ -1012,11 +1032,6 @@ bool AppInitParameterInteraction()
fPruneMode = true;
}
- RegisterAllCoreRPCCommands(tableRPC);
-#ifdef ENABLE_WALLET
- RegisterWalletRPCCommands(tableRPC);
-#endif
-
nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0)
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
@@ -1195,8 +1210,11 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
ShrinkDebugFile();
}
- if (fPrintToDebugLog)
- OpenDebugLog();
+ if (fPrintToDebugLog) {
+ if (!OpenDebugLog()) {
+ return InitError(strprintf("Could not open debug log file %s", GetDebugLogPath().string()));
+ }
+ }
if (!fLogTimestamps)
LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()));
@@ -1219,6 +1237,15 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
+ GetMainSignals().RegisterWithMempoolSignals(mempool);
+
+ /* Register RPC commands regardless of -server setting so they will be
+ * available in the GUI RPC console even if external calls are disabled.
+ */
+ RegisterAllCoreRPCCommands(tableRPC);
+#ifdef ENABLE_WALLET
+ RegisterWalletRPC(tableRPC);
+#endif
/* Start the RPC server already. It will be started in "warmup" mode
* and not really process calls already (but it will signify connections
@@ -1236,7 +1263,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// ********************************************************* Step 5: verify wallet database integrity
#ifdef ENABLE_WALLET
- if (!WalletVerify())
+ if (!VerifyWallets())
return false;
#endif
// ********************************************************* Step 6: network initialization
@@ -1249,9 +1276,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
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));
+ peerLogic.reset(new PeerLogicValidation(&connman, scheduler));
RegisterValidationInterface(peerLogic.get());
- RegisterNodeSignals(GetNodeSignals());
// sanitize comments per BIP-0014, format user agent and check total size
std::vector<std::string> uacomments;
@@ -1386,12 +1412,10 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
do {
try {
UnloadBlockIndex();
- delete pcoinsTip;
- delete pcoinsdbview;
- delete pcoinscatcher;
- delete pblocktree;
-
- pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReset);
+ pcoinsTip.reset();
+ pcoinsdbview.reset();
+ pcoinscatcher.reset();
+ pblocktree.reset(new CBlockTreeDB(nBlockTreeDBCache, false, fReset));
if (fReset) {
pblocktree->WriteReindexing(true);
@@ -1442,8 +1466,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// At this point we're either in reindex or we've loaded a useful
// block tree into mapBlockIndex!
- pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReset || fReindexChainState);
- pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
+ pcoinsdbview.reset(new CCoinsViewDB(nCoinDBCache, false, fReset || fReindexChainState));
+ pcoinscatcher.reset(new CCoinsViewErrorCatcher(pcoinsdbview.get()));
// If necessary, upgrade from older database format.
// This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
@@ -1453,13 +1477,13 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
}
// ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
- if (!ReplayBlocks(chainparams, pcoinsdbview)) {
+ if (!ReplayBlocks(chainparams, pcoinsdbview.get())) {
strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.");
break;
}
// The on-disk coinsdb is now in a good state, create the cache
- pcoinsTip = new CCoinsViewCache(pcoinscatcher);
+ pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get()));
bool is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull();
if (!is_coinsview_empty) {
@@ -1501,7 +1525,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
- if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview, gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
+ if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview.get(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected");
break;
@@ -1557,7 +1581,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// ********************************************************* Step 8: load wallet
#ifdef ENABLE_WALLET
- if (!InitLoadWallet())
+ if (!OpenWallets())
return false;
#else
LogPrintf("No wallet support compiled in!\n");
@@ -1583,9 +1607,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// Note that setting NODE_WITNESS is never required: the only downside from not
// doing so is that after activation, no upgraded nodes will fetch from you.
nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS);
- // Only care about others providing witness capabilities if there is a softfork
- // defined.
- nRelevantServices = ServiceFlags(nRelevantServices | NODE_WITNESS);
}
// ********************************************************* Step 10: import blocks
@@ -1613,7 +1634,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// Wait for genesis block to be processed
{
- boost::unique_lock<boost::mutex> lock(cs_GenesisWait);
+ WaitableLock lock(cs_GenesisWait);
while (!fHaveGenesis) {
condvar_GenesisWait.wait(lock);
}
@@ -1622,9 +1643,16 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// ********************************************************* Step 11: start node
+ int chain_active_height;
+
//// debug print
- LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size());
- LogPrintf("nBestHeight = %d\n", chainActive.Height());
+ {
+ LOCK(cs_main);
+ LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size());
+ chain_active_height = chainActive.Height();
+ }
+ LogPrintf("nBestHeight = %d\n", chain_active_height);
+
if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
StartTorControl(threadGroup, scheduler);
@@ -1635,15 +1663,16 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
CConnman::Options connOptions;
connOptions.nLocalServices = nLocalServices;
- connOptions.nRelevantServices = nRelevantServices;
connOptions.nMaxConnections = nMaxConnections;
connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections);
connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
connOptions.nMaxFeeler = 1;
- connOptions.nBestHeight = chainActive.Height();
+ connOptions.nBestHeight = chain_active_height;
connOptions.uiInterface = &uiInterface;
+ connOptions.m_msgproc = peerLogic.get();
connOptions.nSendBufferMaxSize = 1000*gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize = 1000*gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
+ connOptions.m_added_nodes = gArgs.GetArgs("-addnode");
connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe;
connOptions.nMaxOutboundLimit = nMaxOutboundLimit;
@@ -1674,10 +1703,16 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
connOptions.vWhitelistedRange.push_back(subnet);
}
- if (gArgs.IsArgSet("-seednode")) {
- connOptions.vSeedNodes = gArgs.GetArgs("-seednode");
- }
+ connOptions.vSeedNodes = gArgs.GetArgs("-seednode");
+ // Initiate outbound connections unless connect=0
+ connOptions.m_use_addrman_outgoing = !gArgs.IsArgSet("-connect");
+ if (!connOptions.m_use_addrman_outgoing) {
+ const auto connect = gArgs.GetArgs("-connect");
+ if (connect.size() != 1 || connect[0] != "0") {
+ connOptions.m_specified_outgoing = connect;
+ }
+ }
if (!connman.Start(scheduler, connOptions)) {
return false;
}
@@ -1688,10 +1723,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
uiInterface.InitMessage(_("Done loading"));
#ifdef ENABLE_WALLET
- for (CWalletRef pwallet : vpwallets) {
- pwallet->postInitProcess(scheduler);
- }
+ StartWallets(scheduler);
#endif
- return !fRequestShutdown;
+ return true;
}
diff --git a/src/key.cpp b/src/key.cpp
index 315a3978c8..9d4c4498d8 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -2,13 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "key.h"
+#include <key.h>
-#include "arith_uint256.h"
-#include "crypto/common.h"
-#include "crypto/hmac_sha512.h"
-#include "pubkey.h"
-#include "random.h"
+#include <arith_uint256.h>
+#include <crypto/common.h>
+#include <crypto/hmac_sha512.h>
+#include <random.h>
#include <secp256k1.h>
#include <secp256k1_recovery.h>
diff --git a/src/key.h b/src/key.h
index 151e63531b..c610e5e6cd 100644
--- a/src/key.h
+++ b/src/key.h
@@ -6,10 +6,10 @@
#ifndef BITCOIN_KEY_H
#define BITCOIN_KEY_H
-#include "pubkey.h"
-#include "serialize.h"
-#include "support/allocators/secure.h"
-#include "uint256.h"
+#include <pubkey.h>
+#include <serialize.h>
+#include <support/allocators/secure.h>
+#include <uint256.h>
#include <stdexcept>
#include <vector>
@@ -56,11 +56,6 @@ public:
keydata.resize(32);
}
- //! Destructor (again necessary because of memlocking).
- ~CKey()
- {
- }
-
friend bool operator==(const CKey& a, const CKey& b)
{
return a.fCompressed == b.fCompressed &&
diff --git a/src/keystore.cpp b/src/keystore.cpp
index 8454175ca8..4ab089e032 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -3,11 +3,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "keystore.h"
+#include <keystore.h>
-#include "key.h"
-#include "pubkey.h"
-#include "util.h"
+#include <util.h>
bool CKeyStore::AddKey(const CKey &key) {
return AddKeyPubKey(key, key.GetPubKey());
@@ -36,6 +34,33 @@ bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
return true;
}
+bool CBasicKeyStore::HaveKey(const CKeyID &address) const
+{
+ LOCK(cs_KeyStore);
+ return mapKeys.count(address) > 0;
+}
+
+std::set<CKeyID> CBasicKeyStore::GetKeys() const
+{
+ LOCK(cs_KeyStore);
+ std::set<CKeyID> set_address;
+ for (const auto& mi : mapKeys) {
+ set_address.insert(mi.first);
+ }
+ return set_address;
+}
+
+bool CBasicKeyStore::GetKey(const CKeyID &address, CKey &keyOut) const
+{
+ LOCK(cs_KeyStore);
+ KeyMap::const_iterator mi = mapKeys.find(address);
+ if (mi != mapKeys.end()) {
+ keyOut = mi->second;
+ return true;
+ }
+ return false;
+}
+
bool CBasicKeyStore::AddCScript(const CScript& redeemScript)
{
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
diff --git a/src/keystore.h b/src/keystore.h
index 528b6e0834..4e6d8e8a27 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -6,11 +6,11 @@
#ifndef BITCOIN_KEYSTORE_H
#define BITCOIN_KEYSTORE_H
-#include "key.h"
-#include "pubkey.h"
-#include "script/script.h"
-#include "script/standard.h"
-#include "sync.h"
+#include <key.h>
+#include <pubkey.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <sync.h>
#include <boost/signals2/signal.hpp>
@@ -30,7 +30,7 @@ public:
//! Check whether a key corresponding to a given address is present in the store.
virtual bool HaveKey(const CKeyID &address) const =0;
virtual bool GetKey(const CKeyID &address, CKey& keyOut) const =0;
- virtual void GetKeys(std::set<CKeyID> &setAddress) const =0;
+ virtual std::set<CKeyID> GetKeys() const =0;
virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const =0;
//! Support for BIP 0013 : see https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki
@@ -62,41 +62,9 @@ protected:
public:
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
- bool HaveKey(const CKeyID &address) const override
- {
- bool result;
- {
- LOCK(cs_KeyStore);
- result = (mapKeys.count(address) > 0);
- }
- return result;
- }
- void GetKeys(std::set<CKeyID> &setAddress) const override
- {
- setAddress.clear();
- {
- LOCK(cs_KeyStore);
- KeyMap::const_iterator mi = mapKeys.begin();
- while (mi != mapKeys.end())
- {
- setAddress.insert((*mi).first);
- mi++;
- }
- }
- }
- bool GetKey(const CKeyID &address, CKey &keyOut) const override
- {
- {
- LOCK(cs_KeyStore);
- KeyMap::const_iterator mi = mapKeys.find(address);
- if (mi != mapKeys.end())
- {
- keyOut = mi->second;
- return true;
- }
- }
- return false;
- }
+ bool HaveKey(const CKeyID &address) const override;
+ std::set<CKeyID> GetKeys() const override;
+ bool GetKey(const CKeyID &address, CKey &keyOut) const override;
bool AddCScript(const CScript& redeemScript) override;
bool HaveCScript(const CScriptID &hash) const override;
bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override;
diff --git a/src/memusage.h b/src/memusage.h
index 93fd6a0eb5..d931171d35 100644
--- a/src/memusage.h
+++ b/src/memusage.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_MEMUSAGE_H
#define BITCOIN_MEMUSAGE_H
-#include "indirectmap.h"
+#include <indirectmap.h>
#include <stdlib.h>
diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp
index f0abea0611..f4db9dd57e 100644
--- a/src/merkleblock.cpp
+++ b/src/merkleblock.cpp
@@ -3,39 +3,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "merkleblock.h"
+#include <merkleblock.h>
-#include "hash.h"
-#include "consensus/consensus.h"
-#include "utilstrencodings.h"
+#include <hash.h>
+#include <consensus/consensus.h>
+#include <utilstrencodings.h>
-CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter)
-{
- header = block.GetBlockHeader();
- std::vector<bool> vMatch;
- std::vector<uint256> vHashes;
-
- vMatch.reserve(block.vtx.size());
- vHashes.reserve(block.vtx.size());
-
- for (unsigned int i = 0; i < block.vtx.size(); i++)
- {
- const uint256& hash = block.vtx[i]->GetHash();
- if (filter.IsRelevantAndUpdate(*block.vtx[i]))
- {
- vMatch.push_back(true);
- vMatchedTxn.push_back(std::make_pair(i, hash));
- }
- else
- vMatch.push_back(false);
- vHashes.push_back(hash);
- }
-
- txn = CPartialMerkleTree(vHashes, vMatch);
-}
-
-CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set<uint256>& txids)
+CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids)
{
header = block.GetBlockHeader();
@@ -48,10 +23,14 @@ 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();
- if (txids.count(hash))
+ if (txids && txids->count(hash)) {
vMatch.push_back(true);
- else
+ } else if (filter && filter->IsRelevantAndUpdate(*block.vtx[i])) {
+ vMatch.push_back(true);
+ vMatchedTxn.emplace_back(i, hash);
+ } else {
vMatch.push_back(false);
+ }
vHashes.push_back(hash);
}
diff --git a/src/merkleblock.h b/src/merkleblock.h
index 20f2b36886..b64c34370e 100644
--- a/src/merkleblock.h
+++ b/src/merkleblock.h
@@ -6,10 +6,10 @@
#ifndef BITCOIN_MERKLEBLOCK_H
#define BITCOIN_MERKLEBLOCK_H
-#include "serialize.h"
-#include "uint256.h"
-#include "primitives/block.h"
-#include "bloom.h"
+#include <serialize.h>
+#include <uint256.h>
+#include <primitives/block.h>
+#include <bloom.h>
#include <vector>
@@ -131,8 +131,12 @@ public:
CBlockHeader header;
CPartialMerkleTree txn;
-public:
- /** Public only for unit testing and relay testing (not relayed) */
+ /**
+ * Public only for unit testing and relay testing (not relayed).
+ *
+ * Used only when a bloom filter is specified to allow
+ * testing the transactions which matched the bloom filter.
+ */
std::vector<std::pair<unsigned int, uint256> > vMatchedTxn;
/**
@@ -140,10 +144,10 @@ public:
* Note that this will call IsRelevantAndUpdate on the filter for each transaction,
* thus the filter will likely be modified.
*/
- CMerkleBlock(const CBlock& block, CBloomFilter& filter);
+ CMerkleBlock(const CBlock& block, CBloomFilter& filter) : CMerkleBlock(block, &filter, nullptr) { }
// Create from a CBlock, matching the txids in the set
- CMerkleBlock(const CBlock& block, const std::set<uint256>& txids);
+ CMerkleBlock(const CBlock& block, const std::set<uint256>& txids) : CMerkleBlock(block, nullptr, &txids) { }
CMerkleBlock() {}
@@ -154,6 +158,10 @@ public:
READWRITE(header);
READWRITE(txn);
}
+
+private:
+ // Combined constructor to consolidate code
+ CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids);
};
#endif // BITCOIN_MERKLEBLOCK_H
diff --git a/src/miner.cpp b/src/miner.cpp
index f1942ec570..0fb9b774d3 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -3,29 +3,28 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "miner.h"
-
-#include "amount.h"
-#include "chain.h"
-#include "chainparams.h"
-#include "coins.h"
-#include "consensus/consensus.h"
-#include "consensus/tx_verify.h"
-#include "consensus/merkle.h"
-#include "consensus/validation.h"
-#include "hash.h"
-#include "validation.h"
-#include "net.h"
-#include "policy/feerate.h"
-#include "policy/policy.h"
-#include "pow.h"
-#include "primitives/transaction.h"
-#include "script/standard.h"
-#include "timedata.h"
-#include "txmempool.h"
-#include "util.h"
-#include "utilmoneystr.h"
-#include "validationinterface.h"
+#include <miner.h>
+
+#include <amount.h>
+#include <chain.h>
+#include <chainparams.h>
+#include <coins.h>
+#include <consensus/consensus.h>
+#include <consensus/tx_verify.h>
+#include <consensus/merkle.h>
+#include <consensus/validation.h>
+#include <hash.h>
+#include <validation.h>
+#include <net.h>
+#include <policy/feerate.h>
+#include <policy/policy.h>
+#include <pow.h>
+#include <primitives/transaction.h>
+#include <script/standard.h>
+#include <timedata.h>
+#include <util.h>
+#include <utilmoneystr.h>
+#include <validationinterface.h>
#include <algorithm>
#include <queue>
@@ -43,7 +42,6 @@
// its ancestors.
uint64_t nLastBlockTx = 0;
-uint64_t nLastBlockSize = 0;
uint64_t nLastBlockWeight = 0;
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
@@ -64,7 +62,6 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
BlockAssembler::Options::Options() {
blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;
- nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE;
}
BlockAssembler::BlockAssembler(const CChainParams& params, const Options& options) : chainparams(params)
@@ -72,10 +69,6 @@ BlockAssembler::BlockAssembler(const CChainParams& params, const Options& option
blockMinFeeRate = options.blockMinFeeRate;
// Limit weight to between 4K and MAX_BLOCK_WEIGHT-4K for sanity:
nBlockMaxWeight = std::max<size_t>(4000, std::min<size_t>(MAX_BLOCK_WEIGHT - 4000, options.nBlockMaxWeight));
- // Limit size to between 1K and MAX_BLOCK_SERIALIZED_SIZE-1K for sanity:
- nBlockMaxSize = std::max<size_t>(1000, std::min<size_t>(MAX_BLOCK_SERIALIZED_SIZE - 1000, options.nBlockMaxSize));
- // Whether we need to account for byte usage (in addition to weight usage)
- fNeedSizeAccounting = (nBlockMaxSize < MAX_BLOCK_SERIALIZED_SIZE - 1000);
}
static BlockAssembler::Options DefaultOptions(const CChainParams& params)
@@ -85,20 +78,7 @@ static BlockAssembler::Options DefaultOptions(const CChainParams& params)
// If only one is given, only restrict the specified resource.
// If both are given, restrict both.
BlockAssembler::Options options;
- options.nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;
- options.nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE;
- bool fWeightSet = false;
- if (gArgs.IsArgSet("-blockmaxweight")) {
- options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
- options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE;
- fWeightSet = true;
- }
- if (gArgs.IsArgSet("-blockmaxsize")) {
- options.nBlockMaxSize = gArgs.GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE);
- if (!fWeightSet) {
- options.nBlockMaxWeight = options.nBlockMaxSize * WITNESS_SCALE_FACTOR;
- }
- }
+ options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
if (gArgs.IsArgSet("-blockmintxfee")) {
CAmount n = 0;
ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n);
@@ -116,7 +96,6 @@ void BlockAssembler::resetBlock()
inBlock.clear();
// Reserve space for coinbase tx
- nBlockSize = 1000;
nBlockWeight = 4000;
nBlockSigOpsCost = 400;
fIncludeWitness = false;
@@ -145,6 +124,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
LOCK2(cs_main, mempool.cs);
CBlockIndex* pindexPrev = chainActive.Tip();
+ assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
@@ -175,7 +155,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
int64_t nTime1 = GetTimeMicros();
nLastBlockTx = nBlockTx;
- nLastBlockSize = nBlockSize;
nLastBlockWeight = nBlockWeight;
// Create coinbase transaction.
@@ -190,8 +169,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
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);
+ LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
@@ -238,22 +216,13 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost
// - transaction finality (locktime)
// - premature witness (in case segwit transactions are added to mempool before
// segwit activation)
-// - serialized size (in case -blockmaxsize is in use)
bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package)
{
- uint64_t nPotentialBlockSize = nBlockSize; // only used with fNeedSizeAccounting
for (const CTxMemPool::txiter it : package) {
if (!IsFinalTx(it->GetTx(), nHeight, nLockTimeCutoff))
return false;
if (!fIncludeWitness && it->GetTx().HasWitness())
return false;
- if (fNeedSizeAccounting) {
- uint64_t nTxSize = ::GetSerializeSize(it->GetTx(), SER_NETWORK, PROTOCOL_VERSION);
- if (nPotentialBlockSize + nTxSize >= nBlockMaxSize) {
- return false;
- }
- nPotentialBlockSize += nTxSize;
- }
}
return true;
}
@@ -263,9 +232,6 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
pblock->vtx.emplace_back(iter->GetSharedTx());
pblocktemplate->vTxFees.push_back(iter->GetFee());
pblocktemplate->vTxSigOpsCost.push_back(iter->GetSigOpCost());
- if (fNeedSizeAccounting) {
- nBlockSize += ::GetSerializeSize(iter->GetTx(), SER_NETWORK, PROTOCOL_VERSION);
- }
nBlockWeight += iter->GetTxWeight();
++nBlockTx;
nBlockSigOpsCost += iter->GetSigOpCost();
diff --git a/src/miner.h b/src/miner.h
index abd2ff6199..d81ec6421c 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -6,8 +6,8 @@
#ifndef BITCOIN_MINER_H
#define BITCOIN_MINER_H
-#include "primitives/block.h"
-#include "txmempool.h"
+#include <primitives/block.h>
+#include <txmempool.h>
#include <stdint.h>
#include <memory>
@@ -71,7 +71,7 @@ struct modifiedentry_iter {
// except operating on CTxMemPoolModifiedEntry.
// TODO: refactor to avoid duplication of this logic.
struct CompareModifiedEntry {
- bool operator()(const CTxMemPoolModifiedEntry &a, const CTxMemPoolModifiedEntry &b)
+ bool operator()(const CTxMemPoolModifiedEntry &a, const CTxMemPoolModifiedEntry &b) const
{
double f1 = (double)a.nModFeesWithAncestors * b.nSizeWithAncestors;
double f2 = (double)b.nModFeesWithAncestors * a.nSizeWithAncestors;
@@ -86,7 +86,7 @@ struct CompareModifiedEntry {
// This is sufficient to sort an ancestor package in an order that is valid
// to appear in a block.
struct CompareTxIterByAncestorCount {
- bool operator()(const CTxMemPool::txiter &a, const CTxMemPool::txiter &b)
+ bool operator()(const CTxMemPool::txiter &a, const CTxMemPool::txiter &b) const
{
if (a->GetCountWithAncestors() != b->GetCountWithAncestors())
return a->GetCountWithAncestors() < b->GetCountWithAncestors();
@@ -139,13 +139,11 @@ private:
// Configuration parameters for the block size
bool fIncludeWitness;
- unsigned int nBlockMaxWeight, nBlockMaxSize;
- bool fNeedSizeAccounting;
+ unsigned int nBlockMaxWeight;
CFeeRate blockMinFeeRate;
// Information on the current status of the block
uint64_t nBlockWeight;
- uint64_t nBlockSize;
uint64_t nBlockTx;
uint64_t nBlockSigOpsCost;
CAmount nFees;
@@ -160,7 +158,6 @@ public:
struct Options {
Options();
size_t nBlockMaxWeight;
- size_t nBlockMaxSize;
CFeeRate blockMinFeeRate;
};
diff --git a/src/net.cpp b/src/net.cpp
index 1af317726a..e00faaeecd 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -4,23 +4,21 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "net.h"
-
-#include "addrman.h"
-#include "chainparams.h"
-#include "clientversion.h"
-#include "consensus/consensus.h"
-#include "crypto/common.h"
-#include "crypto/sha256.h"
-#include "hash.h"
-#include "primitives/transaction.h"
-#include "netbase.h"
-#include "scheduler.h"
-#include "ui_interface.h"
-#include "utilstrencodings.h"
+#include <net.h>
+
+#include <chainparams.h>
+#include <clientversion.h>
+#include <consensus/consensus.h>
+#include <crypto/common.h>
+#include <crypto/sha256.h>
+#include <primitives/transaction.h>
+#include <netbase.h>
+#include <scheduler.h>
+#include <ui_interface.h>
+#include <utilstrencodings.h>
#ifdef WIN32
#include <string.h>
@@ -89,10 +87,6 @@ std::string strSubVersion;
limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);
-// Signals for message handling
-static CNodeSignals g_signals;
-CNodeSignals& GetNodeSignals() { return g_signals; }
-
void CConnman::AddOneShot(const std::string& strDest)
{
LOCK(cs_vOneShots);
@@ -114,13 +108,13 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer)
int nBestReachability = -1;
{
LOCK(cs_mapLocalHost);
- for (std::map<CNetAddr, LocalServiceInfo>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++)
+ for (const auto& entry : mapLocalHost)
{
- int nScore = (*it).second.nScore;
- int nReachability = (*it).first.GetReachabilityFrom(paddrPeer);
+ int nScore = entry.second.nScore;
+ int nReachability = entry.first.GetReachabilityFrom(paddrPeer);
if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore))
{
- addr = CService((*it).first, (*it).second.nPort);
+ addr = CService(entry.first, entry.second.nPort);
nBestReachability = nReachability;
nBestScore = nScore;
}
@@ -139,11 +133,10 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
const int64_t nOneWeek = 7*24*60*60;
std::vector<CAddress> vSeedsOut;
vSeedsOut.reserve(vSeedsIn.size());
- for (std::vector<SeedSpec6>::const_iterator i(vSeedsIn.begin()); i != vSeedsIn.end(); ++i)
- {
+ for (const auto& seed_in : vSeedsIn) {
struct in6_addr ip;
- memcpy(&ip, i->addr, sizeof(ip));
- CAddress addr(CService(ip, i->port), NODE_NETWORK);
+ memcpy(&ip, seed_in.addr, sizeof(ip));
+ CAddress addr(CService(ip, seed_in.port), NODE_NETWORK);
addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek;
vSeedsOut.push_back(addr);
}
@@ -303,18 +296,22 @@ bool IsReachable(const CNetAddr& addr)
CNode* CConnman::FindNode(const CNetAddr& ip)
{
LOCK(cs_vNodes);
- for (CNode* pnode : vNodes)
- if ((CNetAddr)pnode->addr == ip)
- return (pnode);
+ for (CNode* pnode : vNodes) {
+ if ((CNetAddr)pnode->addr == ip) {
+ return pnode;
+ }
+ }
return nullptr;
}
CNode* CConnman::FindNode(const CSubNet& subNet)
{
LOCK(cs_vNodes);
- for (CNode* pnode : vNodes)
- if (subNet.Match((CNetAddr)pnode->addr))
- return (pnode);
+ for (CNode* pnode : vNodes) {
+ if (subNet.Match((CNetAddr)pnode->addr)) {
+ return pnode;
+ }
+ }
return nullptr;
}
@@ -323,7 +320,7 @@ CNode* CConnman::FindNode(const std::string& addrName)
LOCK(cs_vNodes);
for (CNode* pnode : vNodes) {
if (pnode->GetAddrName() == addrName) {
- return (pnode);
+ return pnode;
}
}
return nullptr;
@@ -332,9 +329,11 @@ CNode* CConnman::FindNode(const std::string& addrName)
CNode* CConnman::FindNode(const CService& addr)
{
LOCK(cs_vNodes);
- for (CNode* pnode : vNodes)
- if ((CService)pnode->addr == addr)
- return (pnode);
+ for (CNode* pnode : vNodes) {
+ if ((CService)pnode->addr == addr) {
+ return pnode;
+ }
+ }
return nullptr;
}
@@ -384,19 +383,16 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
pszDest ? pszDest : addrConnect.ToString(),
pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
- // Connect
- SOCKET hSocket;
- bool proxyConnectionFailed = false;
- if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) :
- ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed))
- {
- if (!IsSelectableSocket(hSocket)) {
- LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
- CloseSocket(hSocket);
- return nullptr;
- }
-
- if (pszDest && addrConnect.IsValid()) {
+ // Resolve
+ const int default_port = Params().GetDefaultPort();
+ if (pszDest) {
+ std::vector<CService> resolved;
+ if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) {
+ addrConnect = CAddress(resolved[GetRand(resolved.size())], NODE_NONE);
+ if (!addrConnect.IsValid()) {
+ LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s", addrConnect.ToString(), pszDest);
+ return nullptr;
+ }
// It is possible that we already have a connection to the IP/port pszDest resolved to.
// In that case, drop the connection that was just created, and return the existing CNode instead.
// Also store the name we used to connect in that CNode, so that future FindNode() calls to that
@@ -406,30 +402,61 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
if (pnode)
{
pnode->MaybeSetAddrName(std::string(pszDest));
- CloseSocket(hSocket);
LogPrintf("Failed to open new connection, already connected\n");
return nullptr;
}
}
+ }
- addrman.Attempt(addrConnect, fCountFailure);
-
- // Add node
- NodeId id = GetNewNodeId();
- uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
- CAddress addr_bind = GetBindAddress(hSocket);
- CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false);
- pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices);
- pnode->AddRef();
+ // Connect
+ bool connected = false;
+ SOCKET hSocket;
+ proxyType proxy;
+ if (addrConnect.IsValid()) {
+ bool proxyConnectionFailed = false;
- return pnode;
- } else if (!proxyConnectionFailed) {
- // If connecting to the node failed, and failure is not caused by a problem connecting to
- // the proxy, mark this as an attempt.
- addrman.Attempt(addrConnect, fCountFailure);
+ if (GetProxy(addrConnect.GetNetwork(), proxy)) {
+ hSocket = CreateSocket(proxy.proxy);
+ if (hSocket == INVALID_SOCKET) {
+ return nullptr;
+ }
+ connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(), hSocket, nConnectTimeout, &proxyConnectionFailed);
+ } else {
+ // no proxy needed (none set for target network)
+ hSocket = CreateSocket(addrConnect);
+ if (hSocket == INVALID_SOCKET) {
+ return nullptr;
+ }
+ connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout);
+ }
+ if (!proxyConnectionFailed) {
+ // If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
+ // the proxy, mark this as an attempt.
+ addrman.Attempt(addrConnect, fCountFailure);
+ }
+ } else if (pszDest && GetNameProxy(proxy)) {
+ hSocket = CreateSocket(proxy.proxy);
+ if (hSocket == INVALID_SOCKET) {
+ return nullptr;
+ }
+ std::string host;
+ int port = default_port;
+ SplitHostPort(std::string(pszDest), port, host);
+ connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, nullptr);
+ }
+ if (!connected) {
+ CloseSocket(hSocket);
+ return nullptr;
}
- return nullptr;
+ // Add node
+ NodeId id = GetNewNodeId();
+ uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
+ CAddress addr_bind = GetBindAddress(hSocket);
+ CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false);
+ pnode->AddRef();
+
+ return pnode;
}
void CConnman::DumpBanlist()
@@ -478,10 +505,9 @@ void CConnman::ClearBanned()
bool CConnman::IsBanned(CNetAddr ip)
{
LOCK(cs_setBanned);
- for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++)
- {
- CSubNet subNet = (*it).first;
- CBanEntry banEntry = (*it).second;
+ for (const auto& it : setBanned) {
+ CSubNet subNet = it.first;
+ CBanEntry banEntry = it.second;
if (subNet.Match(ip) && GetTime() < banEntry.nBanUntil) {
return true;
@@ -577,21 +603,28 @@ void CConnman::SetBanned(const banmap_t &banMap)
void CConnman::SweepBanned()
{
int64_t now = GetTime();
-
- LOCK(cs_setBanned);
- banmap_t::iterator it = setBanned.begin();
- while(it != setBanned.end())
+ bool notifyUI = false;
{
- CSubNet subNet = (*it).first;
- CBanEntry banEntry = (*it).second;
- if(now > banEntry.nBanUntil)
+ LOCK(cs_setBanned);
+ banmap_t::iterator it = setBanned.begin();
+ while(it != setBanned.end())
{
- setBanned.erase(it++);
- setBannedIsDirty = true;
- LogPrint(BCLog::NET, "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, subNet.ToString());
+ CSubNet subNet = (*it).first;
+ CBanEntry banEntry = (*it).second;
+ if(now > banEntry.nBanUntil)
+ {
+ setBanned.erase(it++);
+ setBannedIsDirty = true;
+ notifyUI = true;
+ LogPrint(BCLog::NET, "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, subNet.ToString());
+ }
+ else
+ ++it;
}
- else
- ++it;
+ }
+ // update UI
+ if(notifyUI && clientInterface) {
+ clientInterface->BannedListChanged();
}
}
@@ -665,7 +698,7 @@ void CNode::copyStats(CNodeStats &stats)
X(cleanSubVer);
}
X(fInbound);
- X(fAddnode);
+ X(m_manual_connection);
X(nStartingHeight);
{
LOCK(cs_vSend);
@@ -942,6 +975,16 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
return a.nTimeConnected > b.nTimeConnected;
}
+
+//! Sort an array by the specified comparator, then erase the last K elements.
+template<typename T, typename Comparator>
+static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, size_t k)
+{
+ std::sort(elements.begin(), elements.end(), comparator);
+ size_t eraseSize = std::min(k, elements.size());
+ elements.erase(elements.end() - eraseSize, elements.end());
+}
+
/** Try to find a connection to evict when the node is full.
* Extreme care must be taken to avoid opening the node to attacker
* triggered network partitioning.
@@ -956,7 +999,7 @@ bool CConnman::AttemptToEvictConnection()
{
LOCK(cs_vNodes);
- for (CNode *node : vNodes) {
+ for (const CNode* node : vNodes) {
if (node->fWhitelisted)
continue;
if (!node->fInbound)
@@ -965,48 +1008,29 @@ bool CConnman::AttemptToEvictConnection()
continue;
NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime,
node->nLastBlockTime, node->nLastTXTime,
- (node->nServices & nRelevantServices) == nRelevantServices,
+ HasAllDesirableServiceFlags(node->nServices),
node->fRelayTxes, node->pfilter != nullptr, node->addr, node->nKeyedNetGroup};
vEvictionCandidates.push_back(candidate);
}
}
- if (vEvictionCandidates.empty()) return false;
-
// Protect connections with certain characteristics
// Deterministically select 4 peers to protect by netgroup.
// An attacker cannot predict which netgroups will be protected
- std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNetGroupKeyed);
- vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
-
- if (vEvictionCandidates.empty()) return false;
-
+ EraseLastKElements(vEvictionCandidates, CompareNetGroupKeyed, 4);
// Protect the 8 nodes with the lowest minimum ping time.
// An attacker cannot manipulate this metric without physically moving nodes closer to the target.
- std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeMinPingTime);
- vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
-
- if (vEvictionCandidates.empty()) return false;
-
+ EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8);
// Protect 4 nodes that most recently sent us transactions.
// An attacker cannot manipulate this metric without performing useful work.
- std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeTXTime);
- vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
-
- if (vEvictionCandidates.empty()) return false;
-
+ EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
// Protect 4 nodes that most recently sent us blocks.
// An attacker cannot manipulate this metric without performing useful work.
- std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockTime);
- vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
-
- if (vEvictionCandidates.empty()) return false;
-
+ EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4);
// Protect the half of the remaining nodes which have been connected the longest.
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
- std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected);
- vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast<int>(vEvictionCandidates.size() / 2), vEvictionCandidates.end());
+ EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size() / 2);
if (vEvictionCandidates.empty()) return false;
@@ -1017,12 +1041,12 @@ bool CConnman::AttemptToEvictConnection()
int64_t nMostConnectionsTime = 0;
std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapNetGroupNodes;
for (const NodeEvictionCandidate &node : vEvictionCandidates) {
- mapNetGroupNodes[node.nKeyedNetGroup].push_back(node);
- int64_t grouptime = mapNetGroupNodes[node.nKeyedNetGroup][0].nTimeConnected;
- size_t groupsize = mapNetGroupNodes[node.nKeyedNetGroup].size();
+ std::vector<NodeEvictionCandidate> &group = mapNetGroupNodes[node.nKeyedNetGroup];
+ group.push_back(node);
+ int64_t grouptime = group[0].nTimeConnected;
- if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) {
- nMostConnections = groupsize;
+ if (group.size() > nMostConnections || (group.size() == nMostConnections && grouptime > nMostConnectionsTime)) {
+ nMostConnections = group.size();
nMostConnectionsTime = grouptime;
naMostConnections = node.nKeyedNetGroup;
}
@@ -1034,9 +1058,9 @@ bool CConnman::AttemptToEvictConnection()
// Disconnect from the network group with the most connections
NodeId evicted = vEvictionCandidates.front().id;
LOCK(cs_vNodes);
- for(std::vector<CNode*>::const_iterator it(vNodes.begin()); it != vNodes.end(); ++it) {
- if ((*it)->GetId() == evicted) {
- (*it)->fDisconnect = true;
+ for (CNode* pnode : vNodes) {
+ if (pnode->GetId() == evicted) {
+ pnode->fDisconnect = true;
return true;
}
}
@@ -1060,9 +1084,9 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr);
{
LOCK(cs_vNodes);
- for (CNode* pnode : vNodes)
- if (pnode->fInbound)
- nInbound++;
+ for (const CNode* pnode : vNodes) {
+ if (pnode->fInbound) nInbound++;
+ }
}
if (hSocket == INVALID_SOCKET)
@@ -1092,7 +1116,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
if (IsBanned(addr) && !whitelisted)
{
- LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
+ LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
CloseSocket(hSocket);
return;
}
@@ -1114,7 +1138,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
pnode->AddRef();
pnode->fWhitelisted = whitelisted;
- GetNodeSignals().InitializeNode(pnode, *this);
+ m_msgproc->InitializeNode(pnode);
LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString());
@@ -1524,22 +1548,20 @@ void ThreadMapPort()
void MapPort(bool fUseUPnP)
{
- static boost::thread* upnp_thread = nullptr;
+ static std::unique_ptr<boost::thread> upnp_thread;
if (fUseUPnP)
{
if (upnp_thread) {
upnp_thread->interrupt();
upnp_thread->join();
- delete upnp_thread;
}
- upnp_thread = new boost::thread(boost::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort));
+ upnp_thread.reset(new boost::thread(boost::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort)));
}
else if (upnp_thread) {
upnp_thread->interrupt();
upnp_thread->join();
- delete upnp_thread;
- upnp_thread = nullptr;
+ upnp_thread.reset();
}
}
@@ -1582,7 +1604,7 @@ void CConnman::ThreadDNSAddressSeed()
LOCK(cs_vNodes);
int nRelevant = 0;
for (auto pnode : vNodes) {
- nRelevant += pnode->fSuccessfullyConnected && ((pnode->nServices & nRelevantServices) == nRelevantServices);
+ nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound;
}
if (nRelevant >= 2) {
LogPrintf("P2P peers available. Skipped DNS seeding.\n");
@@ -1604,7 +1626,7 @@ void CConnman::ThreadDNSAddressSeed()
} else {
std::vector<CNetAddr> vIPs;
std::vector<CAddress> vAdd;
- ServiceFlags requiredServiceBits = nRelevantServices;
+ ServiceFlags requiredServiceBits = GetDesirableServiceFlags(NODE_NONE);
std::string host = GetDNSHost(seed, &requiredServiceBits);
CNetAddr resolveSource;
if (!resolveSource.SetInternal(host)) {
@@ -1674,18 +1696,49 @@ void CConnman::ProcessOneShot()
}
}
-void CConnman::ThreadOpenConnections()
+bool CConnman::GetTryNewOutboundPeer()
+{
+ return m_try_another_outbound_peer;
+}
+
+void CConnman::SetTryNewOutboundPeer(bool flag)
+{
+ m_try_another_outbound_peer = flag;
+ LogPrint(BCLog::NET, "net: setting try another outbound peer=%s\n", flag ? "true" : "false");
+}
+
+// Return the number of peers we have over our outbound connection limit
+// Exclude peers that are marked for disconnect, or are going to be
+// disconnected soon (eg one-shots and feelers)
+// Also exclude peers that haven't finished initial connection handshake yet
+// (so that we don't decide we're over our desired connection limit, and then
+// evict some peer that has finished the handshake)
+int CConnman::GetExtraOutboundCount()
+{
+ int nOutbound = 0;
+ {
+ LOCK(cs_vNodes);
+ for (CNode* pnode : vNodes) {
+ if (!pnode->fInbound && !pnode->m_manual_connection && !pnode->fFeeler && !pnode->fDisconnect && !pnode->fOneShot && pnode->fSuccessfullyConnected) {
+ ++nOutbound;
+ }
+ }
+ }
+ return std::max(nOutbound - nMaxOutbound, 0);
+}
+
+void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
{
// Connect to specific addresses
- if (gArgs.IsArgSet("-connect"))
+ if (!connect.empty())
{
for (int64_t nLoop = 0;; nLoop++)
{
ProcessOneShot();
- for (const std::string& strAddr : gArgs.GetArgs("-connect"))
+ for (const std::string& strAddr : connect)
{
CAddress addr(CService(), NODE_NONE);
- OpenNetworkConnection(addr, false, nullptr, strAddr.c_str());
+ OpenNetworkConnection(addr, false, nullptr, strAddr.c_str(), false, false, true);
for (int i = 0; i < 10 && i < nLoop; i++)
{
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
@@ -1733,17 +1786,11 @@ void CConnman::ThreadOpenConnections()
// Only connect out to one peer per network group (/16 for IPv4).
// Do this here so we don't have to critsect vNodes inside mapAddresses critsect.
int nOutbound = 0;
- int nOutboundRelevant = 0;
std::set<std::vector<unsigned char> > setConnected;
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes) {
- if (!pnode->fInbound && !pnode->fAddnode) {
-
- // Count the peers that have all relevant services
- if (pnode->fSuccessfullyConnected && !pnode->fFeeler && ((pnode->nServices & nRelevantServices) == nRelevantServices)) {
- nOutboundRelevant++;
- }
+ if (!pnode->fInbound && !pnode->m_manual_connection) {
// Netgroups for inbound and addnode peers are not excluded because our goal here
// is to not use multiple of our limited outbound slots on a single netgroup
// but inbound and addnode peers do not use our outbound slots. Inbound peers
@@ -1768,7 +1815,8 @@ void CConnman::ThreadOpenConnections()
// * Only make a feeler connection once every few minutes.
//
bool fFeeler = false;
- if (nOutbound >= nMaxOutbound) {
+
+ if (nOutbound >= nMaxOutbound && !GetTryNewOutboundPeer()) {
int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
if (nTime > nNextFeeler) {
nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
@@ -1798,21 +1846,16 @@ void CConnman::ThreadOpenConnections()
if (IsLimited(addr))
continue;
- // only connect to full nodes
- if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES)
- continue;
-
// only consider very recently tried nodes after 30 failed attempts
if (nANow - addr.nLastTry < 600 && nTries < 30)
continue;
- // only consider nodes missing relevant services after 40 failed attempts and only if less than half the outbound are up.
- ServiceFlags nRequiredServices = nRelevantServices;
- if (nTries >= 40 && nOutbound < (nMaxOutbound >> 1)) {
- nRequiredServices = REQUIRED_SERVICES;
- }
-
- if ((addr.nServices & nRequiredServices) != nRequiredServices) {
+ // for non-feelers, require all the services we'll want,
+ // for feelers, only require they be a full node (only because most
+ // SPV clients don't have a good address DB available)
+ if (!fFeeler && !HasAllDesirableServiceFlags(addr.nServices)) {
+ continue;
+ } else if (fFeeler && !MayHaveUsefulAddressDB(addr.nServices)) {
continue;
}
@@ -1821,13 +1864,6 @@ void CConnman::ThreadOpenConnections()
continue;
addrConnect = addr;
-
- // regardless of the services assumed to be available, only require the minimum if half or more outbound have relevant services
- if (nOutboundRelevant >= (nMaxOutbound >> 1)) {
- addrConnect.nServices = REQUIRED_SERVICES;
- } else {
- addrConnect.nServices = nRequiredServices;
- }
break;
}
@@ -1854,8 +1890,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
{
LOCK(cs_vAddedNodes);
ret.reserve(vAddedNodes.size());
- for (const std::string& strAddNode : vAddedNodes)
- lAddresses.push_back(strAddNode);
+ std::copy(vAddedNodes.cbegin(), vAddedNodes.cend(), std::back_inserter(lAddresses));
}
@@ -1901,11 +1936,6 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
void CConnman::ThreadOpenAddedConnections()
{
- {
- LOCK(cs_vAddedNodes);
- vAddedNodes = gArgs.GetArgs("-addnode");
- }
-
while (true)
{
CSemaphoreGrant grant(*semAddnode);
@@ -1918,11 +1948,9 @@ void CConnman::ThreadOpenAddedConnections()
// the addednodeinfo state might change.
break;
}
- // If strAddedNode is an IP/port, decode it immediately, so
- // OpenNetworkConnection can detect existing connections to that IP/port.
tried = true;
- CService service(LookupNumeric(info.strAddedNode.c_str(), Params().GetDefaultPort()));
- OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false, false, true);
+ CAddress addr(CService(), NODE_NONE);
+ OpenNetworkConnection(addr, false, &grant, info.strAddedNode.c_str(), false, false, true);
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
return;
}
@@ -1934,7 +1962,7 @@ void CConnman::ThreadOpenAddedConnections()
}
// if successful, this moves the passed grant to the constructed node
-bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool fAddnode)
+bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection)
{
//
// Initiate outbound network connection
@@ -1963,10 +1991,10 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
pnode->fOneShot = true;
if (fFeeler)
pnode->fFeeler = true;
- if (fAddnode)
- pnode->fAddnode = true;
+ if (manual_connection)
+ pnode->m_manual_connection = true;
- GetNodeSignals().InitializeNode(pnode, *this);
+ m_msgproc->InitializeNode(pnode);
{
LOCK(cs_vNodes);
vNodes.push_back(pnode);
@@ -1996,16 +2024,16 @@ void CConnman::ThreadMessageHandler()
continue;
// Receive messages
- bool fMoreNodeWork = GetNodeSignals().ProcessMessages(pnode, *this, flagInterruptMsgProc);
+ bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, flagInterruptMsgProc);
fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend);
if (flagInterruptMsgProc)
return;
-
// Send messages
{
LOCK(pnode->cs_sendProcessing);
- GetNodeSignals().SendMessages(pnode, *this, flagInterruptMsgProc);
+ m_msgproc->SendMessages(pnode, flagInterruptMsgProc);
}
+
if (flagInterruptMsgProc)
return;
}
@@ -2044,44 +2072,21 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b
return false;
}
- SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
+ SOCKET hListenSocket = CreateSocket(addrBind);
if (hListenSocket == INVALID_SOCKET)
{
strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %s)", NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError);
return false;
}
- if (!IsSelectableSocket(hListenSocket))
- {
- strError = "Error: Couldn't create a listenable socket for incoming connections";
- LogPrintf("%s\n", strError);
- return false;
- }
-
-
#ifndef WIN32
-#ifdef SO_NOSIGPIPE
- // Different way of disabling SIGPIPE on BSD
- setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int));
-#endif
// Allow binding if the port is still in TIME_WAIT state after
// the program was closed and restarted.
setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int));
- // Disable Nagle's algorithm
- setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&nOne, sizeof(int));
#else
setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOne, sizeof(int));
- setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&nOne, sizeof(int));
#endif
- // Set to non-blocking, incoming connections will also inherit this
- if (!SetSocketNonBlocking(hListenSocket, true)) {
- CloseSocket(hListenSocket);
- strError = strprintf("BindListenPort: Setting listening socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
- LogPrintf("%s\n", strError);
- return false;
- }
-
// some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
// and enable it by default or not. Try to enable it, if possible.
if (addrBind.IsIPv6()) {
@@ -2208,9 +2213,8 @@ CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSe
nLastNodeId = 0;
nSendBufferMaxSize = 0;
nReceiveFloodSize = 0;
- semOutbound = nullptr;
- semAddnode = nullptr;
flagInterruptMsgProc = false;
+ SetTryNewOutboundPeer(false);
Options connOptions;
Init(connOptions);
@@ -2256,10 +2260,16 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
{
Init(connOptions);
- nTotalBytesRecv = 0;
- nTotalBytesSent = 0;
- nMaxOutboundTotalBytesSentInCycle = 0;
- nMaxOutboundCycleStartTime = 0;
+ {
+ LOCK(cs_totalBytesRecv);
+ nTotalBytesRecv = 0;
+ }
+ {
+ LOCK(cs_totalBytesSent);
+ nTotalBytesSent = 0;
+ nMaxOutboundTotalBytesSentInCycle = 0;
+ nMaxOutboundCycleStartTime = 0;
+ }
if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds)) {
if (clientInterface) {
@@ -2314,16 +2324,17 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
if (semOutbound == nullptr) {
// initialize semaphore
- semOutbound = new CSemaphore(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections));
+ semOutbound = MakeUnique<CSemaphore>(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections));
}
if (semAddnode == nullptr) {
// initialize semaphore
- semAddnode = new CSemaphore(nMaxAddnode);
+ semAddnode = MakeUnique<CSemaphore>(nMaxAddnode);
}
//
// Start threads
//
+ assert(m_msgproc);
InterruptSocks5(false);
interruptNet.reset();
flagInterruptMsgProc = false;
@@ -2344,9 +2355,16 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
// Initiate outbound connections from -addnode
threadOpenAddedConnections = std::thread(&TraceThread<std::function<void()> >, "addcon", std::function<void()>(std::bind(&CConnman::ThreadOpenAddedConnections, this)));
- // Initiate outbound connections unless connect=0
- if (!gArgs.IsArgSet("-connect") || gArgs.GetArgs("-connect").size() != 1 || gArgs.GetArgs("-connect")[0] != "0")
- threadOpenConnections = std::thread(&TraceThread<std::function<void()> >, "opencon", std::function<void()>(std::bind(&CConnman::ThreadOpenConnections, this)));
+ if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) {
+ if (clientInterface) {
+ clientInterface->ThreadSafeMessageBox(
+ _("Cannot provide specific connections and have addrman find outgoing connections at the same."),
+ "", CClientUIInterface::MSG_ERROR);
+ }
+ return false;
+ }
+ if (connOptions.m_use_addrman_outgoing || !connOptions.m_specified_outgoing.empty())
+ threadOpenConnections = std::thread(&TraceThread<std::function<void()> >, "opencon", std::function<void()>(std::bind(&CConnman::ThreadOpenConnections, this, connOptions.m_specified_outgoing)));
// Process messages
threadMessageHandler = std::thread(&TraceThread<std::function<void()> >, "msghand", std::function<void()>(std::bind(&CConnman::ThreadMessageHandler, this)));
@@ -2433,19 +2451,18 @@ void CConnman::Stop()
vNodes.clear();
vNodesDisconnected.clear();
vhListenSocket.clear();
- delete semOutbound;
- semOutbound = nullptr;
- delete semAddnode;
- semAddnode = nullptr;
+ semOutbound.reset();
+ semAddnode.reset();
}
void CConnman::DeleteNode(CNode* pnode)
{
assert(pnode);
bool fUpdateConnectionTime = false;
- GetNodeSignals().FinalizeNode(pnode->GetId(), fUpdateConnectionTime);
- if(fUpdateConnectionTime)
+ m_msgproc->FinalizeNode(pnode->GetId(), fUpdateConnectionTime);
+ if(fUpdateConnectionTime) {
addrman.Connected(pnode->addr);
+ }
delete pnode;
}
@@ -2483,9 +2500,8 @@ std::vector<CAddress> CConnman::GetAddresses()
bool CConnman::AddNode(const std::string& strNode)
{
LOCK(cs_vAddedNodes);
- for(std::vector<std::string>::const_iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) {
- if (strNode == *it)
- return false;
+ for (const std::string& it : vAddedNodes) {
+ if (strNode == it) return false;
}
vAddedNodes.push_back(strNode);
@@ -2511,9 +2527,11 @@ size_t CConnman::GetNodeCount(NumConnections flags)
return vNodes.size();
int nNum = 0;
- for(std::vector<CNode*>::const_iterator it = vNodes.begin(); it != vNodes.end(); ++it)
- if (flags & ((*it)->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))
+ for (const auto& pnode : vNodes) {
+ if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) {
nNum++;
+ }
+ }
return nNum;
}
@@ -2523,8 +2541,7 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats)
vstats.clear();
LOCK(cs_vNodes);
vstats.reserve(vNodes.size());
- for(std::vector<CNode*>::iterator it = vNodes.begin(); it != vNodes.end(); ++it) {
- CNode* pnode = *it;
+ for (CNode* pnode : vNodes) {
vstats.emplace_back();
pnode->copyStats(vstats.back());
}
@@ -2691,7 +2708,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
nSendVersion(0)
{
nServices = NODE_NONE;
- nServicesExpected = NODE_NONE;
hSocket = hSocketIn;
nRecvVersion = INIT_PROTO_VERSION;
nLastSend = 0;
@@ -2704,7 +2720,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
strSubVer = "";
fWhitelisted = false;
fOneShot = false;
- fAddnode = false;
+ m_manual_connection = false;
fClient = false; // set by version message
fFeeler = false;
fSuccessfullyConnected = false;
@@ -2722,7 +2738,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
nNextInvSend = 0;
fRelayTxes = false;
fSentAddr = false;
- pfilter = new CBloomFilter();
+ pfilter = MakeUnique<CBloomFilter>();
timeLastMempoolReq = 0;
nLastBlockTime = 0;
nLastTXTime = 0;
@@ -2752,9 +2768,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
CNode::~CNode()
{
CloseSocket(hSocket);
-
- if (pfilter)
- delete pfilter;
}
void CNode::AskFor(const CInv& inv)
diff --git a/src/net.h b/src/net.h
index a32736aa97..d0fd050970 100644
--- a/src/net.h
+++ b/src/net.h
@@ -6,21 +6,21 @@
#ifndef BITCOIN_NET_H
#define BITCOIN_NET_H
-#include "addrdb.h"
-#include "addrman.h"
-#include "amount.h"
-#include "bloom.h"
-#include "compat.h"
-#include "hash.h"
-#include "limitedmap.h"
-#include "netaddress.h"
-#include "policy/feerate.h"
-#include "protocol.h"
-#include "random.h"
-#include "streams.h"
-#include "sync.h"
-#include "uint256.h"
-#include "threadinterrupt.h"
+#include <addrdb.h>
+#include <addrman.h>
+#include <amount.h>
+#include <bloom.h>
+#include <compat.h>
+#include <hash.h>
+#include <limitedmap.h>
+#include <netaddress.h>
+#include <policy/feerate.h>
+#include <protocol.h>
+#include <random.h>
+#include <streams.h>
+#include <sync.h>
+#include <uint256.h>
+#include <threadinterrupt.h>
#include <atomic>
#include <deque>
@@ -33,7 +33,6 @@
#include <arpa/inet.h>
#endif
-#include <boost/signals2/signal.hpp>
class CScheduler;
class CNode;
@@ -85,8 +84,6 @@ static const bool DEFAULT_FORCEDNSSEED = false;
static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000;
-static const ServiceFlags REQUIRED_SERVICES = NODE_NETWORK;
-
// NOTE: When adjusting this, update rpcnet:setban's help ("24h")
static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
@@ -116,7 +113,7 @@ struct CSerializedNetMsg
std::string command;
};
-
+class NetEventsInterface;
class CConnman
{
public:
@@ -131,13 +128,13 @@ public:
struct Options
{
ServiceFlags nLocalServices = NODE_NONE;
- ServiceFlags nRelevantServices = NODE_NONE;
int nMaxConnections = 0;
int nMaxOutbound = 0;
int nMaxAddnode = 0;
int nMaxFeeler = 0;
int nBestHeight = 0;
CClientUIInterface* uiInterface = nullptr;
+ NetEventsInterface* m_msgproc = nullptr;
unsigned int nSendBufferMaxSize = 0;
unsigned int nReceiveFloodSize = 0;
uint64_t nMaxOutboundTimeframe = 0;
@@ -145,22 +142,32 @@ public:
std::vector<std::string> vSeedNodes;
std::vector<CSubNet> vWhitelistedRange;
std::vector<CService> vBinds, vWhiteBinds;
+ bool m_use_addrman_outgoing = true;
+ std::vector<std::string> m_specified_outgoing;
+ std::vector<std::string> m_added_nodes;
};
void Init(const Options& connOptions) {
nLocalServices = connOptions.nLocalServices;
- nRelevantServices = connOptions.nRelevantServices;
nMaxConnections = connOptions.nMaxConnections;
nMaxOutbound = std::min(connOptions.nMaxOutbound, connOptions.nMaxConnections);
nMaxAddnode = connOptions.nMaxAddnode;
nMaxFeeler = connOptions.nMaxFeeler;
nBestHeight = connOptions.nBestHeight;
clientInterface = connOptions.uiInterface;
+ m_msgproc = connOptions.m_msgproc;
nSendBufferMaxSize = connOptions.nSendBufferMaxSize;
nReceiveFloodSize = connOptions.nReceiveFloodSize;
- nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe;
- nMaxOutboundLimit = connOptions.nMaxOutboundLimit;
+ {
+ LOCK(cs_totalBytesSent);
+ nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe;
+ nMaxOutboundLimit = connOptions.nMaxOutboundLimit;
+ }
vWhitelistedRange = connOptions.vWhitelistedRange;
+ {
+ LOCK(cs_vAddedNodes);
+ vAddedNodes = connOptions.m_added_nodes;
+ }
}
CConnman(uint64_t seed0, uint64_t seed1);
@@ -170,7 +177,7 @@ public:
void Interrupt();
bool GetNetworkActive() const { return fNetworkActive; };
void SetNetworkActive(bool active);
- bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool fAddnode = false);
+ bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false);
bool CheckIncomingNonce(uint64_t nonce);
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
@@ -250,6 +257,19 @@ public:
void GetBanned(banmap_t &banmap);
void SetBanned(const banmap_t &banmap);
+ // This allows temporarily exceeding nMaxOutbound, with the goal of finding
+ // a peer that is better than all our current peers.
+ void SetTryNewOutboundPeer(bool flag);
+ bool GetTryNewOutboundPeer();
+
+ // Return the number of outbound peers we have in excess of our target (eg,
+ // if we previously called SetTryNewOutboundPeer(true), and have since set
+ // to false, we may have extra peers that we wish to disconnect). This may
+ // return a value less than (num_outbound_connections - num_outbound_slots)
+ // in cases where some outbound connections are not yet fully connected, or
+ // not yet fully disconnected.
+ int GetExtraOutboundCount();
+
bool AddNode(const std::string& node);
bool RemoveAddedNode(const std::string& node);
std::vector<AddedNodeInfo> GetAddedNodeInfo();
@@ -308,7 +328,7 @@ private:
void ThreadOpenAddedConnections();
void AddOneShot(const std::string& strDest);
void ProcessOneShot();
- void ThreadOpenConnections();
+ void ThreadOpenConnections(std::vector<std::string> connect);
void ThreadMessageHandler();
void AcceptConnection(const ListenSocket& hListenSocket);
void ThreadSocketHandler();
@@ -350,14 +370,14 @@ private:
// Network usage totals
CCriticalSection cs_totalBytesRecv;
CCriticalSection cs_totalBytesSent;
- uint64_t nTotalBytesRecv;
- uint64_t nTotalBytesSent;
+ uint64_t nTotalBytesRecv GUARDED_BY(cs_totalBytesRecv);
+ uint64_t nTotalBytesSent GUARDED_BY(cs_totalBytesSent);
// outbound limit & stats
- uint64_t nMaxOutboundTotalBytesSentInCycle;
- uint64_t nMaxOutboundCycleStartTime;
- uint64_t nMaxOutboundLimit;
- uint64_t nMaxOutboundTimeframe;
+ uint64_t nMaxOutboundTotalBytesSentInCycle GUARDED_BY(cs_totalBytesSent);
+ uint64_t nMaxOutboundCycleStartTime GUARDED_BY(cs_totalBytesSent);
+ uint64_t nMaxOutboundLimit GUARDED_BY(cs_totalBytesSent);
+ uint64_t nMaxOutboundTimeframe GUARDED_BY(cs_totalBytesSent);
// Whitelisted ranges. Any node connecting from these is automatically
// whitelisted (as well as those connecting to whitelisted binds).
@@ -375,7 +395,7 @@ private:
CAddrMan addrman;
std::deque<std::string> vOneShots;
CCriticalSection cs_vOneShots;
- std::vector<std::string> vAddedNodes;
+ std::vector<std::string> vAddedNodes GUARDED_BY(cs_vAddedNodes);
CCriticalSection cs_vAddedNodes;
std::vector<CNode*> vNodes;
std::list<CNode*> vNodesDisconnected;
@@ -385,17 +405,15 @@ private:
/** Services this instance offers */
ServiceFlags nLocalServices;
- /** Services this instance cares about */
- ServiceFlags nRelevantServices;
-
- CSemaphore *semOutbound;
- CSemaphore *semAddnode;
+ std::unique_ptr<CSemaphore> semOutbound;
+ std::unique_ptr<CSemaphore> semAddnode;
int nMaxConnections;
int nMaxOutbound;
int nMaxAddnode;
int nMaxFeeler;
std::atomic<int> nBestHeight;
CClientUIInterface* clientInterface;
+ NetEventsInterface* m_msgproc;
/** SipHasher seeds for deterministic randomness */
const uint64_t nSeed0, nSeed1;
@@ -414,6 +432,13 @@ private:
std::thread threadOpenAddedConnections;
std::thread threadOpenConnections;
std::thread threadMessageHandler;
+
+ /** flag for deciding to connect to an extra outbound peer,
+ * in excess of nMaxOutbound
+ * This takes the place of a feeler connection */
+ std::atomic_bool m_try_another_outbound_peer;
+
+ friend struct CConnmanTest;
};
extern std::unique_ptr<CConnman> g_connman;
void Discover(boost::thread_group& threadGroup);
@@ -436,19 +461,18 @@ struct CombinerAll
}
};
-// Signals for message handling
-struct CNodeSignals
+/**
+ * Interface for message handling
+ */
+class NetEventsInterface
{
- boost::signals2::signal<bool (CNode*, CConnman&, std::atomic<bool>&), CombinerAll> ProcessMessages;
- boost::signals2::signal<bool (CNode*, CConnman&, std::atomic<bool>&), CombinerAll> SendMessages;
- boost::signals2::signal<void (CNode*, CConnman&)> InitializeNode;
- boost::signals2::signal<void (NodeId, bool&)> FinalizeNode;
+public:
+ virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
+ virtual bool SendMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
+ virtual void InitializeNode(CNode* pnode) = 0;
+ virtual void FinalizeNode(NodeId id, bool& update_connection_time) = 0;
};
-
-CNodeSignals& GetNodeSignals();
-
-
enum
{
LOCAL_NONE, // unknown
@@ -508,7 +532,7 @@ public:
int nVersion;
std::string cleanSubVer;
bool fInbound;
- bool fAddnode;
+ bool m_manual_connection;
int nStartingHeight;
uint64_t nSendBytes;
mapMsgCmdSize mapSendBytesPerMsgCmd;
@@ -580,7 +604,6 @@ class CNode
public:
// socket
std::atomic<ServiceFlags> nServices;
- ServiceFlags nServicesExpected;
SOCKET hSocket;
size_t nSendSize; // total size of all vSendMsg entries
size_t nSendOffset; // offset inside the first vSendMsg already sent
@@ -618,7 +641,7 @@ public:
bool fWhitelisted; // This peer can bypass DoS banning.
bool fFeeler; // If true this node is being used as a short lived feeler.
bool fOneShot;
- bool fAddnode;
+ bool m_manual_connection;
bool fClient;
const bool fInbound;
std::atomic_bool fSuccessfullyConnected;
@@ -631,7 +654,7 @@ public:
bool fSentAddr;
CSemaphoreGrant grantOutbound;
CCriticalSection cs_filter;
- CBloomFilter* pfilter;
+ std::unique_ptr<CBloomFilter> pfilter;
std::atomic<int> nRefCount;
const uint64_t nKeyedNetGroup;
@@ -699,13 +722,11 @@ public:
CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false);
~CNode();
+ CNode(const CNode&) = delete;
+ CNode& operator=(const CNode&) = delete;
private:
- CNode(const CNode&);
- void operator=(const CNode&);
const NodeId id;
-
-
const uint64_t nLocalHostNonce;
// Services offered to this peer
const ServiceFlags nLocalServices;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 596ae1139b..20426eaceb 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -3,33 +3,32 @@
// 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 "reverse_iterator.h"
-#include "tinyformat.h"
-#include "txmempool.h"
-#include "ui_interface.h"
-#include "util.h"
-#include "utilmoneystr.h"
-#include "utilstrencodings.h"
-#include "validationinterface.h"
+#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 <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 <reverse_iterator.h>
+#include <scheduler.h>
+#include <tinyformat.h>
+#include <txmempool.h>
+#include <ui_interface.h>
+#include <util.h>
+#include <utilmoneystr.h>
+#include <utilstrencodings.h>
#if defined(NDEBUG)
# error "Bitcoin cannot be compiled without assertions."
@@ -40,7 +39,7 @@ std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of
struct IteratorComparator
{
template<typename I>
- bool operator()(const I& a, const I& b)
+ bool operator()(const I& a, const I& b) const
{
return &(*a) < &(*b);
}
@@ -61,6 +60,14 @@ static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUAR
static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]
+/// Age after which a stale block will no longer be served if requested as
+/// protection against fingerprinting. Set to one month, denominated in seconds.
+static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
+
+/// Age after which a block is considered historical for purposes of rate
+/// limiting block relay. Set to one week, denominated in seconds.
+static const int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
+
// Internal stuff
namespace {
/** Number of nodes with fSyncStarted. */
@@ -116,6 +123,12 @@ namespace {
/** Number of peers from which we're downloading blocks. */
int nPeersWithValidatedDownloads = 0;
+ /** Number of outbound peers with m_chain_sync.m_protect. */
+ int g_outbound_peers_with_protect_from_disconnect = 0;
+
+ /** When our tip was last updated. */
+ int64_t g_last_tip_update = 0;
+
/** Relay map, protected by cs_main. */
typedef std::map<uint256, CTransactionRef> MapRelay;
MapRelay mapRelay;
@@ -123,11 +136,6 @@ namespace {
std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration;
} // namespace
-//////////////////////////////////////////////////////////////////////////////
-//
-// Registration of network node signals.
-//
-
namespace {
struct CBlockReject {
@@ -198,6 +206,36 @@ struct CNodeState {
*/
bool fSupportsDesiredCmpctVersion;
+ /** State used to enforce CHAIN_SYNC_TIMEOUT
+ * Only in effect for outbound, non-manual connections, with
+ * m_protect == false
+ * Algorithm: if a peer's best known block has less work than our tip,
+ * set a timeout CHAIN_SYNC_TIMEOUT seconds in the future:
+ * - If at timeout their best known block now has more work than our tip
+ * when the timeout was set, then either reset the timeout or clear it
+ * (after comparing against our current tip's work)
+ * - If at timeout their best known block still has less work than our
+ * tip did when the timeout was set, then send a getheaders message,
+ * and set a shorter timeout, HEADERS_RESPONSE_TIME seconds in future.
+ * If their best known block is still behind when that new timeout is
+ * reached, disconnect.
+ */
+ struct ChainSyncTimeoutState {
+ //! A timeout used for checking whether our peer has sufficiently synced
+ int64_t m_timeout;
+ //! A header with the work we require on our peer's chain
+ const CBlockIndex * m_work_header;
+ //! After timeout is reached, set to true after sending getheaders
+ bool m_sent_getheaders;
+ //! Whether this peer is protected from disconnection due to a bad/slow chain
+ bool m_protect;
+ };
+
+ ChainSyncTimeoutState m_chain_sync;
+
+ //! Time of last new block announcement
+ int64_t m_last_block_announcement;
+
CNodeState(CAddress addrIn, std::string addrNameIn) : address(addrIn), name(addrNameIn) {
fCurrentlyConnected = false;
nMisbehavior = 0;
@@ -220,6 +258,8 @@ struct CNodeState {
fHaveWitness = false;
fWantsCmpctWitness = false;
fSupportsDesiredCmpctVersion = false;
+ m_chain_sync = { 0, nullptr, false, false };
+ m_last_block_announcement = 0;
}
};
@@ -244,7 +284,7 @@ void UpdatePreferredDownload(CNode* node, CNodeState* state)
nPreferredDownload += state->fPreferredDownload;
}
-void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime)
+void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
{
ServiceFlags nLocalNodeServices = pnode->GetLocalServices();
uint64_t nonce = pnode->GetLocalNonce();
@@ -255,7 +295,7 @@ void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime)
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,
+ connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
nonce, strSubVersion, nNodeStartingHeight, ::fRelayTxes));
if (fLogIPs) {
@@ -265,49 +305,6 @@ void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime)
}
}
-void InitializeNode(CNode *pnode, CConnman& connman) {
- CAddress addr = pnode->addr;
- std::string addrName = pnode->GetAddrName();
- 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;
- }
-
- for (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);
- }
- LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid);
-}
-
// 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
@@ -315,6 +312,7 @@ bool MarkBlockAsReceived(const uint256& hash) {
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
if (itInFlight != mapBlocksInFlight.end()) {
CNodeState *state = State(itInFlight->second.first);
+ assert(state != nullptr);
state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) {
// Last validated block on the queue was received.
@@ -402,7 +400,7 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
}
}
-void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connman) {
+void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) {
AssertLockHeld(cs_main);
CNodeState* nodestate = State(nodeid);
if (!nodestate || !nodestate->fSupportsDesiredCmpctVersion) {
@@ -417,26 +415,33 @@ void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connman) {
return;
}
}
- connman.ForNode(nodeid, [&connman](CNode* pfrom){
- bool fAnnounceUsingCMPCTBLOCK = false;
+ connman->ForNode(nodeid, [connman](CNode* pfrom){
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));
+ connman->ForNode(lNodesAnnouncingHeaderAndIDs.front(), [connman, nCMPCTBLOCKVersion](CNode* pnodeStop){
+ connman->PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
return true;
});
lNodesAnnouncingHeaderAndIDs.pop_front();
}
- fAnnounceUsingCMPCTBLOCK = true;
- connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion));
lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
return true;
});
}
}
+bool TipMayBeStale(const Consensus::Params &consensusParams)
+{
+ AssertLockHeld(cs_main);
+ if (g_last_tip_update == 0) {
+ g_last_tip_update = GetTime();
+ }
+ return g_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty();
+}
+
// Requires cs_main
bool CanDirectFetch(const Consensus::Params &consensusParams)
{
@@ -466,7 +471,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con
// Make sure pindexBestKnownBlock is up to date, we'll need it.
ProcessBlockAvailability(nodeid);
- if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < UintToArith256(consensusParams.nMinimumChainWork)) {
+ if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
// This peer has nothing interesting.
return;
}
@@ -543,6 +548,69 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con
} // namespace
+// This function is used for testing the stale tip eviction logic, see
+// DoS_tests.cpp
+void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
+{
+ LOCK(cs_main);
+ CNodeState *state = State(node);
+ if (state) state->m_last_block_announcement = time_in_seconds;
+}
+
+// Returns true for outbound peers, excluding manual connections, feelers, and
+// one-shots
+bool IsOutboundDisconnectionCandidate(const CNode *node)
+{
+ return !(node->fInbound || node->m_manual_connection || node->fFeeler || node->fOneShot);
+}
+
+void PeerLogicValidation::InitializeNode(CNode *pnode) {
+ CAddress addr = pnode->addr;
+ std::string addrName = pnode->GetAddrName();
+ 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 PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
+ fUpdateConnectionTime = false;
+ LOCK(cs_main);
+ CNodeState *state = State(nodeid);
+ assert(state != nullptr);
+
+ if (state->fSyncStarted)
+ nSyncStarted--;
+
+ if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
+ fUpdateConnectionTime = true;
+ }
+
+ for (const QueuedBlock& entry : state->vBlocksInFlight) {
+ mapBlocksInFlight.erase(entry.hash);
+ }
+ EraseOrphansFor(nodeid);
+ nPreferredDownload -= state->fPreferredDownload;
+ nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
+ assert(nPeersWithValidatedDownloads >= 0);
+ g_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
+ assert(g_outbound_peers_with_protect_from_disconnect >= 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);
+ assert(g_outbound_peers_with_protect_from_disconnect == 0);
+ }
+ LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid);
+}
+
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
LOCK(cs_main);
CNodeState *state = State(nodeid);
@@ -558,28 +626,12 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
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
//
-void AddToCompactExtraTransactions(const CTransactionRef& tx)
+void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN);
if (max_extra_txn <= 0)
@@ -725,9 +777,30 @@ void Misbehaving(NodeId pnode, int howmuch)
// blockchain -> download logic notification
//
-PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) {
+// To prevent fingerprinting attacks, only send blocks/headers outside of the
+// active chain if they are no more than a month older (both in time, and in
+// best equivalent proof of work) than the best header chain we know about and
+// we fully-validated them at some point.
+static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams)
+{
+ AssertLockHeld(cs_main);
+ if (chainActive.Contains(pindex)) return true;
+ return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) &&
+ (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) &&
+ (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
+}
+
+PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler) : connman(connmanIn), m_stale_tip_check_time(0) {
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
+
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+ // Stale tip checking and peer eviction are on two different timers, but we
+ // don't want them to get out of sync due to drift in the scheduler, so we
+ // combine them in one function and schedule at the quicker (peer-eviction)
+ // timer.
+ static_assert(EXTRA_PEER_CHECK_INTERVAL < STALE_CHECK_INTERVAL, "peer eviction timer should be less than stale tip check timer");
+ scheduler.scheduleEvery(std::bind(&PeerLogicValidation::CheckForStaleTipAndEvictPeers, this, consensusParams), EXTRA_PEER_CHECK_INTERVAL * 1000);
}
void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted) {
@@ -758,6 +831,8 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb
}
LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
}
+
+ g_last_tip_update = GetTime();
}
// All of the following cache a recent block, and are protected by cs_most_recent_block
@@ -865,7 +940,7 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
!IsInitialBlockDownload() &&
mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) {
if (it != mapBlockSource.end()) {
- MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, *connman);
+ MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, connman);
}
}
if (it != mapBlockSource.end())
@@ -910,16 +985,16 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
return true;
}
-static void RelayTransaction(const CTransaction& tx, CConnman& connman)
+static void RelayTransaction(const CTransaction& tx, CConnman* connman)
{
CInv inv(MSG_TX, tx.GetHash());
- connman.ForEachNode([&inv](CNode* pnode)
+ connman->ForEachNode([&inv](CNode* pnode)
{
pnode->PushInventory(inv);
});
}
-static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connman)
+static void RelayAddress(const CAddress& addr, bool fReachable, CConnman* connman)
{
unsigned int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)
@@ -927,7 +1002,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connma
// 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));
+ 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}}};
@@ -952,10 +1027,10 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connma
}
};
- connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
+ connman->ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
}
-void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman& connman, const std::atomic<bool>& interruptMsgProc)
+void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman* connman, const std::atomic<bool>& interruptMsgProc)
{
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
std::vector<CInv> vNotFound;
@@ -999,25 +1074,14 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
CValidationState dummy;
ActivateBestChain(dummy, Params(), a_recent_block);
}
- 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 != nullptr) &&
- (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());
- }
+ send = BlockRequestAllowed(mi->second, consensusParams);
+ if (!send) {
+ LogPrint(BCLog::NET, "%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 != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
+ if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
{
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
@@ -1025,6 +1089,16 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
pfrom->fDisconnect = true;
send = false;
}
+ // Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
+ if (send && !pfrom->fWhitelisted && (
+ (((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (chainActive.Tip()->nHeight - mi->second->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
+ )) {
+ LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom->GetId());
+
+ //disconnect node and prevent it from stalling (would otherwise wait for the missing block)
+ 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))
@@ -1040,9 +1114,9 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
pblock = pblockRead;
}
if (inv.type == MSG_BLOCK)
- connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
+ connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
else if (inv.type == MSG_WITNESS_BLOCK)
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
else if (inv.type == MSG_FILTERED_BLOCK)
{
bool sendMerkleBlock = false;
@@ -1055,7 +1129,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
}
}
if (sendMerkleBlock) {
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
+ 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 -
@@ -1064,7 +1138,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// however we MUST always provide at least what the remote peer needs
typedef std::pair<unsigned int, uint256> PairType;
for (PairType& pair : merkleBlock.vMatchedTxn)
- connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
+ connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
}
// else
// no response
@@ -1079,13 +1153,13 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
if (CanDirectFetch(consensusParams) && mi->second->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) {
if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == mi->second->GetBlockHash()) {
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
+ connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
} else {
CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness);
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
}
} else {
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
+ connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
}
}
@@ -1097,7 +1171,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// wait for other stuff first.
std::vector<CInv> vInv;
vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash()));
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv));
pfrom->hashContinue.SetNull();
}
}
@@ -1109,14 +1183,14 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
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));
+ 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));
+ connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx));
push = true;
}
}
@@ -1143,7 +1217,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// 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));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
}
}
@@ -1155,7 +1229,7 @@ uint32_t GetFetchFlags(CNode* pfrom) {
return nFetchFlags;
}
-inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode* pfrom, CConnman& connman) {
+inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode* pfrom, CConnman* connman) {
BlockTransactions resp(req);
for (size_t i = 0; i < req.indexes.size(); i++) {
if (req.indexes[i] >= block.vtx.size()) {
@@ -1169,10 +1243,229 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
LOCK(cs_main);
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
+ connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
+}
+
+bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool punish_duplicate_invalid)
+{
+ const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ size_t nCount = headers.size();
+
+ if (nCount == 0) {
+ // Nothing interesting. Stop asking this peers for more headers.
+ return true;
+ }
+
+ bool received_new_header = false;
+ const CBlockIndex *pindexLast = nullptr;
+ {
+ 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(BCLog::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->GetId(), 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();
+ }
+
+ // If we don't have the last header, then they'll have given us
+ // something new (if these headers are valid).
+ if (mapBlockIndex.find(hashLastBlock) == mapBlockIndex.end()) {
+ received_new_header = true;
+ }
+ }
+
+ CValidationState state;
+ CBlockHeader first_invalid_header;
+ if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) {
+ int nDoS;
+ if (state.IsInvalid(nDoS)) {
+ LOCK(cs_main);
+ if (nDoS > 0) {
+ Misbehaving(pfrom->GetId(), nDoS);
+ }
+ if (punish_duplicate_invalid && mapBlockIndex.find(first_invalid_header.GetHash()) != mapBlockIndex.end()) {
+ // Goal: don't allow outbound peers to use up our outbound
+ // connection slots if they are on incompatible chains.
+ //
+ // We ask the caller to set punish_invalid appropriately based
+ // on the peer and the method of header delivery (compact
+ // blocks are allowed to be invalid in some circumstances,
+ // under BIP 152).
+ // Here, we try to detect the narrow situation that we have a
+ // valid block header (ie it was valid at the time the header
+ // was received, and hence stored in mapBlockIndex) but know the
+ // block is invalid, and that a peer has announced that same
+ // block as being on its active chain.
+ // Disconnect the peer in such a situation.
+ //
+ // Note: if the header that is invalid was not accepted to our
+ // mapBlockIndex at all, that may also be grounds for
+ // disconnecting the peer, as the chain they are on is likely
+ // to be incompatible. However, there is a circumstance where
+ // that does not hold: if the header's timestamp is more than
+ // 2 hours ahead of our current time. In that case, the header
+ // may become valid in the future, and we don't want to
+ // disconnect a peer merely for serving us one too-far-ahead
+ // block header, to prevent an attacker from splitting the
+ // network by mining a block right at the 2 hour boundary.
+ //
+ // TODO: update the DoS logic (or, rather, rewrite the
+ // DoS-interface between validation and net_processing) so that
+ // the interface is cleaner, and so that we disconnect on all the
+ // reasons that a peer's headers chain is incompatible
+ // with ours (eg block->nVersion softforks, MTP violations,
+ // etc), and not just the duplicate-invalid case.
+ pfrom->fDisconnect = true;
+ }
+ return error("invalid header received");
+ }
+ }
+
+ {
+ LOCK(cs_main);
+ CNodeState *nodestate = State(pfrom->GetId());
+ if (nodestate->nUnconnectingHeaders > 0) {
+ LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders);
+ }
+ nodestate->nUnconnectingHeaders = 0;
+
+ assert(pindexLast);
+ UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
+
+ // From here, pindexBestKnownBlock should be guaranteed to be non-null,
+ // because it is set in UpdateBlockAvailability. Some nullptr checks
+ // are still present, however, as belt-and-suspenders.
+
+ if (received_new_header && pindexLast->nChainWork > chainActive.Tip()->nChainWork) {
+ nodestate->m_last_block_announcement = GetTime();
+ }
+
+ 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(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), 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) {
+ std::vector<const CBlockIndex*> vToFetch;
+ const 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(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
+ pindexLast->GetBlockHash().ToString(),
+ pindexLast->nHeight);
+ } else {
+ std::vector<CInv> vGetData;
+ // Download as much as possible, from earliest to latest.
+ for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) {
+ if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ // Can't download any more from this peer
+ break;
+ }
+ uint32_t nFetchFlags = GetFetchFlags(pfrom);
+ vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
+ MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), pindex);
+ LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
+ pindex->GetBlockHash().ToString(), pfrom->GetId());
+ }
+ if (vGetData.size() > 1) {
+ LogPrint(BCLog::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)) {
+ // 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));
+ }
+ }
+ }
+ // If we're in IBD, we want outbound peers that will serve us a useful
+ // chain. Disconnect peers that are on chains with insufficient work.
+ if (IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
+ // When nCount < MAX_HEADERS_RESULTS, we know we have no more
+ // headers to fetch from this peer.
+ if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
+ // This peer has too little work on their headers chain to help
+ // us sync -- disconnect if using an outbound slot (unless
+ // whitelisted or addnode).
+ // Note: We compare their tip to nMinimumChainWork (rather than
+ // chainActive.Tip()) because we won't start block download
+ // until we have a headers chain that has at least
+ // nMinimumChainWork, even if a peer has a chain past our tip,
+ // as an anti-DoS measure.
+ if (IsOutboundDisconnectionCandidate(pfrom)) {
+ LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
+ }
+ }
+ }
+
+ if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr) {
+ // If this is an outbound peer, check to see if we should protect
+ // it from the bad/lagging chain logic.
+ if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
+ LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom->GetId());
+ nodestate->m_chain_sync.m_protect = true;
+ ++g_outbound_peers_with_protect_from_disconnect;
+ }
+ }
+ }
+
+ return true;
}
-bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman& connman, const std::atomic<bool>& interruptMsgProc)
+bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc)
{
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0)
@@ -1225,7 +1518,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// 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, std::string("Duplicate version message")));
+ connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message")));
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 1);
return false;
@@ -1249,13 +1542,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
nServices = ServiceFlags(nServiceInt);
if (!pfrom->fInbound)
{
- connman.SetServices(pfrom->addr, nServices);
+ connman->SetServices(pfrom->addr, nServices);
}
- if (pfrom->nServicesExpected & ~nServices)
+ if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices))
{
- LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, pfrom->nServicesExpected);
- connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
- strprintf("Expected to offer services %08x", pfrom->nServicesExpected)));
+ LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices));
+ connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
+ strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices))));
pfrom->fDisconnect = true;
return false;
}
@@ -1274,8 +1567,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (nVersion < MIN_PEER_PROTO_VERSION)
{
// disconnect from peers older than this proto version
- LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion);
- connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
+ LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), 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;
@@ -1295,7 +1588,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!vRecv.empty())
vRecv >> fRelay;
// Disconnect if we connected to ourself
- if (pfrom->fInbound && !connman.CheckIncomingNonce(nNonce))
+ if (pfrom->fInbound && !connman->CheckIncomingNonce(nNonce))
{
LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString());
pfrom->fDisconnect = true;
@@ -1311,7 +1604,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (pfrom->fInbound)
PushNodeVersion(pfrom, connman, GetAdjustedTime());
- connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
+ connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
pfrom->nServices = nServices;
pfrom->SetAddrLocal(addrMe);
@@ -1362,19 +1655,19 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
// Get recent addresses
- if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman.GetAddressCount() < 1000)
+ if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman->GetAddressCount() < 1000)
{
- connman.PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
+ connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
pfrom->fGetAddr = true;
}
- connman.MarkAddressGood(pfrom->addr);
+ connman->MarkAddressGood(pfrom->addr);
}
std::string remoteAddr;
if (fLogIPs)
remoteAddr = ", peeraddr=" + pfrom->addr.ToString();
- LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
+ LogPrint(BCLog::NET, "receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
cleanSubVer, pfrom->nVersion,
pfrom->nStartingHeight, addrMe.ToString(), pfrom->GetId(),
remoteAddr);
@@ -1386,7 +1679,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// If the peer is old enough to have the old alert system, send it the final alert.
if (pfrom->nVersion <= 70012) {
CDataStream finalAlert(ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffeffff7f01ffffff7f00000000ffffff7f00ffffff7f002f555247454e543a20416c657274206b657920636f6d70726f6d697365642c2075706772616465207265717569726564004630440220653febd6410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3abd5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fecaae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION);
- connman.PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert));
+ connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert));
}
// Feeler connections exist only to verify if address is online.
@@ -1417,6 +1710,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Mark this node as currently connected, so we update its timestamp later.
LOCK(cs_main);
State(pfrom->GetId())->fCurrentlyConnected = true;
+ LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s\n",
+ pfrom->nVersion.load(), pfrom->nStartingHeight, pfrom->GetId(),
+ (fLogIPs ? strprintf(", peeraddr=%s", pfrom->addr.ToString()) : ""));
}
if (pfrom->nVersion >= SENDHEADERS_VERSION) {
@@ -1424,7 +1720,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// 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));
+ 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
@@ -1435,9 +1731,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 2;
if (pfrom->GetLocalServices() & NODE_WITNESS)
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
nCMPCTBLOCKVersion = 1;
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
}
pfrom->fSuccessfullyConnected = true;
}
@@ -1456,7 +1752,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
vRecv >> vAddr;
// Don't want addr from older versions unless seeding
- if (pfrom->nVersion < CADDR_TIME_VERSION && connman.GetAddressCount() > 1000)
+ if (pfrom->nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000)
return true;
if (vAddr.size() > 1000)
{
@@ -1474,7 +1770,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (interruptMsgProc)
return true;
- if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES)
+ // We only bother storing full nodes, though this may include
+ // things which we would not make an outbound connection to, in
+ // part because we may make feeler connections to them.
+ if (!MayHaveUsefulAddressDB(addr.nServices))
continue;
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
@@ -1490,7 +1789,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (fReachable)
vAddrOk.push_back(addr);
}
- connman.AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60);
+ connman->AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60);
if (vAddr.size() < 1000)
pfrom->fGetAddr = false;
if (pfrom->fOneShot)
@@ -1568,7 +1867,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// 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));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash));
LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->GetId());
}
}
@@ -1692,7 +1991,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
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->GetId());
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have", pfrom->GetId());
return true;
}
@@ -1742,6 +2041,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (mi == mapBlockIndex.end())
return true;
pindex = (*mi).second;
+
+ if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) {
+ LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom->GetId());
+ return true;
+ }
}
else
{
@@ -1774,7 +2078,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// will re-announce the new block via headers (or compact blocks again)
// in the SendMessages logic.
nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip();
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
}
@@ -1807,8 +2111,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
std::list<CTransactionRef> lRemovedTxn;
- if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, ptx, true, &fMissingInputs, &lRemovedTxn)) {
- mempool.check(pcoinsTip);
+ if (!AlreadyHave(inv) &&
+ AcceptToMemoryPool(mempool, state, ptx, &fMissingInputs, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
+ mempool.check(pcoinsTip.get());
RelayTransaction(tx, connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
vWorkQueue.emplace_back(inv.hash, i);
@@ -1845,7 +2150,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (setMisbehaving.count(fromPeer))
continue;
- if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, true, &fMissingInputs2, &lRemovedTxn)) {
+ if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, &fMissingInputs2, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
RelayTransaction(orphanTx, connman);
for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
@@ -1875,7 +2180,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
recentRejects->insert(orphanHash);
}
}
- mempool.check(pcoinsTip);
+ mempool.check(pcoinsTip.get());
}
}
@@ -1955,7 +2260,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->GetId(),
FormatStateMessage(state));
if (state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
+ 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);
@@ -1969,15 +2274,21 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
CBlockHeaderAndShortTxIDs cmpctblock;
vRecv >> cmpctblock;
+ bool received_new_header = false;
+
{
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()));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
return true;
}
+
+ if (mapBlockIndex.find(cmpctblock.header.GetHash()) == mapBlockIndex.end()) {
+ received_new_header = true;
+ }
}
const CBlockIndex *pindex = nullptr;
@@ -1986,10 +2297,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
int nDoS;
if (state.IsInvalid(nDoS)) {
if (nDoS > 0) {
+ LogPrintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId());
LOCK(cs_main);
Misbehaving(pfrom->GetId(), nDoS);
+ } else {
+ LogPrint(BCLog::NET, "Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId());
}
- LogPrintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId());
return true;
}
}
@@ -2004,7 +2317,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// If we end up treating this as a plain headers message, call that as well
// without cs_main.
bool fRevertToHeaderProcessing = false;
- CDataStream vHeadersMsg(SER_NETWORK, PROTOCOL_VERSION);
// Keep a CBlock for "optimistic" compactblock reconstructions (see
// below)
@@ -2017,6 +2329,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
assert(pindex);
UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash());
+ CNodeState *nodestate = State(pfrom->GetId());
+
+ // If this was a new header with more work than our tip, update the
+ // peer's last block announcement time
+ if (received_new_header && pindex->nChainWork > chainActive.Tip()->nChainWork) {
+ nodestate->m_last_block_announcement = GetTime();
+ }
+
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator blockInFlightIt = mapBlocksInFlight.find(pindex->GetBlockHash());
bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end();
@@ -2030,7 +2350,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// so we just grab the block via normal getdata
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
}
return true;
}
@@ -2039,8 +2359,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
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.
@@ -2074,7 +2392,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Duplicate txindexes, the block is now in-flight, so just request it
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
return true;
}
@@ -2091,7 +2409,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
fProcessBLOCKTXN = true;
} else {
req.blockhash = pindex->GetBlockHash();
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
}
} else {
// This block is either already in flight from a different
@@ -2117,14 +2435,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// mempool will probably be useless - request the block normally
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ 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);
- vHeadersMsg << headers;
fRevertToHeaderProcessing = true;
}
}
@@ -2133,8 +2447,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (fProcessBLOCKTXN)
return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, interruptMsgProc);
- if (fRevertToHeaderProcessing)
- return ProcessMessage(pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams, connman, interruptMsgProc);
+ if (fRevertToHeaderProcessing) {
+ // Headers received from HB compact block peers are permitted to be
+ // relayed before full validation (see BIP 152), so we don't want to disconnect
+ // the peer if the header turns out to be for an invalid block.
+ // Note that if a peer tries to build on an invalid chain, that
+ // will be detected and the peer will be banned.
+ return ProcessHeadersMessage(pfrom, connman, {cmpctblock.header}, chainparams, /*punish_duplicate_invalid=*/false);
+ }
if (fBlockReconstructed) {
// If we got here, we were able to optimistically reconstruct a
@@ -2144,7 +2464,16 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom->GetId(), false));
}
bool fNewBlock = false;
- ProcessNewBlock(chainparams, pblock, true, &fNewBlock);
+ // Setting fForceProcessing to true means that we bypass some of
+ // our anti-DoS protections in AcceptBlock, which filters
+ // unrequested blocks that might be trying to waste our resources
+ // (eg disk space). Because we only try to reconstruct blocks when
+ // we're close to caught up (via the CanDirectFetch() requirement
+ // above, combined with the behavior of not requesting blocks until
+ // we have a chain with at least nMinimumChainWork), and we ignore
+ // compact blocks with less work than our tip, it is safe to treat
+ // reconstructed compact blocks as having been requested.
+ ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
pfrom->nLastBlockTime = GetTime();
} else {
@@ -2191,7 +2520,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Might have collided, fall back to getdata now :(
std::vector<CInv> invs;
invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom), resp.blockhash));
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
} else {
// Block is either okay, or possibly we received
// READ_STATUS_CHECKBLOCK_FAILED.
@@ -2224,7 +2553,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
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);
+ // This bypasses some anti-DoS logic in AcceptBlock (eg to prevent
+ // disk-space attacks), but this should be safe due to the
+ // protections in the compact block handler -- see related comment
+ // in compact block optimistic reconstruction handling.
+ ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
pfrom->nLastBlockTime = GetTime();
} else {
@@ -2252,136 +2585,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
}
- if (nCount == 0) {
- // Nothing interesting. Stop asking this peers for more headers.
- return true;
- }
-
- const CBlockIndex *pindexLast = nullptr;
- {
- 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(BCLog::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->GetId(), 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(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), 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(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), 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) {
- std::vector<const CBlockIndex*> vToFetch;
- const 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(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
- pindexLast->GetBlockHash().ToString(),
- pindexLast->nHeight);
- } else {
- std::vector<CInv> vGetData;
- // Download as much as possible, from earliest to latest.
- for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) {
- if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
- // Can't download any more from this peer
- break;
- }
- uint32_t nFetchFlags = GetFetchFlags(pfrom);
- vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), pindex);
- LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
- pindex->GetBlockHash().ToString(), pfrom->GetId());
- }
- if (vGetData.size() > 1) {
- LogPrint(BCLog::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)) {
- // 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));
- }
- }
- }
- }
+ // Headers received via a HEADERS message should be valid, and reflect
+ // the chain the peer is on. If we receive a known-invalid header,
+ // disconnect the peer if it is using one of our outbound connection
+ // slots.
+ bool should_punish = !pfrom->fInbound && !pfrom->m_manual_connection;
+ return ProcessHeadersMessage(pfrom, connman, headers, chainparams, should_punish);
}
else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
@@ -2391,11 +2600,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->GetId());
- // 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();
+ bool forceProcessing = false;
const uint256 hash(pblock->GetHash());
{
LOCK(cs_main);
@@ -2438,7 +2643,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->fSentAddr = true;
pfrom->vAddrToSend.clear();
- std::vector<CAddress> vAddr = connman.GetAddresses();
+ std::vector<CAddress> vAddr = connman->GetAddresses();
FastRandomContext insecure_rand;
for (const CAddress &addr : vAddr)
pfrom->PushAddress(addr, insecure_rand);
@@ -2454,7 +2659,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
- if (connman.OutboundTargetReached(false) && !pfrom->fWhitelisted)
+ if (connman->OutboundTargetReached(false) && !pfrom->fWhitelisted)
{
LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
pfrom->fDisconnect = true;
@@ -2483,7 +2688,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// 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));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
}
}
@@ -2559,8 +2764,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
else
{
LOCK(pfrom->cs_filter);
- delete pfrom->pfilter;
- pfrom->pfilter = new CBloomFilter(filter);
+ pfrom->pfilter.reset(new CBloomFilter(filter));
pfrom->pfilter->UpdateEmptyFull();
pfrom->fRelayTxes = true;
}
@@ -2596,8 +2800,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
{
LOCK(pfrom->cs_filter);
if (pfrom->GetLocalServices() & NODE_BLOOM) {
- delete pfrom->pfilter;
- pfrom->pfilter = new CBloomFilter();
+ pfrom->pfilter.reset(new CBloomFilter());
}
pfrom->fRelayTxes = true;
}
@@ -2629,13 +2832,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
-static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman& connman)
+static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman)
{
AssertLockHeld(cs_main);
CNodeState &state = *State(pnode->GetId());
for (const CBlockReject& reject : state.rejects) {
- connman.PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, (std::string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock));
+ connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, (std::string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock));
}
state.rejects.clear();
@@ -2643,15 +2846,15 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman& connman)
state.fShouldBan = false;
if (pnode->fWhitelisted)
LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode->addr.ToString());
- else if (pnode->fAddnode)
- LogPrintf("Warning: not punishing addnoded peer %s!\n", pnode->addr.ToString());
+ else if (pnode->m_manual_connection)
+ LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode->addr.ToString());
else {
pnode->fDisconnect = true;
if (pnode->addr.IsLocal())
LogPrintf("Warning: not banning local peer %s!\n", pnode->addr.ToString());
else
{
- connman.Ban(pnode->addr, BanReasonNodeMisbehaving);
+ connman->Ban(pnode->addr, BanReasonNodeMisbehaving);
}
}
return true;
@@ -2659,7 +2862,7 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman& connman)
return false;
}
-bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& interruptMsgProc)
+bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc)
{
const CChainParams& chainparams = Params();
//
@@ -2693,7 +2896,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& i
// Just take one message
msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin());
pfrom->nProcessQueueSize -= msgs.front().vRecv.size() + CMessageHeader::HEADER_SIZE;
- pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman.GetReceiveFloodSize();
+ pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman->GetReceiveFloodSize();
fMoreWork = !pfrom->vProcessMsg.empty();
}
CNetMessage& msg(msgs.front());
@@ -2701,7 +2904,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& i
msg.SetVersion(pfrom->GetRecvVersion());
// 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->GetId());
+ LogPrint(BCLog::NET, "PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->GetId());
pfrom->fDisconnect = true;
return false;
}
@@ -2710,7 +2913,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& i
CMessageHeader& hdr = msg.hdr;
if (!hdr.IsValid(chainparams.MessageStart()))
{
- LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->GetId());
+ LogPrint(BCLog::NET, "PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->GetId());
return fMoreWork;
}
std::string strCommand = hdr.GetCommand();
@@ -2723,7 +2926,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& i
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__,
+ LogPrint(BCLog::NET, "%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));
@@ -2742,21 +2945,21 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& i
}
catch (const std::ios_base::failure& e)
{
- connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
+ connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::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());
+ LogPrint(BCLog::NET, "%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());
+ LogPrint(BCLog::NET, "%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());
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
}
else
{
@@ -2770,7 +2973,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& i
}
if (!fRet) {
- LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->GetId());
+ LogPrint(BCLog::NET, "%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->GetId());
}
LOCK(cs_main);
@@ -2779,6 +2982,136 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& i
return fMoreWork;
}
+void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
+{
+ AssertLockHeld(cs_main);
+
+ CNodeState &state = *State(pto->GetId());
+ const CNetMsgMaker msgMaker(pto->GetSendVersion());
+
+ if (!state.m_chain_sync.m_protect && IsOutboundDisconnectionCandidate(pto) && state.fSyncStarted) {
+ // This is an outbound peer subject to disconnection if they don't
+ // announce a block with as much work as the current tip within
+ // CHAIN_SYNC_TIMEOUT + HEADERS_RESPONSE_TIME seconds (note: if
+ // their chain has more work than ours, we should sync to it,
+ // unless it's invalid, in which case we should find that out and
+ // disconnect from them elsewhere).
+ if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork) {
+ if (state.m_chain_sync.m_timeout != 0) {
+ state.m_chain_sync.m_timeout = 0;
+ state.m_chain_sync.m_work_header = nullptr;
+ state.m_chain_sync.m_sent_getheaders = false;
+ }
+ } else if (state.m_chain_sync.m_timeout == 0 || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= state.m_chain_sync.m_work_header->nChainWork)) {
+ // Our best block known by this peer is behind our tip, and we're either noticing
+ // that for the first time, OR this peer was able to catch up to some earlier point
+ // where we checked against our tip.
+ // Either way, set a new timeout based on current tip.
+ state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT;
+ state.m_chain_sync.m_work_header = chainActive.Tip();
+ state.m_chain_sync.m_sent_getheaders = false;
+ } else if (state.m_chain_sync.m_timeout > 0 && time_in_seconds > state.m_chain_sync.m_timeout) {
+ // No evidence yet that our peer has synced to a chain with work equal to that
+ // of our tip, when we first detected it was behind. Send a single getheaders
+ // message to give the peer a chance to update us.
+ if (state.m_chain_sync.m_sent_getheaders) {
+ // They've run out of time to catch up!
+ LogPrintf("Disconnecting outbound peer %d for old chain, best known block = %s\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>");
+ pto->fDisconnect = true;
+ } else {
+ assert(state.m_chain_sync.m_work_header);
+ LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
+ state.m_chain_sync.m_sent_getheaders = true;
+ constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes
+ // Bump the timeout to allow a response, which could clear the timeout
+ // (if the response shows the peer has synced), reset the timeout (if
+ // the peer syncs to the required work but not to our tip), or result
+ // in disconnect (if we advance to the timeout and pindexBestKnownBlock
+ // has not sufficiently progressed)
+ state.m_chain_sync.m_timeout = time_in_seconds + HEADERS_RESPONSE_TIME;
+ }
+ }
+ }
+}
+
+void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
+{
+ // Check whether we have too many outbound peers
+ int extra_peers = connman->GetExtraOutboundCount();
+ if (extra_peers > 0) {
+ // If we have more outbound peers than we target, disconnect one.
+ // Pick the outbound peer that least recently announced
+ // us a new block, with ties broken by choosing the more recent
+ // connection (higher node id)
+ NodeId worst_peer = -1;
+ int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max();
+
+ LOCK(cs_main);
+
+ connman->ForEachNode([&](CNode* pnode) {
+ // Ignore non-outbound peers, or nodes marked for disconnect already
+ if (!IsOutboundDisconnectionCandidate(pnode) || pnode->fDisconnect) return;
+ CNodeState *state = State(pnode->GetId());
+ if (state == nullptr) return; // shouldn't be possible, but just in case
+ // Don't evict our protected peers
+ if (state->m_chain_sync.m_protect) return;
+ if (state->m_last_block_announcement < oldest_block_announcement || (state->m_last_block_announcement == oldest_block_announcement && pnode->GetId() > worst_peer)) {
+ worst_peer = pnode->GetId();
+ oldest_block_announcement = state->m_last_block_announcement;
+ }
+ });
+ if (worst_peer != -1) {
+ bool disconnected = connman->ForNode(worst_peer, [&](CNode *pnode) {
+ // Only disconnect a peer that has been connected to us for
+ // some reasonable fraction of our check-frequency, to give
+ // it time for new information to have arrived.
+ // Also don't disconnect any peer we're trying to download a
+ // block from.
+ CNodeState &state = *State(pnode->GetId());
+ if (time_in_seconds - pnode->nTimeConnected > MINIMUM_CONNECT_TIME && state.nBlocksInFlight == 0) {
+ LogPrint(BCLog::NET, "disconnecting extra outbound peer=%d (last block announcement received at time %d)\n", pnode->GetId(), oldest_block_announcement);
+ pnode->fDisconnect = true;
+ return true;
+ } else {
+ LogPrint(BCLog::NET, "keeping outbound peer=%d chosen for eviction (connect time: %d, blocks_in_flight: %d)\n", pnode->GetId(), pnode->nTimeConnected, state.nBlocksInFlight);
+ return false;
+ }
+ });
+ if (disconnected) {
+ // If we disconnected an extra peer, that means we successfully
+ // connected to at least one peer after the last time we
+ // detected a stale tip. Don't try any more extra peers until
+ // we next detect a stale tip, to limit the load we put on the
+ // network from these extra connections.
+ connman->SetTryNewOutboundPeer(false);
+ }
+ }
+ }
+}
+
+void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams)
+{
+ if (connman == nullptr) return;
+
+ int64_t time_in_seconds = GetTime();
+
+ EvictExtraOutboundPeers(time_in_seconds);
+
+ if (time_in_seconds > m_stale_tip_check_time) {
+ LOCK(cs_main);
+ // Check whether our tip is stale, and if so, allow using an extra
+ // outbound peer
+ if (TipMayBeStale(consensusParams)) {
+ LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - g_last_tip_update);
+ connman->SetTryNewOutboundPeer(true);
+ } else if (connman->GetTryNewOutboundPeer()) {
+ connman->SetTryNewOutboundPeer(false);
+ }
+ m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL;
+ }
+}
+
class CompareInvMempoolOrder
{
CTxMemPool *mp;
@@ -2796,7 +3129,7 @@ public:
}
};
-bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interruptMsgProc)
+bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptMsgProc)
{
const Consensus::Params& consensusParams = Params().GetConsensus();
{
@@ -2828,11 +3161,11 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
pto->nPingUsecStart = GetTimeMicros();
if (pto->nVersion > BIP0031_VERSION) {
pto->nPingNonceSent = nonce;
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, 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));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING));
}
}
@@ -2867,14 +3200,14 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
// receiver rejects addr messages larger than 1000
if (vAddr.size() >= 1000)
{
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
vAddr.clear();
}
}
}
pto->vAddrToSend.clear();
if (!vAddr.empty())
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
+ 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();
@@ -2901,7 +3234,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
if (pindexStart->pprev)
pindexStart = pindexStart->pprev;
LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), pto->nStartingHeight);
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256()));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256()));
}
}
@@ -2910,7 +3243,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
// transactions become unconfirmed and spams other nodes.
if (!fReindex && !fImporting && !IsInitialBlockDownload())
{
- GetMainSignals().Broadcast(nTimeBestReceived, &connman);
+ GetMainSignals().Broadcast(nTimeBestReceived, connman);
}
//
@@ -2994,10 +3327,10 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
LOCK(cs_most_recent_block);
if (most_recent_block_hash == pBestIndex->GetBlockHash()) {
if (state.fWantsCmpctWitness || !fWitnessesPresentInMostRecentCompactBlock)
- connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block));
+ connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block));
else {
CBlockHeaderAndShortTxIDs cmpctblock(*most_recent_block, state.fWantsCmpctWitness);
- connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
}
fGotBlockFromCache = true;
}
@@ -3007,7 +3340,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
bool ret = ReadBlockFromDisk(block, pBestIndex, consensusParams);
assert(ret);
CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness);
- connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
}
state.pindexBestHeaderSent = pBestIndex;
} else if (state.fPreferHeaders) {
@@ -3020,7 +3353,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
LogPrint(BCLog::NET, "%s: sending header %s to peer=%d\n", __func__,
vHeaders.front().GetHash().ToString(), pto->GetId());
}
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
state.pindexBestHeaderSent = pBestIndex;
} else
fRevertToInv = true;
@@ -3066,7 +3399,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
for (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));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
}
}
@@ -3112,7 +3445,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
pto->filterInventoryKnown.insert(hash);
vInv.push_back(inv);
if (vInv.size() == MAX_INV_SZ) {
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
}
}
@@ -3178,7 +3511,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
}
}
if (vInv.size() == MAX_INV_SZ) {
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
}
pto->filterInventoryKnown.insert(hash);
@@ -3186,7 +3519,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
}
}
if (!vInv.empty())
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
// Detect whether we're stalling
nNow = GetTimeMicros();
@@ -3245,6 +3578,9 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
}
}
+ // Check that outbound peers have reasonable chains
+ // GetTime() is used by this anti-DoS logic so we can test this using mocktime
+ ConsiderEviction(pto, GetTime());
//
// Message: getdata (blocks)
@@ -3281,7 +3617,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
vGetData.push_back(inv);
if (vGetData.size() >= 1000)
{
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
vGetData.clear();
}
} else {
@@ -3291,7 +3627,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
pto->mapAskFor.erase(pto->mapAskFor.begin());
}
if (!vGetData.empty())
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
//
// Message: feefilter
@@ -3308,7 +3644,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
// We always have a fee filter of at least minRelayTxFee
filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
if (filterToSend != pto->lastSentFeeFilter) {
- connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
pto->lastSentFeeFilter = filterToSend;
}
pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL);
diff --git a/src/net_processing.h b/src/net_processing.h
index f4a43980a5..3fc1e05835 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -6,8 +6,9 @@
#ifndef BITCOIN_NET_PROCESSING_H
#define BITCOIN_NET_PROCESSING_H
-#include "net.h"
-#include "validationinterface.h"
+#include <net.h>
+#include <validationinterface.h>
+#include <consensus/params.h>
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
@@ -21,23 +22,51 @@ static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100;
* Timeout = base + per_header * (expected number of headers) */
static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header
+/** Protect at least this many outbound peers from disconnection due to slow/
+ * behind headers chain.
+ */
+static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
+/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */
+static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes
+/** How frequently to check for stale tips, in seconds */
+static constexpr int64_t STALE_CHECK_INTERVAL = 10 * 60; // 10 minutes
+/** How frequently to check for extra outbound peers and disconnect, in seconds */
+static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45;
+/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */
+static constexpr int64_t MINIMUM_CONNECT_TIME = 30;
-/** 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 {
+class PeerLogicValidation : public CValidationInterface, public NetEventsInterface {
private:
- CConnman* connman;
+ CConnman* const connman;
public:
- explicit PeerLogicValidation(CConnman* connmanIn);
+ explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler);
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override;
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
void BlockChecked(const CBlock& block, const CValidationState& state) override;
void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override;
+
+
+ void InitializeNode(CNode* pnode) override;
+ void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) override;
+ /** Process protocol messages received from a given node */
+ bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override;
+ /**
+ * Send queued protocol messages to be sent to a give node.
+ *
+ * @param[in] pto The node which we are sending messages to.
+ * @param[in] interrupt Interrupt condition for processing threads
+ * @return True if there is more work to be done
+ */
+ bool SendMessages(CNode* pto, std::atomic<bool>& interrupt) override;
+
+ void ConsiderEviction(CNode *pto, int64_t time_in_seconds);
+ void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams);
+ void EvictExtraOutboundPeers(int64_t time_in_seconds);
+
+private:
+ int64_t m_stale_tip_check_time; //! Next time to check for stale tip
};
struct CNodeStateStats {
@@ -52,16 +81,4 @@ 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, const std::atomic<bool>& interrupt);
-/**
- * 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.
- * @param[in] interrupt Interrupt condition for processing threads
- * @return True if there is more work to be done
- */
-bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interrupt);
-
#endif // BITCOIN_NET_PROCESSING_H
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index b8a261c921..fa1c191559 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -3,14 +3,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifdef HAVE_CONFIG_H
-#include "config/bitcoin-config.h"
-#endif
-
-#include "netaddress.h"
-#include "hash.h"
-#include "utilstrencodings.h"
-#include "tinyformat.h"
+#include <netaddress.h>
+#include <hash.h>
+#include <utilstrencodings.h>
+#include <tinyformat.h>
static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
diff --git a/src/netaddress.h b/src/netaddress.h
index 6ca99b36b5..3b542b4748 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -6,11 +6,11 @@
#define BITCOIN_NETADDRESS_H
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "compat.h"
-#include "serialize.h"
+#include <compat.h>
+#include <serialize.h>
#include <stdint.h>
#include <string>
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 05f9f6961c..f4232fc42a 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -3,18 +3,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifdef HAVE_CONFIG_H
-#include "config/bitcoin-config.h"
-#endif
-
-#include "netbase.h"
+#include <netbase.h>
-#include "hash.h"
-#include "sync.h"
-#include "uint256.h"
-#include "random.h"
-#include "util.h"
-#include "utilstrencodings.h"
+#include <hash.h>
+#include <sync.h>
+#include <uint256.h>
+#include <random.h>
+#include <util.h>
+#include <utilstrencodings.h>
#include <atomic>
@@ -184,6 +180,48 @@ struct timeval MillisToTimeval(int64_t nTimeout)
return timeout;
}
+/** SOCKS version */
+enum SOCKSVersion: uint8_t {
+ SOCKS4 = 0x04,
+ SOCKS5 = 0x05
+};
+
+/** Values defined for METHOD in RFC1928 */
+enum SOCKS5Method: uint8_t {
+ NOAUTH = 0x00, //! No authentication required
+ GSSAPI = 0x01, //! GSSAPI
+ USER_PASS = 0x02, //! Username/password
+ NO_ACCEPTABLE = 0xff, //! No acceptable methods
+};
+
+/** Values defined for CMD in RFC1928 */
+enum SOCKS5Command: uint8_t {
+ CONNECT = 0x01,
+ BIND = 0x02,
+ UDP_ASSOCIATE = 0x03
+};
+
+/** Values defined for REP in RFC1928 */
+enum SOCKS5Reply: uint8_t {
+ SUCCEEDED = 0x00, //! Succeeded
+ GENFAILURE = 0x01, //! General failure
+ NOTALLOWED = 0x02, //! Connection not allowed by ruleset
+ NETUNREACHABLE = 0x03, //! Network unreachable
+ HOSTUNREACHABLE = 0x04, //! Network unreachable
+ CONNREFUSED = 0x05, //! Connection refused
+ TTLEXPIRED = 0x06, //! TTL expired
+ CMDUNSUPPORTED = 0x07, //! Command not supported
+ ATYPEUNSUPPORTED = 0x08, //! Address type not supported
+};
+
+/** Values defined for ATYPE in RFC1928 */
+enum SOCKS5Atyp: uint8_t {
+ IPV4 = 0x01,
+ DOMAINNAME = 0x03,
+ IPV6 = 0x04,
+};
+
+/** Status codes that can be returned by InterruptibleRecv */
enum class IntrRecvError {
OK,
Timeout,
@@ -203,7 +241,7 @@ enum class IntrRecvError {
*
* @note This function requires that hSocket is in non-blocking mode.
*/
-static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, const SOCKET& hSocket)
+static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const SOCKET& hSocket)
{
int64_t curTime = GetTimeMillis();
int64_t endTime = curTime + timeout;
@@ -211,7 +249,7 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons
// to break off in case of an interruption.
const int64_t maxWait = 1000;
while (len > 0 && curTime < endTime) {
- ssize_t ret = recv(hSocket, data, len, 0); // Optimistically try the recv first
+ ssize_t ret = recv(hSocket, (char*)data, len, 0); // Optimistically try the recv first
if (ret > 0) {
len -= ret;
data += ret;
@@ -242,66 +280,73 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons
return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout;
}
+/** Credentials for proxy authentication */
struct ProxyCredentials
{
std::string username;
std::string password;
};
-std::string Socks5ErrorString(int err)
+/** Convert SOCKS5 reply to an error message */
+std::string Socks5ErrorString(uint8_t err)
{
switch(err) {
- case 0x01: return "general failure";
- case 0x02: return "connection not allowed";
- case 0x03: return "network unreachable";
- case 0x04: return "host unreachable";
- case 0x05: return "connection refused";
- case 0x06: return "TTL expired";
- case 0x07: return "protocol error";
- case 0x08: return "address type not supported";
- default: return "unknown";
+ case SOCKS5Reply::GENFAILURE:
+ return "general failure";
+ case SOCKS5Reply::NOTALLOWED:
+ return "connection not allowed";
+ case SOCKS5Reply::NETUNREACHABLE:
+ return "network unreachable";
+ case SOCKS5Reply::HOSTUNREACHABLE:
+ return "host unreachable";
+ case SOCKS5Reply::CONNREFUSED:
+ return "connection refused";
+ case SOCKS5Reply::TTLEXPIRED:
+ return "TTL expired";
+ case SOCKS5Reply::CMDUNSUPPORTED:
+ return "protocol error";
+ case SOCKS5Reply::ATYPEUNSUPPORTED:
+ return "address type not supported";
+ default:
+ return "unknown";
}
}
/** Connect using SOCKS5 (as described in RFC1928) */
-static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, SOCKET& hSocket)
+static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, const SOCKET& hSocket)
{
IntrRecvError recvr;
LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest);
if (strDest.size() > 255) {
- CloseSocket(hSocket);
return error("Hostname too long");
}
// Accepted authentication methods
std::vector<uint8_t> vSocks5Init;
- vSocks5Init.push_back(0x05);
+ vSocks5Init.push_back(SOCKSVersion::SOCKS5);
if (auth) {
- vSocks5Init.push_back(0x02); // # METHODS
- vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED
- vSocks5Init.push_back(0x02); // X'02' USERNAME/PASSWORD (RFC1929)
+ vSocks5Init.push_back(0x02); // Number of methods
+ vSocks5Init.push_back(SOCKS5Method::NOAUTH);
+ vSocks5Init.push_back(SOCKS5Method::USER_PASS);
} else {
- vSocks5Init.push_back(0x01); // # METHODS
- vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED
+ vSocks5Init.push_back(0x01); // Number of methods
+ vSocks5Init.push_back(SOCKS5Method::NOAUTH);
}
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");
}
- char pchRet1[2];
+ uint8_t pchRet1[2];
if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
- CloseSocket(hSocket);
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port);
return false;
}
- if (pchRet1[0] != 0x05) {
- CloseSocket(hSocket);
+ if (pchRet1[0] != SOCKSVersion::SOCKS5) {
return error("Proxy failed to initialize");
}
- if (pchRet1[1] == 0x02 && auth) {
+ if (pchRet1[1] == SOCKS5Method::USER_PASS && auth) {
// Perform username/password authentication (as described in RFC1929)
std::vector<uint8_t> vAuth;
- vAuth.push_back(0x01);
+ vAuth.push_back(0x01); // Current (and only) version of user/pass subnegotiation
if (auth->username.size() > 255 || auth->password.size() > 255)
return error("Proxy username or password too long");
vAuth.push_back(auth->username.size());
@@ -310,42 +355,36 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vAuth.insert(vAuth.end(), auth->password.begin(), auth->password.end());
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");
}
LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password);
- char pchRetA[2];
+ uint8_t pchRetA[2];
if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
- CloseSocket(hSocket);
return error("Error reading proxy authentication response");
}
if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) {
- CloseSocket(hSocket);
return error("Proxy authentication unsuccessful");
}
- } else if (pchRet1[1] == 0x00) {
+ } else if (pchRet1[1] == SOCKS5Method::NOAUTH) {
// Perform no authentication
} else {
- CloseSocket(hSocket);
return error("Proxy requested wrong authentication method %02x", pchRet1[1]);
}
std::vector<uint8_t> vSocks5;
- vSocks5.push_back(0x05); // VER protocol version
- vSocks5.push_back(0x01); // CMD CONNECT
- vSocks5.push_back(0x00); // RSV Reserved
- vSocks5.push_back(0x03); // ATYP DOMAINNAME
+ vSocks5.push_back(SOCKSVersion::SOCKS5); // VER protocol version
+ vSocks5.push_back(SOCKS5Command::CONNECT); // CMD CONNECT
+ vSocks5.push_back(0x00); // RSV Reserved must be 0
+ vSocks5.push_back(SOCKS5Atyp::DOMAINNAME); // ATYP DOMAINNAME
vSocks5.push_back(strDest.size()); // Length<=255 is checked at beginning of function
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*)vSocks5.data(), vSocks5.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vSocks5.size()) {
- CloseSocket(hSocket);
return error("Error sending to proxy");
}
- char pchRet2[4];
+ uint8_t pchRet2[4];
if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
- CloseSocket(hSocket);
if (recvr == IntrRecvError::Timeout) {
/* If a timeout happens here, this effectively means we timed out while connecting
* to the remote node. This is very common for Tor, so do not print an
@@ -355,64 +394,62 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
return error("Error while reading proxy response");
}
}
- if (pchRet2[0] != 0x05) {
- CloseSocket(hSocket);
+ if (pchRet2[0] != SOCKSVersion::SOCKS5) {
return error("Proxy failed to accept request");
}
- if (pchRet2[1] != 0x00) {
+ if (pchRet2[1] != SOCKS5Reply::SUCCEEDED) {
// Failures to connect to a peer that are not proxy errors
- CloseSocket(hSocket);
LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port, Socks5ErrorString(pchRet2[1]));
return false;
}
- if (pchRet2[2] != 0x00) {
- CloseSocket(hSocket);
+ if (pchRet2[2] != 0x00) { // Reserved field must be 0
return error("Error: malformed proxy response");
}
- char pchRet3[256];
+ uint8_t pchRet3[256];
switch (pchRet2[3])
{
- case 0x01: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break;
- case 0x04: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break;
- case 0x03:
+ case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break;
+ case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break;
+ case SOCKS5Atyp::DOMAINNAME:
{
recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket);
if (recvr != IntrRecvError::OK) {
- CloseSocket(hSocket);
return error("Error reading from proxy");
}
int nRecv = pchRet3[0];
recvr = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, hSocket);
break;
}
- default: CloseSocket(hSocket); return error("Error: malformed proxy response");
+ default: return error("Error: malformed proxy response");
}
if (recvr != IntrRecvError::OK) {
- CloseSocket(hSocket);
return error("Error reading from proxy");
}
if ((recvr = InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
- CloseSocket(hSocket);
return error("Error reading from proxy");
}
LogPrint(BCLog::NET, "SOCKS5 connected %s\n", strDest);
return true;
}
-bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout)
+SOCKET CreateSocket(const CService &addrConnect)
{
- hSocketRet = INVALID_SOCKET;
-
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
- LogPrintf("Cannot connect to %s: unsupported network\n", addrConnect.ToString());
- return false;
+ LogPrintf("Cannot create socket for %s: unsupported network\n", addrConnect.ToString());
+ return INVALID_SOCKET;
}
SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
if (hSocket == INVALID_SOCKET)
- return false;
+ return INVALID_SOCKET;
+
+ if (!IsSelectableSocket(hSocket)) {
+ CloseSocket(hSocket);
+ LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
+ return INVALID_SOCKET;
+ }
#ifdef SO_NOSIGPIPE
int set = 1;
@@ -426,9 +463,23 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
// Set to non-blocking
if (!SetSocketNonBlocking(hSocket, true)) {
CloseSocket(hSocket);
- return error("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
+ LogPrintf("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
}
+ return hSocket;
+}
+bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout)
+{
+ struct sockaddr_storage sockaddr;
+ socklen_t len = sizeof(sockaddr);
+ if (hSocket == INVALID_SOCKET) {
+ LogPrintf("Cannot connect to %s: invalid socket\n", addrConnect.ToString());
+ return false;
+ }
+ if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
+ LogPrintf("Cannot connect to %s: unsupported network\n", addrConnect.ToString());
+ return false;
+ }
if (connect(hSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
{
int nErr = WSAGetLastError();
@@ -443,13 +494,11 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
if (nRet == 0)
{
LogPrint(BCLog::NET, "connection to %s timeout\n", addrConnect.ToString());
- CloseSocket(hSocket);
return false;
}
if (nRet == SOCKET_ERROR)
{
LogPrintf("select() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
- CloseSocket(hSocket);
return false;
}
socklen_t nRetSize = sizeof(nRet);
@@ -460,13 +509,11 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
#endif
{
LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
- CloseSocket(hSocket);
return false;
}
if (nRet != 0)
{
LogPrintf("connect() to %s failed after select(): %s\n", addrConnect.ToString(), NetworkErrorString(nRet));
- CloseSocket(hSocket);
return false;
}
}
@@ -477,12 +524,9 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
#endif
{
LogPrintf("connect() to %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
- CloseSocket(hSocket);
return false;
}
}
-
- hSocketRet = hSocket;
return true;
}
@@ -534,9 +578,8 @@ bool IsProxy(const CNetAddr &addr) {
return false;
}
-static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed)
+bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocket, int nTimeout, bool *outProxyConnectionFailed)
{
- SOCKET hSocket = INVALID_SOCKET;
// first connect to proxy server
if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout)) {
if (outProxyConnectionFailed)
@@ -548,57 +591,16 @@ static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDe
ProxyCredentials random_auth;
static std::atomic_int counter(0);
random_auth.username = random_auth.password = strprintf("%i", counter++);
- if (!Socks5(strDest, (unsigned short)port, &random_auth, hSocket))
+ if (!Socks5(strDest, (unsigned short)port, &random_auth, hSocket)) {
return false;
+ }
} else {
- if (!Socks5(strDest, (unsigned short)port, 0, hSocket))
+ if (!Socks5(strDest, (unsigned short)port, 0, hSocket)) {
return false;
- }
-
- hSocketRet = hSocket;
- return true;
-}
-
-bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed)
-{
- proxyType proxy;
- if (outProxyConnectionFailed)
- *outProxyConnectionFailed = false;
-
- if (GetProxy(addrDest.GetNetwork(), proxy))
- return ConnectThroughProxy(proxy, addrDest.ToStringIP(), addrDest.GetPort(), hSocketRet, nTimeout, outProxyConnectionFailed);
- else // no proxy needed (none set for target network)
- return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout);
-}
-
-bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed)
-{
- std::string strDest;
- int port = portDefault;
-
- if (outProxyConnectionFailed)
- *outProxyConnectionFailed = false;
-
- SplitHostPort(std::string(pszDest), port, strDest);
-
- proxyType proxy;
- GetNameProxy(proxy);
-
- std::vector<CService> addrResolved;
- if (Lookup(strDest.c_str(), addrResolved, port, fNameLookup && !HaveNameProxy(), 256)) {
- if (addrResolved.size() > 0) {
- addr = addrResolved[GetRand(addrResolved.size())];
- return ConnectSocket(addr, hSocketRet, nTimeout);
}
}
-
- addr = CService();
-
- if (!HaveNameProxy())
- return false;
- return ConnectThroughProxy(proxy, strDest, port, hSocketRet, nTimeout, outProxyConnectionFailed);
+ return true;
}
-
bool LookupSubNet(const char* pszName, CSubNet& ret)
{
std::string strSubnet(pszName);
diff --git a/src/netbase.h b/src/netbase.h
index 6572f0a12e..c89bd5b88f 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -6,12 +6,12 @@
#define BITCOIN_NETBASE_H
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "compat.h"
-#include "netaddress.h"
-#include "serialize.h"
+#include <compat.h>
+#include <netaddress.h>
+#include <serialize.h>
#include <stdint.h>
#include <string>
@@ -44,14 +44,16 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut);
bool IsProxy(const CNetAddr &addr);
bool SetNameProxy(const proxyType &addrProxy);
bool HaveNameProxy();
+bool GetNameProxy(proxyType &nameProxyOut);
bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup);
bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup);
bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup);
bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions);
CService LookupNumeric(const char *pszName, int portDefault = 0);
bool LookupSubNet(const char *pszName, CSubNet& subnet);
-bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = nullptr);
-bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = nullptr);
+SOCKET CreateSocket(const CService &addrConnect);
+bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocketRet, int nTimeout);
+bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed);
/** Return readable error string for a network error code */
std::string NetworkErrorString(int err);
/** Close socket and set hSocket to INVALID_SOCKET */
diff --git a/src/netmessagemaker.h b/src/netmessagemaker.h
index 79b2501c5d..ea7fccacb2 100644
--- a/src/netmessagemaker.h
+++ b/src/netmessagemaker.h
@@ -6,8 +6,8 @@
#ifndef BITCOIN_NETMESSAGEMAKER_H
#define BITCOIN_NETMESSAGEMAKER_H
-#include "net.h"
-#include "serialize.h"
+#include <net.h>
+#include <serialize.h>
class CNetMsgMaker
{
diff --git a/src/noui.cpp b/src/noui.cpp
index 4bfb75fa29..feb3464a56 100644
--- a/src/noui.cpp
+++ b/src/noui.cpp
@@ -3,10 +3,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "noui.h"
+#include <noui.h>
-#include "ui_interface.h"
-#include "util.h"
+#include <ui_interface.h>
+#include <util.h>
#include <cstdio>
#include <stdint.h>
diff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp
index a089c02284..26c44ee0cf 100644
--- a/src/policy/feerate.cpp
+++ b/src/policy/feerate.cpp
@@ -3,9 +3,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "feerate.h"
+#include <policy/feerate.h>
-#include "tinyformat.h"
+#include <tinyformat.h>
const std::string CURRENCY_UNIT = "BTC";
diff --git a/src/policy/feerate.h b/src/policy/feerate.h
index 7e519e3efa..35b1b23786 100644
--- a/src/policy/feerate.h
+++ b/src/policy/feerate.h
@@ -6,8 +6,8 @@
#ifndef BITCOIN_POLICY_FEERATE_H
#define BITCOIN_POLICY_FEERATE_H
-#include "amount.h"
-#include "serialize.h"
+#include <amount.h>
+#include <serialize.h>
#include <string>
@@ -20,10 +20,15 @@ class CFeeRate
{
private:
CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes
+
public:
/** Fee rate of 0 satoshis per kB */
CFeeRate() : nSatoshisPerK(0) { }
- explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { }
+ template<typename I>
+ CFeeRate(const I _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) {
+ // We've previously had bugs creep in from silent double->int conversion...
+ static_assert(std::is_integral<I>::value, "CFeeRate should be used without floats");
+ }
/** Constructor for a fee rate in satoshis per kB. The size in bytes must not exceed (2^63 - 1)*/
CFeeRate(const CAmount& nFeePaid, size_t nBytes);
/**
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index b9476407cf..dc88c4f91a 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -3,16 +3,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "policy/fees.h"
-#include "policy/policy.h"
+#include <policy/fees.h>
+#include <policy/policy.h>
-#include "amount.h"
-#include "clientversion.h"
-#include "primitives/transaction.h"
-#include "random.h"
-#include "streams.h"
-#include "txmempool.h"
-#include "util.h"
+#include <clientversion.h>
+#include <primitives/transaction.h>
+#include <streams.h>
+#include <txmempool.h>
+#include <util.h>
static constexpr double INF_FEERATE = 1e99;
@@ -180,6 +178,7 @@ TxConfirmStats::TxConfirmStats(const std::vector<double>& defaultBuckets,
: buckets(defaultBuckets), bucketMap(defaultBucketMap)
{
decay = _decay;
+ assert(_scale != 0 && "_scale must be non-zero");
scale = _scale;
confAvg.resize(maxPeriods);
for (unsigned int i = 0; i < maxPeriods; i++) {
@@ -418,6 +417,9 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets
throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
}
filein >> scale;
+ if (scale == 0) {
+ throw std::runtime_error("Corrupt estimates file. Scale must be non-zero");
+ }
}
filein >> avg;
@@ -503,6 +505,7 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe
}
}
if (!inBlock && (unsigned int)blocksAgo >= scale) { // Only counts as a failure if not confirmed for entire period
+ assert(scale != 0);
unsigned int periodsAgo = blocksAgo / scale;
for (size_t i = 0; i < periodsAgo && i < failAvg.size(); i++) {
failAvg[i][bucketindex]++;
@@ -543,16 +546,13 @@ CBlockPolicyEstimator::CBlockPolicyEstimator()
bucketMap[INF_FEERATE] = bucketIndex;
assert(bucketMap.size() == buckets.size());
- feeStats = new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE);
- shortStats = new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE);
- longStats = new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE);
+ feeStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE));
+ shortStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
+ longStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
}
CBlockPolicyEstimator::~CBlockPolicyEstimator()
{
- delete feeStats;
- delete shortStats;
- delete longStats;
}
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
@@ -685,16 +685,16 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr
double sufficientTxs = SUFFICIENT_FEETXS;
switch (horizon) {
case FeeEstimateHorizon::SHORT_HALFLIFE: {
- stats = shortStats;
+ stats = shortStats.get();
sufficientTxs = SUFFICIENT_TXS_SHORT;
break;
}
case FeeEstimateHorizon::MED_HALFLIFE: {
- stats = feeStats;
+ stats = feeStats.get();
break;
}
case FeeEstimateHorizon::LONG_HALFLIFE: {
- stats = longStats;
+ stats = longStats.get();
break;
}
default: {
@@ -714,7 +714,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr
if (median < 0)
return CFeeRate(0);
- return CFeeRate(median);
+ return CFeeRate(llround(median));
}
unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const
@@ -901,7 +901,7 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation
if (median < 0) return CFeeRate(0); // error condition
- return CFeeRate(median);
+ return CFeeRate(llround(median));
}
@@ -997,12 +997,9 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein)
}
// Destroy old TxConfirmStats and point to new ones that already reference buckets and bucketMap
- delete feeStats;
- delete shortStats;
- delete longStats;
- feeStats = fileFeeStats.release();
- shortStats = fileShortStats.release();
- longStats = fileLongStats.release();
+ feeStats = std::move(fileFeeStats);
+ shortStats = std::move(fileShortStats);
+ longStats = std::move(fileLongStats);
nBestSeenHeight = nFileBestSeenHeight;
historicalFirst = nFileHistoricalFirst;
@@ -1043,5 +1040,5 @@ CAmount FeeFilterRounder::round(CAmount currentMinFee)
if ((it != feeset.begin() && insecure_rand.rand32() % 3 != 0) || it == feeset.end()) {
it--;
}
- return *it;
+ return static_cast<CAmount>(*it);
}
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 6edaf28714..6528560f52 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -5,11 +5,11 @@
#ifndef BITCOIN_POLICYESTIMATOR_H
#define BITCOIN_POLICYESTIMATOR_H
-#include "amount.h"
-#include "feerate.h"
-#include "uint256.h"
-#include "random.h"
-#include "sync.h"
+#include <amount.h>
+#include <policy/feerate.h>
+#include <uint256.h>
+#include <random.h>
+#include <sync.h>
#include <map>
#include <string>
@@ -245,9 +245,9 @@ private:
std::map<uint256, TxStatsInfo> mapMemPoolTxs;
/** Classes to track historical data on transaction confirmations */
- TxConfirmStats* feeStats;
- TxConfirmStats* shortStats;
- TxConfirmStats* longStats;
+ std::unique_ptr<TxConfirmStats> feeStats;
+ std::unique_ptr<TxConfirmStats> shortStats;
+ std::unique_ptr<TxConfirmStats> longStats;
unsigned int trackedTxs;
unsigned int untrackedTxs;
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 605e3e0696..b69bed5d6f 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -5,14 +5,14 @@
// NOTE: This file is intended to be customised by the end user, and includes only local node policy logic
-#include "policy/policy.h"
+#include <policy/policy.h>
-#include "consensus/validation.h"
-#include "validation.h"
-#include "coins.h"
-#include "tinyformat.h"
-#include "util.h"
-#include "utilstrencodings.h"
+#include <consensus/validation.h>
+#include <validation.h>
+#include <coins.h>
+#include <tinyformat.h>
+#include <util.h>
+#include <utilstrencodings.h>
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
@@ -54,23 +54,6 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn));
}
- /**
- * Check transaction inputs to mitigate two
- * potential denial-of-service attacks:
- *
- * 1. scriptSigs with extra data stuffed into them,
- * not consumed by scriptPubKey (or P2SH script)
- * 2. P2SH scripts with a crazy number of expensive
- * CHECKSIG/CHECKMULTISIG operations
- *
- * Why bother? To avoid denial-of-service attacks; an attacker
- * can submit a standard HASH... OP_EQUAL transaction,
- * which will get accepted into blocks. The redemption
- * script can be anything; an attacker could use a very
- * expensive-to-check-upon-redemption script like:
- * DUP CHECKSIG DROP ... repeated 100 times... OP_1
- */
-
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled)
{
std::vector<std::vector<unsigned char> > vSolutions;
@@ -93,7 +76,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool w
else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH))
return false;
- return whichType != TX_NONSTANDARD;
+ return whichType != TX_NONSTANDARD && whichType != TX_WITNESS_UNKNOWN;
}
bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled)
@@ -160,6 +143,22 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes
return true;
}
+/**
+ * Check transaction inputs to mitigate two
+ * potential denial-of-service attacks:
+ *
+ * 1. scriptSigs with extra data stuffed into them,
+ * not consumed by scriptPubKey (or P2SH script)
+ * 2. P2SH scripts with a crazy number of expensive
+ * CHECKSIG/CHECKMULTISIG operations
+ *
+ * Why bother? To avoid denial-of-service attacks; an attacker
+ * can submit a standard HASH... OP_EQUAL transaction,
+ * which will get accepted into blocks. The redemption
+ * script can be anything; an attacker could use a very
+ * expensive-to-check-upon-redemption script like:
+ * DUP CHECKSIG DROP ... repeated 100 times... OP_1
+ */
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
{
if (tx.IsCoinBase())
diff --git a/src/policy/policy.h b/src/policy/policy.h
index c06820f84e..f3f8ebbbb4 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -6,20 +6,18 @@
#ifndef BITCOIN_POLICY_POLICY_H
#define BITCOIN_POLICY_POLICY_H
-#include "consensus/consensus.h"
-#include "feerate.h"
-#include "script/interpreter.h"
-#include "script/standard.h"
+#include <consensus/consensus.h>
+#include <policy/feerate.h>
+#include <script/interpreter.h>
+#include <script/standard.h>
#include <string>
class CCoinsViewCache;
class CTxOut;
-/** Default for -blockmaxsize, which controls the maximum size of block the mining code will create **/
-static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000;
/** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/
-static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = 3000000;
+static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000;
/** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/
static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000;
/** The maximum weight for transactions we're willing to relay/mine */
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 755ef83c9a..e9692d4b48 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.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 "policy/rbf.h"
+#include <policy/rbf.h>
bool SignalsOptInRBF(const CTransaction &tx)
{
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index 22c73f3319..1a5218e120 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_POLICY_RBF_H
#define BITCOIN_POLICY_RBF_H
-#include "txmempool.h"
+#include <txmempool.h>
static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
diff --git a/src/pow.cpp b/src/pow.cpp
index 7d87c6bbb7..cecb54ca7b 100644
--- a/src/pow.cpp
+++ b/src/pow.cpp
@@ -3,12 +3,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "pow.h"
+#include <pow.h>
-#include "arith_uint256.h"
-#include "chain.h"
-#include "primitives/block.h"
-#include "uint256.h"
+#include <arith_uint256.h>
+#include <chain.h>
+#include <primitives/block.h>
+#include <uint256.h>
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
diff --git a/src/pow.h b/src/pow.h
index e203f492a1..db2f5343b5 100644
--- a/src/pow.h
+++ b/src/pow.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_POW_H
#define BITCOIN_POW_H
-#include "consensus/params.h"
+#include <consensus/params.h>
#include <stdint.h>
diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp
index 3774ac3e4b..9e20565511 100644
--- a/src/primitives/block.cpp
+++ b/src/primitives/block.cpp
@@ -3,12 +3,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "primitives/block.h"
+#include <primitives/block.h>
-#include "hash.h"
-#include "tinyformat.h"
-#include "utilstrencodings.h"
-#include "crypto/common.h"
+#include <hash.h>
+#include <tinyformat.h>
+#include <utilstrencodings.h>
+#include <crypto/common.h>
uint256 CBlockHeader::GetHash() const
{
diff --git a/src/primitives/block.h b/src/primitives/block.h
index 292df40896..4957958c6a 100644
--- a/src/primitives/block.h
+++ b/src/primitives/block.h
@@ -6,9 +6,9 @@
#ifndef BITCOIN_PRIMITIVES_BLOCK_H
#define BITCOIN_PRIMITIVES_BLOCK_H
-#include "primitives/transaction.h"
-#include "serialize.h"
-#include "uint256.h"
+#include <primitives/transaction.h>
+#include <serialize.h>
+#include <uint256.h>
/** Nodes collect new transactions into a block, hash them into a hash tree,
* and scan through nonce values to make the block's hash satisfy proof-of-work
diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp
index 9b6a814e1f..d8230d1423 100644
--- a/src/primitives/transaction.cpp
+++ b/src/primitives/transaction.cpp
@@ -3,11 +3,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "primitives/transaction.h"
+#include <primitives/transaction.h>
-#include "hash.h"
-#include "tinyformat.h"
-#include "utilstrencodings.h"
+#include <hash.h>
+#include <tinyformat.h>
+#include <utilstrencodings.h>
std::string COutPoint::ToString() const
{
@@ -55,7 +55,7 @@ std::string CTxOut::ToString() const
}
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {}
-CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {}
+CMutableTransaction::CMutableTransaction(const CTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime) {}
uint256 CMutableTransaction::GetHash() const
{
@@ -76,9 +76,9 @@ uint256 CTransaction::GetWitnessHash() const
}
/* 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), nLockTime(tx.nLockTime), hash(ComputeHash()) {}
-CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime), hash(ComputeHash()) {}
+CTransaction::CTransaction() : vin(), vout(), nVersion(CTransaction::CURRENT_VERSION), nLockTime(0), hash() {}
+CTransaction::CTransaction(const CMutableTransaction &tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash(ComputeHash()) {}
+CTransaction::CTransaction(CMutableTransaction &&tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash(ComputeHash()) {}
CAmount CTransaction::GetValueOut() const
{
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 041034bb8b..32f45e5748 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -7,10 +7,10 @@
#define BITCOIN_PRIMITIVES_TRANSACTION_H
#include <stdint.h>
-#include "amount.h"
-#include "script/script.h"
-#include "serialize.h"
-#include "uint256.h"
+#include <amount.h>
+#include <script/script.h>
+#include <serialize.h>
+#include <uint256.h>
static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000;
@@ -278,9 +278,9 @@ public:
// actually immutable; deserialization and assignment are implemented,
// and bypass the constness. This is safe, as they update the entire
// structure, including the hash.
- const int32_t nVersion;
const std::vector<CTxIn> vin;
const std::vector<CTxOut> vout;
+ const int32_t nVersion;
const uint32_t nLockTime;
private:
@@ -361,9 +361,9 @@ public:
/** A mutable version of CTransaction. */
struct CMutableTransaction
{
- int32_t nVersion;
std::vector<CTxIn> vin;
std::vector<CTxOut> vout;
+ int32_t nVersion;
uint32_t nLockTime;
CMutableTransaction();
diff --git a/src/protocol.cpp b/src/protocol.cpp
index da87e40091..8c34c4196e 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -3,10 +3,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "protocol.h"
+#include <protocol.h>
-#include "util.h"
-#include "utilstrencodings.h"
+#include <util.h>
+#include <utilstrencodings.h>
#ifndef WIN32
# include <arpa/inet.h>
diff --git a/src/protocol.h b/src/protocol.h
index 67e01d9606..cf1d40db77 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -10,10 +10,10 @@
#ifndef BITCOIN_PROTOCOL_H
#define BITCOIN_PROTOCOL_H
-#include "netaddress.h"
-#include "serialize.h"
-#include "uint256.h"
-#include "version.h"
+#include <netaddress.h>
+#include <serialize.h>
+#include <uint256.h>
+#include <version.h>
#include <stdint.h>
#include <string>
@@ -27,16 +27,13 @@
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
- };
+ static constexpr size_t MESSAGE_START_SIZE = 4;
+ static constexpr size_t COMMAND_SIZE = 12;
+ static constexpr size_t MESSAGE_SIZE_SIZE = 4;
+ static constexpr size_t CHECKSUM_SIZE = 4;
+ static constexpr size_t MESSAGE_SIZE_OFFSET = MESSAGE_START_SIZE + COMMAND_SIZE;
+ static constexpr size_t CHECKSUM_OFFSET = MESSAGE_SIZE_OFFSET + MESSAGE_SIZE_SIZE;
+ static constexpr size_t HEADER_SIZE = MESSAGE_START_SIZE + COMMAND_SIZE + MESSAGE_SIZE_SIZE + CHECKSUM_SIZE;
typedef unsigned char MessageStartChars[MESSAGE_START_SIZE];
explicit CMessageHeader(const MessageStartChars& pchMessageStartIn);
@@ -249,9 +246,8 @@ const std::vector<std::string> &getAllNetMessageTypes();
enum ServiceFlags : uint64_t {
// Nothing
NODE_NONE = 0,
- // NODE_NETWORK means that the node is capable of serving the block chain. It is currently
- // set by all Bitcoin Core nodes, and is unset by SPV clients or other peers that just want
- // network services but don't provide them.
+ // NODE_NETWORK means that the node is capable of serving the complete block chain. It is currently
+ // set by all Bitcoin Core non pruned nodes, and is unset by SPV clients or other light clients.
NODE_NETWORK = (1 << 0),
// NODE_GETUTXO means the node is capable of responding to the getutxo protocol request.
// Bitcoin Core does not support this but a patch set called Bitcoin XT does.
@@ -267,6 +263,10 @@ enum ServiceFlags : uint64_t {
// NODE_XTHIN means the node supports Xtreme Thinblocks
// If this is turned off then the node will not service nor make xthin requests
NODE_XTHIN = (1 << 4),
+ // NODE_NETWORK_LIMITED means the same as NODE_NETWORK with the limitation of only
+ // serving the last 288 (2 day) blocks
+ // See BIP159 for details on how this is implemented.
+ NODE_NETWORK_LIMITED = (1 << 10),
// Bits 24-31 are reserved for temporary experiments. Just pick a bit that
// isn't getting used, or one not being used much, and notify the
@@ -277,6 +277,43 @@ enum ServiceFlags : uint64_t {
// BIP process.
};
+/**
+ * Gets the set of service flags which are "desirable" for a given peer.
+ *
+ * These are the flags which are required for a peer to support for them
+ * to be "interesting" to us, ie for us to wish to use one of our few
+ * outbound connection slots for or for us to wish to prioritize keeping
+ * their connection around.
+ *
+ * Relevant service flags may be peer- and state-specific in that the
+ * version of the peer may determine which flags are required (eg in the
+ * case of NODE_NETWORK_LIMITED where we seek out NODE_NETWORK peers
+ * unless they set NODE_NETWORK_LIMITED and we are out of IBD, in which
+ * case NODE_NETWORK_LIMITED suffices).
+ *
+ * Thus, generally, avoid calling with peerServices == NODE_NONE.
+ */
+static ServiceFlags GetDesirableServiceFlags(ServiceFlags services) {
+ return ServiceFlags(NODE_NETWORK | NODE_WITNESS);
+}
+
+/**
+ * A shortcut for (services & GetDesirableServiceFlags(services))
+ * == GetDesirableServiceFlags(services), ie determines whether the given
+ * set of service flags are sufficient for a peer to be "relevant".
+ */
+static inline bool HasAllDesirableServiceFlags(ServiceFlags services) {
+ return !(GetDesirableServiceFlags(services) & (~services));
+}
+
+/**
+ * Checks if a peer with the given service flags may be capable of having a
+ * robust address-storage DB. Currently an alias for checking NODE_NETWORK.
+ */
+static inline bool MayHaveUsefulAddressDB(ServiceFlags services) {
+ return services & NODE_NETWORK;
+}
+
/** A CService with information about it as peer */
class CAddress : public CService
{
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index 2da7be783f..7f5ec1e8de 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.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 "pubkey.h"
+#include <pubkey.h>
#include <secp256k1.h>
#include <secp256k1_recovery.h>
@@ -126,7 +126,6 @@ static int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1
return 0;
}
spos = pos;
- pos += slen;
/* Ignore leading zeroes in R */
while (rlen > 0 && input[rpos] == 0) {
diff --git a/src/pubkey.h b/src/pubkey.h
index 65738d8fe2..c9f3c18eb3 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -6,9 +6,9 @@
#ifndef BITCOIN_PUBKEY_H
#define BITCOIN_PUBKEY_H
-#include "hash.h"
-#include "serialize.h"
-#include "uint256.h"
+#include <hash.h>
+#include <serialize.h>
+#include <uint256.h>
#include <stdexcept>
#include <vector>
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index f295bd4689..dda8dce391 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -3,18 +3,18 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "addressbookpage.h"
-#include "ui_addressbookpage.h"
+#include <qt/addressbookpage.h>
+#include <qt/forms/ui_addressbookpage.h>
-#include "addresstablemodel.h"
-#include "bitcoingui.h"
-#include "csvmodelwriter.h"
-#include "editaddressdialog.h"
-#include "guiutil.h"
-#include "platformstyle.h"
+#include <qt/addresstablemodel.h>
+#include <qt/bitcoingui.h>
+#include <qt/csvmodelwriter.h>
+#include <qt/editaddressdialog.h>
+#include <qt/guiutil.h>
+#include <qt/platformstyle.h>
#include <QIcon>
#include <QMenu>
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index 2fa032abdc..1d16940acb 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -2,13 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "addresstablemodel.h"
+#include <qt/addresstablemodel.h>
-#include "guiutil.h"
-#include "walletmodel.h"
+#include <qt/guiutil.h>
+#include <qt/walletmodel.h>
-#include "base58.h"
-#include "wallet/wallet.h"
+#include <base58.h>
+#include <wallet/wallet.h>
#include <QFont>
@@ -82,14 +82,14 @@ public:
LOCK(wallet->cs_wallet);
for (const std::pair<CTxDestination, CAddressBookData>& item : wallet->mapAddressBook)
{
- const CBitcoinAddress& address = item.first;
- bool fMine = IsMine(*wallet, address.Get());
+ const CTxDestination& address = item.first;
+ bool fMine = IsMine(*wallet, address);
AddressTableEntry::Type addressType = translateTransactionType(
QString::fromStdString(item.second.purpose), fMine);
const std::string& strName = item.second.name;
cachedAddressTable.append(AddressTableEntry(addressType,
QString::fromStdString(strName),
- QString::fromStdString(address.ToString())));
+ QString::fromStdString(EncodeDestination(address))));
}
}
// qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
@@ -246,7 +246,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
if(role == Qt::EditRole)
{
LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */
- CTxDestination curAddress = CBitcoinAddress(rec->address.toStdString()).Get();
+ CTxDestination curAddress = DecodeDestination(rec->address.toStdString());
if(index.column() == Label)
{
// Do nothing, if old label == new label
@@ -257,7 +257,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
}
wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose);
} else if(index.column() == Address) {
- CTxDestination newAddress = CBitcoinAddress(value.toString().toStdString()).Get();
+ CTxDestination newAddress = DecodeDestination(value.toString().toStdString());
// Refuse to set invalid address, set error status and return false
if(boost::get<CNoDestination>(&newAddress))
{
@@ -358,7 +358,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
// Check for duplicate addresses
{
LOCK(wallet->cs_wallet);
- if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
+ if(wallet->mapAddressBook.count(DecodeDestination(strAddress)))
{
editStatus = DUPLICATE_ADDRESS;
return QString();
@@ -384,7 +384,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
return QString();
}
}
- strAddress = CBitcoinAddress(newKey.GetID()).ToString();
+ strAddress = EncodeDestination(newKey.GetID());
}
else
{
@@ -394,7 +394,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
// Add entry
{
LOCK(wallet->cs_wallet);
- wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel,
+ wallet->SetAddressBook(DecodeDestination(strAddress), strLabel,
(type == Send ? "send" : "receive"));
}
return QString::fromStdString(strAddress);
@@ -412,7 +412,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent
}
{
LOCK(wallet->cs_wallet);
- wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get());
+ wallet->DelAddressBook(DecodeDestination(rec->address.toStdString()));
}
return true;
}
@@ -423,8 +423,8 @@ QString AddressTableModel::labelForAddress(const QString &address) const
{
{
LOCK(wallet->cs_wallet);
- CBitcoinAddress address_parsed(address.toStdString());
- std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
+ CTxDestination destination = DecodeDestination(address.toStdString());
+ std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(destination);
if (mi != wallet->mapAddressBook.end())
{
return QString::fromStdString(mi->second.name);
diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp
index e9f5c77a5b..a720ac956b 100644
--- a/src/qt/askpassphrasedialog.cpp
+++ b/src/qt/askpassphrasedialog.cpp
@@ -3,16 +3,16 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "askpassphrasedialog.h"
-#include "ui_askpassphrasedialog.h"
+#include <qt/askpassphrasedialog.h>
+#include <qt/forms/ui_askpassphrasedialog.h>
-#include "guiconstants.h"
-#include "walletmodel.h"
+#include <qt/guiconstants.h>
+#include <qt/walletmodel.h>
-#include "support/allocators/secure.h"
+#include <support/allocators/secure.h>
#include <QKeyEvent>
#include <QMessageBox>
@@ -70,6 +70,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent) :
break;
}
textChanged();
+ connect(ui->toggleShowPasswordButton, SIGNAL(toggled(bool)), this, SLOT(toggleShowPassword(bool)));
connect(ui->passEdit1, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
connect(ui->passEdit2, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
connect(ui->passEdit3, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
@@ -234,6 +235,15 @@ bool AskPassphraseDialog::event(QEvent *event)
return QWidget::event(event);
}
+void AskPassphraseDialog::toggleShowPassword(bool show)
+{
+ ui->toggleShowPasswordButton->setDown(show);
+ const auto mode = show ? QLineEdit::Normal : QLineEdit::Password;
+ ui->passEdit1->setEchoMode(mode);
+ ui->passEdit2->setEchoMode(mode);
+ ui->passEdit3->setEchoMode(mode);
+}
+
bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event)
{
/* Detect Caps Lock.
diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h
index 34bf7ccb31..7c6acc4650 100644
--- a/src/qt/askpassphrasedialog.h
+++ b/src/qt/askpassphrasedialog.h
@@ -43,6 +43,7 @@ private:
private Q_SLOTS:
void textChanged();
void secureClearPassFields();
+ void toggleShowPassword(bool);
protected:
bool event(QEvent *event);
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index f8a99506c1..c96bdfd5d7 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -2,14 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bantablemodel.h"
+#include <qt/bantablemodel.h>
-#include "clientmodel.h"
-#include "guiconstants.h"
-#include "guiutil.h"
+#include <qt/clientmodel.h>
+#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
-#include "sync.h"
-#include "utiltime.h"
+#include <sync.h>
+#include <utiltime.h>
#include <QDebug>
#include <QList>
@@ -55,11 +55,11 @@ public:
#if QT_VERSION >= 0x040700
cachedBanlist.reserve(banMap.size());
#endif
- for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++)
+ for (const auto& entry : banMap)
{
CCombinedBan banEntry;
- banEntry.subnet = (*it).first;
- banEntry.banEntry = (*it).second;
+ banEntry.subnet = entry.first;
+ banEntry.banEntry = entry.second;
cachedBanlist.append(banEntry);
}
diff --git a/src/qt/bantablemodel.h b/src/qt/bantablemodel.h
index 062cfdc931..dc69dee20a 100644
--- a/src/qt/bantablemodel.h
+++ b/src/qt/bantablemodel.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_BANTABLEMODEL_H
#define BITCOIN_QT_BANTABLEMODEL_H
-#include "net.h"
+#include <net.h>
#include <QAbstractTableModel>
#include <QStringList>
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 3fd58a2f9a..557d7efcec 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -3,38 +3,38 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "bitcoingui.h"
-
-#include "chainparams.h"
-#include "clientmodel.h"
-#include "fs.h"
-#include "guiconstants.h"
-#include "guiutil.h"
-#include "intro.h"
-#include "networkstyle.h"
-#include "optionsmodel.h"
-#include "platformstyle.h"
-#include "splashscreen.h"
-#include "utilitydialog.h"
-#include "winshutdownmonitor.h"
+#include <qt/bitcoingui.h>
+
+#include <chainparams.h>
+#include <qt/clientmodel.h>
+#include <fs.h>
+#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
+#include <qt/intro.h>
+#include <qt/networkstyle.h>
+#include <qt/optionsmodel.h>
+#include <qt/platformstyle.h>
+#include <qt/splashscreen.h>
+#include <qt/utilitydialog.h>
+#include <qt/winshutdownmonitor.h>
#ifdef ENABLE_WALLET
-#include "paymentserver.h"
-#include "walletmodel.h"
+#include <qt/paymentserver.h>
+#include <qt/walletmodel.h>
#endif
-#include "init.h"
-#include "rpc/server.h"
-#include "scheduler.h"
-#include "ui_interface.h"
-#include "util.h"
-#include "warnings.h"
+#include <init.h>
+#include <rpc/server.h>
+#include <scheduler.h>
+#include <ui_interface.h>
+#include <util.h>
+#include <warnings.h>
#ifdef ENABLE_WALLET
-#include "wallet/wallet.h"
+#include <wallet/wallet.h>
#endif
#include <stdint.h>
@@ -261,7 +261,7 @@ private:
void startThread();
};
-#include "bitcoin.moc"
+#include <qt/bitcoin.moc>
BitcoinCore::BitcoinCore():
QObject()
diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp
index d712705c43..e8d174bf29 100644
--- a/src/qt/bitcoinaddressvalidator.cpp
+++ b/src/qt/bitcoinaddressvalidator.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bitcoinaddressvalidator.h"
+#include <qt/bitcoinaddressvalidator.h>
-#include "base58.h"
+#include <base58.h>
/* Base58 characters are:
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
@@ -67,7 +67,7 @@ QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &po
if (((ch >= '0' && ch<='9') ||
(ch >= 'a' && ch<='z') ||
(ch >= 'A' && ch<='Z')) &&
- ch != 'l' && ch != 'I' && ch != '0' && ch != 'O')
+ ch != 'I' && ch != 'O') // Characters invalid in both Base58 and Bech32
{
// Alphanumeric and not a 'forbidden' character
}
@@ -89,9 +89,9 @@ QValidator::State BitcoinAddressCheckValidator::validate(QString &input, int &po
{
Q_UNUSED(pos);
// Validate the passed Bitcoin address
- CBitcoinAddress addr(input.toStdString());
- if (addr.IsValid())
+ if (IsValidDestinationString(input.toStdString())) {
return QValidator::Acceptable;
+ }
return QValidator::Invalid;
}
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index 73eb35a54e..0b21349c54 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -2,11 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bitcoinamountfield.h"
+#include <qt/bitcoinamountfield.h>
-#include "bitcoinunits.h"
-#include "guiconstants.h"
-#include "qvaluecombobox.h"
+#include <qt/bitcoinunits.h>
+#include <qt/guiconstants.h>
+#include <qt/qvaluecombobox.h>
#include <QApplication>
#include <QAbstractSpinBox>
@@ -188,7 +188,7 @@ Q_SIGNALS:
void valueChanged();
};
-#include "bitcoinamountfield.moc"
+#include <qt/bitcoinamountfield.moc>
BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
QWidget(parent),
diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h
index 2f03a3d171..659ecb416b 100644
--- a/src/qt/bitcoinamountfield.h
+++ b/src/qt/bitcoinamountfield.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_BITCOINAMOUNTFIELD_H
#define BITCOIN_QT_BITCOINAMOUNTFIELD_H
-#include "amount.h"
+#include <amount.h>
#include <QWidget>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index be2d21daee..f925ec5359 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -2,39 +2,35 @@
// 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 "bitcoingui.h"
-
-#include "bitcoinunits.h"
-#include "clientmodel.h"
-#include "guiconstants.h"
-#include "guiutil.h"
-#include "modaloverlay.h"
-#include "networkstyle.h"
-#include "notificator.h"
-#include "openuridialog.h"
-#include "optionsdialog.h"
-#include "optionsmodel.h"
-#include "platformstyle.h"
-#include "rpcconsole.h"
-#include "utilitydialog.h"
+#include <qt/bitcoingui.h>
+
+#include <qt/bitcoinunits.h>
+#include <qt/clientmodel.h>
+#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
+#include <qt/modaloverlay.h>
+#include <qt/networkstyle.h>
+#include <qt/notificator.h>
+#include <qt/openuridialog.h>
+#include <qt/optionsdialog.h>
+#include <qt/optionsmodel.h>
+#include <qt/platformstyle.h>
+#include <qt/rpcconsole.h>
+#include <qt/utilitydialog.h>
#ifdef ENABLE_WALLET
-#include "walletframe.h"
-#include "walletmodel.h"
+#include <qt/walletframe.h>
+#include <qt/walletmodel.h>
#endif // ENABLE_WALLET
#ifdef Q_OS_MAC
-#include "macdockiconhandler.h"
+#include <qt/macdockiconhandler.h>
#endif
-#include "chainparams.h"
-#include "init.h"
-#include "ui_interface.h"
-#include "util.h"
+#include <chainparams.h>
+#include <init.h>
+#include <ui_interface.h>
+#include <util.h>
#include <iostream>
@@ -123,7 +119,11 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *
spinnerFrame(0),
platformStyle(_platformStyle)
{
- GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this);
+ QSettings settings;
+ if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) {
+ // Restore failed (perhaps missing setting), center the window
+ move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center());
+ }
QString windowTitle = tr(PACKAGE_NAME) + " - ";
#ifdef ENABLE_WALLET
@@ -261,7 +261,8 @@ BitcoinGUI::~BitcoinGUI()
// Unsubscribe from notifications from core
unsubscribeFromCoreSignals();
- GUIUtil::saveWindowGeometry("nWindow", this);
+ QSettings settings;
+ settings.setValue("MainWindowGeometry", saveGeometry());
if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
trayIcon->hide();
#ifdef Q_OS_MAC
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index aa45ea1f0a..1647975fec 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -6,10 +6,10 @@
#define BITCOIN_QT_BITCOINGUI_H
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "amount.h"
+#include <amount.h>
#include <QLabel>
#include <QMainWindow>
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index 4da414a4e3..c8614fdfe0 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bitcoinunits.h"
+#include <qt/bitcoinunits.h>
-#include "primitives/transaction.h"
+#include <primitives/transaction.h>
#include <QStringList>
diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h
index 6ef37de380..3f5a7fd32d 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_BITCOINUNITS_H
#define BITCOIN_QT_BITCOINUNITS_H
-#include "amount.h"
+#include <amount.h>
#include <QAbstractListModel>
#include <QString>
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 52ce11cefd..78174b90a1 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -2,23 +2,23 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "clientmodel.h"
-
-#include "bantablemodel.h"
-#include "guiconstants.h"
-#include "guiutil.h"
-#include "peertablemodel.h"
-
-#include "chain.h"
-#include "chainparams.h"
-#include "checkpoints.h"
-#include "clientversion.h"
-#include "validation.h"
-#include "net.h"
-#include "txmempool.h"
-#include "ui_interface.h"
-#include "util.h"
-#include "warnings.h"
+#include <qt/clientmodel.h>
+
+#include <qt/bantablemodel.h>
+#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
+#include <qt/peertablemodel.h>
+
+#include <chain.h>
+#include <chainparams.h>
+#include <checkpoints.h>
+#include <clientversion.h>
+#include <validation.h>
+#include <net.h>
+#include <txmempool.h>
+#include <ui_interface.h>
+#include <util.h>
+#include <warnings.h>
#include <stdint.h>
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 562c361799..49f4e74a9f 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -2,24 +2,24 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "coincontroldialog.h"
-#include "ui_coincontroldialog.h"
-
-#include "addresstablemodel.h"
-#include "bitcoinunits.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
-#include "platformstyle.h"
-#include "txmempool.h"
-#include "walletmodel.h"
-
-#include "wallet/coincontrol.h"
-#include "init.h"
-#include "policy/fees.h"
-#include "policy/policy.h"
-#include "validation.h" // For mempool
-#include "wallet/fees.h"
-#include "wallet/wallet.h"
+#include <qt/coincontroldialog.h>
+#include <qt/forms/ui_coincontroldialog.h>
+
+#include <qt/addresstablemodel.h>
+#include <qt/bitcoinunits.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
+#include <qt/platformstyle.h>
+#include <txmempool.h>
+#include <qt/walletmodel.h>
+
+#include <wallet/coincontrol.h>
+#include <init.h>
+#include <policy/fees.h>
+#include <policy/policy.h>
+#include <validation.h> // For mempool
+#include <wallet/fees.h>
+#include <wallet/wallet.h>
#include <QApplication>
#include <QCheckBox>
@@ -28,9 +28,7 @@
#include <QFlags>
#include <QIcon>
#include <QSettings>
-#include <QString>
#include <QTreeWidget>
-#include <QTreeWidgetItem>
QList<CAmount> CoinControlDialog::payAmounts;
CCoinControl* CoinControlDialog::coinControl = new CCoinControl();
@@ -582,7 +580,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold.");
// how many satoshis the estimated fee can vary per byte we guess wrong
- double dFeeVary = (double)nPayFee / nBytes;
+ double dFeeVary = (nBytes != 0) ? (double)nPayFee / nBytes : 0;
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
@@ -660,7 +658,7 @@ void CoinControlDialog::updateView()
QString sAddress = "";
if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress))
{
- sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString());
+ sAddress = QString::fromStdString(EncodeDestination(outputAddress));
// if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs
if (!treeMode || (!(sAddress == sWalletAddress)))
diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h
index 4949c91771..51b0a42907 100644
--- a/src/qt/coincontroldialog.h
+++ b/src/qt/coincontroldialog.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_COINCONTROLDIALOG_H
#define BITCOIN_QT_COINCONTROLDIALOG_H
-#include "amount.h"
+#include <amount.h>
#include <QAbstractButton>
#include <QAction>
diff --git a/src/qt/coincontroltreewidget.cpp b/src/qt/coincontroltreewidget.cpp
index 88510b6168..b628824793 100644
--- a/src/qt/coincontroltreewidget.cpp
+++ b/src/qt/coincontroltreewidget.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "coincontroltreewidget.h"
-#include "coincontroldialog.h"
+#include <qt/coincontroltreewidget.h>
+#include <qt/coincontroldialog.h>
CoinControlTreeWidget::CoinControlTreeWidget(QWidget *parent) :
QTreeWidget(parent)
diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp
index df59927782..702757147b 100644
--- a/src/qt/csvmodelwriter.cpp
+++ b/src/qt/csvmodelwriter.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 "csvmodelwriter.h"
+#include <qt/csvmodelwriter.h>
#include <QAbstractItemModel>
#include <QFile>
diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp
index bb03c12f2d..0437f81a7e 100644
--- a/src/qt/editaddressdialog.cpp
+++ b/src/qt/editaddressdialog.cpp
@@ -2,11 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "editaddressdialog.h"
-#include "ui_editaddressdialog.h"
+#include <qt/editaddressdialog.h>
+#include <qt/forms/ui_editaddressdialog.h>
-#include "addresstablemodel.h"
-#include "guiutil.h"
+#include <qt/addresstablemodel.h>
+#include <qt/guiutil.h>
#include <QDataWidgetMapper>
#include <QMessageBox>
diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui
index a2105ecd0a..69803989cd 100644
--- a/src/qt/forms/askpassphrasedialog.ui
+++ b/src/qt/forms/askpassphrasedialog.ui
@@ -93,6 +93,13 @@
</widget>
</item>
<item row="3" column="1">
+ <widget class="QCheckBox" name="toggleShowPasswordButton">
+ <property name="text">
+ <string>Show password</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
<widget class="QLabel" name="capsLabel">
<property name="font">
<font>
diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui
index 1ea00eb5c3..d1237ad283 100644
--- a/src/qt/forms/coincontroldialog.ui
+++ b/src/qt/forms/coincontroldialog.ui
@@ -487,7 +487,7 @@
<customwidget>
<class>CoinControlTreeWidget</class>
<extends>QTreeWidget</extends>
- <header>coincontroltreewidget.h</header>
+ <header>qt/coincontroltreewidget.h</header>
</customwidget>
</customwidgets>
<resources/>
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 093e644bdc..bba822882e 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -1424,7 +1424,7 @@
<customwidget>
<class>TrafficGraphWidget</class>
<extends>QWidget</extends>
- <header>trafficgraphwidget.h</header>
+ <header>qt/trafficgraphwidget.h</header>
<container>1</container>
<slots>
<slot>clear()</slot>
diff --git a/src/qt/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui
index c1aea36338..e968271107 100644
--- a/src/qt/forms/editaddressdialog.ui
+++ b/src/qt/forms/editaddressdialog.ui
@@ -71,7 +71,7 @@
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
- <header>qvalidatedlineedit.h</header>
+ <header>qt/qvalidatedlineedit.h</header>
</customwidget>
</customwidgets>
<resources/>
diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui
index 65a7a6c77e..fdc52dc455 100644
--- a/src/qt/forms/modaloverlay.ui
+++ b/src/qt/forms/modaloverlay.ui
@@ -367,7 +367,7 @@ QLabel { color: rgb(40,40,40); }</string>
<customwidget>
<class>ModalOverlay</class>
<extends>QWidget</extends>
- <header>modaloverlay.h</header>
+ <header>qt/modaloverlay.h</header>
<container>1</container>
</customwidget>
</customwidgets>
diff --git a/src/qt/forms/openuridialog.ui b/src/qt/forms/openuridialog.ui
index 7fce858bd2..0e1048bc07 100644
--- a/src/qt/forms/openuridialog.ui
+++ b/src/qt/forms/openuridialog.ui
@@ -77,7 +77,7 @@
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
- <header>qvalidatedlineedit.h</header>
+ <header>qt/qvalidatedlineedit.h</header>
</customwidget>
</customwidgets>
<resources/>
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 14078b9ee8..a3721991ee 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -199,10 +199,10 @@
<item>
<widget class="QCheckBox" name="allowIncoming">
<property name="toolTip">
- <string>Accept connections from outside</string>
+ <string>Accept connections from outside.</string>
</property>
<property name="text">
- <string>Allow incoming connections</string>
+ <string>Allow incomin&amp;g connections</string>
</property>
</widget>
</item>
@@ -399,7 +399,7 @@
<string>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</string>
</property>
<property name="text">
- <string>Use separate SOCKS5 proxy to reach peers via Tor hidden services:</string>
+ <string>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</string>
</property>
</widget>
</item>
@@ -507,10 +507,10 @@
<item>
<widget class="QCheckBox" name="hideTrayIcon">
<property name="toolTip">
- <string>&amp;Hide the icon from the system tray.</string>
+ <string>Hide the icon from the system tray.</string>
</property>
<property name="text">
- <string>Hide tray icon</string>
+ <string>&amp;Hide tray icon</string>
</property>
</widget>
</item>
@@ -610,7 +610,7 @@
<string>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</string>
</property>
<property name="text">
- <string>Third party transaction URLs</string>
+ <string>&amp;Third party transaction URLs</string>
</property>
<property name="buddy">
<cstring>thirdPartyTxUrls</cstring>
@@ -824,12 +824,12 @@
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
- <header>qvalidatedlineedit.h</header>
+ <header>qt/qvalidatedlineedit.h</header>
</customwidget>
<customwidget>
<class>QValueComboBox</class>
<extends>QComboBox</extends>
- <header>qvaluecombobox.h</header>
+ <header>qt/qvaluecombobox.h</header>
</customwidget>
</customwidgets>
<resources/>
diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 03fcb2fb50..58f030ebf0 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -28,23 +28,6 @@
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QGridLayout" name="gridLayout">
- <item row="7" column="2">
- <widget class="QCheckBox" name="reuseAddress">
- <property name="toolTip">
- <string>Reuse one of the previously used receiving addresses. Reusing addresses has security and privacy issues. Do not use this unless re-generating a payment request made before.</string>
- </property>
- <property name="text">
- <string>R&amp;euse an existing receiving address (not recommended)</string>
- </property>
- </widget>
- </item>
- <item row="7" column="0">
- <widget class="QLabel" name="label_4">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
<item row="6" column="0">
<widget class="QLabel" name="label_3">
<property name="toolTip">
@@ -127,7 +110,7 @@
</property>
</widget>
</item>
- <item row="8" column="2">
+ <item row="7" column="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="receiveButton">
@@ -184,7 +167,7 @@
</item>
</layout>
</item>
- <item row="8" column="0">
+ <item row="7" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string/>
@@ -316,7 +299,7 @@
<customwidget>
<class>BitcoinAmountField</class>
<extends>QLineEdit</extends>
- <header>bitcoinamountfield.h</header>
+ <header>qt/bitcoinamountfield.h</header>
<container>1</container>
</customwidget>
</customwidgets>
@@ -324,7 +307,6 @@
<tabstop>reqLabel</tabstop>
<tabstop>reqAmount</tabstop>
<tabstop>reqMessage</tabstop>
- <tabstop>reuseAddress</tabstop>
<tabstop>receiveButton</tabstop>
<tabstop>clearButton</tabstop>
<tabstop>recentRequestsView</tabstop>
diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui
index 4163f4189c..dbe966b241 100644
--- a/src/qt/forms/receiverequestdialog.ui
+++ b/src/qt/forms/receiverequestdialog.ui
@@ -127,7 +127,7 @@
<customwidget>
<class>QRImageWidget</class>
<extends>QLabel</extends>
- <header>receiverequestdialog.h</header>
+ <header>qt/receiverequestdialog.h</header>
</customwidget>
</customwidgets>
<resources/>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index 1e2f2302b9..c6fd708cdf 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -846,19 +846,13 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayoutFee13">
<item>
- <widget class="QRadioButton" name="radioCustomPerKilobyte">
+ <widget class="QLabel" name="labelCustomPerKilobyte">
<property name="toolTip">
<string>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then &quot;per kilobyte&quot; only pays 250 satoshis in fee, while &quot;total at least&quot; pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</string>
</property>
<property name="text">
<string>per kilobyte</string>
</property>
- <property name="checked">
- <bool>true</bool>
- </property>
- <attribute name="buttonGroup">
- <string notr="true">groupCustomFee</string>
- </attribute>
</widget>
</item>
<item>
@@ -1114,10 +1108,10 @@
<item>
<widget class="QCheckBox" name="optInRBF">
<property name="text">
- <string>Request Replace-By-Fee</string>
+ <string>Allow increasing fee</string>
</property>
<property name="toolTip">
- <string>Indicates that the sender may wish to replace this transaction with a new one paying higher fees (prior to being confirmed).</string>
+ <string>This allows you to increase the fee later if the transaction takes a long time to confirm. This will also cause the recommended fee to be lower. ("Replace-By-Fee", BIP 125)</string>
</property>
</widget>
</item>
@@ -1270,12 +1264,12 @@
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
- <header>qvalidatedlineedit.h</header>
+ <header>qt/qvalidatedlineedit.h</header>
</customwidget>
<customwidget>
<class>BitcoinAmountField</class>
<extends>QLineEdit</extends>
- <header>bitcoinamountfield.h</header>
+ <header>qt/bitcoinamountfield.h</header>
<container>1</container>
</customwidget>
</customwidgets>
@@ -1285,6 +1279,5 @@
<connections/>
<buttongroups>
<buttongroup name="groupFee"/>
- <buttongroup name="groupCustomFee"/>
</buttongroups>
</ui>
diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui
index df06f36833..3c699abc6a 100644
--- a/src/qt/forms/sendcoinsentry.ui
+++ b/src/qt/forms/sendcoinsentry.ui
@@ -163,7 +163,7 @@
</widget>
</item>
<item row="2" column="1">
- <layout class="QHBoxLayout" name="horizontalLayoutAmount" stretch="0,1">
+ <layout class="QHBoxLayout" name="horizontalLayoutAmount" stretch="0,1,0">
<item>
<widget class="BitcoinAmountField" name="payAmount"/>
</item>
@@ -177,6 +177,13 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QPushButton" name="useAvailableBalanceButton">
+ <property name="text">
+ <string>Use available balance</string>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
<item row="3" column="0">
@@ -1248,12 +1255,12 @@
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
- <header>qvalidatedlineedit.h</header>
+ <header>qt/qvalidatedlineedit.h</header>
</customwidget>
<customwidget>
<class>BitcoinAmountField</class>
<extends>QLineEdit</extends>
- <header>bitcoinamountfield.h</header>
+ <header>qt/bitcoinamountfield.h</header>
</customwidget>
</customwidgets>
<tabstops>
diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui
index 92f6430c51..c9ddd757c1 100644
--- a/src/qt/forms/signverifymessagedialog.ui
+++ b/src/qt/forms/signverifymessagedialog.ui
@@ -380,7 +380,7 @@
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
- <header>qvalidatedlineedit.h</header>
+ <header>qt/qvalidatedlineedit.h</header>
</customwidget>
</customwidgets>
<resources>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index f3c5daebec..eb5d3304af 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -2,21 +2,20 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "guiutil.h"
-
-#include "bitcoinaddressvalidator.h"
-#include "bitcoinunits.h"
-#include "qvalidatedlineedit.h"
-#include "walletmodel.h"
-
-#include "fs.h"
-#include "primitives/transaction.h"
-#include "init.h"
-#include "policy/policy.h"
-#include "protocol.h"
-#include "script/script.h"
-#include "script/standard.h"
-#include "util.h"
+#include <qt/guiutil.h>
+
+#include <qt/bitcoinaddressvalidator.h>
+#include <qt/bitcoinunits.h>
+#include <qt/qvalidatedlineedit.h>
+#include <qt/walletmodel.h>
+
+#include <primitives/transaction.h>
+#include <init.h>
+#include <policy/policy.h>
+#include <protocol.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <util.h>
#ifdef WIN32
#ifdef _WIN32_WINNT
@@ -31,9 +30,9 @@
#ifndef NOMINMAX
#define NOMINMAX
#endif
-#include "shellapi.h"
-#include "shlobj.h"
-#include "shlwapi.h"
+#include <shellapi.h>
+#include <shlobj.h>
+#include <shlwapi.h>
#endif
#include <boost/scoped_array.hpp>
@@ -112,8 +111,9 @@ static std::string DummyAddress(const CChainParams &params)
sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata));
for(int i=0; i<256; ++i) { // Try every trailing byte
std::string s = EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size());
- if (!CBitcoinAddress(s).IsValid())
+ if (!IsValidDestinationString(s)) {
return s;
+ }
sourcedata[sourcedata.size()-1] += 1;
}
return "";
@@ -248,7 +248,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info)
bool isDust(const QString& address, const CAmount& amount)
{
- CTxDestination dest = CBitcoinAddress(address.toStdString()).Get();
+ CTxDestination dest = DecodeDestination(address.toStdString());
CScript script = GetScriptForDestination(dest);
CTxOut txOut(amount, script);
return IsDust(txOut, ::dustRelayFee);
@@ -743,9 +743,10 @@ bool SetStartOnSystemStartup(bool fAutoStart)
else
{
char pszExePath[MAX_PATH+1];
- memset(pszExePath, 0, sizeof(pszExePath));
- if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
+ ssize_t r = readlink("/proc/self/exe", pszExePath, sizeof(pszExePath) - 1);
+ if (r == -1)
return false;
+ pszExePath[r] = '\0';
fs::create_directories(GetAutostartDir());
@@ -780,47 +781,64 @@ bool SetStartOnSystemStartup(bool fAutoStart)
LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl);
LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl)
{
- // loop through the list of startup items and try to find the bitcoin app
CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, nullptr);
+ if (listSnapshot == nullptr) {
+ return nullptr;
+ }
+
+ // loop through the list of startup items and try to find the bitcoin app
for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) {
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i);
UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
CFURLRef currentItemURL = nullptr;
#if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 10100
- if(&LSSharedFileListItemCopyResolvedURL)
- currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, nullptr);
+ if(&LSSharedFileListItemCopyResolvedURL)
+ currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, nullptr);
#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED < 10100
- else
- LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, nullptr);
+ else
+ LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, nullptr);
#endif
#else
- LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, nullptr);
+ LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, nullptr);
#endif
- if(currentItemURL && CFEqual(currentItemURL, findUrl)) {
- // found
- CFRelease(currentItemURL);
- return item;
- }
if(currentItemURL) {
+ if (CFEqual(currentItemURL, findUrl)) {
+ // found
+ CFRelease(listSnapshot);
+ CFRelease(currentItemURL);
+ return item;
+ }
CFRelease(currentItemURL);
}
}
+
+ CFRelease(listSnapshot);
return nullptr;
}
bool GetStartOnSystemStartup()
{
CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+ if (bitcoinAppUrl == nullptr) {
+ return false;
+ }
+
LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr);
LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl);
+
+ CFRelease(bitcoinAppUrl);
return !!foundItem; // return boolified object
}
bool SetStartOnSystemStartup(bool fAutoStart)
{
CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+ if (bitcoinAppUrl == nullptr) {
+ return false;
+ }
+
LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr);
LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl);
@@ -832,6 +850,8 @@ bool SetStartOnSystemStartup(bool fAutoStart)
// remove item
LSSharedFileListItemRemove(loginItems, foundItem);
}
+
+ CFRelease(bitcoinAppUrl);
return true;
}
#pragma GCC diagnostic pop
@@ -842,32 +862,6 @@ bool SetStartOnSystemStartup(bool fAutoStart) { return false; }
#endif
-void saveWindowGeometry(const QString& strSetting, QWidget *parent)
-{
- QSettings settings;
- settings.setValue(strSetting + "Pos", parent->pos());
- settings.setValue(strSetting + "Size", parent->size());
-}
-
-void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, QWidget *parent)
-{
- QSettings settings;
- QPoint pos = settings.value(strSetting + "Pos").toPoint();
- QSize size = settings.value(strSetting + "Size", defaultSize).toSize();
-
- parent->resize(size);
- parent->move(pos);
-
- if ((!pos.x() && !pos.y()) || (QApplication::desktop()->screenNumber(parent) == -1))
- {
- QRect screen = QApplication::desktop()->screenGeometry();
- QPoint defaultPos((screen.width() - defaultSize.width()) / 2,
- (screen.height() - defaultSize.height()) / 2);
- parent->resize(defaultSize);
- parent->move(defaultPos);
- }
-}
-
void setClipboard(const QString& str)
{
QApplication::clipboard()->setText(str, QClipboard::Clipboard);
@@ -989,6 +983,18 @@ QString formatNiceTimeOffset(qint64 secs)
return timeBehindText;
}
+QString formatBytes(uint64_t bytes)
+{
+ if(bytes < 1024)
+ return QString(QObject::tr("%1 B")).arg(bytes);
+ if(bytes < 1024 * 1024)
+ return QString(QObject::tr("%1 KB")).arg(bytes / 1024);
+ if(bytes < 1024 * 1024 * 1024)
+ return QString(QObject::tr("%1 MB")).arg(bytes / 1024 / 1024);
+
+ return QString(QObject::tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024);
+}
+
void ClickableLabel::mouseReleaseEvent(QMouseEvent *event)
{
Q_EMIT clicked(event->pos());
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index d6aa8c4ea6..d0ab491952 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -5,8 +5,8 @@
#ifndef BITCOIN_QT_GUIUTIL_H
#define BITCOIN_QT_GUIUTIL_H
-#include "amount.h"
-#include "fs.h"
+#include <amount.h>
+#include <fs.h>
#include <QEvent>
#include <QHeaderView>
@@ -179,11 +179,6 @@ namespace GUIUtil
bool GetStartOnSystemStartup();
bool SetStartOnSystemStartup(bool fAutoStart);
- /** Save window size and position */
- void saveWindowGeometry(const QString& strSetting, QWidget *parent);
- /** Restore window size and position */
- void restoreWindowGeometry(const QString& strSetting, const QSize &defaultSizeIn, QWidget *parent);
-
/* Convert QString to OS specific boost path through UTF-8 */
fs::path qstringToBoostPath(const QString &path);
@@ -204,6 +199,8 @@ namespace GUIUtil
QString formatNiceTimeOffset(qint64 secs);
+ QString formatBytes(uint64_t bytes);
+
class ClickableLabel : public QLabel
{
Q_OBJECT
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 0ff95d8502..7f8a8394e6 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -3,16 +3,16 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "fs.h"
-#include "intro.h"
-#include "ui_intro.h"
+#include <fs.h>
+#include <qt/intro.h>
+#include <qt/forms/ui_intro.h>
-#include "guiutil.h"
+#include <qt/guiutil.h>
-#include "util.h"
+#include <util.h>
#include <QFileDialog>
#include <QSettings>
@@ -60,7 +60,7 @@ private:
Intro *intro;
};
-#include "intro.moc"
+#include <qt/intro.moc>
FreespaceChecker::FreespaceChecker(Intro *_intro)
{
@@ -214,7 +214,10 @@ bool Intro::pickDataDirectory()
}
dataDir = intro.getDataDirectory();
try {
- TryCreateDirectories(GUIUtil::qstringToBoostPath(dataDir));
+ if (TryCreateDirectories(GUIUtil::qstringToBoostPath(dataDir))) {
+ // If a new data directory has been created, make wallets subdirectory too
+ TryCreateDirectories(GUIUtil::qstringToBoostPath(dataDir) / "wallets");
+ }
break;
} catch (const fs::filesystem_error&) {
QMessageBox::critical(0, tr(PACKAGE_NAME),
diff --git a/src/qt/macnotificationhandler.h b/src/qt/macnotificationhandler.h
index d4749b3d5f..3a005c3c46 100644
--- a/src/qt/macnotificationhandler.h
+++ b/src/qt/macnotificationhandler.h
@@ -7,20 +7,17 @@
#include <QObject>
-/** Macintosh-specific notification handler (supports UserNotificationCenter and Growl).
+/** Macintosh-specific notification handler (supports UserNotificationCenter).
*/
class MacNotificationHandler : public QObject
{
Q_OBJECT
public:
- /** shows a 10.8+ UserNotification in the UserNotificationCenter
+ /** shows a macOS 10.8+ UserNotification in the UserNotificationCenter
*/
void showNotification(const QString &title, const QString &text);
- /** executes AppleScript */
- void sendAppleScript(const QString &script);
-
/** check if OS can handle UserNotifications */
bool hasUserNotificationCenterSupport(void);
static MacNotificationHandler *instance();
diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm
index 4c96d08c8a..1b16c5f524 100644
--- a/src/qt/macnotificationhandler.mm
+++ b/src/qt/macnotificationhandler.mm
@@ -47,20 +47,6 @@ void MacNotificationHandler::showNotification(const QString &title, const QStrin
}
}
-// sendAppleScript just take a QString and executes it as apple script
-void MacNotificationHandler::sendAppleScript(const QString &script)
-{
- QByteArray utf8 = script.toUtf8();
- char* cString = (char *)utf8.constData();
- NSString *scriptApple = [[NSString alloc] initWithUTF8String:cString];
-
- NSAppleScript *as = [[NSAppleScript alloc] initWithSource:scriptApple];
- NSDictionary *err = nil;
- [as executeAndReturnError:&err];
- [as release];
- [scriptApple release];
-}
-
bool MacNotificationHandler::hasUserNotificationCenterSupport(void)
{
Class possibleClass = NSClassFromString(@"NSUserNotificationCenter");
diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp
index a83f285034..b707b0dda9 100644
--- a/src/qt/modaloverlay.cpp
+++ b/src/qt/modaloverlay.cpp
@@ -2,12 +2,12 @@
// 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 <qt/modaloverlay.h>
+#include <qt/forms/ui_modaloverlay.h>
-#include "guiutil.h"
+#include <qt/guiutil.h>
-#include "chainparams.h"
+#include <chainparams.h>
#include <QResizeEvent>
#include <QPropertyAnimation>
@@ -82,36 +82,38 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri
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;
+ if (blockProcessTime.size() >= 2) {
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++)
- {
+ 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;
+ progressDelta = blockProcessTime[0].second - sample.second;
timeDelta = blockProcessTime[0].first - sample.first;
- progressPerHour = progressDelta/(double)timeDelta*1000*3600;
- remainingMSecs = remainingProgress / progressDelta * timeDelta;
+ progressPerHour = progressDelta / (double) timeDelta * 1000 * 3600;
+ remainingMSecs = (progressDelta > 0) ? remainingProgress / progressDelta * timeDelta : -1;
break;
}
}
// show progress increase per hour
- ui->progressIncreasePerH->setText(QString::number(progressPerHour*100, 'f', 2)+"%");
+ ui->progressIncreasePerH->setText(QString::number(progressPerHour * 100, 'f', 2)+"%");
// show expected remaining time
- ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs/1000.0));
+ if(remainingMSecs >= 0) {
+ ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs / 1000.0));
+ } else {
+ ui->expectedTimeLeft->setText(QObject::tr("unknown"));
+ }
static const int MAX_SAMPLES = 5000;
- if (blockProcessTime.count() > MAX_SAMPLES)
- blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count()-MAX_SAMPLES);
+ if (blockProcessTime.count() > MAX_SAMPLES) {
+ blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count() - MAX_SAMPLES);
+ }
}
// show the last block date
diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp
index 4b81c54d36..42ae3ae566 100644
--- a/src/qt/networkstyle.cpp
+++ b/src/qt/networkstyle.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "networkstyle.h"
+#include <qt/networkstyle.h>
-#include "guiconstants.h"
+#include <qt/guiconstants.h>
#include <QApplication>
diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp
index a7a7a4ce11..f4383bf679 100644
--- a/src/qt/notificator.cpp
+++ b/src/qt/notificator.cpp
@@ -2,11 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "notificator.h"
+#include <qt/notificator.h>
#include <QApplication>
#include <QByteArray>
-#include <QIcon>
#include <QImageWriter>
#include <QMessageBox>
#include <QMetaType>
@@ -24,7 +23,7 @@
// #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
#ifdef Q_OS_MAC
#include <ApplicationServices/ApplicationServices.h>
-#include "macnotificationhandler.h"
+#include <qt/macnotificationhandler.h>
#endif
@@ -60,22 +59,6 @@ Notificator::Notificator(const QString &_programName, QSystemTrayIcon *_trayIcon
if( MacNotificationHandler::instance()->hasUserNotificationCenterSupport()) {
mode = UserNotificationCenter;
}
- else {
- // Check if Growl is installed (based on Qt's tray icon implementation)
- CFURLRef cfurl;
- OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl);
- if (status != kLSApplicationNotFoundErr) {
- CFBundleRef bundle = CFBundleCreate(0, cfurl);
- if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"), kCFCompareCaseInsensitive | kCFCompareBackwards) == kCFCompareEqualTo) {
- if (CFStringHasSuffix(CFURLGetString(cfurl), CFSTR("/Growl.app/")))
- mode = Growl13;
- else
- mode = Growl12;
- }
- CFRelease(cfurl);
- CFRelease(bundle);
- }
- }
#endif
}
@@ -241,52 +224,6 @@ void Notificator::notifySystray(Class cls, const QString &title, const QString &
// Based on Qt's tray icon implementation
#ifdef Q_OS_MAC
-void Notificator::notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon)
-{
- const QString script(
- "tell application \"%5\"\n"
- " set the allNotificationsList to {\"Notification\"}\n" // -- Make a list of all the notification types (all)
- " set the enabledNotificationsList to {\"Notification\"}\n" // -- Make a list of the notifications (enabled)
- " register as application \"%1\" all notifications allNotificationsList default notifications enabledNotificationsList\n" // -- Register our script with Growl
- " notify with name \"Notification\" title \"%2\" description \"%3\" application name \"%1\"%4\n" // -- Send a Notification
- "end tell"
- );
-
- QString notificationApp(QApplication::applicationName());
- if (notificationApp.isEmpty())
- notificationApp = "Application";
-
- QPixmap notificationIconPixmap;
- if (icon.isNull()) { // If no icon specified, set icon based on class
- QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
- switch (cls)
- {
- case Information: sicon = QStyle::SP_MessageBoxInformation; break;
- case Warning: sicon = QStyle::SP_MessageBoxWarning; break;
- case Critical: sicon = QStyle::SP_MessageBoxCritical; break;
- }
- notificationIconPixmap = QApplication::style()->standardPixmap(sicon);
- }
- else {
- QSize size = icon.actualSize(QSize(48, 48));
- notificationIconPixmap = icon.pixmap(size);
- }
-
- QString notificationIcon;
- QTemporaryFile notificationIconFile;
- if (!notificationIconPixmap.isNull() && notificationIconFile.open()) {
- QImageWriter writer(&notificationIconFile, "PNG");
- if (writer.write(notificationIconPixmap.toImage()))
- notificationIcon = QString(" image from location \"file://%1\"").arg(notificationIconFile.fileName());
- }
-
- QString quotedTitle(title), quotedText(text);
- quotedTitle.replace("\\", "\\\\").replace("\"", "\\");
- quotedText.replace("\\", "\\\\").replace("\"", "\\");
- QString growlApp(this->mode == Notificator::Growl13 ? "Growl" : "GrowlHelperApp");
- MacNotificationHandler::instance()->sendAppleScript(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon, growlApp));
-}
-
void Notificator::notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon) {
// icon is not supported by the user notification center yet. OSX will use the app icon.
MacNotificationHandler::instance()->showNotification(title, text);
@@ -310,10 +247,6 @@ void Notificator::notify(Class cls, const QString &title, const QString &text, c
case UserNotificationCenter:
notifyMacUserNotificationCenter(cls, title, text, icon);
break;
- case Growl12:
- case Growl13:
- notifyGrowl(cls, title, text, icon);
- break;
#endif
default:
if(cls == Critical)
diff --git a/src/qt/notificator.h b/src/qt/notificator.h
index f92b791d4a..ef6f5c7ec5 100644
--- a/src/qt/notificator.h
+++ b/src/qt/notificator.h
@@ -6,7 +6,7 @@
#define BITCOIN_QT_NOTIFICATOR_H
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
#include <QIcon>
@@ -58,8 +58,6 @@ private:
None, /**< Ignore informational notifications, and show a modal pop-up dialog for Critical notifications. */
Freedesktop, /**< Use DBus org.freedesktop.Notifications */
QSystemTray, /**< Use QSystemTray::showMessage */
- Growl12, /**< Use the Growl 1.2 notification system (Mac only) */
- Growl13, /**< Use the Growl 1.3 notification system (Mac only) */
UserNotificationCenter /**< Use the 10.8+ User Notification Center (Mac only) */
};
QString programName;
@@ -72,7 +70,6 @@ private:
#endif
void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout);
#ifdef Q_OS_MAC
- void notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon);
void notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon);
#endif
};
diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp
index 3ee656d470..40671922f4 100644
--- a/src/qt/openuridialog.cpp
+++ b/src/qt/openuridialog.cpp
@@ -2,11 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "openuridialog.h"
-#include "ui_openuridialog.h"
+#include <qt/openuridialog.h>
+#include <qt/forms/ui_openuridialog.h>
-#include "guiutil.h"
-#include "walletmodel.h"
+#include <qt/guiutil.h>
+#include <qt/walletmodel.h>
#include <QUrl>
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 53e2e5053c..a0f78d5ead 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -3,19 +3,19 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "optionsdialog.h"
-#include "ui_optionsdialog.h"
+#include <qt/optionsdialog.h>
+#include <qt/forms/ui_optionsdialog.h>
-#include "bitcoinunits.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
+#include <qt/bitcoinunits.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
-#include "validation.h" // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS
-#include "netbase.h"
-#include "txdb.h" // for -dbcache defaults
+#include <validation.h> // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS
+#include <netbase.h>
+#include <txdb.h> // for -dbcache defaults
#include <QDataWidgetMapper>
#include <QDir>
@@ -338,7 +338,7 @@ QValidator::State ProxyAddressValidator::validate(QString &input, int &pos) cons
{
Q_UNUSED(pos);
// Validate the proxy
- CService serv(LookupNumeric(input.toStdString().c_str(), 9050));
+ CService serv(LookupNumeric(input.toStdString().c_str(), DEFAULT_GUI_PROXY_PORT));
proxyType addrProxy = proxyType(serv, true);
if (addrProxy.IsValid())
return QValidator::Acceptable;
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index e9960a01b1..c9e4f517c7 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -3,31 +3,32 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "optionsmodel.h"
+#include <qt/optionsmodel.h>
-#include "bitcoinunits.h"
-#include "guiutil.h"
+#include <qt/bitcoinunits.h>
+#include <qt/guiutil.h>
-#include "amount.h"
-#include "init.h"
-#include "validation.h" // For DEFAULT_SCRIPTCHECK_THREADS
-#include "net.h"
-#include "netbase.h"
-#include "txdb.h" // for -dbcache defaults
-#include "intro.h"
+#include <init.h>
+#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
+#include <net.h>
+#include <netbase.h>
+#include <txdb.h> // for -dbcache defaults
+#include <qt/intro.h>
#ifdef ENABLE_WALLET
-#include "wallet/wallet.h"
-#include "wallet/walletdb.h"
+#include <wallet/wallet.h>
+#include <wallet/walletdb.h>
#endif
#include <QNetworkProxy>
#include <QSettings>
#include <QStringList>
+const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
+
OptionsModel::OptionsModel(QObject *parent, bool resetSettings) :
QAbstractListModel(parent)
{
@@ -125,7 +126,7 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("fUseProxy"))
settings.setValue("fUseProxy", false);
if (!settings.contains("addrProxy"))
- settings.setValue("addrProxy", "127.0.0.1:9050");
+ settings.setValue("addrProxy", QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST, DEFAULT_GUI_PROXY_PORT));
// Only try to set -proxy, if user has enabled fUseProxy
if (settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()))
addOverriddenOption("-proxy");
@@ -135,7 +136,7 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("fUseSeparateProxyTor"))
settings.setValue("fUseSeparateProxyTor", false);
if (!settings.contains("addrSeparateProxyTor"))
- settings.setValue("addrSeparateProxyTor", "127.0.0.1:9050");
+ settings.setValue("addrSeparateProxyTor", QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST, DEFAULT_GUI_PROXY_PORT));
// Only try to set -onion, if user has enabled fUseSeparateProxyTor
if (settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString()))
addOverriddenOption("-onion");
@@ -151,10 +152,32 @@ void OptionsModel::Init(bool resetSettings)
language = settings.value("language").toString();
}
+/** Helper function to copy contents from one QSettings to another.
+ * By using allKeys this also covers nested settings in a hierarchy.
+ */
+static void CopySettings(QSettings& dst, const QSettings& src)
+{
+ for (const QString& key : src.allKeys()) {
+ dst.setValue(key, src.value(key));
+ }
+}
+
+/** Back up a QSettings to an ini-formatted file. */
+static void BackupSettings(const fs::path& filename, const QSettings& src)
+{
+ qWarning() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename);
+ QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat);
+ dst.clear();
+ CopySettings(dst, src);
+}
+
void OptionsModel::Reset()
{
QSettings settings;
+ // Backup old settings to chain-specific datadir for troubleshooting
+ BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings);
+
// Save the strDataDir setting
QString dataDir = Intro::getDefaultDataDirectory();
dataDir = settings.value("strDataDir", dataDir).toString();
@@ -178,6 +201,33 @@ int OptionsModel::rowCount(const QModelIndex & parent) const
return OptionIDRowCount;
}
+struct ProxySetting {
+ bool is_set;
+ QString ip;
+ QString port;
+};
+
+static ProxySetting GetProxySetting(QSettings &settings, const QString &name)
+{
+ static const ProxySetting default_val = {false, DEFAULT_GUI_PROXY_HOST, QString("%1").arg(DEFAULT_GUI_PROXY_PORT)};
+ // Handle the case that the setting is not set at all
+ if (!settings.contains(name)) {
+ return default_val;
+ }
+ // contains IP at index 0 and port at index 1
+ QStringList ip_port = settings.value(name).toString().split(":", QString::SkipEmptyParts);
+ if (ip_port.size() == 2) {
+ return {true, ip_port.at(0), ip_port.at(1)};
+ } else { // Invalid: return default
+ return default_val;
+ }
+}
+
+static void SetProxySetting(QSettings &settings, const QString &name, const ProxySetting &ip_port)
+{
+ settings.setValue(name, ip_port.ip + ":" + ip_port.port);
+}
+
// read QSettings values and return them
QVariant OptionsModel::data(const QModelIndex & index, int role) const
{
@@ -204,30 +254,18 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
// default proxy
case ProxyUse:
return settings.value("fUseProxy", false);
- case ProxyIP: {
- // contains IP at index 0 and port at index 1
- QStringList strlIpPort = settings.value("addrProxy").toString().split(":", QString::SkipEmptyParts);
- return strlIpPort.at(0);
- }
- case ProxyPort: {
- // contains IP at index 0 and port at index 1
- QStringList strlIpPort = settings.value("addrProxy").toString().split(":", QString::SkipEmptyParts);
- return strlIpPort.at(1);
- }
+ case ProxyIP:
+ return GetProxySetting(settings, "addrProxy").ip;
+ case ProxyPort:
+ return GetProxySetting(settings, "addrProxy").port;
// separate Tor proxy
case ProxyUseTor:
return settings.value("fUseSeparateProxyTor", false);
- case ProxyIPTor: {
- // contains IP at index 0 and port at index 1
- QStringList strlIpPort = settings.value("addrSeparateProxyTor").toString().split(":", QString::SkipEmptyParts);
- return strlIpPort.at(0);
- }
- case ProxyPortTor: {
- // contains IP at index 0 and port at index 1
- QStringList strlIpPort = settings.value("addrSeparateProxyTor").toString().split(":", QString::SkipEmptyParts);
- return strlIpPort.at(1);
- }
+ case ProxyIPTor:
+ return GetProxySetting(settings, "addrSeparateProxyTor").ip;
+ case ProxyPortTor:
+ return GetProxySetting(settings, "addrSeparateProxyTor").port;
#ifdef ENABLE_WALLET
case SpendZeroConfChange:
@@ -292,25 +330,19 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
}
break;
case ProxyIP: {
- // contains current IP at index 0 and current port at index 1
- QStringList strlIpPort = settings.value("addrProxy").toString().split(":", QString::SkipEmptyParts);
- // if that key doesn't exist or has a changed IP
- if (!settings.contains("addrProxy") || strlIpPort.at(0) != value.toString()) {
- // construct new value from new IP and current port
- QString strNewValue = value.toString() + ":" + strlIpPort.at(1);
- settings.setValue("addrProxy", strNewValue);
+ auto ip_port = GetProxySetting(settings, "addrProxy");
+ if (!ip_port.is_set || ip_port.ip != value.toString()) {
+ ip_port.ip = value.toString();
+ SetProxySetting(settings, "addrProxy", ip_port);
setRestartRequired(true);
}
}
break;
case ProxyPort: {
- // contains current IP at index 0 and current port at index 1
- QStringList strlIpPort = settings.value("addrProxy").toString().split(":", QString::SkipEmptyParts);
- // if that key doesn't exist or has a changed port
- if (!settings.contains("addrProxy") || strlIpPort.at(1) != value.toString()) {
- // construct new value from current IP and new port
- QString strNewValue = strlIpPort.at(0) + ":" + value.toString();
- settings.setValue("addrProxy", strNewValue);
+ auto ip_port = GetProxySetting(settings, "addrProxy");
+ if (!ip_port.is_set || ip_port.port != value.toString()) {
+ ip_port.port = value.toString();
+ SetProxySetting(settings, "addrProxy", ip_port);
setRestartRequired(true);
}
}
@@ -324,25 +356,19 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
}
break;
case ProxyIPTor: {
- // contains current IP at index 0 and current port at index 1
- QStringList strlIpPort = settings.value("addrSeparateProxyTor").toString().split(":", QString::SkipEmptyParts);
- // if that key doesn't exist or has a changed IP
- if (!settings.contains("addrSeparateProxyTor") || strlIpPort.at(0) != value.toString()) {
- // construct new value from new IP and current port
- QString strNewValue = value.toString() + ":" + strlIpPort.at(1);
- settings.setValue("addrSeparateProxyTor", strNewValue);
+ auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
+ if (!ip_port.is_set || ip_port.ip != value.toString()) {
+ ip_port.ip = value.toString();
+ SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
setRestartRequired(true);
}
}
break;
case ProxyPortTor: {
- // contains current IP at index 0 and current port at index 1
- QStringList strlIpPort = settings.value("addrSeparateProxyTor").toString().split(":", QString::SkipEmptyParts);
- // if that key doesn't exist or has a changed port
- if (!settings.contains("addrSeparateProxyTor") || strlIpPort.at(1) != value.toString()) {
- // construct new value from current IP and new port
- QString strNewValue = strlIpPort.at(0) + ":" + value.toString();
- settings.setValue("addrSeparateProxyTor", strNewValue);
+ auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
+ if (!ip_port.is_set || ip_port.port != value.toString()) {
+ ip_port.port = value.toString();
+ SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
setRestartRequired(true);
}
}
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 0ac82a4148..f8782dd204 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_OPTIONSMODEL_H
#define BITCOIN_QT_OPTIONSMODEL_H
-#include "amount.h"
+#include <amount.h>
#include <QAbstractListModel>
@@ -13,6 +13,9 @@ QT_BEGIN_NAMESPACE
class QNetworkProxy;
QT_END_NAMESPACE
+extern const char *DEFAULT_GUI_PROXY_HOST;
+static constexpr unsigned short DEFAULT_GUI_PROXY_PORT = 9050;
+
/** Interface from Qt to configuration data structure for Bitcoin client.
To Qt, the options are presented as a list with the different options
laid out vertically.
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index ba1839e7b4..7818607d30 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -2,18 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "overviewpage.h"
-#include "ui_overviewpage.h"
-
-#include "bitcoinunits.h"
-#include "clientmodel.h"
-#include "guiconstants.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
-#include "platformstyle.h"
-#include "transactionfilterproxy.h"
-#include "transactiontablemodel.h"
-#include "walletmodel.h"
+#include <qt/overviewpage.h>
+#include <qt/forms/ui_overviewpage.h>
+
+#include <qt/bitcoinunits.h>
+#include <qt/clientmodel.h>
+#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
+#include <qt/platformstyle.h>
+#include <qt/transactionfilterproxy.h>
+#include <qt/transactiontablemodel.h>
+#include <qt/walletmodel.h>
#include <QAbstractItemDelegate>
#include <QPainter>
@@ -106,7 +106,7 @@ public:
const PlatformStyle *platformStyle;
};
-#include "overviewpage.moc"
+#include <qt/overviewpage.moc>
OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) :
QWidget(parent),
diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h
index d76b651ce6..1e09d907b0 100644
--- a/src/qt/overviewpage.h
+++ b/src/qt/overviewpage.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_OVERVIEWPAGE_H
#define BITCOIN_QT_OVERVIEWPAGE_H
-#include "amount.h"
+#include <amount.h>
#include <QWidget>
#include <memory>
diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp
index d3799f59ab..4503728882 100644
--- a/src/qt/paymentrequestplus.cpp
+++ b/src/qt/paymentrequestplus.cpp
@@ -7,9 +7,9 @@
// with some extra methods
//
-#include "paymentrequestplus.h"
+#include <qt/paymentrequestplus.h>
-#include "util.h"
+#include <util.h>
#include <stdexcept>
@@ -194,8 +194,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c
qWarning() << "PaymentRequestPlus::getMerchant: SSL error: " << err.what();
}
- if (website)
- delete[] website;
+ delete[] website;
X509_STORE_CTX_free(store_ctx);
for (unsigned int i = 0; i < certs.size(); i++)
X509_free(certs[i]);
diff --git a/src/qt/paymentrequestplus.h b/src/qt/paymentrequestplus.h
index a2fea3fdc6..e1388f2b5d 100644
--- a/src/qt/paymentrequestplus.h
+++ b/src/qt/paymentrequestplus.h
@@ -7,10 +7,10 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#include "paymentrequest.pb.h"
+#include <qt/paymentrequest.pb.h>
#pragma GCC diagnostic pop
-#include "base58.h"
+#include <base58.h>
#include <openssl/x509.h>
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index d4137d280f..510a3783ae 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -2,18 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "paymentserver.h"
+#include <qt/paymentserver.h>
-#include "bitcoinunits.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
+#include <qt/bitcoinunits.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
-#include "base58.h"
-#include "chainparams.h"
-#include "policy/policy.h"
-#include "ui_interface.h"
-#include "util.h"
-#include "wallet/wallet.h"
+#include <base58.h>
+#include <chainparams.h>
+#include <policy/policy.h>
+#include <ui_interface.h>
+#include <util.h>
+#include <wallet/wallet.h>
#include <cstdlib>
@@ -218,17 +218,15 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
SendCoinsRecipient r;
if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty())
{
- CBitcoinAddress address(r.address.toStdString());
auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN);
- if (address.IsValid(*tempChainParams))
- {
+ if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
SelectParams(CBaseChainParams::MAIN);
- }
- else {
+ } else {
tempChainParams = CreateChainParams(CBaseChainParams::TESTNET);
- if (address.IsValid(*tempChainParams))
+ if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
SelectParams(CBaseChainParams::TESTNET);
+ }
}
}
}
@@ -364,8 +362,7 @@ void PaymentServer::initNetManager()
{
if (!optionsModel)
return;
- if (netManager != nullptr)
- delete netManager;
+ delete netManager;
// netManager is used to fetch paymentrequests given in bitcoin: URIs
netManager = new QNetworkAccessManager(this);
@@ -441,8 +438,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
SendCoinsRecipient recipient;
if (GUIUtil::parseBitcoinURI(s, &recipient))
{
- CBitcoinAddress address(recipient.address.toStdString());
- if (!address.IsValid()) {
+ if (!IsValidDestinationString(recipient.address.toStdString())) {
Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address),
CClientUIInterface::MSG_ERROR);
}
@@ -560,7 +556,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen
CTxDestination dest;
if (ExtractDestination(sendingTo.first, dest)) {
// Append destination address
- addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString()));
+ addresses.append(QString::fromStdString(EncodeDestination(dest)));
}
else if (!recipient.authenticatedMerchant.isEmpty()) {
// Unauthenticated payment requests to custom bitcoin addresses are not supported
diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h
index 98b2364b92..9adef9743d 100644
--- a/src/qt/paymentserver.h
+++ b/src/qt/paymentserver.h
@@ -32,8 +32,8 @@
// sends them to the server.
//
-#include "paymentrequestplus.h"
-#include "walletmodel.h"
+#include <qt/paymentrequestplus.h>
+#include <qt/walletmodel.h>
#include <QObject>
#include <QString>
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 42934f8055..cfafab2c8a 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -2,14 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "peertablemodel.h"
+#include <qt/peertablemodel.h>
-#include "clientmodel.h"
-#include "guiconstants.h"
-#include "guiutil.h"
+#include <qt/clientmodel.h>
+#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
-#include "validation.h" // for cs_main
-#include "sync.h"
+#include <validation.h> // for cs_main
+#include <sync.h>
#include <QDebug>
#include <QList>
@@ -33,6 +33,10 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine
return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
case PeerTableModel::Ping:
return pLeft->dMinPing < pRight->dMinPing;
+ case PeerTableModel::Sent:
+ return pLeft->nSendBytes < pRight->nSendBytes;
+ case PeerTableModel::Received:
+ return pLeft->nRecvBytes < pRight->nRecvBytes;
}
return false;
@@ -114,7 +118,7 @@ PeerTableModel::PeerTableModel(ClientModel *parent) :
clientModel(parent),
timer(0)
{
- columns << tr("NodeId") << tr("Node/Service") << tr("User Agent") << tr("Ping");
+ columns << tr("NodeId") << tr("Node/Service") << tr("Ping") << tr("Sent") << tr("Received") << tr("User Agent");
priv.reset(new PeerTablePriv());
// default to unsorted
priv->sortColumn = -1;
@@ -173,10 +177,20 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
return QString::fromStdString(rec->nodeStats.cleanSubVer);
case Ping:
return GUIUtil::formatPingTime(rec->nodeStats.dMinPing);
+ case Sent:
+ return GUIUtil::formatBytes(rec->nodeStats.nSendBytes);
+ case Received:
+ return GUIUtil::formatBytes(rec->nodeStats.nRecvBytes);
}
} else if (role == Qt::TextAlignmentRole) {
- if (index.column() == Ping)
- return (QVariant)(Qt::AlignRight | Qt::AlignVCenter);
+ switch (index.column()) {
+ case Ping:
+ case Sent:
+ case Received:
+ return QVariant(Qt::AlignRight | Qt::AlignVCenter);
+ default:
+ return QVariant();
+ }
}
return QVariant();
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index cc47b67ec9..e41fe4bb03 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -5,8 +5,8 @@
#ifndef BITCOIN_QT_PEERTABLEMODEL_H
#define BITCOIN_QT_PEERTABLEMODEL_H
-#include "net_processing.h" // For CNodeStateStats
-#include "net.h"
+#include <net_processing.h> // For CNodeStateStats
+#include <net.h>
#include <QAbstractTableModel>
#include <QStringList>
@@ -55,8 +55,10 @@ public:
enum ColumnIndex {
NetNodeId = 0,
Address = 1,
- Subversion = 2,
- Ping = 3
+ Ping = 2,
+ Sent = 3,
+ Received = 4,
+ Subversion = 5
};
/** @name Methods overridden from QAbstractTableModel
diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp
index 1f4e1a442f..0e1bd59ada 100644
--- a/src/qt/platformstyle.cpp
+++ b/src/qt/platformstyle.cpp
@@ -2,16 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "platformstyle.h"
+#include <qt/platformstyle.h>
-#include "guiconstants.h"
+#include <qt/guiconstants.h>
#include <QApplication>
#include <QColor>
-#include <QIcon>
#include <QImage>
#include <QPalette>
-#include <QPixmap>
static const struct {
const char *platformId;
diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp
index 179ecdc8b3..4d6d5891c9 100644
--- a/src/qt/qvalidatedlineedit.cpp
+++ b/src/qt/qvalidatedlineedit.cpp
@@ -2,10 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "qvalidatedlineedit.h"
+#include <qt/qvalidatedlineedit.h>
-#include "bitcoinaddressvalidator.h"
-#include "guiconstants.h"
+#include <qt/bitcoinaddressvalidator.h>
+#include <qt/guiconstants.h>
QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) :
QLineEdit(parent),
diff --git a/src/qt/qvaluecombobox.cpp b/src/qt/qvaluecombobox.cpp
index a3b9b994ec..af5f6f8fd0 100644
--- a/src/qt/qvaluecombobox.cpp
+++ b/src/qt/qvaluecombobox.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 "qvaluecombobox.h"
+#include <qt/qvaluecombobox.h>
QValueComboBox::QValueComboBox(QWidget *parent) :
QComboBox(parent), role(Qt::UserRole)
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 84f43266e1..e9dcbc0a72 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -2,22 +2,20 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "receivecoinsdialog.h"
-#include "ui_receivecoinsdialog.h"
-
-#include "addressbookpage.h"
-#include "addresstablemodel.h"
-#include "bitcoinunits.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
-#include "platformstyle.h"
-#include "receiverequestdialog.h"
-#include "recentrequeststablemodel.h"
-#include "walletmodel.h"
+#include <qt/receivecoinsdialog.h>
+#include <qt/forms/ui_receivecoinsdialog.h>
+
+#include <qt/addressbookpage.h>
+#include <qt/addresstablemodel.h>
+#include <qt/bitcoinunits.h>
+#include <qt/optionsmodel.h>
+#include <qt/platformstyle.h>
+#include <qt/receiverequestdialog.h>
+#include <qt/recentrequeststablemodel.h>
+#include <qt/walletmodel.h>
#include <QAction>
#include <QCursor>
-#include <QItemSelection>
#include <QMessageBox>
#include <QScrollBar>
#include <QTextDocument>
@@ -106,7 +104,6 @@ void ReceiveCoinsDialog::clear()
ui->reqAmount->clear();
ui->reqLabel->setText("");
ui->reqMessage->setText("");
- ui->reuseAddress->setChecked(false);
updateDisplayUnit();
}
@@ -135,25 +132,8 @@ void ReceiveCoinsDialog::on_receiveButton_clicked()
QString address;
QString label = ui->reqLabel->text();
- if(ui->reuseAddress->isChecked())
- {
- /* Choose existing receiving address */
- AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this);
- dlg.setModel(model->getAddressTableModel());
- if(dlg.exec())
- {
- address = dlg.getReturnValue();
- if(label.isEmpty()) /* If no label provided, use the previously used label */
- {
- label = model->getAddressTableModel()->labelForAddress(address);
- }
- } else {
- return;
- }
- } else {
- /* Generate new receiving address */
- address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, "");
- }
+ /* Generate new receiving address */
+ address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, "");
SendCoinsRecipient info(address, label,
ui->reqAmount->value(), ui->reqMessage->text());
ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this);
diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h
index 385f98565c..3687cca87b 100644
--- a/src/qt/receivecoinsdialog.h
+++ b/src/qt/receivecoinsdialog.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_RECEIVECOINSDIALOG_H
#define BITCOIN_QT_RECEIVECOINSDIALOG_H
-#include "guiutil.h"
+#include <qt/guiutil.h>
#include <QDialog>
#include <QHeaderView>
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index 4aa6375d8a..d30e9b2dca 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -2,14 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "receiverequestdialog.h"
-#include "ui_receiverequestdialog.h"
+#include <qt/receiverequestdialog.h>
+#include <qt/forms/ui_receiverequestdialog.h>
-#include "bitcoinunits.h"
-#include "guiconstants.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
-#include "walletmodel.h"
+#include <qt/bitcoinunits.h>
+#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
#include <QClipboard>
#include <QDrag>
@@ -22,7 +21,7 @@
#endif
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h" /* for USE_QRCODE */
+#include <config/bitcoin-config.h> /* for USE_QRCODE */
#endif
#ifdef USE_QRCODE
diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h
index 1a9b165237..23b81135e2 100644
--- a/src/qt/receiverequestdialog.h
+++ b/src/qt/receiverequestdialog.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_RECEIVEREQUESTDIALOG_H
#define BITCOIN_QT_RECEIVEREQUESTDIALOG_H
-#include "walletmodel.h"
+#include <qt/walletmodel.h>
#include <QDialog>
#include <QImage>
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 1c4f7aca86..4d6e401d0d 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -2,14 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "recentrequeststablemodel.h"
+#include <qt/recentrequeststablemodel.h>
-#include "bitcoinunits.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
+#include <qt/bitcoinunits.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
-#include "clientversion.h"
-#include "streams.h"
+#include <clientversion.h>
+#include <streams.h>
RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel *parent) :
diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h
index 0c02968f92..c88b8c4426 100644
--- a/src/qt/recentrequeststablemodel.h
+++ b/src/qt/recentrequeststablemodel.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_RECENTREQUESTSTABLEMODEL_H
#define BITCOIN_QT_RECENTREQUESTSTABLEMODEL_H
-#include "walletmodel.h"
+#include <qt/walletmodel.h>
#include <QAbstractTableModel>
#include <QStringList>
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 3590a98efa..0b90205270 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -3,21 +3,20 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "rpcconsole.h"
-#include "ui_debugwindow.h"
+#include <qt/rpcconsole.h>
+#include <qt/forms/ui_debugwindow.h>
-#include "bantablemodel.h"
-#include "clientmodel.h"
-#include "guiutil.h"
-#include "platformstyle.h"
-#include "chainparams.h"
-#include "netbase.h"
-#include "rpc/server.h"
-#include "rpc/client.h"
-#include "util.h"
+#include <qt/bantablemodel.h>
+#include <qt/clientmodel.h>
+#include <qt/platformstyle.h>
+#include <chainparams.h>
+#include <netbase.h>
+#include <rpc/server.h>
+#include <rpc/client.h>
+#include <util.h>
#include <openssl/crypto.h>
@@ -28,13 +27,13 @@
#include <wallet/wallet.h>
#endif
+#include <QDesktopWidget>
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QScrollBar>
#include <QSettings>
#include <QSignalMapper>
-#include <QThread>
#include <QTime>
#include <QTimer>
#include <QStringList>
@@ -124,7 +123,7 @@ public:
};
-#include "rpcconsole.moc"
+#include <qt/rpcconsole.moc>
/**
* Split shell command line into a list of arguments and optionally execute the command(s).
@@ -391,11 +390,37 @@ void RPCExecutor::request(const QString &command)
{
std::string result;
std::string executableCommand = command.toStdString() + "\n";
+
+ // Catch the console-only-help command before RPC call is executed and reply with help text as-if a RPC reply.
+ if(executableCommand == "help-console\n")
+ {
+ Q_EMIT reply(RPCConsole::CMD_REPLY, QString(("\n"
+ "This console accepts RPC commands using the standard syntax.\n"
+ " example: getblockhash 0\n\n"
+
+ "This console can also accept RPC commands using parenthesized syntax.\n"
+ " example: getblockhash(0)\n\n"
+
+ "Commands may be nested when specified with the parenthesized syntax.\n"
+ " example: getblock(getblockhash(0) 1)\n\n"
+
+ "A space or a comma can be used to delimit arguments for either syntax.\n"
+ " example: getblockhash 0\n"
+ " getblockhash,0\n\n"
+
+ "Named results can be queried with a non-quoted key string in brackets.\n"
+ " example: getblock(getblockhash(0) true)[tx]\n\n"
+
+ "Results without keys can be queried using an integer in brackets.\n"
+ " example: getblock(getblockhash(0),true)[tx][0]\n\n")));
+ return;
+ }
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)
@@ -428,7 +453,11 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) :
consoleFontSize(0)
{
ui->setupUi(this);
- GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this);
+ QSettings settings;
+ if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) {
+ // Restore failed (perhaps missing setting), center the window
+ move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center());
+ }
ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(tr(PACKAGE_NAME)));
@@ -466,14 +495,14 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) :
ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
- QSettings settings;
consoleFontSize = settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()).toInt();
clear();
}
RPCConsole::~RPCConsole()
{
- GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this);
+ QSettings settings;
+ settings.setValue("RPCConsoleWindowGeometry", saveGeometry());
RPCUnsetTimerInterface(rpcTimerInterface);
delete rpcTimerInterface;
delete ui;
@@ -640,6 +669,7 @@ void RPCConsole::setClientModel(ClientModel *model)
wordList << ("help " + commandList[i]).c_str();
}
+ wordList << "help-console";
wordList.sort();
autoCompleter = new QCompleter(wordList, this);
autoCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel);
@@ -745,10 +775,11 @@ void RPCConsole::clear(bool clearHistory)
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 %1 to clear screen.").arg("<b>"+clsKey+"</b>") + "<br>" +
- tr("Type <b>help</b> for an overview of available commands.")) +
- "<br><span class=\"secwarning\">" +
+ tr("Type %1 for an overview of available commands.").arg("<b>help</b>") + "<br>" +
+ tr("For more information on using this console type %1.").arg("<b>help-console</b>") +
+ "<br><span class=\"secwarning\"><br>" +
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 ramifications of a command.") +
- "</span>",
+ "</span>"),
true);
}
@@ -930,18 +961,6 @@ void RPCConsole::on_sldGraphRange_valueChanged(int value)
setTrafficGraphRange(mins);
}
-QString RPCConsole::FormatBytes(quint64 bytes)
-{
- if(bytes < 1024)
- return QString(tr("%1 B")).arg(bytes);
- if(bytes < 1024 * 1024)
- return QString(tr("%1 KB")).arg(bytes / 1024);
- if(bytes < 1024 * 1024 * 1024)
- return QString(tr("%1 MB")).arg(bytes / 1024 / 1024);
-
- return QString(tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024);
-}
-
void RPCConsole::setTrafficGraphRange(int mins)
{
ui->trafficGraph->setGraphRangeMins(mins);
@@ -950,8 +969,8 @@ void RPCConsole::setTrafficGraphRange(int mins)
void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
{
- ui->lblBytesIn->setText(FormatBytes(totalBytesIn));
- ui->lblBytesOut->setText(FormatBytes(totalBytesOut));
+ ui->lblBytesIn->setText(GUIUtil::formatBytes(totalBytesIn));
+ ui->lblBytesOut->setText(GUIUtil::formatBytes(totalBytesOut));
}
void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelection &deselected)
@@ -1045,8 +1064,8 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices));
ui->peerLastSend->setText(stats->nodeStats.nLastSend ? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nLastSend) : tr("never"));
ui->peerLastRecv->setText(stats->nodeStats.nLastRecv ? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nLastRecv) : tr("never"));
- ui->peerBytesSent->setText(FormatBytes(stats->nodeStats.nSendBytes));
- ui->peerBytesRecv->setText(FormatBytes(stats->nodeStats.nRecvBytes));
+ ui->peerBytesSent->setText(GUIUtil::formatBytes(stats->nodeStats.nSendBytes));
+ ui->peerBytesRecv->setText(GUIUtil::formatBytes(stats->nodeStats.nRecvBytes));
ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nTimeConnected));
ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime));
ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingWait));
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index da06818f87..5960410cdd 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -5,10 +5,10 @@
#ifndef BITCOIN_QT_RPCCONSOLE_H
#define BITCOIN_QT_RPCCONSOLE_H
-#include "guiutil.h"
-#include "peertablemodel.h"
+#include <qt/guiutil.h>
+#include <qt/peertablemodel.h>
-#include "net.h"
+#include <net.h>
#include <QWidget>
#include <QCompleter>
@@ -123,7 +123,6 @@ Q_SIGNALS:
void cmdRequest(const QString &command);
private:
- static QString FormatBytes(quint64 bytes);
void startExecutor();
void setTrafficGraphRange(int mins);
/** show detailed information on ui about selected node */
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index a056e858a7..faddfcd4d0 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -2,34 +2,31 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "sendcoinsdialog.h"
-#include "ui_sendcoinsdialog.h"
-
-#include "addresstablemodel.h"
-#include "bitcoinunits.h"
-#include "clientmodel.h"
-#include "coincontroldialog.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
-#include "platformstyle.h"
-#include "sendcoinsentry.h"
-#include "walletmodel.h"
-
-#include "base58.h"
-#include "chainparams.h"
-#include "wallet/coincontrol.h"
-#include "validation.h" // mempool and minRelayTxFee
-#include "ui_interface.h"
-#include "txmempool.h"
-#include "policy/fees.h"
-#include "wallet/fees.h"
+#include <qt/sendcoinsdialog.h>
+#include <qt/forms/ui_sendcoinsdialog.h>
+
+#include <qt/addresstablemodel.h>
+#include <qt/bitcoinunits.h>
+#include <qt/clientmodel.h>
+#include <qt/coincontroldialog.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
+#include <qt/platformstyle.h>
+#include <qt/sendcoinsentry.h>
+
+#include <base58.h>
+#include <chainparams.h>
+#include <wallet/coincontrol.h>
+#include <validation.h> // mempool and minRelayTxFee
+#include <ui_interface.h>
+#include <txmempool.h>
+#include <policy/fees.h>
+#include <wallet/fees.h>
#include <QFontMetrics>
-#include <QMessageBox>
#include <QScrollBar>
#include <QSettings>
#include <QTextDocument>
-#include <QTimer>
static const std::array<int, 9> confTargets = { {2, 4, 6, 12, 24, 48, 144, 504, 1008} };
int getConfTargetForIndex(int index) {
@@ -114,10 +111,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
settings.setValue("nFeeRadio", 1); // custom
if (!settings.contains("nFeeRadio"))
settings.setValue("nFeeRadio", 0); // recommended
- if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
- settings.setValue("nCustomFeeRadio", 1); // total at least
- if (!settings.contains("nCustomFeeRadio"))
- settings.setValue("nCustomFeeRadio", 0); // per kilobyte
if (!settings.contains("nSmartFeeSliderPosition"))
settings.setValue("nSmartFeeSliderPosition", 0);
if (!settings.contains("nTransactionFee"))
@@ -127,8 +120,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
ui->groupFee->setId(ui->radioSmartFee, 0);
ui->groupFee->setId(ui->radioCustomFee, 1);
ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
- ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0);
- ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true);
ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
@@ -171,14 +162,13 @@ void SendCoinsDialog::setModel(WalletModel *_model)
coinControlUpdateLabels();
// fee section
- for (const int &n : confTargets) {
+ for (const int n : confTargets) {
ui->confTargetSelector->addItem(tr("%1 (%2 blocks)").arg(GUIUtil::formatNiceTimeOffset(n*Params().GetConsensus().nPowTargetSpacing)).arg(n));
}
connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSmartFeeLabel()));
connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(coinControlUpdateLabels()));
connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls()));
connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
- connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls()));
@@ -214,7 +204,6 @@ SendCoinsDialog::~SendCoinsDialog()
QSettings settings;
settings.setValue("fFeeSectionMinimized", fFeeMinimized);
settings.setValue("nFeeRadio", ui->groupFee->checkedId());
- settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId());
settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex()));
settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
@@ -353,7 +342,7 @@ void SendCoinsDialog::on_sendButton_clicked()
if (ui->optInRBF->isChecked())
{
questionString.append("<hr /><span>");
- questionString.append(tr("This transaction signals replaceability (optin-RBF)."));
+ questionString.append(tr("You can increase the fee later (signals Replace-By-Fee)."));
questionString.append("</span>");
}
@@ -410,6 +399,7 @@ SendCoinsEntry *SendCoinsDialog::addEntry()
entry->setModel(model);
ui->entries->addWidget(entry);
connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
+ connect(entry, SIGNAL(useAvailableBalance(SendCoinsEntry*)), this, SLOT(useAvailableBalance(SendCoinsEntry*)));
connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels()));
connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this, SLOT(coinControlUpdateLabels()));
@@ -607,9 +597,33 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked()
minimizeFeeSection(true);
}
+void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
+{
+ // Get CCoinControl instance if CoinControl is enabled or create a new one.
+ CCoinControl coin_control;
+ if (model->getOptionsModel()->getCoinControlFeatures()) {
+ coin_control = *CoinControlDialog::coinControl;
+ }
+
+ // Calculate available amount to send.
+ CAmount amount = model->getBalance(&coin_control);
+ for (int i = 0; i < ui->entries->count(); ++i) {
+ SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
+ if (e && !e->isHidden() && e != entry) {
+ amount -= e->getValue().amount;
+ }
+ }
+
+ if (amount > 0) {
+ entry->checkSubtractFeeFromAmount();
+ entry->setAmount(amount);
+ } else {
+ entry->setAmount(0);
+ }
+}
+
void SendCoinsDialog::setMinimumFee()
{
- ui->radioCustomPerKilobyte->setChecked(true);
ui->customFee->setValue(GetRequiredFee(1000));
}
@@ -622,7 +636,7 @@ void SendCoinsDialog::updateFeeSectionControls()
ui->labelFeeEstimation ->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());
+ ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
}
@@ -634,8 +648,7 @@ void SendCoinsDialog::updateFeeMinimizedLabel()
if (ui->radioSmartFee->isChecked())
ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
else {
- ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) +
- ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : ""));
+ ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + "/kB");
}
}
@@ -777,19 +790,18 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
CoinControlDialog::coinControl->destChange = CNoDestination();
ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
- CBitcoinAddress addr = CBitcoinAddress(text.toStdString());
+ const CTxDestination dest = DecodeDestination(text.toStdString());
if (text.isEmpty()) // Nothing entered
{
ui->labelCoinControlChangeLabel->setText("");
}
- else if (!addr.IsValid()) // Invalid address
+ else if (!IsValidDestination(dest)) // Invalid address
{
ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
}
else // Valid address
{
- const CTxDestination dest = addr.Get();
if (!model->IsSpendable(dest)) {
ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 70b4aa5a03..300116fd9f 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_SENDCOINSDIALOG_H
#define BITCOIN_QT_SENDCOINSDIALOG_H
-#include "walletmodel.h"
+#include <qt/walletmodel.h>
#include <QDialog>
#include <QMessageBox>
@@ -76,6 +76,7 @@ private Q_SLOTS:
void on_buttonChooseFee_clicked();
void on_buttonMinimizeFee_clicked();
void removeEntry(SendCoinsEntry* entry);
+ void useAvailableBalance(SendCoinsEntry* entry);
void updateDisplayUnit();
void coinControlFeatureChanged(bool);
void coinControlButtonClicked();
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index bb0f47b21c..4cf4bb9ef7 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -2,15 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "sendcoinsentry.h"
-#include "ui_sendcoinsentry.h"
+#include <qt/sendcoinsentry.h>
+#include <qt/forms/ui_sendcoinsentry.h>
-#include "addressbookpage.h"
-#include "addresstablemodel.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
-#include "platformstyle.h"
-#include "walletmodel.h"
+#include <qt/addressbookpage.h>
+#include <qt/addresstablemodel.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
+#include <qt/platformstyle.h>
#include <QApplication>
#include <QClipboard>
@@ -48,6 +47,7 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par
connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
connect(ui->deleteButton_is, SIGNAL(clicked()), this, SLOT(deleteClicked()));
connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked()));
+ connect(ui->useAvailableBalanceButton, SIGNAL(clicked()), this, SLOT(useAvailableBalanceClicked()));
}
SendCoinsEntry::~SendCoinsEntry()
@@ -112,11 +112,21 @@ void SendCoinsEntry::clear()
updateDisplayUnit();
}
+void SendCoinsEntry::checkSubtractFeeFromAmount()
+{
+ ui->checkboxSubtractFeeFromAmount->setChecked(true);
+}
+
void SendCoinsEntry::deleteClicked()
{
Q_EMIT removeEntry(this);
}
+void SendCoinsEntry::useAvailableBalanceClicked()
+{
+ Q_EMIT useAvailableBalance(this);
+}
+
bool SendCoinsEntry::validate()
{
if (!model)
@@ -228,6 +238,11 @@ void SendCoinsEntry::setAddress(const QString &address)
ui->payAmount->setFocus();
}
+void SendCoinsEntry::setAmount(const CAmount &amount)
+{
+ ui->payAmount->setValue(amount);
+}
+
bool SendCoinsEntry::isClear()
{
return ui->payTo->text().isEmpty() && ui->payTo_is->text().isEmpty() && ui->payTo_s->text().isEmpty();
diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h
index a8be670c2a..b470a0d72e 100644
--- a/src/qt/sendcoinsentry.h
+++ b/src/qt/sendcoinsentry.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_SENDCOINSENTRY_H
#define BITCOIN_QT_SENDCOINSENTRY_H
-#include "walletmodel.h"
+#include <qt/walletmodel.h>
#include <QStackedWidget>
@@ -38,6 +38,7 @@ public:
void setValue(const SendCoinsRecipient &value);
void setAddress(const QString &address);
+ void setAmount(const CAmount &amount);
/** Set up the tab chain manually, as Qt messes up the tab chain by default in some cases
* (issue https://bugreports.qt-project.org/browse/QTBUG-10907).
@@ -48,14 +49,17 @@ public:
public Q_SLOTS:
void clear();
+ void checkSubtractFeeFromAmount();
Q_SIGNALS:
void removeEntry(SendCoinsEntry *entry);
+ void useAvailableBalance(SendCoinsEntry* entry);
void payAmountChanged();
void subtractFeeFromAmountChanged();
private Q_SLOTS:
void deleteClicked();
+ void useAvailableBalanceClicked();
void on_payTo_textChanged(const QString &address);
void on_addressBookButton_clicked();
void on_pasteButton_clicked();
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 0950ed0234..4cd0c76b16 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -2,18 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "signverifymessagedialog.h"
-#include "ui_signverifymessagedialog.h"
+#include <qt/signverifymessagedialog.h>
+#include <qt/forms/ui_signverifymessagedialog.h>
-#include "addressbookpage.h"
-#include "guiutil.h"
-#include "platformstyle.h"
-#include "walletmodel.h"
+#include <qt/addressbookpage.h>
+#include <qt/guiutil.h>
+#include <qt/platformstyle.h>
+#include <qt/walletmodel.h>
-#include "base58.h"
-#include "init.h"
-#include "validation.h" // For strMessageMagic
-#include "wallet/wallet.h"
+#include <base58.h>
+#include <init.h>
+#include <validation.h> // For strMessageMagic
+#include <wallet/wallet.h>
#include <string>
#include <vector>
@@ -117,16 +117,14 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
/* Clear old signature to ensure users don't get confused on error with an old signature displayed */
ui->signatureOut_SM->clear();
- CBitcoinAddress addr(ui->addressIn_SM->text().toStdString());
- if (!addr.IsValid())
- {
+ CTxDestination destination = DecodeDestination(ui->addressIn_SM->text().toStdString());
+ if (!IsValidDestination(destination)) {
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
return;
}
- CKeyID keyID;
- if (!addr.GetKeyID(keyID))
- {
+ const CKeyID* keyID = boost::get<CKeyID>(&destination);
+ if (!keyID) {
ui->addressIn_SM->setValid(false);
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again."));
@@ -142,7 +140,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
}
CKey key;
- if (!model->getPrivKey(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."));
@@ -164,7 +162,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
ui->statusLabel_SM->setStyleSheet("QLabel { color: green; }");
ui->statusLabel_SM->setText(QString("<nobr>") + tr("Message signed.") + QString("</nobr>"));
- ui->signatureOut_SM->setText(QString::fromStdString(EncodeBase64(&vchSig[0], vchSig.size())));
+ ui->signatureOut_SM->setText(QString::fromStdString(EncodeBase64(vchSig.data(), vchSig.size())));
}
void SignVerifyMessageDialog::on_copySignatureButton_SM_clicked()
@@ -197,16 +195,13 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked()
void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
{
- CBitcoinAddress addr(ui->addressIn_VM->text().toStdString());
- if (!addr.IsValid())
- {
+ CTxDestination destination = DecodeDestination(ui->addressIn_VM->text().toStdString());
+ if (!IsValidDestination(destination)) {
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
return;
}
- CKeyID keyID;
- if (!addr.GetKeyID(keyID))
- {
+ if (!boost::get<CKeyID>(&destination)) {
ui->addressIn_VM->setValid(false);
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again."));
@@ -237,8 +232,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
return;
}
- if (!(CBitcoinAddress(pubkey.GetID()) == addr))
- {
+ if (!(CTxDestination(pubkey.GetID()) == destination)) {
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>"));
return;
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index 1b7cc69231..5aa1de5553 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -3,21 +3,21 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "splashscreen.h"
+#include <qt/splashscreen.h>
-#include "networkstyle.h"
+#include <qt/networkstyle.h>
-#include "clientversion.h"
-#include "init.h"
-#include "util.h"
-#include "ui_interface.h"
-#include "version.h"
+#include <clientversion.h>
+#include <init.h>
+#include <util.h>
+#include <ui_interface.h>
+#include <version.h>
#ifdef ENABLE_WALLET
-#include "wallet/wallet.h"
+#include <wallet/wallet.h>
#endif
#include <QApplication>
@@ -142,8 +142,8 @@ SplashScreen::~SplashScreen()
bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) {
if (ev->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
- if(keyEvent->text()[0] == 'q' && breakAction != nullptr) {
- breakAction();
+ if(keyEvent->text()[0] == 'q') {
+ StartShutdown();
}
}
return QObject::eventFilter(obj, ev);
@@ -170,27 +170,18 @@ static void InitMessage(SplashScreen *splash, const std::string &message)
Q_ARG(QColor, QColor(55,55,55)));
}
-static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress)
+static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress, bool resume_possible)
{
- InitMessage(splash, title + strprintf("%d", nProgress) + "%");
-}
-
-void SplashScreen::setBreakAction(const std::function<void(void)> &action)
-{
- breakAction = action;
-}
-
-static void SetProgressBreakAction(SplashScreen *splash, const std::function<void(void)> &action)
-{
- QMetaObject::invokeMethod(splash, "setBreakAction",
- Qt::QueuedConnection,
- Q_ARG(std::function<void(void)>, action));
+ InitMessage(splash, title + std::string("\n") +
+ (resume_possible ? _("(press q to shutdown and continue later)")
+ : _("press q to shutdown")) +
+ strprintf("\n%d", nProgress) + "%");
}
#ifdef ENABLE_WALLET
void SplashScreen::ConnectWallet(CWallet* wallet)
{
- wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
+ wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2, false));
connectedWallets.push_back(wallet);
}
#endif
@@ -199,8 +190,7 @@ void SplashScreen::subscribeToCoreSignals()
{
// Connect signals to client
uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1));
- uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
- uiInterface.SetProgressBreakAction.connect(boost::bind(SetProgressBreakAction, this, _1));
+ uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2, _3));
#ifdef ENABLE_WALLET
uiInterface.LoadWallet.connect(boost::bind(&SplashScreen::ConnectWallet, this, _1));
#endif
@@ -210,10 +200,10 @@ void SplashScreen::unsubscribeFromCoreSignals()
{
// Disconnect signals from client
uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, _1));
- uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
+ uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2, _3));
#ifdef ENABLE_WALLET
for (CWallet* const & pwallet : connectedWallets) {
- pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
+ pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2, false));
}
#endif
}
diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h
index a88ebb98a8..c6cfd503f7 100644
--- a/src/qt/splashscreen.h
+++ b/src/qt/splashscreen.h
@@ -36,8 +36,6 @@ public Q_SLOTS:
/** Show message and progress */
void showMessage(const QString &message, int alignment, const QColor &color);
- /** Sets the break action */
- void setBreakAction(const std::function<void(void)> &action);
protected:
bool eventFilter(QObject * obj, QEvent * ev);
@@ -55,8 +53,6 @@ private:
int curAlignment;
QList<CWallet*> connectedWallets;
-
- std::function<void(void)> breakAction;
};
#endif // BITCOIN_QT_SPLASHSCREEN_H
diff --git a/src/qt/test/compattests.cpp b/src/qt/test/compattests.cpp
index 2a7284b5b2..7a4388e940 100644
--- a/src/qt/test/compattests.cpp
+++ b/src/qt/test/compattests.cpp
@@ -2,11 +2,11 @@
// 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 <qt/paymentrequestplus.h> // this includes protobuf's port.h which defines its own bswap macos
-#include "compattests.h"
+#include <qt/test/compattests.h>
-#include "compat/byteswap.h"
+#include <compat/byteswap.h>
void CompatTests::bswapTests()
{
diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp
index c7830071ed..1864604372 100644
--- a/src/qt/test/paymentservertests.cpp
+++ b/src/qt/test/paymentservertests.cpp
@@ -2,17 +2,17 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "paymentservertests.h"
+#include <qt/test/paymentservertests.h>
-#include "optionsmodel.h"
-#include "paymentrequestdata.h"
+#include <qt/optionsmodel.h>
+#include <qt/test/paymentrequestdata.h>
-#include "amount.h"
-#include "random.h"
-#include "script/script.h"
-#include "script/standard.h"
-#include "util.h"
-#include "utilstrencodings.h"
+#include <amount.h>
+#include <random.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <util.h>
+#include <utilstrencodings.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
@@ -24,7 +24,7 @@ X509 *parse_b64der_cert(const char* cert_data)
{
std::vector<unsigned char> data = DecodeBase64(cert_data);
assert(data.size() > 0);
- const unsigned char* dptr = &data[0];
+ const unsigned char* dptr = data.data();
X509 *cert = d2i_X509(nullptr, &dptr, data.size());
assert(cert);
return cert;
@@ -43,7 +43,7 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsig
// Write data to a temp file:
QTemporaryFile f;
f.open();
- f.write((const char*)&data[0], data.size());
+ f.write((const char*)data.data(), data.size());
f.close();
// Create a QObject, install event filter from PaymentServer
@@ -139,7 +139,7 @@ void PaymentServerTests::paymentServerTests()
// Contains a testnet paytoaddress, so payment request network doesn't match client network:
data = DecodeBase64(paymentrequest1_cert2_BASE64);
- byteArray = QByteArray((const char*)&data[0], data.size());
+ byteArray = QByteArray((const char*)data.data(), data.size());
r.paymentRequest.parse(byteArray);
// Ensure the request is initialized, because network "main" is default, even for
// uninitialized payment requests and that will fail our test here.
@@ -148,7 +148,7 @@ void PaymentServerTests::paymentServerTests()
// Expired payment request (expires is set to 1 = 1970-01-01 00:00:01):
data = DecodeBase64(paymentrequest2_cert2_BASE64);
- byteArray = QByteArray((const char*)&data[0], data.size());
+ byteArray = QByteArray((const char*)data.data(), data.size());
r.paymentRequest.parse(byteArray);
// Ensure the request is initialized
QVERIFY(r.paymentRequest.IsInitialized());
@@ -159,7 +159,7 @@ void PaymentServerTests::paymentServerTests()
// 9223372036854775807 (uint64), 9223372036854775807 (int64_t) and -1 (int32_t)
// -1 is 1969-12-31 23:59:59 (for a 32 bit time values)
data = DecodeBase64(paymentrequest3_cert2_BASE64);
- byteArray = QByteArray((const char*)&data[0], data.size());
+ byteArray = QByteArray((const char*)data.data(), data.size());
r.paymentRequest.parse(byteArray);
// Ensure the request is initialized
QVERIFY(r.paymentRequest.IsInitialized());
@@ -170,7 +170,7 @@ void PaymentServerTests::paymentServerTests()
// 9223372036854775808 (uint64), -9223372036854775808 (int64_t) and 0 (int32_t)
// 0 is 1970-01-01 00:00:00 (for a 32 bit time values)
data = DecodeBase64(paymentrequest4_cert2_BASE64);
- byteArray = QByteArray((const char*)&data[0], data.size());
+ byteArray = QByteArray((const char*)data.data(), data.size());
r.paymentRequest.parse(byteArray);
// Ensure the request is initialized
QVERIFY(r.paymentRequest.IsInitialized());
@@ -190,7 +190,7 @@ void PaymentServerTests::paymentServerTests()
// Payment request with amount overflow (amount is set to 21000001 BTC):
data = DecodeBase64(paymentrequest5_cert2_BASE64);
- byteArray = QByteArray((const char*)&data[0], data.size());
+ byteArray = QByteArray((const char*)data.data(), data.size());
r.paymentRequest.parse(byteArray);
// Ensure the request is initialized
QVERIFY(r.paymentRequest.IsInitialized());
diff --git a/src/qt/test/paymentservertests.h b/src/qt/test/paymentservertests.h
index faf167f2c6..6d84c38287 100644
--- a/src/qt/test/paymentservertests.h
+++ b/src/qt/test/paymentservertests.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_TEST_PAYMENTSERVERTESTS_H
#define BITCOIN_QT_TEST_PAYMENTSERVERTESTS_H
-#include "../paymentserver.h"
+#include <qt/paymentserver.h>
#include <QObject>
#include <QTest>
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index 00edd64f72..0596ccf90e 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -2,18 +2,18 @@
// 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 "fs.h"
-#include "validation.h"
-#include "rpc/register.h"
-#include "rpc/server.h"
-#include "rpcconsole.h"
-#include "test/test_bitcoin.h"
-#include "univalue.h"
-#include "util.h"
+#include <qt/test/rpcnestedtests.h>
+
+#include <chainparams.h>
+#include <consensus/validation.h>
+#include <fs.h>
+#include <validation.h>
+#include <rpc/register.h>
+#include <rpc/server.h>
+#include <qt/rpcconsole.h>
+#include <test/test_bitcoin.h>
+#include <univalue.h>
+#include <util.h>
#include <QDir>
#include <QtGlobal>
@@ -36,11 +36,6 @@ void RPCNestedTests::rpcNestedTests()
// do some test setup
// could be moved to a more generic place when we add more tests on QT level
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(".");
- gArgs.ForceSetArg("-datadir", path);
//mempool.setSanityCheck(1.0);
TestingSetup test;
@@ -135,6 +130,4 @@ void RPCNestedTests::rpcNestedTests()
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
-
- fs::remove_all(fs::path(path));
}
diff --git a/src/qt/test/rpcnestedtests.h b/src/qt/test/rpcnestedtests.h
index 9ad409019f..04a9d124aa 100644
--- a/src/qt/test/rpcnestedtests.h
+++ b/src/qt/test/rpcnestedtests.h
@@ -8,8 +8,8 @@
#include <QObject>
#include <QTest>
-#include "txdb.h"
-#include "txmempool.h"
+#include <txdb.h>
+#include <txmempool.h>
class RPCNestedTests : public QObject
{
@@ -17,9 +17,6 @@ class RPCNestedTests : public QObject
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 80a00a634a..2e11b4f4d6 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -3,18 +3,18 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "chainparams.h"
-#include "rpcnestedtests.h"
-#include "util.h"
-#include "uritests.h"
-#include "compattests.h"
+#include <chainparams.h>
+#include <qt/test/rpcnestedtests.h>
+#include <util.h>
+#include <qt/test/uritests.h>
+#include <qt/test/compattests.h>
#ifdef ENABLE_WALLET
-#include "paymentservertests.h"
-#include "wallettests.h"
+#include <qt/test/paymentservertests.h>
+#include <qt/test/wallettests.h>
#endif
#include <QApplication>
@@ -53,6 +53,10 @@ int main(int argc, char *argv[])
SetupNetworking();
SelectParams(CBaseChainParams::MAIN);
noui_connect();
+ ClearDatadirCache();
+ fs::path pathTemp = fs::temp_directory_path() / strprintf("test_bitcoin-qt_%lu_%i", (unsigned long)GetTime(), (int)GetRand(100000));
+ fs::create_directories(pathTemp);
+ gArgs.ForceSetArg("-datadir", pathTemp.string());
bool fInvalid = false;
@@ -97,5 +101,7 @@ int main(int argc, char *argv[])
}
#endif
+ fs::remove_all(pathTemp);
+
return fInvalid;
}
diff --git a/src/qt/test/uritests.cpp b/src/qt/test/uritests.cpp
index 8b53c0d5c7..e47aa8b546 100644
--- a/src/qt/test/uritests.cpp
+++ b/src/qt/test/uritests.cpp
@@ -2,10 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "uritests.h"
+#include <qt/test/uritests.h>
-#include "guiutil.h"
-#include "walletmodel.h"
+#include <qt/guiutil.h>
+#include <qt/walletmodel.h>
#include <QUrl>
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index ff1eb59f16..4b7c3bd726 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -1,18 +1,22 @@
-#include "wallettests.h"
-
-#include "qt/bitcoinamountfield.h"
-#include "qt/callback.h"
-#include "qt/optionsmodel.h"
-#include "qt/platformstyle.h"
-#include "qt/qvalidatedlineedit.h"
-#include "qt/sendcoinsdialog.h"
-#include "qt/sendcoinsentry.h"
-#include "qt/transactiontablemodel.h"
-#include "qt/transactionview.h"
-#include "qt/walletmodel.h"
-#include "test/test_bitcoin.h"
-#include "validation.h"
-#include "wallet/wallet.h"
+#include <qt/test/wallettests.h>
+
+#include <qt/bitcoinamountfield.h>
+#include <qt/callback.h>
+#include <qt/optionsmodel.h>
+#include <qt/platformstyle.h>
+#include <qt/qvalidatedlineedit.h>
+#include <qt/sendcoinsdialog.h>
+#include <qt/sendcoinsentry.h>
+#include <qt/transactiontablemodel.h>
+#include <qt/transactionview.h>
+#include <qt/walletmodel.h>
+#include <test/test_bitcoin.h>
+#include <validation.h>
+#include <wallet/wallet.h>
+#include <qt/overviewpage.h>
+#include <qt/receivecoinsdialog.h>
+#include <qt/recentrequeststablemodel.h>
+#include <qt/receiverequestdialog.h>
#include <QAbstractButton>
#include <QAction>
@@ -21,6 +25,9 @@
#include <QPushButton>
#include <QTimer>
#include <QVBoxLayout>
+#include <QTextEdit>
+#include <QListView>
+#include <QDialogButtonBox>
namespace
{
@@ -57,11 +64,11 @@ void ConfirmSend(QString* text = nullptr, bool cancel = false)
}
//! Send coins to address and return txid.
-uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CBitcoinAddress& address, CAmount amount, bool rbf)
+uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDestination& address, CAmount amount, bool rbf)
{
QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries");
SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget());
- entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(address.ToString()));
+ entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(EncodeDestination(address)));
entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount);
sendCoinsDialog.findChild<QFrame*>("frameFee")
->findChild<QFrame*>("frameFeeSelection")
@@ -140,7 +147,7 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st
// src/qt/test/test_bitcoin-qt -platform xcb # Linux
// src/qt/test/test_bitcoin-qt -platform windows # Windows
// src/qt/test/test_bitcoin-qt -platform cocoa # macOS
-void TestSendCoins()
+void TestGUI()
{
// Set up wallet and chain with 105 blocks (5 mature blocks for spending).
TestChain100Setup test;
@@ -157,7 +164,10 @@ void TestSendCoins()
wallet.SetAddressBook(test.coinbaseKey.GetPubKey().GetID(), "", "receive");
wallet.AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
}
- wallet.ScanForWalletTransactions(chainActive.Genesis(), true);
+ {
+ LOCK(cs_main);
+ wallet.ScanForWalletTransactions(chainActive.Genesis(), nullptr, true);
+ }
wallet.SetBroadcastTransactions(true);
// Create widgets for sending coins and listing transactions.
@@ -172,8 +182,8 @@ void TestSendCoins()
// Send two transactions, and verify they are added to transaction list.
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
QCOMPARE(transactionTableModel->rowCount({}), 105);
- uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 5 * COIN, false /* rbf */);
- uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 10 * COIN, true /* rbf */);
+ uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 5 * COIN, false /* rbf */);
+ uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 10 * COIN, true /* rbf */);
QCOMPARE(transactionTableModel->rowCount({}), 107);
QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
QVERIFY(FindTx(*transactionTableModel, txid2).isValid());
@@ -184,6 +194,68 @@ void TestSendCoins()
BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, false /* cancel */);
BumpFee(transactionView, txid2, true /* expect disabled */, "already bumped" /* expected error */, false /* cancel */);
+ // Check current balance on OverviewPage
+ OverviewPage overviewPage(platformStyle.get());
+ overviewPage.setWalletModel(&walletModel);
+ QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance");
+ QString balanceText = balanceLabel->text();
+ int unit = walletModel.getOptionsModel()->getDisplayUnit();
+ CAmount balance = walletModel.getBalance();
+ QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways);
+ QCOMPARE(balanceText, balanceComparison);
+
+ // Check Request Payment button
+ ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get());
+ receiveCoinsDialog.setModel(&walletModel);
+ RecentRequestsTableModel* requestTableModel = walletModel.getRecentRequestsTableModel();
+
+ // Label input
+ QLineEdit* labelInput = receiveCoinsDialog.findChild<QLineEdit*>("reqLabel");
+ labelInput->setText("TEST_LABEL_1");
+
+ // Amount input
+ BitcoinAmountField* amountInput = receiveCoinsDialog.findChild<BitcoinAmountField*>("reqAmount");
+ amountInput->setValue(1);
+
+ // Message input
+ QLineEdit* messageInput = receiveCoinsDialog.findChild<QLineEdit*>("reqMessage");
+ messageInput->setText("TEST_MESSAGE_1");
+ int initialRowCount = requestTableModel->rowCount({});
+ QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>("receiveButton");
+ requestPaymentButton->click();
+ for (QWidget* widget : QApplication::topLevelWidgets()) {
+ if (widget->inherits("ReceiveRequestDialog")) {
+ ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget);
+ QTextEdit* rlist = receiveRequestDialog->QObject::findChild<QTextEdit*>("outUri");
+ QString paymentText = rlist->toPlainText();
+ QStringList paymentTextList = paymentText.split('\n');
+ QCOMPARE(paymentTextList.at(0), QString("Payment information"));
+ QVERIFY(paymentTextList.at(1).indexOf(QString("URI: bitcoin:")) != -1);
+ QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1);
+ QCOMPARE(paymentTextList.at(3), QString("Amount: 0.00000001 ") + QString::fromStdString(CURRENCY_UNIT));
+ QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1"));
+ QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1"));
+ }
+ }
+
+ // Clear button
+ QPushButton* clearButton = receiveCoinsDialog.findChild<QPushButton*>("clearButton");
+ clearButton->click();
+ QCOMPARE(labelInput->text(), QString(""));
+ QCOMPARE(amountInput->value(), CAmount(0));
+ QCOMPARE(messageInput->text(), QString(""));
+
+ // Check addition to history
+ int currentRowCount = requestTableModel->rowCount({});
+ QCOMPARE(currentRowCount, initialRowCount+1);
+
+ // Check Remove button
+ QTableView* table = receiveCoinsDialog.findChild<QTableView*>("recentRequestsView");
+ table->selectRow(currentRowCount-1);
+ QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton");
+ removeRequestButton->click();
+ QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1);
+
bitdb.Flush(true);
bitdb.Reset();
}
@@ -192,5 +264,5 @@ void TestSendCoins()
void WalletTests::walletTests()
{
- TestSendCoins();
+ TestGUI();
}
diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp
index 5bb863451f..b5e4ccb406 100644
--- a/src/qt/trafficgraphwidget.cpp
+++ b/src/qt/trafficgraphwidget.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "trafficgraphwidget.h"
-#include "clientmodel.h"
+#include <qt/trafficgraphwidget.h>
+#include <qt/clientmodel.h>
#include <QPainter>
#include <QColor>
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index bcacc47ef3..3f245e6c33 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -2,21 +2,21 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "transactiondesc.h"
-
-#include "bitcoinunits.h"
-#include "guiutil.h"
-#include "paymentserver.h"
-#include "transactionrecord.h"
-
-#include "base58.h"
-#include "consensus/consensus.h"
-#include "validation.h"
-#include "script/script.h"
-#include "timedata.h"
-#include "util.h"
-#include "wallet/db.h"
-#include "wallet/wallet.h"
+#include <qt/transactiondesc.h>
+
+#include <qt/bitcoinunits.h>
+#include <qt/guiutil.h>
+#include <qt/paymentserver.h>
+#include <qt/transactionrecord.h>
+
+#include <base58.h>
+#include <consensus/consensus.h>
+#include <validation.h>
+#include <script/script.h>
+#include <timedata.h>
+#include <util.h>
+#include <wallet/db.h>
+#include <wallet/wallet.h>
#include <stdint.h>
#include <string>
@@ -24,7 +24,7 @@
QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
{
AssertLockHeld(cs_main);
- if (!CheckFinalTx(wtx))
+ if (!CheckFinalTx(*wtx.tx))
{
if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - chainActive.Height());
@@ -91,9 +91,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
if (nNet > 0)
{
// Credit
- if (CBitcoinAddress(rec->address).IsValid())
- {
- CTxDestination address = CBitcoinAddress(rec->address).Get();
+ CTxDestination address = DecodeDestination(rec->address);
+ if (IsValidDestination(address)) {
if (wallet->mapAddressBook.count(address))
{
strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
@@ -118,7 +117,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// Online transaction
std::string strAddress = wtx.mapValue["to"];
strHTML += "<b>" + tr("To") + ":</b> ";
- CTxDestination dest = CBitcoinAddress(strAddress).Get();
+ CTxDestination dest = DecodeDestination(strAddress);
if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty())
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " ";
strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>";
@@ -189,7 +188,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<b>" + tr("To") + ":</b> ";
if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty())
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
- strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString());
+ strHTML += GUIUtil::HtmlEscape(EncodeDestination(address));
if(toSelf == ISMINE_SPENDABLE)
strHTML += " (own address)";
else if(toSelf & ISMINE_WATCH_ONLY)
@@ -304,7 +303,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
{
if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty())
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
- strHTML += QString::fromStdString(CBitcoinAddress(address).ToString());
+ strHTML += QString::fromStdString(EncodeDestination(address));
}
strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue);
strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";
diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp
index 65f163deb2..74d7a8f525 100644
--- a/src/qt/transactiondescdialog.cpp
+++ b/src/qt/transactiondescdialog.cpp
@@ -2,10 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "transactiondescdialog.h"
-#include "ui_transactiondescdialog.h"
+#include <qt/transactiondescdialog.h>
+#include <qt/forms/ui_transactiondescdialog.h>
-#include "transactiontablemodel.h"
+#include <qt/transactiontablemodel.h>
#include <QModelIndex>
diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp
index 44e4ef8238..47890fc7e2 100644
--- a/src/qt/transactionfilterproxy.cpp
+++ b/src/qt/transactionfilterproxy.cpp
@@ -2,15 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "transactionfilterproxy.h"
+#include <qt/transactionfilterproxy.h>
-#include "transactiontablemodel.h"
-#include "transactionrecord.h"
+#include <qt/transactiontablemodel.h>
+#include <qt/transactionrecord.h>
#include <cstdlib>
-#include <QDateTime>
-
// Earliest date that can be represented (far in the past)
const QDateTime TransactionFilterProxy::MIN_DATE = QDateTime::fromTime_t(0);
// Last date that can be represented (far in the future)
@@ -20,7 +18,7 @@ TransactionFilterProxy::TransactionFilterProxy(QObject *parent) :
QSortFilterProxyModel(parent),
dateFrom(MIN_DATE),
dateTo(MAX_DATE),
- addrPrefix(),
+ m_search_string(),
typeFilter(ALL_TYPES),
watchOnlyFilter(WatchOnlyFilter_All),
minAmount(0),
@@ -38,6 +36,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &
bool involvesWatchAddress = index.data(TransactionTableModel::WatchonlyRole).toBool();
QString address = index.data(TransactionTableModel::AddressRole).toString();
QString label = index.data(TransactionTableModel::LabelRole).toString();
+ QString txid = index.data(TransactionTableModel::TxIDRole).toString();
qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong());
int status = index.data(TransactionTableModel::StatusRole).toInt();
@@ -51,8 +50,11 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &
return false;
if(datetime < dateFrom || datetime > dateTo)
return false;
- if (!address.contains(addrPrefix, Qt::CaseInsensitive) && !label.contains(addrPrefix, Qt::CaseInsensitive))
+ if (!address.contains(m_search_string, Qt::CaseInsensitive) &&
+ ! label.contains(m_search_string, Qt::CaseInsensitive) &&
+ ! txid.contains(m_search_string, Qt::CaseInsensitive)) {
return false;
+ }
if(amount < minAmount)
return false;
@@ -66,9 +68,10 @@ void TransactionFilterProxy::setDateRange(const QDateTime &from, const QDateTime
invalidateFilter();
}
-void TransactionFilterProxy::setAddressPrefix(const QString &_addrPrefix)
+void TransactionFilterProxy::setSearchString(const QString &search_string)
{
- this->addrPrefix = _addrPrefix;
+ if (m_search_string == search_string) return;
+ m_search_string = search_string;
invalidateFilter();
}
diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h
index 7db02cd61f..fea7502b26 100644
--- a/src/qt/transactionfilterproxy.h
+++ b/src/qt/transactionfilterproxy.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_TRANSACTIONFILTERPROXY_H
#define BITCOIN_QT_TRANSACTIONFILTERPROXY_H
-#include "amount.h"
+#include <amount.h>
#include <QDateTime>
#include <QSortFilterProxyModel>
@@ -35,7 +35,7 @@ public:
};
void setDateRange(const QDateTime &from, const QDateTime &to);
- void setAddressPrefix(const QString &addrPrefix);
+ void setSearchString(const QString &);
/**
@note Type filter takes a bit field created with TYPE() or ALL_TYPES
*/
@@ -57,7 +57,7 @@ protected:
private:
QDateTime dateFrom;
QDateTime dateTo;
- QString addrPrefix;
+ QString m_search_string;
quint32 typeFilter;
WatchOnlyFilter watchOnlyFilter;
CAmount minAmount;
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 36d98ce49d..e3e8377cb9 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -2,13 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "transactionrecord.h"
+#include <qt/transactionrecord.h>
-#include "base58.h"
-#include "consensus/consensus.h"
-#include "validation.h"
-#include "timedata.h"
-#include "wallet/wallet.h"
+#include <base58.h>
+#include <consensus/consensus.h>
+#include <validation.h>
+#include <timedata.h>
+#include <wallet/wallet.h>
#include <stdint.h>
@@ -55,7 +55,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
{
// Received by Bitcoin Address
sub.type = TransactionRecord::RecvWithAddress;
- sub.address = CBitcoinAddress(address).ToString();
+ sub.address = EncodeDestination(address);
}
else
{
@@ -127,7 +127,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
{
// Sent to Bitcoin Address
sub.type = TransactionRecord::SendToAddress;
- sub.address = CBitcoinAddress(address).ToString();
+ sub.address = EncodeDestination(address);
}
else
{
@@ -182,7 +182,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
status.depth = wtx.GetDepthInMainChain();
status.cur_num_blocks = chainActive.Height();
- if (!CheckFinalTx(wtx))
+ if (!CheckFinalTx(*wtx.tx))
{
if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
{
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index a26e676142..ee85acf4ac 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -5,8 +5,8 @@
#ifndef BITCOIN_QT_TRANSACTIONRECORD_H
#define BITCOIN_QT_TRANSACTIONRECORD_H
-#include "amount.h"
-#include "uint256.h"
+#include <amount.h>
+#include <uint256.h>
#include <QList>
#include <QString>
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 59cef555b1..67580f11ae 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -2,23 +2,23 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "transactiontablemodel.h"
-
-#include "addresstablemodel.h"
-#include "guiconstants.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
-#include "platformstyle.h"
-#include "transactiondesc.h"
-#include "transactionrecord.h"
-#include "walletmodel.h"
-
-#include "core_io.h"
-#include "validation.h"
-#include "sync.h"
-#include "uint256.h"
-#include "util.h"
-#include "wallet/wallet.h"
+#include <qt/transactiontablemodel.h>
+
+#include <qt/addresstablemodel.h>
+#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
+#include <qt/platformstyle.h>
+#include <qt/transactiondesc.h>
+#include <qt/transactionrecord.h>
+#include <qt/walletmodel.h>
+
+#include <core_io.h>
+#include <validation.h>
+#include <sync.h>
+#include <uint256.h>
+#include <util.h>
+#include <wallet/wallet.h>
#include <QColor>
#include <QDateTime>
@@ -80,10 +80,10 @@ public:
cachedWallet.clear();
{
LOCK2(cs_main, wallet->cs_wallet);
- for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
+ for (const auto& entry : wallet->mapWallet)
{
- if(TransactionRecord::showTransaction(it->second))
- cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second));
+ if (TransactionRecord::showTransaction(entry.second))
+ cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, entry.second));
}
}
}
@@ -230,7 +230,7 @@ public:
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
if(mi != wallet->mapWallet.end())
{
- std::string strHex = EncodeHexTx(static_cast<CTransaction>(mi->second));
+ std::string strHex = EncodeHexTx(*mi->second.tx);
return QString::fromStdString(strHex);
}
return QString();
diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h
index b1f81498b2..a769707163 100644
--- a/src/qt/transactiontablemodel.h
+++ b/src/qt/transactiontablemodel.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_TRANSACTIONTABLEMODEL_H
#define BITCOIN_QT_TRANSACTIONTABLEMODEL_H
-#include "bitcoinunits.h"
+#include <qt/bitcoinunits.h>
#include <QAbstractTableModel>
#include <QStringList>
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 53c38da9db..b7e39e05ec 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -2,23 +2,22 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "transactionview.h"
-
-#include "addresstablemodel.h"
-#include "bitcoinunits.h"
-#include "csvmodelwriter.h"
-#include "editaddressdialog.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
-#include "platformstyle.h"
-#include "sendcoinsdialog.h"
-#include "transactiondescdialog.h"
-#include "transactionfilterproxy.h"
-#include "transactionrecord.h"
-#include "transactiontablemodel.h"
-#include "walletmodel.h"
-
-#include "ui_interface.h"
+#include <qt/transactionview.h>
+
+#include <qt/addresstablemodel.h>
+#include <qt/bitcoinunits.h>
+#include <qt/csvmodelwriter.h>
+#include <qt/editaddressdialog.h>
+#include <qt/optionsmodel.h>
+#include <qt/platformstyle.h>
+#include <qt/sendcoinsdialog.h>
+#include <qt/transactiondescdialog.h>
+#include <qt/transactionfilterproxy.h>
+#include <qt/transactionrecord.h>
+#include <qt/transactiontablemodel.h>
+#include <qt/walletmodel.h>
+
+#include <ui_interface.h>
#include <QComboBox>
#include <QDateTimeEdit>
@@ -33,6 +32,7 @@
#include <QScrollBar>
#include <QSignalMapper>
#include <QTableView>
+#include <QTimer>
#include <QUrl>
#include <QVBoxLayout>
@@ -94,11 +94,11 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
hlayout->addWidget(typeWidget);
- addressWidget = new QLineEdit(this);
+ search_widget = new QLineEdit(this);
#if QT_VERSION >= 0x040700
- addressWidget->setPlaceholderText(tr("Enter address or label to search"));
+ search_widget->setPlaceholderText(tr("Enter address, transaction id, or label to search"));
#endif
- hlayout->addWidget(addressWidget);
+ hlayout->addWidget(search_widget);
amountWidget = new QLineEdit(this);
#if QT_VERSION >= 0x040700
@@ -112,6 +112,17 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this));
hlayout->addWidget(amountWidget);
+ // Delay before filtering transactions in ms
+ static const int input_filter_delay = 200;
+
+ QTimer* amount_typing_delay = new QTimer(this);
+ amount_typing_delay->setSingleShot(true);
+ amount_typing_delay->setInterval(input_filter_delay);
+
+ QTimer* prefix_typing_delay = new QTimer(this);
+ prefix_typing_delay->setSingleShot(true);
+ prefix_typing_delay->setInterval(input_filter_delay);
+
QVBoxLayout *vlayout = new QVBoxLayout(this);
vlayout->setContentsMargins(0,0,0,0);
vlayout->setSpacing(0);
@@ -173,8 +184,10 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int)));
connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int)));
connect(watchOnlyWidget, SIGNAL(activated(int)), this, SLOT(chooseWatchonly(int)));
- connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString)));
- connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString)));
+ connect(amountWidget, SIGNAL(textChanged(QString)), amount_typing_delay, SLOT(start()));
+ connect(amount_typing_delay, SIGNAL(timeout()), this, SLOT(changedAmount()));
+ connect(search_widget, SIGNAL(textChanged(QString)), prefix_typing_delay, SLOT(start()));
+ connect(prefix_typing_delay, SIGNAL(timeout()), this, SLOT(changedSearch()));
connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex)));
connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
@@ -312,20 +325,19 @@ void TransactionView::chooseWatchonly(int idx)
(TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx).toInt());
}
-void TransactionView::changedPrefix(const QString &prefix)
+void TransactionView::changedSearch()
{
if(!transactionProxyModel)
return;
- transactionProxyModel->setAddressPrefix(prefix);
+ transactionProxyModel->setSearchString(search_widget->text());
}
-void TransactionView::changedAmount(const QString &amount)
+void TransactionView::changedAmount()
{
if(!transactionProxyModel)
return;
CAmount amount_parsed = 0;
- if(BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount, &amount_parsed))
- {
+ if (BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amountWidget->text(), &amount_parsed)) {
transactionProxyModel->setMinAmount(amount_parsed);
}
else
diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h
index 52e57cae4c..f72a828b00 100644
--- a/src/qt/transactionview.h
+++ b/src/qt/transactionview.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_TRANSACTIONVIEW_H
#define BITCOIN_QT_TRANSACTIONVIEW_H
-#include "guiutil.h"
+#include <qt/guiutil.h>
#include <QWidget>
#include <QKeyEvent>
@@ -66,7 +66,7 @@ private:
QComboBox *dateWidget;
QComboBox *typeWidget;
QComboBox *watchOnlyWidget;
- QLineEdit *addressWidget;
+ QLineEdit *search_widget;
QLineEdit *amountWidget;
QMenu *contextMenu;
@@ -112,8 +112,8 @@ public Q_SLOTS:
void chooseDate(int idx);
void chooseType(int idx);
void chooseWatchonly(int idx);
- void changedPrefix(const QString &prefix);
- void changedAmount(const QString &amount);
+ void changedAmount();
+ void changedSearch();
void exportClicked();
void focusTransaction(const QModelIndex&);
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index 5d8c23d13c..6d5a2f3101 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -3,23 +3,23 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "utilitydialog.h"
+#include <qt/utilitydialog.h>
-#include "ui_helpmessagedialog.h"
+#include <qt/forms/ui_helpmessagedialog.h>
-#include "bitcoingui.h"
-#include "clientmodel.h"
-#include "guiconstants.h"
-#include "intro.h"
-#include "paymentrequestplus.h"
-#include "guiutil.h"
+#include <qt/bitcoingui.h>
+#include <qt/clientmodel.h>
+#include <qt/guiconstants.h>
+#include <qt/intro.h>
+#include <qt/paymentrequestplus.h>
+#include <qt/guiutil.h>
-#include "clientversion.h"
-#include "init.h"
-#include "util.h"
+#include <clientversion.h>
+#include <init.h>
+#include <util.h>
#include <stdio.h>
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index f3183320f0..573b3f1412 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -2,11 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "walletframe.h"
+#include <qt/walletframe.h>
-#include "bitcoingui.h"
-#include "walletview.h"
+#include <qt/bitcoingui.h>
+#include <qt/walletview.h>
+#include <cassert>
#include <cstdio>
#include <QHBoxLayout>
@@ -69,6 +70,7 @@ bool WalletFrame::setCurrentWallet(const QString& name)
WalletView *walletView = mapWalletViews.value(name);
walletStack->setCurrentWidget(walletView);
+ assert(walletView);
walletView->updateEncryptionStatus();
return true;
}
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index a3f243a25f..a38e233608 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -2,32 +2,32 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "walletmodel.h"
-
-#include "addresstablemodel.h"
-#include "consensus/validation.h"
-#include "guiconstants.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
-#include "paymentserver.h"
-#include "recentrequeststablemodel.h"
-#include "sendcoinsdialog.h"
-#include "transactiontablemodel.h"
-
-#include "base58.h"
-#include "chain.h"
-#include "keystore.h"
-#include "validation.h"
-#include "net.h" // for g_connman
-#include "policy/fees.h"
-#include "policy/rbf.h"
-#include "sync.h"
-#include "ui_interface.h"
-#include "util.h" // for GetBoolArg
-#include "wallet/coincontrol.h"
-#include "wallet/feebumper.h"
-#include "wallet/wallet.h"
-#include "wallet/walletdb.h" // for BackupWallet
+#include <qt/walletmodel.h>
+
+#include <qt/addresstablemodel.h>
+#include <consensus/validation.h>
+#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
+#include <qt/paymentserver.h>
+#include <qt/recentrequeststablemodel.h>
+#include <qt/sendcoinsdialog.h>
+#include <qt/transactiontablemodel.h>
+
+#include <base58.h>
+#include <chain.h>
+#include <keystore.h>
+#include <validation.h>
+#include <net.h> // for g_connman
+#include <policy/fees.h>
+#include <policy/rbf.h>
+#include <sync.h>
+#include <ui_interface.h>
+#include <util.h> // for GetBoolArg
+#include <wallet/coincontrol.h>
+#include <wallet/feebumper.h>
+#include <wallet/wallet.h>
+#include <wallet/walletdb.h> // for BackupWallet
#include <stdint.h>
@@ -188,8 +188,7 @@ void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly)
bool WalletModel::validateAddress(const QString &address)
{
- CBitcoinAddress addressParsed(address.toStdString());
- return addressParsed.IsValid();
+ return IsValidDestinationString(address.toStdString());
}
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl)
@@ -247,7 +246,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
setAddress.insert(rcp.address);
++nAddresses;
- CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get());
+ CScript scriptPubKey = GetScriptForDestination(DecodeDestination(rcp.address.toStdString()));
CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount};
vecSend.push_back(recipient);
@@ -348,7 +347,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
if (!rcp.paymentRequest.IsInitialized())
{
std::string strAddress = rcp.address.toStdString();
- CTxDestination dest = CBitcoinAddress(strAddress).Get();
+ CTxDestination dest = DecodeDestination(strAddress);
std::string strLabel = rcp.label.toStdString();
{
LOCK(wallet->cs_wallet);
@@ -464,7 +463,7 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
const CTxDestination &address, const std::string &label, bool isMine,
const std::string &purpose, ChangeType status)
{
- QString strAddress = QString::fromStdString(CBitcoinAddress(address).ToString());
+ QString strAddress = QString::fromStdString(EncodeDestination(address));
QString strLabel = QString::fromStdString(label);
QString strPurpose = QString::fromStdString(purpose);
@@ -596,7 +595,7 @@ bool WalletModel::isSpent(const COutPoint& outpoint) const
void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const
{
for (auto& group : wallet->ListCoins()) {
- auto& resultGroup = mapCoins[QString::fromStdString(CBitcoinAddress(group.first).ToString())];
+ auto& resultGroup = mapCoins[QString::fromStdString(EncodeDestination(group.first))];
for (auto& coin : group.second) {
resultGroup.emplace_back(std::move(coin));
}
@@ -634,7 +633,7 @@ void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests
bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
{
- CTxDestination dest = CBitcoinAddress(sAddress).Get();
+ CTxDestination dest = DecodeDestination(sAddress);
std::stringstream ss;
ss << nId;
@@ -660,45 +659,39 @@ bool WalletModel::abandonTransaction(uint256 hash) const
bool WalletModel::transactionCanBeBumped(uint256 hash) const
{
- LOCK2(cs_main, wallet->cs_wallet);
- const CWalletTx *wtx = wallet->GetWalletTx(hash);
- return wtx && SignalsOptInRBF(*wtx) && !wtx->mapValue.count("replaced_by_txid");
+ return feebumper::TransactionCanBeBumped(wallet, hash);
}
bool WalletModel::bumpFee(uint256 hash)
{
- std::unique_ptr<CFeeBumper> feeBump;
- {
- CCoinControl coin_control;
- coin_control.signalRbf = true;
- LOCK2(cs_main, wallet->cs_wallet);
- feeBump.reset(new CFeeBumper(wallet, hash, coin_control, 0));
- }
- if (feeBump->getResult() != BumpFeeResult::OK)
- {
+ CCoinControl coin_control;
+ coin_control.signalRbf = true;
+ std::vector<std::string> errors;
+ CAmount old_fee;
+ CAmount new_fee;
+ CMutableTransaction mtx;
+ if (feebumper::CreateTransaction(wallet, hash, coin_control, 0 /* totalFee */, errors, old_fee, new_fee, mtx) != feebumper::Result::OK) {
QMessageBox::critical(0, tr("Fee bump error"), tr("Increasing transaction fee failed") + "<br />(" +
- (feeBump->getErrors().size() ? QString::fromStdString(feeBump->getErrors()[0]) : "") +")");
+ (errors.size() ? QString::fromStdString(errors[0]) : "") +")");
return false;
}
// allow a user based fee verification
QString questionString = tr("Do you want to increase the fee?");
questionString.append("<br />");
- CAmount oldFee = feeBump->getOldFee();
- CAmount newFee = feeBump->getNewFee();
questionString.append("<table style=\"text-align: left;\">");
questionString.append("<tr><td>");
questionString.append(tr("Current fee:"));
questionString.append("</td><td>");
- questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), oldFee));
+ questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), old_fee));
questionString.append("</td></tr><tr><td>");
questionString.append(tr("Increase:"));
questionString.append("</td><td>");
- questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), newFee - oldFee));
+ questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), new_fee - old_fee));
questionString.append("</td></tr><tr><td>");
questionString.append(tr("New fee:"));
questionString.append("</td><td>");
- questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), newFee));
+ questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), new_fee));
questionString.append("</td></tr></table>");
SendConfirmationDialog confirmationDialog(tr("Confirm fee bump"), questionString);
confirmationDialog.exec();
@@ -716,23 +709,15 @@ bool WalletModel::bumpFee(uint256 hash)
}
// sign bumped transaction
- bool res = false;
- {
- LOCK2(cs_main, wallet->cs_wallet);
- res = feeBump->signTransaction(wallet);
- }
- if (!res) {
+ if (!feebumper::SignTransaction(wallet, mtx)) {
QMessageBox::critical(0, tr("Fee bump error"), tr("Can't sign transaction."));
return false;
}
// commit the bumped transaction
- {
- LOCK2(cs_main, wallet->cs_wallet);
- res = feeBump->commit(wallet);
- }
- if(!res) {
+ uint256 txid;
+ if (feebumper::CommitTransaction(wallet, hash, std::move(mtx), errors, txid) != feebumper::Result::OK) {
QMessageBox::critical(0, tr("Fee bump error"), tr("Could not commit transaction") + "<br />(" +
- QString::fromStdString(feeBump->getErrors()[0])+")");
+ QString::fromStdString(errors[0])+")");
return false;
}
return true;
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 05733f8272..028146c187 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -5,10 +5,10 @@
#ifndef BITCOIN_QT_WALLETMODEL_H
#define BITCOIN_QT_WALLETMODEL_H
-#include "paymentrequestplus.h"
-#include "walletmodeltransaction.h"
+#include <qt/paymentrequestplus.h>
+#include <qt/walletmodeltransaction.h>
-#include "support/allocators/secure.h"
+#include <support/allocators/secure.h>
#include <map>
#include <vector>
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index eae2c27f8a..2188b39b92 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -2,15 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "walletmodeltransaction.h"
+#include <qt/walletmodeltransaction.h>
-#include "policy/policy.h"
-#include "wallet/wallet.h"
+#include <policy/policy.h>
+#include <wallet/wallet.h>
WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &_recipients) :
recipients(_recipients),
walletTransaction(0),
- keyChange(0),
fee(0)
{
walletTransaction = new CWalletTx();
@@ -18,7 +17,6 @@ WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &
WalletModelTransaction::~WalletModelTransaction()
{
- delete keyChange;
delete walletTransaction;
}
@@ -34,7 +32,7 @@ CWalletTx *WalletModelTransaction::getTransaction() const
unsigned int WalletModelTransaction::getTransactionSize()
{
- return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction));
+ return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction->tx));
}
CAmount WalletModelTransaction::getTransactionFee() const
@@ -91,10 +89,10 @@ CAmount WalletModelTransaction::getTotalTransactionAmount() const
void WalletModelTransaction::newPossibleKeyChange(CWallet *wallet)
{
- keyChange = new CReserveKey(wallet);
+ keyChange.reset(new CReserveKey(wallet));
}
CReserveKey *WalletModelTransaction::getPossibleKeyChange()
{
- return keyChange;
+ return keyChange.get();
}
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
index d7ecd7aa8c..519cf9df99 100644
--- a/src/qt/walletmodeltransaction.h
+++ b/src/qt/walletmodeltransaction.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_WALLETMODELTRANSACTION_H
#define BITCOIN_QT_WALLETMODELTRANSACTION_H
-#include "walletmodel.h"
+#include <qt/walletmodel.h>
#include <QObject>
@@ -40,7 +40,7 @@ public:
private:
QList<SendCoinsRecipient> recipients;
CWalletTx *walletTransaction;
- CReserveKey *keyChange;
+ std::unique_ptr<CReserveKey> keyChange;
CAmount fee;
};
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 971f5e0e1a..a589fad9bd 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -2,24 +2,24 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "walletview.h"
-
-#include "addressbookpage.h"
-#include "askpassphrasedialog.h"
-#include "bitcoingui.h"
-#include "clientmodel.h"
-#include "guiutil.h"
-#include "optionsmodel.h"
-#include "overviewpage.h"
-#include "platformstyle.h"
-#include "receivecoinsdialog.h"
-#include "sendcoinsdialog.h"
-#include "signverifymessagedialog.h"
-#include "transactiontablemodel.h"
-#include "transactionview.h"
-#include "walletmodel.h"
-
-#include "ui_interface.h"
+#include <qt/walletview.h>
+
+#include <qt/addressbookpage.h>
+#include <qt/askpassphrasedialog.h>
+#include <qt/bitcoingui.h>
+#include <qt/clientmodel.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
+#include <qt/overviewpage.h>
+#include <qt/platformstyle.h>
+#include <qt/receivecoinsdialog.h>
+#include <qt/sendcoinsdialog.h>
+#include <qt/signverifymessagedialog.h>
+#include <qt/transactiontablemodel.h>
+#include <qt/transactionview.h>
+#include <qt/walletmodel.h>
+
+#include <ui_interface.h>
#include <QAction>
#include <QActionGroup>
@@ -122,8 +122,8 @@ void WalletView::setWalletModel(WalletModel *_walletModel)
overviewPage->setWalletModel(_walletModel);
receiveCoinsPage->setModel(_walletModel);
sendCoinsPage->setModel(_walletModel);
- usedReceivingAddressesPage->setModel(_walletModel->getAddressTableModel());
- usedSendingAddressesPage->setModel(_walletModel->getAddressTableModel());
+ usedReceivingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr);
+ usedSendingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr);
if (_walletModel)
{
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index c1f8422f0c..4c90874135 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_WALLETVIEW_H
#define BITCOIN_QT_WALLETVIEW_H
-#include "amount.h"
+#include <amount.h>
#include <QStackedWidget>
diff --git a/src/qt/winshutdownmonitor.cpp b/src/qt/winshutdownmonitor.cpp
index d78d9a2358..aa6c273431 100644
--- a/src/qt/winshutdownmonitor.cpp
+++ b/src/qt/winshutdownmonitor.cpp
@@ -2,11 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "winshutdownmonitor.h"
+#include <qt/winshutdownmonitor.h>
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
-#include "init.h"
-#include "util.h"
+#include <init.h>
+#include <util.h>
#include <windows.h>
diff --git a/src/random.cpp b/src/random.cpp
index 7e0e94439e..3075a77182 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -3,16 +3,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "random.h"
+#include <random.h>
-#include "crypto/sha512.h"
-#include "support/cleanse.h"
+#include <crypto/sha512.h>
+#include <support/cleanse.h>
#ifdef WIN32
-#include "compat.h" // for Windows API
+#include <compat.h> // for Windows API
#include <wincrypt.h>
#endif
-#include "util.h" // for LogPrint()
-#include "utilstrencodings.h" // for GetTime()
+#include <util.h> // for LogPrint()
+#include <utilstrencodings.h> // for GetTime()
#include <stdlib.h>
#include <limits>
diff --git a/src/random.h b/src/random.h
index c60ab36179..a984545467 100644
--- a/src/random.h
+++ b/src/random.h
@@ -6,9 +6,9 @@
#ifndef BITCOIN_RANDOM_H
#define BITCOIN_RANDOM_H
-#include "crypto/chacha20.h"
-#include "crypto/common.h"
-#include "uint256.h"
+#include <crypto/chacha20.h>
+#include <crypto/common.h>
+#include <uint256.h>
#include <stdint.h>
@@ -128,7 +128,7 @@ public:
* sure that the underlying OS APIs for all platforms support the number.
* (many cap out at 256 bytes).
*/
-static const ssize_t NUM_OS_RANDOM_BYTES = 32;
+static const int NUM_OS_RANDOM_BYTES = 32;
/** Get 32 bytes of system entropy. Do not use this in application code: use
* GetStrongRandBytes instead.
diff --git a/src/rest.cpp b/src/rest.cpp
index 154ee04eed..86ee7804a3 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -3,20 +3,20 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "chain.h"
-#include "chainparams.h"
-#include "core_io.h"
-#include "primitives/block.h"
-#include "primitives/transaction.h"
-#include "validation.h"
-#include "httpserver.h"
-#include "rpc/blockchain.h"
-#include "rpc/server.h"
-#include "streams.h"
-#include "sync.h"
-#include "txmempool.h"
-#include "utilstrencodings.h"
-#include "version.h"
+#include <chain.h>
+#include <chainparams.h>
+#include <core_io.h>
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <validation.h>
+#include <httpserver.h>
+#include <rpc/blockchain.h>
+#include <rpc/server.h>
+#include <streams.h>
+#include <sync.h>
+#include <txmempool.h>
+#include <utilstrencodings.h>
+#include <version.h>
#include <boost/algorithm/string.hpp>
@@ -178,8 +178,11 @@ static bool rest_headers(HTTPRequest* req,
}
case RF_JSON: {
UniValue jsonHeaders(UniValue::VARR);
- for (const CBlockIndex *pindex : headers) {
- jsonHeaders.push_back(blockheaderToJSON(pindex));
+ {
+ LOCK(cs_main);
+ for (const CBlockIndex *pindex : headers) {
+ jsonHeaders.push_back(blockheaderToJSON(pindex));
+ }
}
std::string strJSON = jsonHeaders.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
@@ -190,9 +193,6 @@ static bool rest_headers(HTTPRequest* req,
return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)");
}
}
-
- // not reached
- return true; // continue to process further HTTP reqs on this cxn
}
static bool rest_block(HTTPRequest* req,
@@ -242,7 +242,11 @@ static bool rest_block(HTTPRequest* req,
}
case RF_JSON: {
- UniValue objBlock = blockToJSON(block, pblockindex, showTxDetails);
+ UniValue objBlock;
+ {
+ LOCK(cs_main);
+ objBlock = blockToJSON(block, pblockindex, showTxDetails);
+ }
std::string strJSON = objBlock.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);
@@ -253,9 +257,6 @@ static bool rest_block(HTTPRequest* req,
return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
}
}
-
- // not reached
- return true; // continue to process further HTTP reqs on this cxn
}
static bool rest_block_extended(HTTPRequest* req, const std::string& strURIPart)
@@ -292,9 +293,6 @@ static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart)
return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
}
}
-
- // not reached
- return true; // continue to process further HTTP reqs on this cxn
}
static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart)
@@ -317,9 +315,6 @@ static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart)
return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
}
}
-
- // not reached
- return true; // continue to process further HTTP reqs on this cxn
}
static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPart)
@@ -342,9 +337,6 @@ static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPar
return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
}
}
-
- // not reached
- return true; // continue to process further HTTP reqs on this cxn
}
static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
@@ -394,9 +386,6 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
}
}
-
- // not reached
- return true; // continue to process further HTTP reqs on this cxn
}
static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
@@ -427,10 +416,8 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
if (uriParts.size() > 0)
{
-
//inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
- if (uriParts.size() > 0 && uriParts[0] == "checkmempool")
- fCheckMemPool = true;
+ if (uriParts[0] == "checkmempool") fCheckMemPool = true;
for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++)
{
@@ -581,9 +568,6 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
}
}
-
- // not reached
- return true; // continue to process further HTTP reqs on this cxn
}
static const struct {
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index ef61e5a55d..b7895b86f7 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -3,27 +3,28 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "rpc/blockchain.h"
-
-#include "amount.h"
-#include "chain.h"
-#include "chainparams.h"
-#include "checkpoints.h"
-#include "coins.h"
-#include "consensus/validation.h"
-#include "validation.h"
-#include "core_io.h"
-#include "policy/feerate.h"
-#include "policy/policy.h"
-#include "primitives/transaction.h"
-#include "rpc/server.h"
-#include "streams.h"
-#include "sync.h"
-#include "txdb.h"
-#include "txmempool.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "hash.h"
+#include <rpc/blockchain.h>
+
+#include <amount.h>
+#include <chain.h>
+#include <chainparams.h>
+#include <checkpoints.h>
+#include <coins.h>
+#include <consensus/validation.h>
+#include <validation.h>
+#include <core_io.h>
+#include <policy/feerate.h>
+#include <policy/policy.h>
+#include <primitives/transaction.h>
+#include <rpc/server.h>
+#include <streams.h>
+#include <sync.h>
+#include <txdb.h>
+#include <txmempool.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <hash.h>
+#include <warnings.h>
#include <stdint.h>
@@ -77,6 +78,7 @@ double GetDifficulty(const CBlockIndex* blockindex)
UniValue blockheaderToJSON(const CBlockIndex* blockindex)
{
+ AssertLockHeld(cs_main);
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex()));
int confirmations = -1;
@@ -105,6 +107,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex)
UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails)
{
+ AssertLockHeld(cs_main);
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex()));
int confirmations = -1;
@@ -343,6 +346,7 @@ std::string EntryDescriptionString()
" \"ancestorcount\" : n, (numeric) number of in-mempool ancestor transactions (including this one)\n"
" \"ancestorsize\" : n, (numeric) virtual transaction size of in-mempool ancestors (including this one)\n"
" \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one)\n"
+ " \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n"
" \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n"
" \"transactionid\", (string) parent transaction id\n"
" ... ]\n";
@@ -363,6 +367,7 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e)
info.push_back(Pair("ancestorcount", e.GetCountWithAncestors()));
info.push_back(Pair("ancestorsize", e.GetSizeWithAncestors()));
info.push_back(Pair("ancestorfees", e.GetModFeesWithAncestors()));
+ info.push_back(Pair("wtxid", mempool.vTxHashes[e.vTxHashesIdx].first.ToString()));
const CTransaction& tx = e.GetTx();
std::set<std::string> setDepends;
for (const CTxIn& txin : tx.vin)
@@ -812,6 +817,7 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash,
static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
{
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
+ assert(pcursor);
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
stats.hashBlock = pcursor->GetBestBlock();
@@ -922,7 +928,7 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request)
CCoinsStats stats;
FlushStateToDisk();
- if (GetUTXOStats(pcoinsdbview, stats)) {
+ if (GetUTXOStats(pcoinsdbview.get(), stats)) {
ret.push_back(Pair("height", (int64_t)stats.nHeight));
ret.push_back(Pair("bestblock", stats.hashBlock.GetHex()));
ret.push_back(Pair("transactions", (int64_t)stats.nTransactions));
@@ -990,7 +996,7 @@ UniValue gettxout(const JSONRPCRequest& request)
Coin coin;
if (fMempool) {
LOCK(mempool.cs);
- CCoinsViewMemPool view(pcoinsTip, mempool);
+ CCoinsViewMemPool view(pcoinsTip.get(), mempool);
if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
return NullUniValue;
}
@@ -1042,7 +1048,7 @@ UniValue verifychain(const JSONRPCRequest& request)
if (!request.params[1].isNull())
nCheckDepth = request.params[1].get_int();
- return CVerifyDB().VerifyDB(Params(), pcoinsTip, nCheckLevel, nCheckDepth);
+ return CVerifyDB().VerifyDB(Params(), pcoinsTip.get(), nCheckLevel, nCheckDepth);
}
/** Implementation of IsSuperMajority with better feedback */
@@ -1107,13 +1113,13 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
return rv;
}
-void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
+void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
{
// Deployments with timeout value of 0 are hidden.
// A timeout value of 0 guarantees a softfork will never be activated.
// This is used when softfork codes are merged without specifying the deployment schedule.
if (consensusParams.vDeployments[id].nTimeout > 0)
- bip9_softforks.push_back(Pair(name, BIP9SoftForkDesc(consensusParams, id)));
+ bip9_softforks.push_back(Pair(VersionBitsDeploymentInfo[id].name, BIP9SoftForkDesc(consensusParams, id)));
}
UniValue getblockchaininfo(const JSONRPCRequest& request)
@@ -1124,41 +1130,46 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
"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"
- " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n"
- " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n"
- " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n"
- " \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
- " \"mediantime\": xxxxxx, (numeric) median time for the current best block\n"
+ " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
+ " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n"
+ " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n"
+ " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n"
+ " \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
+ " \"mediantime\": xxxxxx, (numeric) median time for the current best block\n"
" \"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) lowest-height complete block stored\n"
- " \"softforks\": [ (array) status of softforks in progress\n"
+ " \"initialblockdownload\": xxxx, (bool) (debug information) estimate of whether this node is in Initial Block Download mode.\n"
+ " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n"
+ " \"size_on_disk\": xxxxxx, (numeric) the estimated size of the block and undo files on disk\n"
+ " \"pruned\": xx, (boolean) if the blocks are subject to pruning\n"
+ " \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n"
+ " \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n"
+ " \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n"
+ " \"softforks\": [ (array) status of softforks in progress\n"
" {\n"
- " \"id\": \"xxxx\", (string) name of softfork\n"
- " \"version\": xx, (numeric) block version\n"
- " \"reject\": { (object) progress toward rejecting pre-softfork blocks\n"
- " \"status\": xx, (boolean) true if threshold reached\n"
+ " \"id\": \"xxxx\", (string) name of softfork\n"
+ " \"version\": xx, (numeric) block version\n"
+ " \"reject\": { (object) progress toward rejecting pre-softfork blocks\n"
+ " \"status\": xx, (boolean) true if threshold reached\n"
" },\n"
" }, ...\n"
" ],\n"
- " \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n"
- " \"xxxx\" : { (string) name of the softfork\n"
- " \"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"
- " \"since\": xx, (numeric) height of the first block to which the status applies\n"
- " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n"
- " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n"
- " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
- " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
- " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n"
- " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n"
+ " \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n"
+ " \"xxxx\" : { (string) name of the softfork\n"
+ " \"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"
+ " \"since\": xx, (numeric) height of the first block to which the status applies\n"
+ " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n"
+ " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n"
+ " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
+ " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
+ " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n"
+ " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n"
" }\n"
" }\n"
" }\n"
+ " \"warnings\" : \"...\", (string) any network and blockchain warnings.\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getblockchaininfo", "")
@@ -1175,8 +1186,26 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast()));
obj.push_back(Pair("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip())));
+ obj.push_back(Pair("initialblockdownload", IsInitialBlockDownload()));
obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex()));
+ obj.push_back(Pair("size_on_disk", CalculateCurrentUsage()));
obj.push_back(Pair("pruned", fPruneMode));
+ if (fPruneMode) {
+ CBlockIndex* block = chainActive.Tip();
+ assert(block);
+ while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
+ block = block->pprev;
+ }
+
+ obj.push_back(Pair("pruneheight", block->nHeight));
+
+ // if 0, execution bypasses the whole if block.
+ bool automatic_pruning = (gArgs.GetArg("-prune", 0) != 1);
+ obj.push_back(Pair("automatic_pruning", automatic_pruning));
+ if (automatic_pruning) {
+ obj.push_back(Pair("prune_target_size", nPruneTarget));
+ }
+ }
const Consensus::Params& consensusParams = Params().GetConsensus();
CBlockIndex* tip = chainActive.Tip();
@@ -1185,19 +1214,13 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams));
softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams));
- BIP9SoftForkDescPushBack(bip9_softforks, "csv", consensusParams, Consensus::DEPLOYMENT_CSV);
- BIP9SoftForkDescPushBack(bip9_softforks, "segwit", consensusParams, Consensus::DEPLOYMENT_SEGWIT);
+ for (int pos = Consensus::DEPLOYMENT_CSV; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) {
+ BIP9SoftForkDescPushBack(bip9_softforks, consensusParams, static_cast<Consensus::DeploymentPos>(pos));
+ }
obj.push_back(Pair("softforks", softforks));
obj.push_back(Pair("bip9_softforks", bip9_softforks));
- if (fPruneMode)
- {
- CBlockIndex *block = chainActive.Tip();
- while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA))
- block = block->pprev;
-
- obj.push_back(Pair("pruneheight", block->nHeight));
- }
+ obj.push_back(Pair("warnings", GetWarnings("statusbar")));
return obj;
}
@@ -1478,9 +1501,12 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
"2. \"blockhash\" (string, optional) The hash of the block that ends the window.\n"
"\nResult:\n"
"{\n"
- " \"time\": xxxxx, (numeric) The timestamp for the statistics in UNIX format.\n"
- " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n"
- " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window.\n"
+ " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n"
+ " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n"
+ " \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n"
+ " \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n"
+ " \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n"
+ " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0.\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getchaintxstats", "")
@@ -1490,10 +1516,6 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
const CBlockIndex* pindex;
int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
- if (!request.params[0].isNull()) {
- blockcount = request.params[0].get_int();
- }
-
bool havehash = !request.params[1].isNull();
uint256 hash;
if (havehash) {
@@ -1515,9 +1537,17 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
pindex = chainActive.Tip();
}
}
+
+ assert(pindex != nullptr);
- if (blockcount < 1 || blockcount >= pindex->nHeight) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 1 and the block's height");
+ if (request.params[0].isNull()) {
+ blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1));
+ } else {
+ blockcount = request.params[0].get_int();
+
+ if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1");
+ }
}
const CBlockIndex* pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount);
@@ -1527,11 +1557,37 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("time", (int64_t)pindex->nTime));
ret.push_back(Pair("txcount", (int64_t)pindex->nChainTx));
- ret.push_back(Pair("txrate", ((double)nTxDiff) / nTimeDiff));
+ ret.push_back(Pair("window_block_count", blockcount));
+ if (blockcount > 0) {
+ ret.push_back(Pair("window_tx_count", nTxDiff));
+ ret.push_back(Pair("window_interval", nTimeDiff));
+ if (nTimeDiff > 0) {
+ ret.push_back(Pair("txrate", ((double)nTxDiff) / nTimeDiff));
+ }
+ }
return ret;
}
+UniValue savemempool(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 0) {
+ throw std::runtime_error(
+ "savemempool\n"
+ "\nDumps the mempool to disk.\n"
+ "\nExamples:\n"
+ + HelpExampleCli("savemempool", "")
+ + HelpExampleRpc("savemempool", "")
+ );
+ }
+
+ if (!DumpMempool()) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
+ }
+
+ return NullUniValue;
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@@ -1552,6 +1608,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} },
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {} },
{ "blockchain", "pruneblockchain", &pruneblockchain, {"height"} },
+ { "blockchain", "savemempool", &savemempool, {} },
{ "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} },
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index e73319d146..e641493e1e 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -3,15 +3,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "rpc/client.h"
-#include "rpc/protocol.h"
-#include "util.h"
+#include <rpc/client.h>
+#include <rpc/protocol.h>
+#include <util.h>
#include <set>
#include <stdint.h>
-#include <univalue.h>
-
class CRPCConvertParam
{
public:
@@ -117,8 +115,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "keypoolrefill", 0, "newsize" },
{ "getrawmempool", 0, "verbose" },
{ "estimatefee", 0, "nblocks" },
- { "estimatesmartfee", 0, "nblocks" },
- { "estimaterawfee", 0, "nblocks" },
+ { "estimatesmartfee", 0, "conf_target" },
+ { "estimaterawfee", 0, "conf_target" },
{ "estimaterawfee", 1, "threshold" },
{ "prioritisetransaction", 1, "dummy" },
{ "prioritisetransaction", 2, "fee_delta" },
@@ -131,6 +129,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "logging", 0, "include" },
{ "logging", 1, "exclude" },
{ "disconnectnode", 1, "nodeid" },
+ { "addwitnessaddress", 1, "p2sh" },
// Echo with conversion (For testing only)
{ "echojson", 0, "arg0" },
{ "echojson", 1, "arg1" },
@@ -142,6 +141,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "echojson", 7, "arg7" },
{ "echojson", 8, "arg8" },
{ "echojson", 9, "arg9" },
+ { "rescanblockchain", 0, "start_height"},
+ { "rescanblockchain", 1, "stop_height"},
};
class CRPCConvertTable
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 73860f375f..d003be4954 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -3,34 +3,32 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "base58.h"
-#include "amount.h"
-#include "chain.h"
-#include "chainparams.h"
-#include "consensus/consensus.h"
-#include "consensus/params.h"
-#include "consensus/validation.h"
-#include "core_io.h"
-#include "init.h"
-#include "validation.h"
-#include "miner.h"
-#include "net.h"
-#include "policy/fees.h"
-#include "pow.h"
-#include "rpc/blockchain.h"
-#include "rpc/mining.h"
-#include "rpc/server.h"
-#include "txmempool.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "validationinterface.h"
-#include "warnings.h"
+#include <base58.h>
+#include <amount.h>
+#include <chain.h>
+#include <chainparams.h>
+#include <consensus/consensus.h>
+#include <consensus/params.h>
+#include <consensus/validation.h>
+#include <core_io.h>
+#include <init.h>
+#include <validation.h>
+#include <miner.h>
+#include <net.h>
+#include <policy/fees.h>
+#include <pow.h>
+#include <rpc/blockchain.h>
+#include <rpc/mining.h>
+#include <rpc/server.h>
+#include <txmempool.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <validationinterface.h>
+#include <warnings.h>
#include <memory>
#include <stdint.h>
-#include <univalue.h>
-
unsigned int ParseConfirmTarget(const UniValue& value)
{
int target = value.get_int();
@@ -176,12 +174,13 @@ UniValue generatetoaddress(const JSONRPCRequest& request)
nMaxTries = request.params[2].get_int();
}
- CBitcoinAddress address(request.params[1].get_str());
- if (!address.IsValid())
+ CTxDestination destination = DecodeDestination(request.params[1].get_str());
+ if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
+ }
std::shared_ptr<CReserveScript> coinbaseScript = std::make_shared<CReserveScript>();
- coinbaseScript->reserveScript = GetScriptForDestination(address.Get());
+ coinbaseScript->reserveScript = GetScriptForDestination(destination);
return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false);
}
@@ -195,14 +194,14 @@ UniValue getmininginfo(const JSONRPCRequest& request)
"\nResult:\n"
"{\n"
" \"blocks\": nnn, (numeric) The current block\n"
- " \"currentblocksize\": nnn, (numeric) The last block size\n"
" \"currentblockweight\": nnn, (numeric) The last block weight\n"
" \"currentblocktx\": nnn, (numeric) The last block transaction\n"
" \"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 mempool\n"
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
+ " \"warnings\": \"...\" (string) any network and blockchain warnings\n"
+ " \"errors\": \"...\" (string) DEPRECATED. Same as warnings. Only shown when bitcoind is started with -deprecatedrpc=getmininginfo\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getmininginfo", "")
@@ -214,14 +213,17 @@ UniValue getmininginfo(const JSONRPCRequest& request)
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("blocks", (int)chainActive.Height()));
- obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize));
obj.push_back(Pair("currentblockweight", (uint64_t)nLastBlockWeight));
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(request)));
obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));
obj.push_back(Pair("chain", Params().NetworkIDString()));
+ if (IsDeprecatedRPCEnabled("getmininginfo")) {
+ obj.push_back(Pair("errors", GetWarnings("statusbar")));
+ } else {
+ obj.push_back(Pair("warnings", GetWarnings("statusbar")));
+ }
return obj;
}
@@ -451,7 +453,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
{
// Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
uint256 hashWatchedChain;
- boost::system_time checktxtime;
+ std::chrono::steady_clock::time_point checktxtime;
unsigned int nTransactionsUpdatedLastLP;
if (lpval.isStr())
@@ -472,17 +474,17 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
// Release the wallet and main lock while waiting
LEAVE_CRITICAL_SECTION(cs_main);
{
- checktxtime = boost::get_system_time() + boost::posix_time::minutes(1);
+ checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1);
- boost::unique_lock<boost::mutex> lock(csBestBlock);
+ WaitableLock lock(csBestBlock);
while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
{
- if (!cvBlockChange.timed_wait(lock, checktxtime))
+ if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout)
{
// Timeout: Check transactions for update
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
break;
- checktxtime += boost::posix_time::seconds(10);
+ checktxtime += std::chrono::seconds(10);
}
}
}
@@ -790,6 +792,12 @@ UniValue estimatefee(const JSONRPCRequest& request)
+ HelpExampleCli("estimatefee", "6")
);
+ if (!IsDeprecatedRPCEnabled("estimatefee")) {
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee is deprecated and will be fully removed in v0.17. "
+ "To use estimatefee in v0.16, restart bitcoind with -deprecatedrpc=estimatefee.\n"
+ "Projects should transition to using estimatesmartfee before upgrading to v0.17");
+ }
+
RPCTypeCheck(request.params, {UniValue::VNUM});
int nBlocks = request.params[0].get_int();
@@ -979,7 +987,7 @@ static const CRPCCommand commands[] =
{ "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} },
{ "util", "estimatefee", &estimatefee, {"nblocks"} },
- { "util", "estimatesmartfee", &estimatesmartfee, {"nblocks", "estimate_mode"} },
+ { "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} },
{ "hidden", "estimaterawfee", &estimaterawfee, {"conf_target", "threshold"} },
};
diff --git a/src/rpc/mining.h b/src/rpc/mining.h
index 868d7002b5..8d46273159 100644
--- a/src/rpc/mining.h
+++ b/src/rpc/mining.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_RPC_MINING_H
#define BITCOIN_RPC_MINING_H
-#include "script/script.h"
+#include <script/script.h>
#include <univalue.h>
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 76eefabd29..327af2e237 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -3,26 +3,27 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "base58.h"
-#include "chain.h"
-#include "clientversion.h"
-#include "core_io.h"
-#include "init.h"
-#include "validation.h"
-#include "httpserver.h"
-#include "net.h"
-#include "netbase.h"
-#include "rpc/blockchain.h"
-#include "rpc/server.h"
-#include "timedata.h"
-#include "util.h"
-#include "utilstrencodings.h"
+#include <base58.h>
+#include <chain.h>
+#include <clientversion.h>
+#include <core_io.h>
+#include <crypto/ripemd160.h>
+#include <init.h>
+#include <validation.h>
+#include <httpserver.h>
+#include <net.h>
+#include <netbase.h>
+#include <rpc/blockchain.h>
+#include <rpc/server.h>
+#include <timedata.h>
+#include <util.h>
+#include <utilstrencodings.h>
#ifdef ENABLE_WALLET
-#include "wallet/rpcwallet.h"
-#include "wallet/wallet.h"
-#include "wallet/walletdb.h"
+#include <wallet/rpcwallet.h>
+#include <wallet/wallet.h>
+#include <wallet/walletdb.h>
#endif
-#include "warnings.h"
+#include <warnings.h>
#include <stdint.h>
#ifdef HAVE_MALLOC_INFO
@@ -31,94 +32,6 @@
#include <univalue.h>
-/**
- * @note Do not add or change anything in the information returned by this
- * method. `getinfo` exists for backwards-compatibility only. It combines
- * information from wildly different sources in the program, which is a mess,
- * and is thus planned to be deprecated eventually.
- *
- * Based on the source of the information, new information should be added to:
- * - `getblockchaininfo`,
- * - `getnetworkinfo` or
- * - `getwalletinfo`
- *
- * Or alternatively, create a specific query method for the information.
- **/
-UniValue getinfo(const JSONRPCRequest& request)
-{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
- "getinfo\n"
- "\nDEPRECATED. Returns an object containing various state info.\n"
- "\nResult:\n"
- "{\n"
- " \"deprecation-warning\": \"...\" (string) warning that the getinfo command is deprecated and will be removed in 0.16\n"
- " \"version\": xxxxx, (numeric) the server version\n"
- " \"protocolversion\": xxxxx, (numeric) the protocol version\n"
- " \"walletversion\": xxxxx, (numeric) the wallet version\n"
- " \"balance\": xxxxxxx, (numeric) the total bitcoin balance of the wallet\n"
- " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n"
- " \"timeoffset\": xxxxx, (numeric) the time offset\n"
- " \"connections\": xxxxx, (numeric) the number of connections\n"
- " \"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 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"
- " \"relayfee\": x.xxxx, (numeric) minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB\n"
- " \"errors\": \"...\" (string) any error messages\n"
- "}\n"
- "\nExamples:\n"
- + HelpExampleCli("getinfo", "")
- + HelpExampleRpc("getinfo", "")
- );
-
-#ifdef ENABLE_WALLET
- CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
-
- LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr);
-#else
- LOCK(cs_main);
-#endif
-
- proxyType proxy;
- GetProxy(NET_IPV4, proxy);
-
- UniValue obj(UniValue::VOBJ);
- obj.push_back(Pair("deprecation-warning", "WARNING: getinfo is deprecated and will be fully removed in 0.16."
- " Projects should transition to using getblockchaininfo, getnetworkinfo, and getwalletinfo before upgrading to 0.16"));
- obj.push_back(Pair("version", CLIENT_VERSION));
- obj.push_back(Pair("protocolversion", PROTOCOL_VERSION));
-#ifdef ENABLE_WALLET
- if (pwallet) {
- obj.push_back(Pair("walletversion", pwallet->GetVersion()));
- obj.push_back(Pair("balance", ValueFromAmount(pwallet->GetBalance())));
- }
-#endif
- obj.push_back(Pair("blocks", (int)chainActive.Height()));
- obj.push_back(Pair("timeoffset", GetTimeOffset()));
- if(g_connman)
- obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)));
- obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string())));
- obj.push_back(Pair("difficulty", (double)GetDifficulty()));
- obj.push_back(Pair("testnet", Params().NetworkIDString() == CBaseChainParams::TESTNET));
-#ifdef ENABLE_WALLET
- if (pwallet) {
- obj.push_back(Pair("keypoololdest", pwallet->GetOldestKeyPoolTime()));
- obj.push_back(Pair("keypoolsize", (int)pwallet->GetKeyPoolSize()));
- }
- if (pwallet && pwallet->IsCrypted()) {
- obj.push_back(Pair("unlocked_until", pwallet->nRelockTime));
- }
- obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
-#endif
- obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
- obj.push_back(Pair("errors", GetWarnings("statusbar")));
- return obj;
-}
-
#ifdef ENABLE_WALLET
class DescribeAddressVisitor : public boost::static_visitor<UniValue>
{
@@ -133,6 +46,7 @@ public:
UniValue obj(UniValue::VOBJ);
CPubKey vchPubKey;
obj.push_back(Pair("isscript", false));
+ obj.push_back(Pair("iswitness", false));
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
@@ -144,6 +58,7 @@ public:
UniValue obj(UniValue::VOBJ);
CScript subscript;
obj.push_back(Pair("isscript", true));
+ obj.push_back(Pair("iswitness", false));
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
std::vector<CTxDestination> addresses;
txnouttype whichType;
@@ -152,14 +67,56 @@ public:
obj.push_back(Pair("script", GetTxnOutputType(whichType)));
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
UniValue a(UniValue::VARR);
- for (const CTxDestination& addr : addresses)
- a.push_back(CBitcoinAddress(addr).ToString());
+ for (const CTxDestination& addr : addresses) {
+ a.push_back(EncodeDestination(addr));
+ }
obj.push_back(Pair("addresses", a));
if (whichType == TX_MULTISIG)
obj.push_back(Pair("sigsrequired", nRequired));
}
return obj;
}
+
+ UniValue operator()(const WitnessV0KeyHash& id) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ CPubKey pubkey;
+ obj.push_back(Pair("isscript", false));
+ obj.push_back(Pair("iswitness", true));
+ obj.push_back(Pair("witness_version", 0));
+ obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end())));
+ if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) {
+ obj.push_back(Pair("pubkey", HexStr(pubkey)));
+ }
+ return obj;
+ }
+
+ UniValue operator()(const WitnessV0ScriptHash& id) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ CScript subscript;
+ obj.push_back(Pair("isscript", true));
+ obj.push_back(Pair("iswitness", true));
+ obj.push_back(Pair("witness_version", 0));
+ obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end())));
+ CRIPEMD160 hasher;
+ uint160 hash;
+ hasher.Write(id.begin(), 32).Finalize(hash.begin());
+ if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) {
+ obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
+ }
+ return obj;
+ }
+
+ UniValue operator()(const WitnessUnknown& id) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ CScript subscript;
+ obj.push_back(Pair("iswitness", true));
+ obj.push_back(Pair("witness_version", (int)id.version));
+ obj.push_back(Pair("witness_program", HexStr(id.program, id.program + id.length)));
+ return obj;
+ }
};
#endif
@@ -207,15 +164,14 @@ UniValue validateaddress(const JSONRPCRequest& request)
LOCK(cs_main);
#endif
- CBitcoinAddress address(request.params[0].get_str());
- bool isValid = address.IsValid();
+ CTxDestination dest = DecodeDestination(request.params[0].get_str());
+ bool isValid = IsValidDestination(dest);
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("isvalid", isValid));
if (isValid)
{
- CTxDestination dest = address.Get();
- std::string currentAddress = address.ToString();
+ std::string currentAddress = EncodeDestination(dest);
ret.push_back(Pair("address", currentAddress));
CScript scriptPubKey = GetScriptForDestination(dest);
@@ -230,18 +186,25 @@ UniValue validateaddress(const JSONRPCRequest& request)
if (pwallet && pwallet->mapAddressBook.count(dest)) {
ret.push_back(Pair("account", pwallet->mapAddressBook[dest].name));
}
- CKeyID keyID;
if (pwallet) {
- const auto& meta = pwallet->mapKeyMetadata;
- auto it = address.GetKeyID(keyID) ? meta.find(keyID) : meta.end();
- if (it == meta.end()) {
- it = meta.find(CScriptID(scriptPubKey));
+ const CKeyMetadata* meta = nullptr;
+ if (const CKeyID* key_id = boost::get<CKeyID>(&dest)) {
+ auto it = pwallet->mapKeyMetadata.find(*key_id);
+ if (it != pwallet->mapKeyMetadata.end()) {
+ meta = &it->second;
+ }
+ }
+ if (!meta) {
+ auto it = pwallet->m_script_metadata.find(CScriptID(scriptPubKey));
+ if (it != pwallet->m_script_metadata.end()) {
+ meta = &it->second;
+ }
}
- if (it != meta.end()) {
- ret.push_back(Pair("timestamp", it->second.nCreateTime));
- if (!it->second.hdKeypath.empty()) {
- ret.push_back(Pair("hdkeypath", it->second.hdKeypath));
- ret.push_back(Pair("hdmasterkeyid", it->second.hdMasterKeyID.GetHex()));
+ if (meta) {
+ ret.push_back(Pair("timestamp", meta->nCreateTime));
+ if (!meta->hdKeypath.empty()) {
+ ret.push_back(Pair("hdkeypath", meta->hdKeypath));
+ ret.push_back(Pair("hdmasterkeyid", meta->hdMasterKeyID.GetHex()));
}
}
}
@@ -277,16 +240,15 @@ CScript _createmultisig_redeemScript(CWallet * const pwallet, const UniValue& pa
const std::string& ks = keys[i].get_str();
#ifdef ENABLE_WALLET
// Case 1: Bitcoin address and we have full public key:
- CBitcoinAddress address(ks);
- if (pwallet && address.IsValid()) {
- CKeyID keyID;
- if (!address.GetKeyID(keyID))
- throw std::runtime_error(
- strprintf("%s does not refer to a key",ks));
+ CTxDestination dest = DecodeDestination(ks);
+ if (pwallet && IsValidDestination(dest)) {
+ const CKeyID *keyID = boost::get<CKeyID>(&dest);
+ if (!keyID) {
+ throw std::runtime_error(strprintf("%s does not refer to a key", ks));
+ }
CPubKey vchPubKey;
- if (!pwallet->GetPubKey(keyID, vchPubKey)) {
- throw std::runtime_error(
- strprintf("no full public key for address %s",ks));
+ if (!pwallet->GetPubKey(*keyID, vchPubKey)) {
+ throw std::runtime_error(strprintf("no full public key for address %s", ks));
}
if (!vchPubKey.IsFullyValid())
throw std::runtime_error(" Invalid public key: "+ks);
@@ -357,10 +319,9 @@ UniValue createmultisig(const JSONRPCRequest& request)
// Construct using pay-to-script-hash:
CScript inner = _createmultisig_redeemScript(pwallet, request.params);
CScriptID innerID(inner);
- CBitcoinAddress address(innerID);
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("address", address.ToString()));
+ result.push_back(Pair("address", EncodeDestination(innerID)));
result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end())));
return result;
@@ -395,13 +356,15 @@ UniValue verifymessage(const JSONRPCRequest& request)
std::string strSign = request.params[1].get_str();
std::string strMessage = request.params[2].get_str();
- CBitcoinAddress addr(strAddress);
- if (!addr.IsValid())
+ CTxDestination destination = DecodeDestination(strAddress);
+ if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
+ }
- CKeyID keyID;
- if (!addr.GetKeyID(keyID))
+ const CKeyID *keyID = boost::get<CKeyID>(&destination);
+ if (!keyID) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
+ }
bool fInvalid = false;
std::vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
@@ -417,7 +380,7 @@ UniValue verifymessage(const JSONRPCRequest& request)
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
return false;
- return (pubkey.GetID() == keyID);
+ return (pubkey.GetID() == *keyID);
}
UniValue signmessagewithprivkey(const JSONRPCRequest& request)
@@ -459,7 +422,7 @@ UniValue signmessagewithprivkey(const JSONRPCRequest& request)
if (!key.SignCompact(ss.GetHash(), vchSig))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
- return EncodeBase64(&vchSig[0], vchSig.size());
+ return EncodeBase64(vchSig.data(), vchSig.size());
}
UniValue setmocktime(const JSONRPCRequest& request)
@@ -577,6 +540,9 @@ uint32_t getCategoryMask(UniValue cats) {
if (!GetLogCategory(&flag, &cat)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
}
+ if (flag == BCLog::NONE) {
+ return 0;
+ }
mask |= flag;
}
return mask;
@@ -586,16 +552,32 @@ UniValue logging(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
- "logging [include,...] <exclude>\n"
+ "logging ( <include> <exclude> )\n"
"Gets and sets the logging configuration.\n"
- "When called without an argument, returns the list of categories that are currently being debug logged.\n"
- "When called with arguments, adds or removes categories from debug logging.\n"
+ "When called without an argument, returns the list of categories with status that are currently being debug logged or not.\n"
+ "When called with arguments, adds or removes categories from debug logging and return the lists above.\n"
+ "The arguments are evaluated in order \"include\", \"exclude\".\n"
+ "If an item is both included and excluded, it will thus end up being excluded.\n"
"The valid logging categories are: " + ListLogCategories() + "\n"
- "libevent logging is configured on startup and cannot be modified by this RPC during runtime."
- "Arguments:\n"
- "1. \"include\" (array of strings) add debug logging for these categories.\n"
- "2. \"exclude\" (array of strings) remove debug logging for these categories.\n"
- "\nResult: <categories> (string): a list of the logging categories that are active.\n"
+ "In addition, the following are available as category names with special meanings:\n"
+ " - \"all\", \"1\" : represent all logging categories.\n"
+ " - \"none\", \"0\" : even if other logging categories are specified, ignore all of them.\n"
+ "\nArguments:\n"
+ "1. \"include\" (array of strings, optional) A json array of categories to add debug logging\n"
+ " [\n"
+ " \"category\" (string) the valid logging category\n"
+ " ,...\n"
+ " ]\n"
+ "2. \"exclude\" (array of strings, optional) A json array of categories to remove debug logging\n"
+ " [\n"
+ " \"category\" (string) the valid logging category\n"
+ " ,...\n"
+ " ]\n"
+ "\nResult:\n"
+ "{ (json object where keys are the logging categories, and values indicates its status\n"
+ " \"category\": 0|1, (numeric) if being debug logged or not. 0:inactive, 1:active\n"
+ " ...\n"
+ "}\n"
"\nExamples:\n"
+ HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
+ HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"")
@@ -651,8 +633,8 @@ UniValue echo(const JSONRPCRequest& request)
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
- { "control", "getinfo", &getinfo, {} }, /* uses wallet if enabled */
{ "control", "getmemoryinfo", &getmemoryinfo, {"mode"} },
+ { "control", "logging", &logging, {"include", "exclude"}},
{ "util", "validateaddress", &validateaddress, {"address"} }, /* uses wallet if enabled */
{ "util", "createmultisig", &createmultisig, {"nrequired","keys"} },
{ "util", "verifymessage", &verifymessage, {"address","signature","message"} },
@@ -662,7 +644,6 @@ static const CRPCCommand commands[] =
{ "hidden", "setmocktime", &setmocktime, {"timestamp"}},
{ "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
{ "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
- { "hidden", "logging", &logging, {"include", "exclude"}},
};
void RegisterMiscRPCCommands(CRPCTable &t)
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 7faf216047..9de249855c 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -2,24 +2,24 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "rpc/server.h"
-
-#include "chainparams.h"
-#include "clientversion.h"
-#include "core_io.h"
-#include "validation.h"
-#include "net.h"
-#include "net_processing.h"
-#include "netbase.h"
-#include "policy/policy.h"
-#include "protocol.h"
-#include "sync.h"
-#include "timedata.h"
-#include "ui_interface.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "version.h"
-#include "warnings.h"
+#include <rpc/server.h>
+
+#include <chainparams.h>
+#include <clientversion.h>
+#include <core_io.h>
+#include <validation.h>
+#include <net.h>
+#include <net_processing.h>
+#include <netbase.h>
+#include <policy/policy.h>
+#include <rpc/protocol.h>
+#include <sync.h>
+#include <timedata.h>
+#include <ui_interface.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <version.h>
+#include <warnings.h>
#include <univalue.h>
@@ -92,7 +92,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
" \"version\": v, (numeric) The peer version, such as 7001\n"
" \"subver\": \"/Satoshi:0.8.5/\", (string) The string version\n"
" \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n"
- " \"addnode\": true|false, (boolean) Whether connection was due to addnode and is using an addnode slot\n"
+ " \"addnode\": true|false, (boolean) Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n"
" \"startingheight\": n, (numeric) The starting height (block) of the peer\n"
" \"banscore\": n, (numeric) The ban score\n"
" \"synced_headers\": n, (numeric) The last header we have in common with this peer\n"
@@ -146,7 +146,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
obj.push_back(Pair("timeoffset", stats.nTimeOffset));
if (stats.dPingTime > 0.0)
obj.push_back(Pair("pingtime", stats.dPingTime));
- if (stats.dMinPing < std::numeric_limits<int64_t>::max()/1e6)
+ if (stats.dMinPing < static_cast<double>(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));
@@ -156,7 +156,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
// their ver message.
obj.push_back(Pair("subver", stats.cleanSubVer));
obj.push_back(Pair("inbound", stats.fInbound));
- obj.push_back(Pair("addnode", stats.fAddnode));
+ obj.push_back(Pair("addnode", stats.m_manual_connection));
obj.push_back(Pair("startingheight", stats.nStartingHeight));
if (fStateStats) {
obj.push_back(Pair("banscore", statestats.nMisbehavior));
@@ -201,6 +201,8 @@ UniValue addnode(const JSONRPCRequest& request)
"addnode \"node\" \"add|remove|onetry\"\n"
"\nAttempts to add or remove a node from the addnode list.\n"
"Or try a connection to a node once.\n"
+ "Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n"
+ "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n"
"\nArguments:\n"
"1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n"
"2. \"command\" (string, required) 'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once\n"
@@ -217,7 +219,7 @@ UniValue addnode(const JSONRPCRequest& request)
if (strCommand == "onetry")
{
CAddress addr;
- g_connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str());
+ g_connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), false, false, true);
return NullUniValue;
}
@@ -447,7 +449,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request)
" }\n"
" ,...\n"
" ]\n"
- " \"warnings\": \"...\" (string) any network warnings\n"
+ " \"warnings\": \"...\" (string) any network and blockchain warnings\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getnetworkinfo", "")
@@ -569,11 +571,11 @@ UniValue listbanned(const JSONRPCRequest& request)
g_connman->GetBanned(banMap);
UniValue bannedAddresses(UniValue::VARR);
- for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++)
+ for (const auto& entry : banMap)
{
- CBanEntry banEntry = (*it).second;
+ const CBanEntry& banEntry = entry.second;
UniValue rec(UniValue::VOBJ);
- rec.push_back(Pair("address", (*it).first.ToString()));
+ rec.push_back(Pair("address", entry.first.ToString()));
rec.push_back(Pair("banned_until", banEntry.nBanUntil));
rec.push_back(Pair("ban_created", banEntry.nCreateTime));
rec.push_back(Pair("ban_reason", banEntry.banReasonToString()));
diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp
index dc6bcec382..d999a08d74 100644
--- a/src/rpc/protocol.cpp
+++ b/src/rpc/protocol.cpp
@@ -3,23 +3,22 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "rpc/protocol.h"
+#include <rpc/protocol.h>
-#include "random.h"
-#include "tinyformat.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "utiltime.h"
-#include "version.h"
+#include <random.h>
+#include <tinyformat.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <utiltime.h>
+#include <version.h>
-#include <stdint.h>
#include <fstream>
/**
* JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
* but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
* unspecified (HTTP errors and contents of 'error').
- *
+ *
* 1.0 spec: http://json-rpc.org/wiki/specification
* 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html
*/
@@ -135,3 +134,22 @@ void DeleteAuthCookie()
}
}
+std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num)
+{
+ if (!in.isArray()) {
+ throw std::runtime_error("Batch must be an array");
+ }
+ std::vector<UniValue> batch(num);
+ for (size_t i=0; i<in.size(); ++i) {
+ const UniValue &rec = in[i];
+ if (!rec.isObject()) {
+ throw std::runtime_error("Batch member must be object");
+ }
+ size_t id = rec["id"].get_int();
+ if (id >= num) {
+ throw std::runtime_error("Batch member id larger than size");
+ }
+ batch[id] = rec;
+ }
+ return batch;
+}
diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h
index 5c9c64f67d..4b9bd41994 100644
--- a/src/rpc/protocol.h
+++ b/src/rpc/protocol.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_RPCPROTOCOL_H
#define BITCOIN_RPCPROTOCOL_H
-#include "fs.h"
+#include <fs.h>
#include <list>
#include <map>
@@ -57,6 +57,7 @@ enum RPCErrorCode
RPC_VERIFY_REJECTED = -26, //!< Transaction or block was rejected by network rules
RPC_VERIFY_ALREADY_IN_CHAIN = -27, //!< Transaction already in chain
RPC_IN_WARMUP = -28, //!< Client still warming up
+ RPC_METHOD_DEPRECATED = -32, //!< RPC method is deprecated
//! Aliases for backward compatibility
RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR,
@@ -97,5 +98,7 @@ bool GenerateAuthCookie(std::string *cookie_out);
bool GetAuthCookie(std::string *cookie_out);
/** Delete RPC authentication cookie from disk */
void DeleteAuthCookie();
+/** Parse JSON-RPC batch reply into a vector */
+std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num);
#endif // BITCOIN_RPCPROTOCOL_H
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 249b59bdf8..1049131d5e 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -3,33 +3,35 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "base58.h"
-#include "chain.h"
-#include "coins.h"
-#include "consensus/validation.h"
-#include "core_io.h"
-#include "init.h"
-#include "keystore.h"
-#include "validation.h"
-#include "merkleblock.h"
-#include "net.h"
-#include "policy/policy.h"
-#include "policy/rbf.h"
-#include "primitives/transaction.h"
-#include "rpc/safemode.h"
-#include "rpc/server.h"
-#include "script/script.h"
-#include "script/script_error.h"
-#include "script/sign.h"
-#include "script/standard.h"
-#include "txmempool.h"
-#include "uint256.h"
-#include "utilstrencodings.h"
+#include <base58.h>
+#include <chain.h>
+#include <coins.h>
+#include <consensus/validation.h>
+#include <core_io.h>
+#include <init.h>
+#include <keystore.h>
+#include <validation.h>
+#include <validationinterface.h>
+#include <merkleblock.h>
+#include <net.h>
+#include <policy/policy.h>
+#include <policy/rbf.h>
+#include <primitives/transaction.h>
+#include <rpc/safemode.h>
+#include <rpc/server.h>
+#include <script/script.h>
+#include <script/script_error.h>
+#include <script/sign.h>
+#include <script/standard.h>
+#include <txmempool.h>
+#include <uint256.h>
+#include <utilstrencodings.h>
#ifdef ENABLE_WALLET
-#include "wallet/rpcwallet.h"
-#include "wallet/wallet.h"
+#include <wallet/rpcwallet.h>
+#include <wallet/wallet.h>
#endif
+#include <future>
#include <stdint.h>
#include <univalue.h>
@@ -62,12 +64,15 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
UniValue getrawtransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "getrawtransaction \"txid\" ( verbose )\n"
+ "getrawtransaction \"txid\" ( verbose \"blockhash\" )\n"
"\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n"
- "enabled, it also works for blockchain transactions.\n"
+ "enabled, it also works for blockchain transactions. If the block which contains the transaction\n"
+ "is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is\n"
+ "provided, only that block will be searched and if the transaction is in the mempool or other\n"
+ "blocks, or if this node does not have the given block available, the transaction will not be found.\n"
"DEPRECATED: for now, it also works for transactions with unspent outputs.\n"
"\nReturn the raw transaction data.\n"
@@ -76,13 +81,15 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
- "2. verbose (bool, optional, default=false) If false, return a string, otherwise return a json object\n"
+ "2. verbose (bool, optional, default=false) If false, return a string, otherwise return a json object\n"
+ "3. \"blockhash\" (string, optional) The block in which to look for the transaction\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 is set to true):\n"
"{\n"
+ " \"in_active_chain\": b, (bool) Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)\n"
" \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n"
" \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
" \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
@@ -130,42 +137,56 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("getrawtransaction", "\"mytxid\"")
+ HelpExampleCli("getrawtransaction", "\"mytxid\" true")
+ HelpExampleRpc("getrawtransaction", "\"mytxid\", true")
+ + HelpExampleCli("getrawtransaction", "\"mytxid\" false \"myblockhash\"")
+ + HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"")
);
LOCK(cs_main);
+ bool in_active_chain = true;
uint256 hash = ParseHashV(request.params[0], "parameter 1");
+ CBlockIndex* blockindex = nullptr;
// Accept either a bool (true) or a num (>=1) to indicate verbose output.
bool fVerbose = false;
if (!request.params[1].isNull()) {
- 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.");
+ fVerbose = request.params[1].isNum() ? (request.params[1].get_int() != 0) : request.params[1].get_bool();
+ }
+
+ if (!request.params[2].isNull()) {
+ uint256 blockhash = ParseHashV(request.params[2], "parameter 3");
+ BlockMap::iterator it = mapBlockIndex.find(blockhash);
+ if (it == mapBlockIndex.end()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found");
}
+ blockindex = it->second;
+ in_active_chain = chainActive.Contains(blockindex);
}
CTransactionRef tx;
- uint256 hashBlock;
- if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true))
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction"
- : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") +
- ". Use gettransaction for wallet transactions.");
+ uint256 hash_block;
+ if (!GetTransaction(hash, tx, Params().GetConsensus(), hash_block, true, blockindex)) {
+ std::string errmsg;
+ if (blockindex) {
+ if (!(blockindex->nStatus & BLOCK_HAVE_DATA)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Block not available");
+ }
+ errmsg = "No such transaction found in the provided block";
+ } else {
+ errmsg = fTxIndex
+ ? "No such mempool or blockchain transaction"
+ : "No such mempool transaction. Use -txindex to enable blockchain transaction queries";
+ }
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errmsg + ". Use gettransaction for wallet transactions.");
+ }
- if (!fVerbose)
+ if (!fVerbose) {
return EncodeHexTx(*tx, RPCSerializationFlags());
+ }
UniValue result(UniValue::VOBJ);
- TxToJSON(*tx, hashBlock, result);
+ if (blockindex) result.push_back(Pair("in_active_chain", in_active_chain));
+ TxToJSON(*tx, hash_block, result);
return result;
}
@@ -328,7 +349,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"")
);
- RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VOBJ, UniValue::VNUM}, true);
+ RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VOBJ, UniValue::VNUM, UniValue::VBOOL}, 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");
@@ -384,7 +405,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
rawTx.vin.push_back(in);
}
- std::set<CBitcoinAddress> setAddress;
+ std::set<CTxDestination> destinations;
std::vector<std::string> addrList = sendTo.getKeys();
for (const std::string& name_ : addrList) {
@@ -394,15 +415,16 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
CTxOut out(0, CScript() << OP_RETURN << data);
rawTx.vout.push_back(out);
} else {
- CBitcoinAddress address(name_);
- if (!address.IsValid())
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+name_);
+ CTxDestination destination = DecodeDestination(name_);
+ if (!IsValidDestination(destination)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
+ }
- if (setAddress.count(address))
- throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+name_);
- setAddress.insert(address);
+ if (!destinations.insert(destination).second) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
+ }
- CScript scriptPubKey = GetScriptForDestination(address.Get());
+ CScript scriptPubKey = GetScriptForDestination(destination);
CAmount nAmount = AmountFromValue(sendTo[name_]);
CTxOut out(nAmount, scriptPubKey);
@@ -535,7 +557,7 @@ UniValue decodescript(const JSONRPCRequest& request)
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()));
+ r.push_back(Pair("p2sh", EncodeDestination(CScriptID(script))));
}
return r;
@@ -878,7 +900,12 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
- TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
+ if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
+ // Unable to sign input and verification failed (possible attempt to partially sign).
+ TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)");
+ } else {
+ TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
+ }
}
}
bool fComplete = vErrors.empty();
@@ -917,7 +944,9 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
);
ObserveSafeMode();
- LOCK(cs_main);
+
+ std::promise<void> promise;
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
// parse hex string from parameter
@@ -931,6 +960,8 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
if (!request.params[1].isNull() && request.params[1].get_bool())
nMaxRawTxFee = 0;
+ { // cs_main scope
+ LOCK(cs_main);
CCoinsViewCache &view = *pcoinsTip;
bool fHaveChain = false;
for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
@@ -942,8 +973,8 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
// push to local node and sync with wallets
CValidationState state;
bool fMissingInputs;
- bool fLimitFree = true;
- if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, nullptr, false, nMaxRawTxFee)) {
+ if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) {
if (state.IsInvalid()) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
} else {
@@ -952,10 +983,28 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
}
throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason());
}
+ } else {
+ // If wallet is enabled, ensure that the wallet has been made aware
+ // of the new transaction prior to returning. This prevents a race
+ // where a user might call sendrawtransaction with a transaction
+ // to/from their wallet, immediately call some wallet RPC, and get
+ // a stale result because callbacks have not yet been processed.
+ CallFunctionInValidationInterfaceQueue([&promise] {
+ promise.set_value();
+ });
}
} else if (fHaveChain) {
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
+ } else {
+ // Make sure we don't block forever if re-sending
+ // a transaction already in mempool.
+ promise.set_value();
}
+
+ } // cs_main
+
+ promise.get_future().wait();
+
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -964,13 +1013,14 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
{
pnode->PushInventory(inv);
});
+
return hashTx.GetHex();
}
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
- { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose"} },
+ { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
diff --git a/src/rpc/safemode.cpp b/src/rpc/safemode.cpp
index 24770ad47f..9f3a9d30b8 100644
--- a/src/rpc/safemode.cpp
+++ b/src/rpc/safemode.cpp
@@ -1,8 +1,8 @@
-#include "safemode.h"
+#include <rpc/safemode.h>
-#include "rpc/protocol.h"
-#include "util.h"
-#include "warnings.h"
+#include <rpc/protocol.h>
+#include <util.h>
+#include <warnings.h>
void ObserveSafeMode()
{
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 428ab3b9b0..2e7e0ba474 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -3,18 +3,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "rpc/server.h"
+#include <rpc/server.h>
-#include "base58.h"
-#include "fs.h"
-#include "init.h"
-#include "random.h"
-#include "sync.h"
-#include "ui_interface.h"
-#include "util.h"
-#include "utilstrencodings.h"
-
-#include <univalue.h>
+#include <base58.h>
+#include <fs.h>
+#include <init.h>
+#include <random.h>
+#include <sync.h>
+#include <ui_interface.h>
+#include <util.h>
+#include <utilstrencodings.h>
#include <boost/bind.hpp>
#include <boost/signals2/signal.hpp>
@@ -160,8 +158,8 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest&
std::set<rpcfn_type> setDone;
std::vector<std::pair<std::string, const CRPCCommand*> > vCommands;
- for (std::map<std::string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
- vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second));
+ for (const auto& entry : mapCommands)
+ vCommands.push_back(make_pair(entry.second->category + entry.first, entry.second));
sort(vCommands.begin(), vCommands.end());
JSONRPCRequest jreq(helpreq);
@@ -382,11 +380,17 @@ void JSONRPCRequest::parse(const UniValue& valRequest)
throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
}
-static UniValue JSONRPCExecOne(const UniValue& req)
+bool IsDeprecatedRPCEnabled(const std::string& method)
+{
+ const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");
+
+ return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end();
+}
+
+static UniValue JSONRPCExecOne(JSONRPCRequest jreq, const UniValue& req)
{
UniValue rpc_result(UniValue::VOBJ);
- JSONRPCRequest jreq;
try {
jreq.parse(req);
@@ -406,11 +410,11 @@ static UniValue JSONRPCExecOne(const UniValue& req)
return rpc_result;
}
-std::string JSONRPCExecBatch(const UniValue& vReq)
+std::string JSONRPCExecBatch(const JSONRPCRequest& jreq, const UniValue& vReq)
{
UniValue ret(UniValue::VARR);
for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
- ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
+ ret.push_back(JSONRPCExecOne(jreq, vReq[reqIdx]));
return ret.write() + "\n";
}
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 777acbcb94..cd201bf316 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -6,9 +6,9 @@
#ifndef BITCOIN_RPCSERVER_H
#define BITCOIN_RPCSERVER_H
-#include "amount.h"
-#include "rpc/protocol.h"
-#include "uint256.h"
+#include <amount.h>
+#include <rpc/protocol.h>
+#include <uint256.h>
#include <list>
#include <map>
@@ -171,6 +171,8 @@ public:
bool appendCommand(const std::string& name, const CRPCCommand* pcmd);
};
+bool IsDeprecatedRPCEnabled(const std::string& method);
+
extern CRPCTable tableRPC;
/**
@@ -189,7 +191,7 @@ extern std::string HelpExampleRpc(const std::string& methodname, const std::stri
bool StartRPC();
void InterruptRPC();
void StopRPC();
-std::string JSONRPCExecBatch(const UniValue& vReq);
+std::string JSONRPCExecBatch(const JSONRPCRequest& jreq, const UniValue& vReq);
// Retrieves any serialization flags requested in command line argument
int RPCSerializationFlags();
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index 4edb2c6d9b..260f6fa60e 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -2,10 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "scheduler.h"
+#include <scheduler.h>
-#include "random.h"
-#include "reverselock.h"
+#include <random.h>
+#include <reverselock.h>
#include <assert.h>
#include <boost/bind.hpp>
diff --git a/src/scheduler.h b/src/scheduler.h
index db93bcb21e..b99f165343 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -14,7 +14,7 @@
#include <boost/thread.hpp>
#include <map>
-#include "sync.h"
+#include <sync.h>
//
// Simple class for background tasks that should be run
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp
index 03128917fd..77edc85b8c 100644
--- a/src/script/bitcoinconsensus.cpp
+++ b/src/script/bitcoinconsensus.cpp
@@ -3,12 +3,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bitcoinconsensus.h"
+#include <script/bitcoinconsensus.h>
-#include "primitives/transaction.h"
-#include "pubkey.h"
-#include "script/interpreter.h"
-#include "version.h"
+#include <primitives/transaction.h>
+#include <pubkey.h>
+#include <script/interpreter.h>
+#include <version.h>
namespace {
diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h
index 33bf80e5a7..f09cd59b92 100644
--- a/src/script/bitcoinconsensus.h
+++ b/src/script/bitcoinconsensus.h
@@ -9,7 +9,7 @@
#include <stdint.h>
#if defined(BUILD_BITCOIN_INTERNAL) && defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#if defined(_WIN32)
#if defined(DLL_EXPORT)
#if defined(HAVE_FUNC_ATTRIBUTE_DLLEXPORT)
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index f9716dfc66..3c3f92fe46 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -3,15 +3,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "interpreter.h"
+#include <script/interpreter.h>
-#include "primitives/transaction.h"
-#include "crypto/ripemd160.h"
-#include "crypto/sha1.h"
-#include "crypto/sha256.h"
-#include "pubkey.h"
-#include "script/script.h"
-#include "uint256.h"
+#include <crypto/ripemd160.h>
+#include <crypto/sha1.h>
+#include <crypto/sha256.h>
+#include <pubkey.h>
+#include <script/script.h>
+#include <uint256.h>
typedef std::vector<unsigned char> valtype;
@@ -349,9 +348,6 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
{
if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) {
// not enabled; treat as a NOP2
- if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
- return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
- }
break;
}
@@ -391,9 +387,6 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
{
if (!(flags & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) {
// not enabled; treat as a NOP3
- if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
- return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
- }
break;
}
@@ -1168,29 +1161,36 @@ uint256 GetOutputsHash(const CTransaction& txTo) {
PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
{
- hashPrevouts = GetPrevoutHash(txTo);
- hashSequence = GetSequenceHash(txTo);
- hashOutputs = GetOutputsHash(txTo);
+ // Cache is calculated only for transactions with witness
+ if (txTo.HasWitness()) {
+ hashPrevouts = GetPrevoutHash(txTo);
+ hashSequence = GetSequenceHash(txTo);
+ hashOutputs = GetOutputsHash(txTo);
+ ready = true;
+ }
}
uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
+ assert(nIn < txTo.vin.size());
+
if (sigversion == SIGVERSION_WITNESS_V0) {
uint256 hashPrevouts;
uint256 hashSequence;
uint256 hashOutputs;
+ const bool cacheready = cache && cache->ready;
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
- hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo);
+ hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo);
}
if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
- hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo);
+ hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo);
}
if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
- hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo);
+ hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo);
} else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
CHashWriter ss(SER_GETHASH, 0);
ss << txTo.vout[nIn];
@@ -1221,10 +1221,6 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig
}
static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
- if (nIn >= txTo.vin.size()) {
- // nIn out of range
- return one;
- }
// Check for invalid use of SIGHASH_SINGLE
if ((nHashType & 0x1f) == SIGHASH_SINGLE) {
@@ -1366,7 +1362,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
stack = std::vector<std::vector<unsigned char> >(witness.stack.begin(), witness.stack.end() - 1);
uint256 hashScriptPubKey;
CSHA256().Write(&scriptPubKey[0], scriptPubKey.size()).Finalize(hashScriptPubKey.begin());
- if (memcmp(hashScriptPubKey.begin(), &program[0], 32)) {
+ if (memcmp(hashScriptPubKey.begin(), program.data(), 32)) {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
}
} else if (program.size() == 20) {
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index f845e1943b..83a96739b1 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -6,8 +6,8 @@
#ifndef BITCOIN_SCRIPT_INTERPRETER_H
#define BITCOIN_SCRIPT_INTERPRETER_H
-#include "script_error.h"
-#include "primitives/transaction.h"
+#include <script/script_error.h>
+#include <primitives/transaction.h>
#include <vector>
#include <stdint.h>
@@ -27,37 +27,40 @@ enum
SIGHASH_ANYONECANPAY = 0x80,
};
-/** Script verification flags */
+/** Script verification flags.
+ *
+ * All flags are intended to be soft forks: the set of acceptable scripts under
+ * flags (A | B) is a subset of the acceptable scripts under flag (A).
+ */
enum
{
SCRIPT_VERIFY_NONE = 0,
- // Evaluate P2SH subscripts (softfork safe, BIP16).
+ // Evaluate P2SH subscripts (BIP16).
SCRIPT_VERIFY_P2SH = (1U << 0),
// Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
// Evaluating a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script failure.
- // (softfork safe, but not used or intended as a consensus rule).
+ // (not used or intended as a consensus rule).
SCRIPT_VERIFY_STRICTENC = (1U << 1),
- // Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP62 rule 1)
+ // Passing a non-strict-DER signature to a checksig operation causes script failure (BIP62 rule 1)
SCRIPT_VERIFY_DERSIG = (1U << 2),
// Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
- // (softfork safe, BIP62 rule 5).
+ // (BIP62 rule 5).
SCRIPT_VERIFY_LOW_S = (1U << 3),
- // verify dummy stack item consumed by CHECKMULTISIG is of zero-length (softfork safe, BIP62 rule 7).
+ // verify dummy stack item consumed by CHECKMULTISIG is of zero-length (BIP62 rule 7).
SCRIPT_VERIFY_NULLDUMMY = (1U << 4),
- // Using a non-push operator in the scriptSig causes script failure (softfork safe, BIP62 rule 2).
+ // Using a non-push operator in the scriptSig causes script failure (BIP62 rule 2).
SCRIPT_VERIFY_SIGPUSHONLY = (1U << 5),
// Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
// pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
// any other push causes the script to fail (BIP62 rule 3).
// In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
- // (softfork safe)
SCRIPT_VERIFY_MINIMALDATA = (1U << 6),
// Discourage use of NOPs reserved for upgrades (NOP1-10)
@@ -68,12 +71,14 @@ enum
// discouraged NOPs fails the script. This verification flag will never be
// a mandatory flag applied to scripts in a block. NOPs that are not
// executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected.
+ // NOPs that have associated forks to give them new meaning (CLTV, CSV)
+ // are not subject to this rule.
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1U << 7),
// Require that only a single stack element remains after evaluation. This changes the success criterion from
// "At least one stack element must remain, and when interpreted as a boolean, it must be true" to
// "Exactly one stack element must remain, and when interpreted as a boolean, it must be true".
- // (softfork safe, BIP62 rule 6)
+ // (BIP62 rule 6)
// Note: CLEANSTACK should never be used without P2SH or WITNESS.
SCRIPT_VERIFY_CLEANSTACK = (1U << 8),
@@ -113,6 +118,7 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
struct PrecomputedTransactionData
{
uint256 hashPrevouts, hashSequence, hashOutputs;
+ bool ready = false;
explicit PrecomputedTransactionData(const CTransaction& tx);
};
diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp
index 0a39619734..5849f55651 100644
--- a/src/script/ismine.cpp
+++ b/src/script/ismine.cpp
@@ -3,13 +3,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "ismine.h"
+#include <script/ismine.h>
-#include "key.h"
-#include "keystore.h"
-#include "script/script.h"
-#include "script/standard.h"
-#include "script/sign.h"
+#include <key.h>
+#include <keystore.h>
+#include <script/script.h>
+#include <script/sign.h>
typedef std::vector<unsigned char> valtype;
@@ -46,6 +45,8 @@ isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest, bool& i
isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion)
{
+ isInvalid = false;
+
std::vector<valtype> vSolutions;
txnouttype whichType;
if (!Solver(scriptPubKey, whichType, vSolutions)) {
@@ -59,6 +60,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
+ case TX_WITNESS_UNKNOWN:
break;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
diff --git a/src/script/ismine.h b/src/script/ismine.h
index 1aa5937b34..77ffa82432 100644
--- a/src/script/ismine.h
+++ b/src/script/ismine.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_SCRIPT_ISMINE_H
#define BITCOIN_SCRIPT_ISMINE_H
-#include "script/standard.h"
+#include <script/standard.h>
#include <stdint.h>
diff --git a/src/script/script.cpp b/src/script/script.cpp
index a10b619f7d..a7bfb170eb 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -3,10 +3,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "script.h"
+#include <script/script.h>
-#include "tinyformat.h"
-#include "utilstrencodings.h"
+#include <tinyformat.h>
+#include <utilstrencodings.h>
const char* GetOpName(opcodetype opcode)
{
diff --git a/src/script/script.h b/src/script/script.h
index 587f2d26eb..263db62edf 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -6,9 +6,9 @@
#ifndef BITCOIN_SCRIPT_SCRIPT_H
#define BITCOIN_SCRIPT_SCRIPT_H
-#include "crypto/common.h"
-#include "prevector.h"
-#include "serialize.h"
+#include <crypto/common.h>
+#include <prevector.h>
+#include <serialize.h>
#include <assert.h>
#include <climits>
@@ -420,6 +420,7 @@ public:
CScript& operator+=(const CScript& b)
{
+ reserve(size() + b.size());
insert(end(), b.begin(), b.end());
return *this;
}
diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp
index c9d13c92a8..32aa639ac4 100644
--- a/src/script/script_error.cpp
+++ b/src/script/script_error.cpp
@@ -3,7 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "script_error.h"
+#include <script/script_error.h>
const char* ScriptErrorString(const ScriptError serror)
{
@@ -73,6 +73,8 @@ const char* ScriptErrorString(const ScriptError serror)
return "Witness version reserved for soft-fork upgrades";
case SCRIPT_ERR_PUBKEYTYPE:
return "Public key is neither compressed or uncompressed";
+ case SCRIPT_ERR_CLEANSTACK:
+ return "Extra items left on stack after execution";
case SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH:
return "Witness program has incorrect length";
case SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY:
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index 4cc7afa2f5..88d1d67559 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -3,15 +3,15 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "sigcache.h"
+#include <script/sigcache.h>
-#include "memusage.h"
-#include "pubkey.h"
-#include "random.h"
-#include "uint256.h"
-#include "util.h"
+#include <memusage.h>
+#include <pubkey.h>
+#include <random.h>
+#include <uint256.h>
+#include <util.h>
-#include "cuckoocache.h"
+#include <cuckoocache.h>
#include <boost/thread.hpp>
namespace {
diff --git a/src/script/sigcache.h b/src/script/sigcache.h
index 5832b264b3..1019061fbb 100644
--- a/src/script/sigcache.h
+++ b/src/script/sigcache.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_SCRIPT_SIGCACHE_H
#define BITCOIN_SCRIPT_SIGCACHE_H
-#include "script/interpreter.h"
+#include <script/interpreter.h>
#include <vector>
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index dc50467d3f..117a4d8a52 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -3,14 +3,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "script/sign.h"
+#include <script/sign.h>
-#include "key.h"
-#include "keystore.h"
-#include "policy/policy.h"
-#include "primitives/transaction.h"
-#include "script/standard.h"
-#include "uint256.h"
+#include <key.h>
+#include <keystore.h>
+#include <policy/policy.h>
+#include <primitives/transaction.h>
+#include <script/standard.h>
+#include <uint256.h>
typedef std::vector<unsigned char> valtype;
@@ -79,6 +79,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
+ case TX_WITNESS_UNKNOWN:
return false;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
@@ -309,6 +310,7 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
+ case TX_WITNESS_UNKNOWN:
// Don't know anything about this, assume bigger one is correct:
if (sigs1.script.size() >= sigs2.script.size())
return sigs1;
diff --git a/src/script/sign.h b/src/script/sign.h
index a0d8ee4ff9..400c0c0865 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_SCRIPT_SIGN_H
#define BITCOIN_SCRIPT_SIGN_H
-#include "script/interpreter.h"
+#include <script/interpreter.h>
class CKeyID;
class CKeyStore;
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 2aed393921..b7b33fade6 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -3,12 +3,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "script/standard.h"
+#include <script/standard.h>
-#include "pubkey.h"
-#include "script/script.h"
-#include "util.h"
-#include "utilstrencodings.h"
+#include <pubkey.h>
+#include <script/script.h>
+#include <util.h>
+#include <utilstrencodings.h>
typedef std::vector<unsigned char> valtype;
@@ -30,6 +30,7 @@ const char* GetTxnOutputType(txnouttype t)
case TX_NULL_DATA: return "nulldata";
case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
+ case TX_WITNESS_UNKNOWN: return "witness_unknown";
}
return nullptr;
}
@@ -75,6 +76,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
vSolutionsRet.push_back(witnessprogram);
return true;
}
+ if (witnessversion != 0) {
+ typeRet = TX_WITNESS_UNKNOWN;
+ vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
+ vSolutionsRet.push_back(std::move(witnessprogram));
+ return true;
+ }
return false;
}
@@ -198,6 +205,23 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
{
addressRet = CScriptID(uint160(vSolutions[0]));
return true;
+ } else if (whichType == TX_WITNESS_V0_KEYHASH) {
+ WitnessV0KeyHash hash;
+ std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
+ addressRet = hash;
+ return true;
+ } else if (whichType == TX_WITNESS_V0_SCRIPTHASH) {
+ WitnessV0ScriptHash hash;
+ std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
+ addressRet = hash;
+ return true;
+ } else if (whichType == TX_WITNESS_UNKNOWN) {
+ WitnessUnknown unk;
+ unk.version = vSolutions[0][0];
+ std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
+ unk.length = vSolutions[1].size();
+ addressRet = unk;
+ return true;
}
// Multisig txns have more than one address...
return false;
@@ -268,6 +292,27 @@ public:
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
return true;
}
+
+ bool operator()(const WitnessV0KeyHash& id) const
+ {
+ script->clear();
+ *script << OP_0 << ToByteVector(id);
+ return true;
+ }
+
+ bool operator()(const WitnessV0ScriptHash& id) const
+ {
+ script->clear();
+ *script << OP_0 << ToByteVector(id);
+ return true;
+ }
+
+ bool operator()(const WitnessUnknown& id) const
+ {
+ script->clear();
+ *script << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length);
+ return true;
+ }
};
} // namespace
@@ -317,3 +362,7 @@ CScript GetScriptForWitness(const CScript& redeemscript)
ret << OP_0 << ToByteVector(hash);
return ret;
}
+
+bool IsValidDestination(const CTxDestination& dest) {
+ return dest.which() != 0;
+}
diff --git a/src/script/standard.h b/src/script/standard.h
index 7619192264..3eeeabdc15 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -6,8 +6,8 @@
#ifndef BITCOIN_SCRIPT_STANDARD_H
#define BITCOIN_SCRIPT_STANDARD_H
-#include "script/interpreter.h"
-#include "uint256.h"
+#include <script/interpreter.h>
+#include <uint256.h>
#include <boost/variant.hpp>
@@ -64,6 +64,7 @@ enum txnouttype
TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data
TX_WITNESS_V0_SCRIPTHASH,
TX_WITNESS_V0_KEYHASH,
+ TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
};
class CNoDestination {
@@ -72,14 +73,45 @@ public:
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
};
+struct WitnessV0ScriptHash : public uint256 {};
+struct WitnessV0KeyHash : public uint160 {};
+
+//! CTxDestination subtype to encode any future Witness version
+struct WitnessUnknown
+{
+ unsigned int version;
+ unsigned int length;
+ unsigned char program[40];
+
+ friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) {
+ if (w1.version != w2.version) return false;
+ if (w1.length != w2.length) return false;
+ return std::equal(w1.program, w1.program + w1.length, w2.program);
+ }
+
+ friend bool operator<(const WitnessUnknown& w1, const WitnessUnknown& w2) {
+ if (w1.version < w2.version) return true;
+ if (w1.version > w2.version) return false;
+ if (w1.length < w2.length) return true;
+ if (w1.length > w2.length) return false;
+ return std::lexicographical_compare(w1.program, w1.program + w1.length, w2.program, w2.program + w2.length);
+ }
+};
+
/**
* A txout script template with a specific destination. It is either:
* * CNoDestination: no destination set
- * * CKeyID: TX_PUBKEYHASH destination
- * * CScriptID: TX_SCRIPTHASH destination
- * A CTxDestination is the internal data type encoded in a CBitcoinAddress
+ * * CKeyID: TX_PUBKEYHASH destination (P2PKH)
+ * * CScriptID: TX_SCRIPTHASH destination (P2SH)
+ * * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH)
+ * * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH)
+ * * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???)
+ * A CTxDestination is the internal data type encoded in a bitcoin address
*/
-typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
+typedef boost::variant<CNoDestination, CKeyID, CScriptID, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
+
+/** Check whether a CTxDestination is a CNoDestination. */
+bool IsValidDestination(const CTxDestination& dest);
/** Get the name of a txnouttype as a C string, or nullptr if unknown. */
const char* GetTxnOutputType(txnouttype t);
@@ -101,7 +133,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
* Parse a standard scriptPubKey for the destination address. Assigns result to
* the addressRet parameter and returns true if successful. For multisig
* scripts, instead use ExtractDestinations. Currently only works for P2PK,
- * P2PKH, and P2SH scripts.
+ * P2PKH, P2SH, P2WPKH, and P2WSH scripts.
*/
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
diff --git a/src/secp256k1/contrib/lax_der_parsing.h b/src/secp256k1/contrib/lax_der_parsing.h
index 6d27871a7c..7eaf63bf6a 100644
--- a/src/secp256k1/contrib/lax_der_parsing.h
+++ b/src/secp256k1/contrib/lax_der_parsing.h
@@ -48,14 +48,14 @@
* 8.3.1.
*/
-#ifndef _SECP256K1_CONTRIB_LAX_DER_PARSING_H_
-#define _SECP256K1_CONTRIB_LAX_DER_PARSING_H_
+#ifndef SECP256K1_CONTRIB_LAX_DER_PARSING_H
+#define SECP256K1_CONTRIB_LAX_DER_PARSING_H
#include <secp256k1.h>
-# ifdef __cplusplus
+#ifdef __cplusplus
extern "C" {
-# endif
+#endif
/** Parse a signature in "lax DER" format
*
@@ -88,4 +88,4 @@ int ecdsa_signature_parse_der_lax(
}
#endif
-#endif
+#endif /* SECP256K1_CONTRIB_LAX_DER_PARSING_H */
diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.h b/src/secp256k1/contrib/lax_der_privatekey_parsing.h
index 2fd088f8ab..fece261fb9 100644
--- a/src/secp256k1/contrib/lax_der_privatekey_parsing.h
+++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.h
@@ -25,14 +25,14 @@
* library are sufficient.
*/
-#ifndef _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_
-#define _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_
+#ifndef SECP256K1_CONTRIB_BER_PRIVATEKEY_H
+#define SECP256K1_CONTRIB_BER_PRIVATEKEY_H
#include <secp256k1.h>
-# ifdef __cplusplus
+#ifdef __cplusplus
extern "C" {
-# endif
+#endif
/** Export a private key in DER format.
*
@@ -87,4 +87,4 @@ SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der(
}
#endif
-#endif
+#endif /* SECP256K1_CONTRIB_BER_PRIVATEKEY_H */
diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h
index fc4c5cefbb..3e9c098d19 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -1,9 +1,9 @@
-#ifndef _SECP256K1_
-# define _SECP256K1_
+#ifndef SECP256K1_H
+#define SECP256K1_H
-# ifdef __cplusplus
+#ifdef __cplusplus
extern "C" {
-# endif
+#endif
#include <stddef.h>
@@ -61,7 +61,7 @@ typedef struct {
* 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, transmission, or
* comparison, use the secp256k1_ecdsa_signature_serialize_* and
- * secp256k1_ecdsa_signature_serialize_* functions.
+ * secp256k1_ecdsa_signature_parse_* functions.
*/
typedef struct {
unsigned char data[64];
@@ -159,6 +159,13 @@ typedef int (*secp256k1_nonce_function)(
#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION)
+/** Prefix byte used to tag various encoded curvepoints for specific purposes */
+#define SECP256K1_TAG_PUBKEY_EVEN 0x02
+#define SECP256K1_TAG_PUBKEY_ODD 0x03
+#define SECP256K1_TAG_PUBKEY_UNCOMPRESSED 0x04
+#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06
+#define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07
+
/** Create a secp256k1 context object.
*
* Returns: a newly created context object.
@@ -607,8 +614,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine(
size_t n
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
-# ifdef __cplusplus
+#ifdef __cplusplus
}
-# endif
-
#endif
+
+#endif /* SECP256K1_H */
diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h
index 4b84d7a963..88492dc1a4 100644
--- a/src/secp256k1/include/secp256k1_ecdh.h
+++ b/src/secp256k1/include/secp256k1_ecdh.h
@@ -1,11 +1,11 @@
-#ifndef _SECP256K1_ECDH_
-# define _SECP256K1_ECDH_
+#ifndef SECP256K1_ECDH_H
+#define SECP256K1_ECDH_H
-# include "secp256k1.h"
+#include "secp256k1.h"
-# ifdef __cplusplus
+#ifdef __cplusplus
extern "C" {
-# endif
+#endif
/** Compute an EC Diffie-Hellman secret in constant time
* Returns: 1: exponentiation was successful
@@ -24,8 +24,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh(
const unsigned char *privkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
-# ifdef __cplusplus
+#ifdef __cplusplus
}
-# endif
-
#endif
+
+#endif /* SECP256K1_ECDH_H */
diff --git a/src/secp256k1/include/secp256k1_recovery.h b/src/secp256k1/include/secp256k1_recovery.h
index 0553797253..cf6c5ed7f5 100644
--- a/src/secp256k1/include/secp256k1_recovery.h
+++ b/src/secp256k1/include/secp256k1_recovery.h
@@ -1,11 +1,11 @@
-#ifndef _SECP256K1_RECOVERY_
-# define _SECP256K1_RECOVERY_
+#ifndef SECP256K1_RECOVERY_H
+#define SECP256K1_RECOVERY_H
-# include "secp256k1.h"
+#include "secp256k1.h"
-# ifdef __cplusplus
+#ifdef __cplusplus
extern "C" {
-# endif
+#endif
/** Opaque data structured that holds a parsed ECDSA signature,
* supporting pubkey recovery.
@@ -103,8 +103,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover(
const unsigned char *msg32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
-# ifdef __cplusplus
+#ifdef __cplusplus
}
-# endif
-
#endif
+
+#endif /* SECP256K1_RECOVERY_H */
diff --git a/src/secp256k1/sage/group_prover.sage b/src/secp256k1/sage/group_prover.sage
index 5198724bea..8521f07999 100644
--- a/src/secp256k1/sage/group_prover.sage
+++ b/src/secp256k1/sage/group_prover.sage
@@ -17,7 +17,7 @@
# - A constraint describing the requirements of the law, called "require"
# * Implementations are transliterated into functions that operate as well on
# algebraic input points, and are called once per combination of branches
-# exectured. Each execution returns:
+# executed. Each execution returns:
# - A constraint describing the assumptions this implementation requires
# (such as Z1=1), called "assumeFormula"
# - A constraint describing the assumptions this specific branch requires,
diff --git a/src/secp256k1/src/asm/field_10x26_arm.s b/src/secp256k1/src/asm/field_10x26_arm.s
index bd2b629e1c..5a9cc3ffcf 100644
--- a/src/secp256k1/src/asm/field_10x26_arm.s
+++ b/src/secp256k1/src/asm/field_10x26_arm.s
@@ -23,7 +23,7 @@ Note:
.eabi_attribute 10, 0 @ Tag_FP_arch = none
.eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP
- .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed
+ .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Aggressive Speed
.eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6
.text
diff --git a/src/secp256k1/src/basic-config.h b/src/secp256k1/src/basic-config.h
index c4c16eb7ca..fc588061ca 100644
--- a/src/secp256k1/src/basic-config.h
+++ b/src/secp256k1/src/basic-config.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_BASIC_CONFIG_
-#define _SECP256K1_BASIC_CONFIG_
+#ifndef SECP256K1_BASIC_CONFIG_H
+#define SECP256K1_BASIC_CONFIG_H
#ifdef USE_BASIC_CONFIG
@@ -28,5 +28,6 @@
#define USE_FIELD_10X26 1
#define USE_SCALAR_8X32 1
-#endif // USE_BASIC_CONFIG
-#endif // _SECP256K1_BASIC_CONFIG_
+#endif /* USE_BASIC_CONFIG */
+
+#endif /* SECP256K1_BASIC_CONFIG_H */
diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h
index d67f08a426..d5ebe01301 100644
--- a/src/secp256k1/src/bench.h
+++ b/src/secp256k1/src/bench.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_BENCH_H_
-#define _SECP256K1_BENCH_H_
+#ifndef SECP256K1_BENCH_H
+#define SECP256K1_BENCH_H
#include <stdio.h>
#include <math.h>
@@ -63,4 +63,4 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v
printf("us\n");
}
-#endif
+#endif /* SECP256K1_BENCH_H */
diff --git a/src/secp256k1/src/ecdsa.h b/src/secp256k1/src/ecdsa.h
index 54ae101b92..80590c7cc8 100644
--- a/src/secp256k1/src/ecdsa.h
+++ b/src/secp256k1/src/ecdsa.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_ECDSA_
-#define _SECP256K1_ECDSA_
+#ifndef SECP256K1_ECDSA_H
+#define SECP256K1_ECDSA_H
#include <stddef.h>
@@ -18,4 +18,4 @@ 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* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message);
static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid);
-#endif
+#endif /* SECP256K1_ECDSA_H */
diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h
index 453bb11880..c3400042d8 100644
--- a/src/secp256k1/src/ecdsa_impl.h
+++ b/src/secp256k1/src/ecdsa_impl.h
@@ -5,8 +5,8 @@
**********************************************************************/
-#ifndef _SECP256K1_ECDSA_IMPL_H_
-#define _SECP256K1_ECDSA_IMPL_H_
+#ifndef SECP256K1_ECDSA_IMPL_H
+#define SECP256K1_ECDSA_IMPL_H
#include "scalar.h"
#include "field.h"
@@ -81,8 +81,6 @@ static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned cha
return -1;
}
while (lenleft > 0) {
- if ((ret >> ((sizeof(size_t) - 1) * 8)) != 0) {
- }
ret = (ret << 8) | **sigp;
if (ret + lenleft > (size_t)(sigend - *sigp)) {
/* Result exceeds the length of the passed array. */
@@ -312,4 +310,4 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
return 1;
}
-#endif
+#endif /* SECP256K1_ECDSA_IMPL_H */
diff --git a/src/secp256k1/src/eckey.h b/src/secp256k1/src/eckey.h
index 42739a3bea..b621f1e6c3 100644
--- a/src/secp256k1/src/eckey.h
+++ b/src/secp256k1/src/eckey.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_ECKEY_
-#define _SECP256K1_ECKEY_
+#ifndef SECP256K1_ECKEY_H
+#define SECP256K1_ECKEY_H
#include <stddef.h>
@@ -22,4 +22,4 @@ static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx,
static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak);
static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak);
-#endif
+#endif /* SECP256K1_ECKEY_H */
diff --git a/src/secp256k1/src/eckey_impl.h b/src/secp256k1/src/eckey_impl.h
index ce38071ac2..1ab9a68ec0 100644
--- a/src/secp256k1/src/eckey_impl.h
+++ b/src/secp256k1/src/eckey_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_ECKEY_IMPL_H_
-#define _SECP256K1_ECKEY_IMPL_H_
+#ifndef SECP256K1_ECKEY_IMPL_H
+#define SECP256K1_ECKEY_IMPL_H
#include "eckey.h"
@@ -15,16 +15,17 @@
#include "ecmult_gen.h"
static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size) {
- if (size == 33 && (pub[0] == 0x02 || pub[0] == 0x03)) {
+ if (size == 33 && (pub[0] == SECP256K1_TAG_PUBKEY_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_ODD)) {
secp256k1_fe x;
- return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == 0x03);
+ return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD);
} else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) {
secp256k1_fe x, y;
if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) {
return 0;
}
secp256k1_ge_set_xy(elem, &x, &y);
- if ((pub[0] == 0x06 || pub[0] == 0x07) && secp256k1_fe_is_odd(&y) != (pub[0] == 0x07)) {
+ if ((pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD) &&
+ secp256k1_fe_is_odd(&y) != (pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) {
return 0;
}
return secp256k1_ge_is_valid_var(elem);
@@ -42,10 +43,10 @@ static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *p
secp256k1_fe_get_b32(&pub[1], &elem->x);
if (compressed) {
*size = 33;
- pub[0] = 0x02 | (secp256k1_fe_is_odd(&elem->y) ? 0x01 : 0x00);
+ pub[0] = secp256k1_fe_is_odd(&elem->y) ? SECP256K1_TAG_PUBKEY_ODD : SECP256K1_TAG_PUBKEY_EVEN;
} else {
*size = 65;
- pub[0] = 0x04;
+ pub[0] = SECP256K1_TAG_PUBKEY_UNCOMPRESSED;
secp256k1_fe_get_b32(&pub[33], &elem->y);
}
return 1;
@@ -96,4 +97,4 @@ static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx,
return 1;
}
-#endif
+#endif /* SECP256K1_ECKEY_IMPL_H */
diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h
index 20484134f5..6d44aba60b 100644
--- a/src/secp256k1/src/ecmult.h
+++ b/src/secp256k1/src/ecmult.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_ECMULT_
-#define _SECP256K1_ECMULT_
+#ifndef SECP256K1_ECMULT_H
+#define SECP256K1_ECMULT_H
#include "num.h"
#include "group.h"
@@ -28,4 +28,4 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx
/** Double multiply: R = na*A + ng*G */
static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng);
-#endif
+#endif /* SECP256K1_ECMULT_H */
diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h
index 2b0097655c..72bf7d7582 100644
--- a/src/secp256k1/src/ecmult_const.h
+++ b/src/secp256k1/src/ecmult_const.h
@@ -4,12 +4,12 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_ECMULT_CONST_
-#define _SECP256K1_ECMULT_CONST_
+#ifndef SECP256K1_ECMULT_CONST_H
+#define SECP256K1_ECMULT_CONST_H
#include "scalar.h"
#include "group.h"
static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q);
-#endif
+#endif /* SECP256K1_ECMULT_CONST_H */
diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h
index 0db314c48e..7d7a172b7b 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_ECMULT_CONST_IMPL_
-#define _SECP256K1_ECMULT_CONST_IMPL_
+#ifndef SECP256K1_ECMULT_CONST_IMPL_H
+#define SECP256K1_ECMULT_CONST_IMPL_H
#include "scalar.h"
#include "group.h"
@@ -42,11 +42,12 @@
} while(0)
-/** Convert a number to WNAF notation. The number becomes represented by sum(2^{wi} * wnaf[i], i=0..return_val)
- * with the following guarantees:
+/** Convert a number to WNAF notation.
+ * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val.
+ * It has the following guarantees:
* - each wnaf[i] an odd integer between -(1 << w) and (1 << w)
* - each wnaf[i] is nonzero
- * - the number of words set is returned; this is always (WNAF_BITS + w - 1) / w
+ * - the number of words set is always WNAF_SIZE(w) + 1
*
* Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar
* Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.)
@@ -236,4 +237,4 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
}
}
-#endif
+#endif /* SECP256K1_ECMULT_CONST_IMPL_H */
diff --git a/src/secp256k1/src/ecmult_gen.h b/src/secp256k1/src/ecmult_gen.h
index eb2cc9ead6..7564b7015f 100644
--- a/src/secp256k1/src/ecmult_gen.h
+++ b/src/secp256k1/src/ecmult_gen.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_ECMULT_GEN_
-#define _SECP256K1_ECMULT_GEN_
+#ifndef SECP256K1_ECMULT_GEN_H
+#define SECP256K1_ECMULT_GEN_H
#include "scalar.h"
#include "group.h"
@@ -40,4 +40,4 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context* ctx, secp25
static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32);
-#endif
+#endif /* SECP256K1_ECMULT_GEN_H */
diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h
index 35f2546077..9615b932dd 100644
--- a/src/secp256k1/src/ecmult_gen_impl.h
+++ b/src/secp256k1/src/ecmult_gen_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_ECMULT_GEN_IMPL_H_
-#define _SECP256K1_ECMULT_GEN_IMPL_H_
+#ifndef SECP256K1_ECMULT_GEN_IMPL_H
+#define SECP256K1_ECMULT_GEN_IMPL_H
#include "scalar.h"
#include "group.h"
@@ -207,4 +207,4 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
secp256k1_gej_clear(&gb);
}
-#endif
+#endif /* SECP256K1_ECMULT_GEN_IMPL_H */
diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h
index 4e40104ad4..93d3794cb4 100644
--- a/src/secp256k1/src/ecmult_impl.h
+++ b/src/secp256k1/src/ecmult_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_ECMULT_IMPL_H_
-#define _SECP256K1_ECMULT_IMPL_H_
+#ifndef SECP256K1_ECMULT_IMPL_H
+#define SECP256K1_ECMULT_IMPL_H
#include <string.h>
@@ -403,4 +403,4 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
}
}
-#endif
+#endif /* SECP256K1_ECMULT_IMPL_H */
diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h
index bbb1ee866c..bb6692ad57 100644
--- a/src/secp256k1/src/field.h
+++ b/src/secp256k1/src/field.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_FIELD_
-#define _SECP256K1_FIELD_
+#ifndef SECP256K1_FIELD_H
+#define SECP256K1_FIELD_H
/** Field element module.
*
@@ -129,4 +129,4 @@ static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_f
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */
static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag);
-#endif
+#endif /* SECP256K1_FIELD_H */
diff --git a/src/secp256k1/src/field_10x26.h b/src/secp256k1/src/field_10x26.h
index 61ee1e0965..727c5267fb 100644
--- a/src/secp256k1/src/field_10x26.h
+++ b/src/secp256k1/src/field_10x26.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_FIELD_REPR_
-#define _SECP256K1_FIELD_REPR_
+#ifndef SECP256K1_FIELD_REPR_H
+#define SECP256K1_FIELD_REPR_H
#include <stdint.h>
@@ -44,4 +44,5 @@ typedef struct {
#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}
#define SECP256K1_FE_STORAGE_CONST_GET(d) d.n[7], d.n[6], d.n[5], d.n[4],d.n[3], d.n[2], d.n[1], d.n[0]
-#endif
+
+#endif /* SECP256K1_FIELD_REPR_H */
diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h
index 234c13a644..94f8132fc8 100644
--- a/src/secp256k1/src/field_10x26_impl.h
+++ b/src/secp256k1/src/field_10x26_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_FIELD_REPR_IMPL_H_
-#define _SECP256K1_FIELD_REPR_IMPL_H_
+#ifndef SECP256K1_FIELD_REPR_IMPL_H
+#define SECP256K1_FIELD_REPR_IMPL_H
#include "util.h"
#include "num.h"
@@ -1158,4 +1158,4 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se
#endif
}
-#endif
+#endif /* SECP256K1_FIELD_REPR_IMPL_H */
diff --git a/src/secp256k1/src/field_5x52.h b/src/secp256k1/src/field_5x52.h
index 8e69a560dc..bccd8feb4d 100644
--- a/src/secp256k1/src/field_5x52.h
+++ b/src/secp256k1/src/field_5x52.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_FIELD_REPR_
-#define _SECP256K1_FIELD_REPR_
+#ifndef SECP256K1_FIELD_REPR_H
+#define SECP256K1_FIELD_REPR_H
#include <stdint.h>
@@ -44,4 +44,4 @@ typedef struct {
(d6) | (((uint64_t)(d7)) << 32) \
}}
-#endif
+#endif /* SECP256K1_FIELD_REPR_H */
diff --git a/src/secp256k1/src/field_5x52_asm_impl.h b/src/secp256k1/src/field_5x52_asm_impl.h
index 98cc004bf0..1fc3171f6b 100644
--- a/src/secp256k1/src/field_5x52_asm_impl.h
+++ b/src/secp256k1/src/field_5x52_asm_impl.h
@@ -11,8 +11,8 @@
* - December 2014, Pieter Wuille: converted from YASM to GCC inline assembly
*/
-#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_
-#define _SECP256K1_FIELD_INNER5X52_IMPL_H_
+#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H
+#define SECP256K1_FIELD_INNER5X52_IMPL_H
SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) {
/**
@@ -499,4 +499,4 @@ __asm__ __volatile__(
);
}
-#endif
+#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */
diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h
index 8e8b286baf..957c61b014 100644
--- a/src/secp256k1/src/field_5x52_impl.h
+++ b/src/secp256k1/src/field_5x52_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_FIELD_REPR_IMPL_H_
-#define _SECP256K1_FIELD_REPR_IMPL_H_
+#ifndef SECP256K1_FIELD_REPR_IMPL_H
+#define SECP256K1_FIELD_REPR_IMPL_H
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
@@ -493,4 +493,4 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se
#endif
}
-#endif
+#endif /* SECP256K1_FIELD_REPR_IMPL_H */
diff --git a/src/secp256k1/src/field_5x52_int128_impl.h b/src/secp256k1/src/field_5x52_int128_impl.h
index 0bf22bdd3e..95a0d1791c 100644
--- a/src/secp256k1/src/field_5x52_int128_impl.h
+++ b/src/secp256k1/src/field_5x52_int128_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_
-#define _SECP256K1_FIELD_INNER5X52_IMPL_H_
+#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H
+#define SECP256K1_FIELD_INNER5X52_IMPL_H
#include <stdint.h>
@@ -274,4 +274,4 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t
/* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
}
-#endif
+#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */
diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h
index 5127b279bc..20428648af 100644
--- a/src/secp256k1/src/field_impl.h
+++ b/src/secp256k1/src/field_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_FIELD_IMPL_H_
-#define _SECP256K1_FIELD_IMPL_H_
+#ifndef SECP256K1_FIELD_IMPL_H
+#define SECP256K1_FIELD_IMPL_H
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
@@ -312,4 +312,4 @@ static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) {
#endif
}
-#endif
+#endif /* SECP256K1_FIELD_IMPL_H */
diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h
index 4957b248fe..ea1302deb8 100644
--- a/src/secp256k1/src/group.h
+++ b/src/secp256k1/src/group.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_GROUP_
-#define _SECP256K1_GROUP_
+#ifndef SECP256K1_GROUP_H
+#define SECP256K1_GROUP_H
#include "num.h"
#include "field.h"
@@ -141,4 +141,4 @@ static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_g
/** Rescale a jacobian point by b which must be non-zero. Constant-time. */
static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b);
-#endif
+#endif /* SECP256K1_GROUP_H */
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index 7d723532ff..b31b6c12ef 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_GROUP_IMPL_H_
-#define _SECP256K1_GROUP_IMPL_H_
+#ifndef SECP256K1_GROUP_IMPL_H
+#define SECP256K1_GROUP_IMPL_H
#include "num.h"
#include "field.h"
@@ -697,4 +697,4 @@ static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) {
return secp256k1_fe_is_quad_var(&yz);
}
-#endif
+#endif /* SECP256K1_GROUP_IMPL_H */
diff --git a/src/secp256k1/src/hash.h b/src/secp256k1/src/hash.h
index fca98cab9f..e08d25d225 100644
--- a/src/secp256k1/src/hash.h
+++ b/src/secp256k1/src/hash.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_HASH_
-#define _SECP256K1_HASH_
+#ifndef SECP256K1_HASH_H
+#define SECP256K1_HASH_H
#include <stdlib.h>
#include <stdint.h>
@@ -38,4 +38,4 @@ static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha2
static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen);
static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng);
-#endif
+#endif /* SECP256K1_HASH_H */
diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h
index b47e65f830..4c9964ee06 100644
--- a/src/secp256k1/src/hash_impl.h
+++ b/src/secp256k1/src/hash_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_HASH_IMPL_H_
-#define _SECP256K1_HASH_IMPL_H_
+#ifndef SECP256K1_HASH_IMPL_H
+#define SECP256K1_HASH_IMPL_H
#include "hash.h"
@@ -278,4 +278,4 @@ static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256
#undef Maj
#undef Ch
-#endif
+#endif /* SECP256K1_HASH_IMPL_H */
diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h
index 9e30fb73dd..01ecba4d53 100644
--- a/src/secp256k1/src/modules/ecdh/main_impl.h
+++ b/src/secp256k1/src/modules/ecdh/main_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_MODULE_ECDH_MAIN_
-#define _SECP256K1_MODULE_ECDH_MAIN_
+#ifndef SECP256K1_MODULE_ECDH_MAIN_H
+#define SECP256K1_MODULE_ECDH_MAIN_H
#include "include/secp256k1_ecdh.h"
#include "ecmult_const_impl.h"
@@ -51,4 +51,4 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const se
return ret;
}
-#endif
+#endif /* SECP256K1_MODULE_ECDH_MAIN_H */
diff --git a/src/secp256k1/src/modules/ecdh/tests_impl.h b/src/secp256k1/src/modules/ecdh/tests_impl.h
index 85a5d0a9a6..cec30b67c6 100644
--- a/src/secp256k1/src/modules/ecdh/tests_impl.h
+++ b/src/secp256k1/src/modules/ecdh/tests_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_MODULE_ECDH_TESTS_
-#define _SECP256K1_MODULE_ECDH_TESTS_
+#ifndef SECP256K1_MODULE_ECDH_TESTS_H
+#define SECP256K1_MODULE_ECDH_TESTS_H
void test_ecdh_api(void) {
/* Setup context that just counts errors */
@@ -102,4 +102,4 @@ void run_ecdh_tests(void) {
test_bad_scalar();
}
-#endif
+#endif /* SECP256K1_MODULE_ECDH_TESTS_H */
diff --git a/src/secp256k1/src/modules/recovery/main_impl.h b/src/secp256k1/src/modules/recovery/main_impl.h
index c6fbe23981..2f6691c5a1 100755
--- a/src/secp256k1/src/modules/recovery/main_impl.h
+++ b/src/secp256k1/src/modules/recovery/main_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_MODULE_RECOVERY_MAIN_
-#define _SECP256K1_MODULE_RECOVERY_MAIN_
+#ifndef SECP256K1_MODULE_RECOVERY_MAIN_H
+#define SECP256K1_MODULE_RECOVERY_MAIN_H
#include "include/secp256k1_recovery.h"
@@ -190,4 +190,4 @@ int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubk
}
}
-#endif
+#endif /* SECP256K1_MODULE_RECOVERY_MAIN_H */
diff --git a/src/secp256k1/src/modules/recovery/tests_impl.h b/src/secp256k1/src/modules/recovery/tests_impl.h
index 765c7dd81e..5c9bbe8610 100644
--- a/src/secp256k1/src/modules/recovery/tests_impl.h
+++ b/src/secp256k1/src/modules/recovery/tests_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_MODULE_RECOVERY_TESTS_
-#define _SECP256K1_MODULE_RECOVERY_TESTS_
+#ifndef SECP256K1_MODULE_RECOVERY_TESTS_H
+#define SECP256K1_MODULE_RECOVERY_TESTS_H
static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) {
(void) msg32;
@@ -390,4 +390,4 @@ void run_recovery_tests(void) {
test_ecdsa_recovery_edge_cases();
}
-#endif
+#endif /* SECP256K1_MODULE_RECOVERY_TESTS_H */
diff --git a/src/secp256k1/src/num.h b/src/secp256k1/src/num.h
index 7bb9c5be8c..49f2dd791d 100644
--- a/src/secp256k1/src/num.h
+++ b/src/secp256k1/src/num.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_NUM_
-#define _SECP256K1_NUM_
+#ifndef SECP256K1_NUM_H
+#define SECP256K1_NUM_H
#ifndef USE_NUM_NONE
@@ -71,4 +71,4 @@ static void secp256k1_num_negate(secp256k1_num *r);
#endif
-#endif
+#endif /* SECP256K1_NUM_H */
diff --git a/src/secp256k1/src/num_gmp.h b/src/secp256k1/src/num_gmp.h
index 7dd813088a..3619844bd5 100644
--- a/src/secp256k1/src/num_gmp.h
+++ b/src/secp256k1/src/num_gmp.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_NUM_REPR_
-#define _SECP256K1_NUM_REPR_
+#ifndef SECP256K1_NUM_REPR_H
+#define SECP256K1_NUM_REPR_H
#include <gmp.h>
@@ -17,4 +17,4 @@ typedef struct {
int limbs;
} secp256k1_num;
-#endif
+#endif /* SECP256K1_NUM_REPR_H */
diff --git a/src/secp256k1/src/num_gmp_impl.h b/src/secp256k1/src/num_gmp_impl.h
index 3a46495eea..0ae2a8ba0e 100644
--- a/src/secp256k1/src/num_gmp_impl.h
+++ b/src/secp256k1/src/num_gmp_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_NUM_REPR_IMPL_H_
-#define _SECP256K1_NUM_REPR_IMPL_H_
+#ifndef SECP256K1_NUM_REPR_IMPL_H
+#define SECP256K1_NUM_REPR_IMPL_H
#include <string.h>
#include <stdlib.h>
@@ -285,4 +285,4 @@ static void secp256k1_num_negate(secp256k1_num *r) {
r->neg ^= 1;
}
-#endif
+#endif /* SECP256K1_NUM_REPR_IMPL_H */
diff --git a/src/secp256k1/src/num_impl.h b/src/secp256k1/src/num_impl.h
index 0b0e3a072a..c45193b033 100644
--- a/src/secp256k1/src/num_impl.h
+++ b/src/secp256k1/src/num_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_NUM_IMPL_H_
-#define _SECP256K1_NUM_IMPL_H_
+#ifndef SECP256K1_NUM_IMPL_H
+#define SECP256K1_NUM_IMPL_H
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
@@ -21,4 +21,4 @@
#error "Please select num implementation"
#endif
-#endif
+#endif /* SECP256K1_NUM_IMPL_H */
diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h
index 27e9d8375e..59304cb66e 100644
--- a/src/secp256k1/src/scalar.h
+++ b/src/secp256k1/src/scalar.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_SCALAR_
-#define _SECP256K1_SCALAR_
+#ifndef SECP256K1_SCALAR_H
+#define SECP256K1_SCALAR_H
#include "num.h"
@@ -103,4 +103,4 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar
/** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */
static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift);
-#endif
+#endif /* SECP256K1_SCALAR_H */
diff --git a/src/secp256k1/src/scalar_4x64.h b/src/secp256k1/src/scalar_4x64.h
index cff406038f..19c7495d1c 100644
--- a/src/secp256k1/src/scalar_4x64.h
+++ b/src/secp256k1/src/scalar_4x64.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_SCALAR_REPR_
-#define _SECP256K1_SCALAR_REPR_
+#ifndef SECP256K1_SCALAR_REPR_H
+#define SECP256K1_SCALAR_REPR_H
#include <stdint.h>
@@ -16,4 +16,4 @@ typedef struct {
#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{((uint64_t)(d1)) << 32 | (d0), ((uint64_t)(d3)) << 32 | (d2), ((uint64_t)(d5)) << 32 | (d4), ((uint64_t)(d7)) << 32 | (d6)}}
-#endif
+#endif /* SECP256K1_SCALAR_REPR_H */
diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h
index 56e7bd82af..db1ebf94be 100644
--- a/src/secp256k1/src/scalar_4x64_impl.h
+++ b/src/secp256k1/src/scalar_4x64_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_
-#define _SECP256K1_SCALAR_REPR_IMPL_H_
+#ifndef SECP256K1_SCALAR_REPR_IMPL_H
+#define SECP256K1_SCALAR_REPR_IMPL_H
/* Limbs of the secp256k1 order. */
#define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL)
@@ -946,4 +946,4 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r,
secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1);
}
-#endif
+#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scalar_8x32.h b/src/secp256k1/src/scalar_8x32.h
index 1319664f65..2c9a348e24 100644
--- a/src/secp256k1/src/scalar_8x32.h
+++ b/src/secp256k1/src/scalar_8x32.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_SCALAR_REPR_
-#define _SECP256K1_SCALAR_REPR_
+#ifndef SECP256K1_SCALAR_REPR_H
+#define SECP256K1_SCALAR_REPR_H
#include <stdint.h>
@@ -16,4 +16,4 @@ typedef struct {
#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)}}
-#endif
+#endif /* SECP256K1_SCALAR_REPR_H */
diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h
index aae4f35c08..4f9ed61fea 100644
--- a/src/secp256k1/src/scalar_8x32_impl.h
+++ b/src/secp256k1/src/scalar_8x32_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_
-#define _SECP256K1_SCALAR_REPR_IMPL_H_
+#ifndef SECP256K1_SCALAR_REPR_IMPL_H
+#define SECP256K1_SCALAR_REPR_IMPL_H
/* Limbs of the secp256k1 order. */
#define SECP256K1_N_0 ((uint32_t)0xD0364141UL)
@@ -718,4 +718,4 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r,
secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1);
}
-#endif
+#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h
index 2690d86558..fa790570ff 100644
--- a/src/secp256k1/src/scalar_impl.h
+++ b/src/secp256k1/src/scalar_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_SCALAR_IMPL_H_
-#define _SECP256K1_SCALAR_IMPL_H_
+#ifndef SECP256K1_SCALAR_IMPL_H
+#define SECP256K1_SCALAR_IMPL_H
#include "group.h"
#include "scalar.h"
@@ -330,4 +330,4 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar
#endif
#endif
-#endif
+#endif /* SECP256K1_SCALAR_IMPL_H */
diff --git a/src/secp256k1/src/scalar_low.h b/src/secp256k1/src/scalar_low.h
index 5574c44c7a..5836febc5b 100644
--- a/src/secp256k1/src/scalar_low.h
+++ b/src/secp256k1/src/scalar_low.h
@@ -4,12 +4,12 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_SCALAR_REPR_
-#define _SECP256K1_SCALAR_REPR_
+#ifndef SECP256K1_SCALAR_REPR_H
+#define SECP256K1_SCALAR_REPR_H
#include <stdint.h>
/** A scalar modulo the group order of the secp256k1 curve. */
typedef uint32_t secp256k1_scalar;
-#endif
+#endif /* SECP256K1_SCALAR_REPR_H */
diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h
index 4f94441f49..c80e70c5a2 100644
--- a/src/secp256k1/src/scalar_low_impl.h
+++ b/src/secp256k1/src/scalar_low_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_
-#define _SECP256K1_SCALAR_REPR_IMPL_H_
+#ifndef SECP256K1_SCALAR_REPR_IMPL_H
+#define SECP256K1_SCALAR_REPR_IMPL_H
#include "scalar.h"
@@ -111,4 +111,4 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const
return *a == *b;
}
-#endif
+#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/testrand.h b/src/secp256k1/src/testrand.h
index f8efa93c7c..f1f9be077e 100644
--- a/src/secp256k1/src/testrand.h
+++ b/src/secp256k1/src/testrand.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_TESTRAND_H_
-#define _SECP256K1_TESTRAND_H_
+#ifndef SECP256K1_TESTRAND_H
+#define SECP256K1_TESTRAND_H
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
@@ -35,4 +35,4 @@ static void secp256k1_rand256_test(unsigned char *b32);
/** Generate pseudorandom bytes with long sequences of zero and one bits. */
static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len);
-#endif
+#endif /* SECP256K1_TESTRAND_H */
diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h
index 15c7b9f12d..1255574209 100644
--- a/src/secp256k1/src/testrand_impl.h
+++ b/src/secp256k1/src/testrand_impl.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_TESTRAND_IMPL_H_
-#define _SECP256K1_TESTRAND_IMPL_H_
+#ifndef SECP256K1_TESTRAND_IMPL_H
+#define SECP256K1_TESTRAND_IMPL_H
#include <stdint.h>
#include <string.h>
@@ -107,4 +107,4 @@ static void secp256k1_rand256_test(unsigned char *b32) {
secp256k1_rand_bytes_test(b32, 32);
}
-#endif
+#endif /* SECP256K1_TESTRAND_IMPL_H */
diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h
index 4092a86c91..b0441d8e30 100644
--- a/src/secp256k1/src/util.h
+++ b/src/secp256k1/src/util.h
@@ -4,8 +4,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#ifndef _SECP256K1_UTIL_H_
-#define _SECP256K1_UTIL_H_
+#ifndef SECP256K1_UTIL_H
+#define SECP256K1_UTIL_H
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
@@ -110,4 +110,4 @@ static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_
SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t;
#endif
-#endif
+#endif /* SECP256K1_UTIL_H */
diff --git a/src/serialize.h b/src/serialize.h
index eeb05fa76c..02d3e4f7c6 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_SERIALIZE_H
#define BITCOIN_SERIALIZE_H
-#include "compat/endian.h"
+#include <compat/endian.h>
#include <algorithm>
#include <assert.h>
@@ -21,7 +21,7 @@
#include <utility>
#include <vector>
-#include "prevector.h"
+#include <prevector.h>
static const unsigned int MAX_SIZE = 0x02000000;
@@ -732,8 +732,8 @@ template<typename Stream, typename K, typename T, typename Pred, typename A>
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));
+ for (const auto& entry : m)
+ Serialize(os, entry);
}
template<typename Stream, typename K, typename T, typename Pred, typename A>
diff --git a/src/streams.h b/src/streams.h
index 159847279d..c26b1f618c 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -6,8 +6,8 @@
#ifndef BITCOIN_STREAMS_H
#define BITCOIN_STREAMS_H
-#include "support/allocators/zeroafterfree.h"
-#include "serialize.h"
+#include <support/allocators/zeroafterfree.h>
+#include <serialize.h>
#include <algorithm>
#include <assert.h>
@@ -241,8 +241,8 @@ public:
const_reference operator[](size_type pos) const { return vch[pos + nReadPos]; }
reference operator[](size_type pos) { return vch[pos + nReadPos]; }
void clear() { vch.clear(); nReadPos = 0; }
- iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); }
- void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); }
+ iterator insert(iterator it, const char x=char()) { return vch.insert(it, x); }
+ void insert(iterator it, size_type n, const char x) { vch.insert(it, n, x); }
value_type* data() { return vch.data() + nReadPos; }
const value_type* data() const { return vch.data() + nReadPos; }
@@ -345,18 +345,16 @@ public:
// Read from the beginning of the buffer
unsigned int nReadPosNext = nReadPos + nSize;
- if (nReadPosNext >= vch.size())
+ if (nReadPosNext > vch.size()) {
+ throw std::ios_base::failure("CDataStream::read(): end of data");
+ }
+ memcpy(pch, &vch[nReadPos], nSize);
+ if (nReadPosNext == vch.size())
{
- if (nReadPosNext > vch.size())
- {
- throw std::ios_base::failure("CDataStream::read(): end of data");
- }
- memcpy(pch, &vch[nReadPos], nSize);
nReadPos = 0;
vch.clear();
return;
}
- memcpy(pch, &vch[nReadPos], nSize);
nReadPos = nReadPosNext;
}
@@ -455,10 +453,6 @@ public:
class CAutoFile
{
private:
- // Disallow copies
- CAutoFile(const CAutoFile&);
- CAutoFile& operator=(const CAutoFile&);
-
const int nType;
const int nVersion;
@@ -475,6 +469,10 @@ public:
fclose();
}
+ // Disallow copies
+ CAutoFile(const CAutoFile&) = delete;
+ CAutoFile& operator=(const CAutoFile&) = delete;
+
void fclose()
{
if (file) {
@@ -564,10 +562,6 @@ public:
class CBufferedFile
{
private:
- // Disallow copies
- CBufferedFile(const CBufferedFile&);
- CBufferedFile& operator=(const CBufferedFile&);
-
const int nType;
const int nVersion;
@@ -609,6 +603,10 @@ public:
fclose();
}
+ // Disallow copies
+ CBufferedFile(const CBufferedFile&) = delete;
+ CBufferedFile& operator=(const CBufferedFile&) = delete;
+
int GetVersion() const { return nVersion; }
int GetType() const { return nType; }
diff --git a/src/support/allocators/secure.h b/src/support/allocators/secure.h
index 39347c73bb..7cd031bfe8 100644
--- a/src/support/allocators/secure.h
+++ b/src/support/allocators/secure.h
@@ -6,8 +6,8 @@
#ifndef BITCOIN_SUPPORT_ALLOCATORS_SECURE_H
#define BITCOIN_SUPPORT_ALLOCATORS_SECURE_H
-#include "support/lockedpool.h"
-#include "support/cleanse.h"
+#include <support/lockedpool.h>
+#include <support/cleanse.h>
#include <string>
diff --git a/src/support/allocators/zeroafterfree.h b/src/support/allocators/zeroafterfree.h
index 618874ceee..23ef96c13f 100644
--- a/src/support/allocators/zeroafterfree.h
+++ b/src/support/allocators/zeroafterfree.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_SUPPORT_ALLOCATORS_ZEROAFTERFREE_H
#define BITCOIN_SUPPORT_ALLOCATORS_ZEROAFTERFREE_H
-#include "support/cleanse.h"
+#include <support/cleanse.h>
#include <memory>
#include <vector>
diff --git a/src/support/cleanse.cpp b/src/support/cleanse.cpp
index a2141b2449..8d3c7369b4 100644
--- a/src/support/cleanse.cpp
+++ b/src/support/cleanse.cpp
@@ -3,11 +3,41 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "cleanse.h"
+#include <support/cleanse.h>
-#include <openssl/crypto.h>
+#include <cstring>
+#if defined(_MSC_VER)
+#include <Windows.h> // For SecureZeroMemory.
+#endif
+
+/* Compilers have a bad habit of removing "superfluous" memset calls that
+ * are trying to zero memory. For example, when memset()ing a buffer and
+ * then free()ing it, the compiler might decide that the memset is
+ * unobservable and thus can be removed.
+ *
+ * Previously we used OpenSSL which tried to stop this by a) implementing
+ * memset in assembly on x86 and b) putting the function in its own file
+ * for other platforms.
+ *
+ * This change removes those tricks in favour of using asm directives to
+ * scare the compiler away. As best as our compiler folks can tell, this is
+ * sufficient and will continue to be so.
+ *
+ * Adam Langley <agl@google.com>
+ * Commit: ad1907fe73334d6c696c8539646c21b11178f20f
+ * BoringSSL (LICENSE: ISC)
+ */
void memory_cleanse(void *ptr, size_t len)
{
- OPENSSL_cleanse(ptr, len);
+ std::memset(ptr, 0, len);
+
+ /* As best as we can tell, this is sufficient to break any optimisations that
+ might try to eliminate "superfluous" memsets. If there's an easy way to
+ detect memset_s, it would be better to use that. */
+#if defined(_MSC_VER)
+ SecureZeroMemory(ptr, len);
+#else
+ __asm__ __volatile__("" : : "r"(ptr) : "memory");
+#endif
}
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index 2ead72185f..98e8694181 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -2,11 +2,11 @@
// 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"
+#include <support/lockedpool.h>
+#include <support/cleanse.h>
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
#ifdef WIN32
diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h
index cecbdec1aa..834f0371e2 100644
--- a/src/support/lockedpool.h
+++ b/src/support/lockedpool.h
@@ -50,6 +50,9 @@ public:
Arena(void *base, size_t size, size_t alignment);
virtual ~Arena();
+ Arena(const Arena& other) = delete; // non construction-copyable
+ Arena& operator=(const Arena&) = delete; // non copyable
+
/** Memory statistics. */
struct Stats
{
@@ -85,9 +88,6 @@ public:
*/
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.
*/
@@ -153,6 +153,9 @@ public:
explicit LockedPool(std::unique_ptr<LockedPageAllocator> allocator, LockingFailed_Callback lf_cb_in = nullptr);
~LockedPool();
+ LockedPool(const LockedPool& other) = delete; // non construction-copyable
+ LockedPool& operator=(const LockedPool&) = delete; // non copyable
+
/** Allocate size bytes from this arena.
* Returns pointer on success, or 0 if memory is full or
* the application tried to allocate 0 bytes.
@@ -168,9 +171,6 @@ public:
/** 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 */
diff --git a/src/sync.cpp b/src/sync.cpp
index 9c351ea487..3f51383ea2 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -2,16 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "sync.h"
+#include <sync.h>
-#include "util.h"
-#include "utilstrencodings.h"
+#include <set>
+#include <util.h>
+#include <utilstrencodings.h>
#include <stdio.h>
-#include <boost/thread.hpp>
-
#ifdef DEBUG_LOCKCONTENTION
+#if !defined(HAVE_THREAD_LOCAL)
+static_assert(false, "thread_local is not supported");
+#endif
void PrintLockContention(const char* pszName, const char* pszFile, int nLine)
{
LogPrintf("LOCKCONTENTION: %s\n", pszName);
@@ -45,8 +47,8 @@ struct CLockLocation {
return mutexName + " " + sourceFile + ":" + itostr(sourceLine) + (fTry ? " (TRY)" : "");
}
- bool fTry;
private:
+ bool fTry;
std::string mutexName;
std::string sourceFile;
int sourceLine;
@@ -67,10 +69,10 @@ struct LockData {
LockOrders lockorders;
InvLockOrders invlockorders;
- boost::mutex dd_mutex;
+ std::mutex dd_mutex;
} static lockdata;
-boost::thread_specific_ptr<LockStack> lockstack;
+static thread_local std::unique_ptr<LockStack> lockstack;
static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2)
{
@@ -98,14 +100,14 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
assert(false);
}
-static void push_lock(void* c, const CLockLocation& locklocation, bool fTry)
+static void push_lock(void* c, const CLockLocation& locklocation)
{
- if (lockstack.get() == nullptr)
+ if (!lockstack)
lockstack.reset(new LockStack);
- boost::unique_lock<boost::mutex> lock(lockdata.dd_mutex);
+ std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
- (*lockstack).push_back(std::make_pair(c, locklocation));
+ lockstack->push_back(std::make_pair(c, locklocation));
for (const std::pair<void*, CLockLocation> & i : (*lockstack)) {
if (i.first == c)
@@ -130,7 +132,7 @@ static void pop_lock()
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
{
- push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry), fTry);
+ push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry));
}
void LeaveCritical()
@@ -155,13 +157,23 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine,
abort();
}
+void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
+{
+ for (const std::pair<void*, CLockLocation>& i : *lockstack) {
+ if (i.first == cs) {
+ fprintf(stderr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
+ abort();
+ }
+ }
+}
+
void DeleteLock(void* cs)
{
if (!lockdata.available) {
// We're already shutting down.
return;
}
- boost::unique_lock<boost::mutex> lock(lockdata.dd_mutex);
+ std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
std::pair<void*, void*> item = std::make_pair(cs, nullptr);
LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
while (it != lockdata.lockorders.end() && it->first.first == cs) {
diff --git a/src/sync.h b/src/sync.h
index 0871c5fb4d..3af27c65d0 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -6,11 +6,11 @@
#ifndef BITCOIN_SYNC_H
#define BITCOIN_SYNC_H
-#include "threadsafety.h"
+#include <threadsafety.h>
-#include <boost/thread/condition_variable.hpp>
-#include <boost/thread/mutex.hpp>
-#include <boost/thread/recursive_mutex.hpp>
+#include <condition_variable>
+#include <thread>
+#include <mutex>
////////////////////////////////////////////////
@@ -21,17 +21,17 @@
/*
CCriticalSection mutex;
- boost::recursive_mutex mutex;
+ std::recursive_mutex mutex;
LOCK(mutex);
- boost::unique_lock<boost::recursive_mutex> criticalblock(mutex);
+ std::unique_lock<std::recursive_mutex> criticalblock(mutex);
LOCK2(mutex1, mutex2);
- boost::unique_lock<boost::recursive_mutex> criticalblock1(mutex1);
- boost::unique_lock<boost::recursive_mutex> criticalblock2(mutex2);
+ std::unique_lock<std::recursive_mutex> criticalblock1(mutex1);
+ std::unique_lock<std::recursive_mutex> criticalblock2(mutex2);
TRY_LOCK(mutex, name);
- boost::unique_lock<boost::recursive_mutex> name(mutex, boost::try_to_lock_t);
+ std::unique_lock<std::recursive_mutex> name(mutex, std::try_to_lock_t);
ENTER_CRITICAL_SECTION(mutex); // no RAII
mutex.lock();
@@ -75,20 +75,23 @@ void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs
void LeaveCritical();
std::string LocksHeld();
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
+void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
void DeleteLock(void* cs);
#else
void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
void static inline LeaveCritical() {}
void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
+void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
void static inline DeleteLock(void* cs) {}
#endif
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
+#define AssertLockNotHeld(cs) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs)
/**
- * Wrapped boost mutex: supports recursive locking, but no waiting
+ * Wrapped mutex: supports recursive locking, but no waiting
* TODO: We should move away from using the recursive lock by default.
*/
-class CCriticalSection : public AnnotatedMixin<boost::recursive_mutex>
+class CCriticalSection : public AnnotatedMixin<std::recursive_mutex>
{
public:
~CCriticalSection() {
@@ -96,22 +99,24 @@ public:
}
};
-/** Wrapped boost mutex: supports waiting but not recursive locking */
-typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection;
+/** Wrapped mutex: supports waiting but not recursive locking */
+typedef AnnotatedMixin<std::mutex> CWaitableCriticalSection;
-/** Just a typedef for boost::condition_variable, can be wrapped later if desired */
-typedef boost::condition_variable CConditionVariable;
+/** Just a typedef for std::condition_variable, can be wrapped later if desired */
+typedef std::condition_variable CConditionVariable;
+
+/** Just a typedef for std::unique_lock, can be wrapped later if desired */
+typedef std::unique_lock<std::mutex> WaitableLock;
#ifdef DEBUG_LOCKCONTENTION
void PrintLockContention(const char* pszName, const char* pszFile, int nLine);
#endif
-/** Wrapper around boost::unique_lock<Mutex> */
-template <typename Mutex>
-class SCOPED_LOCKABLE CMutexLock
+/** Wrapper around std::unique_lock<CCriticalSection> */
+class SCOPED_LOCKABLE CCriticalBlock
{
private:
- boost::unique_lock<Mutex> lock;
+ std::unique_lock<CCriticalSection> lock;
void Enter(const char* pszName, const char* pszFile, int nLine)
{
@@ -136,7 +141,7 @@ private:
}
public:
- CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, boost::defer_lock)
+ CCriticalBlock(CCriticalSection& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, std::defer_lock)
{
if (fTry)
TryEnter(pszName, pszFile, nLine);
@@ -144,18 +149,18 @@ public:
Enter(pszName, pszFile, nLine);
}
- CMutexLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
+ CCriticalBlock(CCriticalSection* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
{
if (!pmutexIn) return;
- lock = boost::unique_lock<Mutex>(*pmutexIn, boost::defer_lock);
+ lock = std::unique_lock<CCriticalSection>(*pmutexIn, std::defer_lock);
if (fTry)
TryEnter(pszName, pszFile, nLine);
else
Enter(pszName, pszFile, nLine);
}
- ~CMutexLock() UNLOCK_FUNCTION()
+ ~CCriticalBlock() UNLOCK_FUNCTION()
{
if (lock.owns_lock())
LeaveCritical();
@@ -167,8 +172,6 @@ public:
}
};
-typedef CMutexLock<CCriticalSection> CCriticalBlock;
-
#define PASTE(x, y) x ## y
#define PASTE2(x, y) PASTE(x, y)
@@ -191,8 +194,8 @@ typedef CMutexLock<CCriticalSection> CCriticalBlock;
class CSemaphore
{
private:
- boost::condition_variable condition;
- boost::mutex mutex;
+ std::condition_variable condition;
+ std::mutex mutex;
int value;
public:
@@ -200,16 +203,14 @@ public:
void wait()
{
- boost::unique_lock<boost::mutex> lock(mutex);
- while (value < 1) {
- condition.wait(lock);
- }
+ std::unique_lock<std::mutex> lock(mutex);
+ condition.wait(lock, [&]() { return value >= 1; });
value--;
}
bool try_wait()
{
- boost::unique_lock<boost::mutex> lock(mutex);
+ std::lock_guard<std::mutex> lock(mutex);
if (value < 1)
return false;
value--;
@@ -219,7 +220,7 @@ public:
void post()
{
{
- boost::unique_lock<boost::mutex> lock(mutex);
+ std::lock_guard<std::mutex> lock(mutex);
value++;
}
condition.notify_one();
diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp
index ffbeeb7d91..6be176a1ad 100644
--- a/src/test/DoS_tests.cpp
+++ b/src/test/DoS_tests.cpp
@@ -4,17 +4,17 @@
// Unit tests for denial-of-service detection/prevention code
-#include "chainparams.h"
-#include "keystore.h"
-#include "net.h"
-#include "net_processing.h"
-#include "pow.h"
-#include "script/sign.h"
-#include "serialize.h"
-#include "util.h"
-#include "validation.h"
-
-#include "test/test_bitcoin.h"
+#include <chainparams.h>
+#include <keystore.h>
+#include <net.h>
+#include <net_processing.h>
+#include <pow.h>
+#include <script/sign.h>
+#include <serialize.h>
+#include <util.h>
+#include <validation.h>
+
+#include <test/test_bitcoin.h>
#include <stdint.h>
@@ -40,8 +40,141 @@ CService ip(uint32_t i)
static NodeId id = 0;
+void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds);
+
BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup)
+// Test eviction of an outbound peer whose chain never advances
+// Mock a node connection, and use mocktime to simulate a peer
+// which never sends any headers messages. PeerLogic should
+// decide to evict that outbound peer, after the appropriate timeouts.
+// Note that we protect 4 outbound nodes from being subject to
+// this logic; this test takes advantage of that protection only
+// being applied to nodes which send headers with sufficient
+// work.
+BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
+{
+ std::atomic<bool> interruptDummy(false);
+
+ // Mock an outbound peer
+ CAddress addr1(ip(0xa0b0c001), NODE_NONE);
+ CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", /*fInboundIn=*/ false);
+ dummyNode1.SetSendVersion(PROTOCOL_VERSION);
+
+ peerLogic->InitializeNode(&dummyNode1);
+ dummyNode1.nVersion = 1;
+ dummyNode1.fSuccessfullyConnected = true;
+
+ // This test requires that we have a chain with non-zero work.
+ LOCK(cs_main);
+ BOOST_CHECK(chainActive.Tip() != nullptr);
+ BOOST_CHECK(chainActive.Tip()->nChainWork > 0);
+
+ // Test starts here
+ LOCK(dummyNode1.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in getheaders
+ LOCK(dummyNode1.cs_vSend);
+ BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
+ dummyNode1.vSendMsg.clear();
+
+ int64_t nStartTime = GetTime();
+ // Wait 21 minutes
+ SetMockTime(nStartTime+21*60);
+ peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in getheaders
+ BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
+ // Wait 3 more minutes
+ SetMockTime(nStartTime+24*60);
+ peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in disconnect
+ BOOST_CHECK(dummyNode1.fDisconnect == true);
+ SetMockTime(0);
+
+ bool dummy;
+ peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
+}
+
+void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic)
+{
+ CAddress addr(ip(GetRandInt(0xffffffff)), NODE_NONE);
+ vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", /*fInboundIn=*/ false));
+ CNode &node = *vNodes.back();
+ node.SetSendVersion(PROTOCOL_VERSION);
+
+ peerLogic.InitializeNode(&node);
+ node.nVersion = 1;
+ node.fSuccessfullyConnected = true;
+
+ CConnmanTest::AddNode(node);
+}
+
+BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
+{
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+ constexpr int nMaxOutbound = 8;
+ CConnman::Options options;
+ options.nMaxConnections = 125;
+ options.nMaxOutbound = nMaxOutbound;
+ options.nMaxFeeler = 1;
+
+ connman->Init(options);
+ std::vector<CNode *> vNodes;
+
+ // Mock some outbound peers
+ for (int i=0; i<nMaxOutbound; ++i) {
+ AddRandomOutboundPeer(vNodes, *peerLogic);
+ }
+
+ peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
+
+ // No nodes should be marked for disconnection while we have no extra peers
+ for (const CNode *node : vNodes) {
+ BOOST_CHECK(node->fDisconnect == false);
+ }
+
+ SetMockTime(GetTime() + 3*consensusParams.nPowTargetSpacing + 1);
+
+ // Now tip should definitely be stale, and we should look for an extra
+ // outbound peer
+ peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
+ BOOST_CHECK(connman->GetTryNewOutboundPeer());
+
+ // Still no peers should be marked for disconnection
+ for (const CNode *node : vNodes) {
+ BOOST_CHECK(node->fDisconnect == false);
+ }
+
+ // If we add one more peer, something should get marked for eviction
+ // on the next check (since we're mocking the time to be in the future, the
+ // required time connected check should be satisfied).
+ AddRandomOutboundPeer(vNodes, *peerLogic);
+
+ peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
+ for (int i=0; i<nMaxOutbound; ++i) {
+ BOOST_CHECK(vNodes[i]->fDisconnect == false);
+ }
+ // Last added node should get marked for eviction
+ BOOST_CHECK(vNodes.back()->fDisconnect == true);
+
+ vNodes.back()->fDisconnect = false;
+
+ // Update the last announced block time for the last
+ // peer, and check that the next newest node gets evicted.
+ UpdateLastBlockAnnounceTime(vNodes.back()->GetId(), GetTime());
+
+ peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
+ for (int i=0; i<nMaxOutbound-1; ++i) {
+ BOOST_CHECK(vNodes[i]->fDisconnect == false);
+ }
+ BOOST_CHECK(vNodes[nMaxOutbound-1]->fDisconnect == true);
+ BOOST_CHECK(vNodes.back()->fDisconnect == false);
+
+ bool dummy;
+ for (const CNode *node : vNodes) {
+ peerLogic->FinalizeNode(node->GetId(), dummy);
+ }
+
+ CConnmanTest::ClearNodes();
+}
+
BOOST_AUTO_TEST_CASE(DoS_banning)
{
std::atomic<bool> interruptDummy(false);
@@ -50,27 +183,42 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", true);
dummyNode1.SetSendVersion(PROTOCOL_VERSION);
- GetNodeSignals().InitializeNode(&dummyNode1, *connman);
+ peerLogic->InitializeNode(&dummyNode1);
dummyNode1.nVersion = 1;
dummyNode1.fSuccessfullyConnected = true;
- Misbehaving(dummyNode1.GetId(), 100); // Should get banned
- SendMessages(&dummyNode1, *connman, interruptDummy);
+ {
+ LOCK(cs_main);
+ Misbehaving(dummyNode1.GetId(), 100); // Should get banned
+ }
+ LOCK(dummyNode1.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode1, interruptDummy);
BOOST_CHECK(connman->IsBanned(addr1));
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, 1, 1, CAddress(), "", true);
dummyNode2.SetSendVersion(PROTOCOL_VERSION);
- GetNodeSignals().InitializeNode(&dummyNode2, *connman);
+ peerLogic->InitializeNode(&dummyNode2);
dummyNode2.nVersion = 1;
dummyNode2.fSuccessfullyConnected = true;
- Misbehaving(dummyNode2.GetId(), 50);
- SendMessages(&dummyNode2, *connman, interruptDummy);
+ {
+ LOCK(cs_main);
+ Misbehaving(dummyNode2.GetId(), 50);
+ }
+ LOCK(dummyNode2.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode2, interruptDummy);
BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet...
BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be
- Misbehaving(dummyNode2.GetId(), 50);
- SendMessages(&dummyNode2, *connman, interruptDummy);
+ {
+ LOCK(cs_main);
+ Misbehaving(dummyNode2.GetId(), 50);
+ }
+ peerLogic->SendMessages(&dummyNode2, interruptDummy);
BOOST_CHECK(connman->IsBanned(addr2));
+
+ bool dummy;
+ peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
+ peerLogic->FinalizeNode(dummyNode2.GetId(), dummy);
}
BOOST_AUTO_TEST_CASE(DoS_banscore)
@@ -82,19 +230,32 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, CAddress(), "", true);
dummyNode1.SetSendVersion(PROTOCOL_VERSION);
- GetNodeSignals().InitializeNode(&dummyNode1, *connman);
+ peerLogic->InitializeNode(&dummyNode1);
dummyNode1.nVersion = 1;
dummyNode1.fSuccessfullyConnected = true;
- Misbehaving(dummyNode1.GetId(), 100);
- SendMessages(&dummyNode1, *connman, interruptDummy);
+ {
+ LOCK(cs_main);
+ Misbehaving(dummyNode1.GetId(), 100);
+ }
+ LOCK(dummyNode1.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode1, interruptDummy);
BOOST_CHECK(!connman->IsBanned(addr1));
- Misbehaving(dummyNode1.GetId(), 10);
- SendMessages(&dummyNode1, *connman, interruptDummy);
+ {
+ LOCK(cs_main);
+ Misbehaving(dummyNode1.GetId(), 10);
+ }
+ peerLogic->SendMessages(&dummyNode1, interruptDummy);
BOOST_CHECK(!connman->IsBanned(addr1));
- Misbehaving(dummyNode1.GetId(), 1);
- SendMessages(&dummyNode1, *connman, interruptDummy);
+ {
+ LOCK(cs_main);
+ Misbehaving(dummyNode1.GetId(), 1);
+ }
+ peerLogic->SendMessages(&dummyNode1, interruptDummy);
BOOST_CHECK(connman->IsBanned(addr1));
gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD));
+
+ bool dummy;
+ peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
}
BOOST_AUTO_TEST_CASE(DoS_bantime)
@@ -108,12 +269,16 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
CAddress addr(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, CAddress(), "", true);
dummyNode.SetSendVersion(PROTOCOL_VERSION);
- GetNodeSignals().InitializeNode(&dummyNode, *connman);
+ peerLogic->InitializeNode(&dummyNode);
dummyNode.nVersion = 1;
dummyNode.fSuccessfullyConnected = true;
- Misbehaving(dummyNode.GetId(), 100);
- SendMessages(&dummyNode, *connman, interruptDummy);
+ {
+ LOCK(cs_main);
+ Misbehaving(dummyNode.GetId(), 100);
+ }
+ LOCK(dummyNode.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode, interruptDummy);
BOOST_CHECK(connman->IsBanned(addr));
SetMockTime(nStartTime+60*60);
@@ -121,11 +286,15 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
SetMockTime(nStartTime+60*60*24+1);
BOOST_CHECK(!connman->IsBanned(addr));
+
+ bool dummy;
+ peerLogic->FinalizeNode(dummyNode.GetId(), dummy);
}
CTransactionRef RandomOrphan()
{
std::map<uint256, COrphanTx>::iterator it;
+ LOCK(cs_main);
it = mapOrphanTransactions.lower_bound(InsecureRand256());
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
@@ -195,6 +364,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
BOOST_CHECK(!AddOrphanTx(MakeTransactionRef(tx), i));
}
+ LOCK(cs_main);
// Test EraseOrphansFor:
for (NodeId i = 0; i < 3; i++)
{
diff --git a/src/test/README.md b/src/test/README.md
index eeb04c6ffa..01da32109b 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -5,7 +5,10 @@ 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 run the bitcoind tests manually, launch `src/test/test_bitcoin`. To recompile
+after a test file was modified, run `make` and then run the test again. If you
+modify a non-test file, use `make -C src/test` to recompile only what's needed
+to run the bitcoind tests.
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
@@ -49,4 +52,4 @@ examine `uint256_tests.cpp`.
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/).
+[http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/](http://archive.is/dRBGf).
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 7be29c6d6b..609b1a05d3 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -1,14 +1,14 @@
// 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 "addrman.h"
-#include "test/test_bitcoin.h"
+#include <addrman.h>
+#include <test/test_bitcoin.h>
#include <string>
#include <boost/test/unit_test.hpp>
-#include "hash.h"
-#include "netbase.h"
-#include "random.h"
+#include <hash.h>
+#include <netbase.h>
+#include <random.h>
class CAddrManTest : public CAddrMan
{
diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp
index 4a533b5bf2..83fa3b76be 100644
--- a/src/test/allocator_tests.cpp
+++ b/src/test/allocator_tests.cpp
@@ -2,10 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "util.h"
+#include <util.h>
-#include "support/allocators/secure.h"
-#include "test/test_bitcoin.h"
+#include <support/allocators/secure.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp
index 952cf901f0..7ebbd22c5d 100644
--- a/src/test/amount_tests.cpp
+++ b/src/test/amount_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "amount.h"
-#include "policy/feerate.h"
-#include "test/test_bitcoin.h"
+#include <amount.h>
+#include <policy/feerate.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp
index 2c98fbcfd6..eb3148a1de 100644
--- a/src/test/arith_uint256_tests.cpp
+++ b/src/test/arith_uint256_tests.cpp
@@ -8,11 +8,11 @@
#include <iomanip>
#include <limits>
#include <cmath>
-#include "uint256.h"
-#include "arith_uint256.h"
+#include <uint256.h>
+#include <arith_uint256.h>
#include <string>
-#include "version.h"
-#include "test/test_bitcoin.h"
+#include <version.h>
+#include <test/test_bitcoin.h>
BOOST_FIXTURE_TEST_SUITE(arith_uint256_tests, BasicTestingSetup)
diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp
index 6422b3a88f..abf236b953 100644
--- a/src/test/base32_tests.cpp
+++ b/src/test/base32_tests.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "utilstrencodings.h"
-#include "test/test_bitcoin.h"
+#include <utilstrencodings.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index ee633249e9..3156cc53d0 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -2,22 +2,23 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "base58.h"
+#include <base58.h>
-#include "data/base58_encode_decode.json.h"
-#include "data/base58_keys_invalid.json.h"
-#include "data/base58_keys_valid.json.h"
+#include <test/data/base58_encode_decode.json.h>
+#include <test/data/base58_keys_invalid.json.h>
+#include <test/data/base58_keys_valid.json.h>
-#include "key.h"
-#include "script/script.h"
-#include "uint256.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "test/test_bitcoin.h"
+#include <key.h>
+#include <script/script.h>
+#include <test/test_bitcoin.h>
+#include <uint256.h>
+#include <util.h>
+#include <utilstrencodings.h>
+
+#include <univalue.h>
#include <boost/test/unit_test.hpp>
-#include <univalue.h>
extern UniValue read_json(const std::string& jsondata);
@@ -72,63 +73,18 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
}
-// Visitor to check address type
-class TestAddrTypeVisitor : public boost::static_visitor<bool>
-{
-private:
- std::string exp_addrType;
-public:
- explicit TestAddrTypeVisitor(const std::string &_exp_addrType) : exp_addrType(_exp_addrType) { }
- bool operator()(const CKeyID &id) const
- {
- return (exp_addrType == "pubkey");
- }
- bool operator()(const CScriptID &id) const
- {
- return (exp_addrType == "script");
- }
- bool operator()(const CNoDestination &no) const
- {
- return (exp_addrType == "none");
- }
-};
-
-// Visitor to check address payload
-class TestPayloadVisitor : public boost::static_visitor<bool>
-{
-private:
- std::vector<unsigned char> exp_payload;
-public:
- explicit TestPayloadVisitor(std::vector<unsigned char> &_exp_payload) : exp_payload(_exp_payload) { }
- bool operator()(const CKeyID &id) const
- {
- uint160 exp_key(exp_payload);
- return exp_key == id;
- }
- bool operator()(const CScriptID &id) const
- {
- uint160 exp_key(exp_payload);
- return exp_key == id;
- }
- bool operator()(const CNoDestination &no) const
- {
- return exp_payload.size() == 0;
- }
-};
-
// Goal: check that parsed keys match test payload
BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
{
UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
CBitcoinSecret secret;
- CBitcoinAddress addr;
+ CTxDestination destination;
SelectParams(CBaseChainParams::MAIN);
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
- if (test.size() < 3) // Allow for extra stuff (useful for comments)
- {
+ if (test.size() < 3) { // Allow for extra stuff (useful for comments)
BOOST_ERROR("Bad test: " << strTest);
continue;
}
@@ -136,16 +92,11 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
const UniValue &metadata = test[2].get_obj();
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
- bool isTestnet = find_value(metadata, "isTestnet").get_bool();
- if (isTestnet)
- SelectParams(CBaseChainParams::TESTNET);
- else
- SelectParams(CBaseChainParams::MAIN);
- if(isPrivkey)
- {
+ SelectParams(find_value(metadata, "chain").get_str());
+ bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool();
+ if (isPrivkey) {
bool isCompressed = find_value(metadata, "isCompressed").get_bool();
// Must be valid private key
- // Note: CBitcoinSecret::SetString tests isValid, whereas CBitcoinAddress does not!
BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest);
BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest);
CKey privkey = secret.GetKey();
@@ -153,18 +104,29 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest);
// Private key must be invalid public key
- addr.SetString(exp_base58string);
- BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid privkey as pubkey:" + strTest);
- }
- else
- {
- std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey"
+ destination = DecodeDestination(exp_base58string);
+ BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest);
+ } else {
// Must be valid public key
- BOOST_CHECK_MESSAGE(addr.SetString(exp_base58string), "SetString:" + strTest);
- BOOST_CHECK_MESSAGE(addr.IsValid(), "!IsValid:" + strTest);
- BOOST_CHECK_MESSAGE(addr.IsScript() == (exp_addrType == "script"), "isScript mismatch" + strTest);
- CTxDestination dest = addr.Get();
- BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch" + strTest);
+ destination = DecodeDestination(exp_base58string);
+ CScript script = GetScriptForDestination(destination);
+ BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
+ BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
+
+ // Try flipped case version
+ for (char& c : exp_base58string) {
+ if (c >= 'a' && c <= 'z') {
+ c = (c - 'a') + 'A';
+ } else if (c >= 'A' && c <= 'Z') {
+ c = (c - 'A') + 'a';
+ }
+ }
+ destination = DecodeDestination(exp_base58string);
+ BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest);
+ if (IsValidDestination(destination)) {
+ script = GetScriptForDestination(destination);
+ BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
+ }
// Public key must be invalid private key
secret.SetString(exp_base58string);
@@ -190,13 +152,8 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
const UniValue &metadata = test[2].get_obj();
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
- bool isTestnet = find_value(metadata, "isTestnet").get_bool();
- if (isTestnet)
- SelectParams(CBaseChainParams::TESTNET);
- else
- SelectParams(CBaseChainParams::MAIN);
- if(isPrivkey)
- {
+ SelectParams(find_value(metadata, "chain").get_str());
+ if (isPrivkey) {
bool isCompressed = find_value(metadata, "isCompressed").get_bool();
CKey key;
key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
@@ -204,48 +161,26 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
CBitcoinSecret secret;
secret.SetKey(key);
BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest);
- }
- else
- {
- std::string exp_addrType = find_value(metadata, "addrType").get_str();
+ } else {
CTxDestination dest;
- if(exp_addrType == "pubkey")
- {
- dest = CKeyID(uint160(exp_payload));
- }
- else if(exp_addrType == "script")
- {
- dest = CScriptID(uint160(exp_payload));
- }
- else if(exp_addrType == "none")
- {
- dest = CNoDestination();
- }
- else
- {
- BOOST_ERROR("Bad addrtype: " << strTest);
- continue;
- }
- CBitcoinAddress addrOut;
- BOOST_CHECK_MESSAGE(addrOut.Set(dest), "encode dest: " + strTest);
- BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest);
+ CScript exp_script(exp_payload.begin(), exp_payload.end());
+ ExtractDestination(exp_script, dest);
+ std::string address = EncodeDestination(dest);
+
+ BOOST_CHECK_EQUAL(address, exp_base58string);
}
}
- // Visiting a CNoDestination must fail
- CBitcoinAddress dummyAddr;
- CTxDestination nodest = CNoDestination();
- BOOST_CHECK(!dummyAddr.Set(nodest));
-
SelectParams(CBaseChainParams::MAIN);
}
+
// Goal: check that base58 parsing code is robust against a variety of corrupted data
BOOST_AUTO_TEST_CASE(base58_keys_invalid)
{
UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases
CBitcoinSecret secret;
- CBitcoinAddress addr;
+ CTxDestination destination;
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
@@ -258,13 +193,15 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid)
std::string exp_base58string = test[0].get_str();
// must be invalid as public and as private key
- addr.SetString(exp_base58string);
- BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid pubkey:" + strTest);
- secret.SetString(exp_base58string);
- BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest);
+ for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) {
+ SelectParams(chain);
+ destination = DecodeDestination(exp_base58string);
+ BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest);
+ secret.SetString(exp_base58string);
+ BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey in mainnet:" + strTest);
+ }
}
}
BOOST_AUTO_TEST_SUITE_END()
-
diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp
index ccad94d946..46b3a04f35 100644
--- a/src/test/base64_tests.cpp
+++ b/src/test/base64_tests.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "utilstrencodings.h"
-#include "test/test_bitcoin.h"
+#include <utilstrencodings.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp
new file mode 100644
index 0000000000..495290c8d9
--- /dev/null
+++ b/src/test/bech32_tests.cpp
@@ -0,0 +1,67 @@
+// Copyright (c) 2017 Pieter Wuille
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bech32.h>
+#include <test/test_bitcoin.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup)
+
+bool CaseInsensitiveEqual(const std::string &s1, const std::string &s2)
+{
+ if (s1.size() != s2.size()) return false;
+ for (size_t i = 0; i < s1.size(); ++i) {
+ char c1 = s1[i];
+ if (c1 >= 'A' && c1 <= 'Z') c1 -= ('A' - 'a');
+ char c2 = s2[i];
+ if (c2 >= 'A' && c2 <= 'Z') c2 -= ('A' - 'a');
+ if (c1 != c2) return false;
+ }
+ return true;
+}
+
+BOOST_AUTO_TEST_CASE(bip173_testvectors_valid)
+{
+ static const std::string CASES[] = {
+ "A12UEL5L",
+ "a12uel5l",
+ "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs",
+ "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
+ "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j",
+ "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
+ "?1ezyfcl",
+ };
+ for (const std::string& str : CASES) {
+ auto ret = bech32::Decode(str);
+ BOOST_CHECK(!ret.first.empty());
+ std::string recode = bech32::Encode(ret.first, ret.second);
+ BOOST_CHECK(!recode.empty());
+ BOOST_CHECK(CaseInsensitiveEqual(str, recode));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid)
+{
+ static const std::string CASES[] = {
+ " 1nwldj5",
+ "\x7f""1axkwrx",
+ "\x80""1eym55h",
+ "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx",
+ "pzry9x0s0muk",
+ "1pzry9x0s0muk",
+ "x1b4n0q5v",
+ "li1dgmt3",
+ "de1lg7wt\xff",
+ "A1G7SGD8",
+ "10a06t8",
+ "1qzzfhee",
+ };
+ for (const std::string& str : CASES) {
+ auto ret = bech32::Decode(str);
+ BOOST_CHECK(ret.first.empty());
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index c851ab2849..e3e727439b 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -4,12 +4,12 @@
#include <boost/test/unit_test.hpp>
-#include "base58.h"
-#include "key.h"
-#include "uint256.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "test/test_bitcoin.h"
+#include <base58.h>
+#include <key.h>
+#include <uint256.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <test/test_bitcoin.h>
#include <string>
#include <vector>
@@ -91,7 +91,7 @@ void RunTest(const TestVector &test) {
std::vector<unsigned char> seed = ParseHex(test.strHexMaster);
CExtKey key;
CExtPubKey pubkey;
- key.SetMaster(&seed[0], seed.size());
+ key.SetMaster(seed.data(), seed.size());
pubkey = key.Neuter();
for (const TestDerivation &derive : test.vDerive) {
unsigned char data[74];
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index f2d5b385d0..718b5bdfb9 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -2,12 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "blockencodings.h"
-#include "consensus/merkle.h"
-#include "chainparams.h"
-#include "random.h"
+#include <blockencodings.h>
+#include <consensus/merkle.h>
+#include <chainparams.h>
+#include <random.h>
-#include "test/test_bitcoin.h"
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
@@ -62,6 +62,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
CBlock block(BuildBlockTestCase());
pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2]));
+ LOCK(pool.cs);
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
// Do a simple ShortTxIDs RT
@@ -161,6 +162,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
CBlock block(BuildBlockTestCase());
pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2]));
+ LOCK(pool.cs);
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
uint256 txhash;
@@ -227,6 +229,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
CBlock block(BuildBlockTestCase());
pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(*block.vtx[1]));
+ LOCK(pool.cs);
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
uint256 txhash;
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 2085b5cb2b..fdd30be0b1 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -2,19 +2,20 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bloom.h"
-
-#include "base58.h"
-#include "clientversion.h"
-#include "key.h"
-#include "merkleblock.h"
-#include "random.h"
-#include "serialize.h"
-#include "streams.h"
-#include "uint256.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "test/test_bitcoin.h"
+#include <bloom.h>
+
+#include <base58.h>
+#include <clientversion.h>
+#include <key.h>
+#include <merkleblock.h>
+#include <primitives/block.h>
+#include <random.h>
+#include <serialize.h>
+#include <streams.h>
+#include <uint256.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <test/test_bitcoin.h>
#include <vector>
@@ -154,8 +155,8 @@ BOOST_AUTO_TEST_CASE(bloom_match)
COutPoint prevOutPoint(uint256S("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0);
{
std::vector<unsigned char> data(32 + sizeof(unsigned int));
- memcpy(&data[0], prevOutPoint.hash.begin(), 32);
- memcpy(&data[32], &prevOutPoint.n, sizeof(unsigned int));
+ memcpy(data.data(), prevOutPoint.hash.begin(), 32);
+ memcpy(data.data()+32, &prevOutPoint.n, sizeof(unsigned int));
filter.insert(data);
}
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match manually serialized COutPoint");
@@ -179,20 +180,15 @@ BOOST_AUTO_TEST_CASE(bloom_match)
BOOST_AUTO_TEST_CASE(merkle_block_1)
{
- // Random real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
- // With 9 txes
- CBlock block;
- CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION);
- stream >> block;
-
+ CBlock block = getBlock13b8a();
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the last transaction
filter.insert(uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
CMerkleBlock merkleBlock(block, filter);
- BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
+ BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex());
- BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
+ BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 1);
std::pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
diff --git a/src/test/bswap_tests.cpp b/src/test/bswap_tests.cpp
index 7b3134d327..ab4f08b999 100644
--- a/src/test/bswap_tests.cpp
+++ b/src/test/bswap_tests.cpp
@@ -2,8 +2,8 @@
// 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 <compat/byteswap.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 6ae0bcadd0..42f9dd0600 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -2,12 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "util.h"
-#include "utiltime.h"
-#include "validation.h"
+#include <util.h>
+#include <utiltime.h>
+#include <validation.h>
-#include "test/test_bitcoin.h"
-#include "checkqueue.h"
+#include <test/test_bitcoin.h>
+#include <checkqueue.h>
#include <boost/test/unit_test.hpp>
#include <boost/thread.hpp>
#include <atomic>
@@ -18,13 +18,13 @@
#include <unordered_set>
#include <memory>
-#include "random.h"
+#include <random.h>
// BasicTestingSetup not sufficient because nScriptCheckThreads is not set
// otherwise.
BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup)
-static const int QUEUE_BATCH_SIZE = 128;
+static const unsigned int QUEUE_BATCH_SIZE = 128;
struct FakeCheck {
bool operator()()
@@ -38,7 +38,7 @@ struct FakeCheckCheckCompletion {
static std::atomic<size_t> n_calls;
bool operator()()
{
- ++n_calls;
+ n_calls.fetch_add(1, std::memory_order_relaxed);
return true;
}
void swap(FakeCheckCheckCompletion& x){};
@@ -88,15 +88,15 @@ struct MemoryCheck {
//
// Really, copy constructor should be deletable, but CCheckQueue breaks
// if it is deleted because of internal push_back.
- fake_allocated_memory += b;
+ fake_allocated_memory.fetch_add(b, std::memory_order_relaxed);
};
MemoryCheck(bool b_) : b(b_)
{
- fake_allocated_memory += b;
+ fake_allocated_memory.fetch_add(b, std::memory_order_relaxed);
};
- ~MemoryCheck(){
- fake_allocated_memory -= b;
-
+ ~MemoryCheck()
+ {
+ fake_allocated_memory.fetch_sub(b, std::memory_order_relaxed);
};
void swap(MemoryCheck& x) { std::swap(b, x.b); };
};
@@ -117,9 +117,9 @@ struct FrozenCleanupCheck {
{
if (should_freeze) {
std::unique_lock<std::mutex> l(m);
- nFrozen = 1;
+ nFrozen.store(1, std::memory_order_relaxed);
cv.notify_one();
- cv.wait(l, []{ return nFrozen == 0;});
+ cv.wait(l, []{ return nFrozen.load(std::memory_order_relaxed) == 0;});
}
}
void swap(FrozenCleanupCheck& x){std::swap(should_freeze, x.should_freeze);};
@@ -262,7 +262,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure)
control.Add(vChecks);
}
bool r =control.Wait();
- BOOST_REQUIRE(r || end_fails);
+ BOOST_REQUIRE(r != end_fails);
}
}
tg.interrupt_all();
@@ -337,7 +337,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory)
tg.join_all();
}
-// Test that a new verification cannot occur until all checks
+// Test that a new verification cannot occur until all checks
// have been destructed
BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup)
{
@@ -361,11 +361,14 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup)
std::unique_lock<std::mutex> l(FrozenCleanupCheck::m);
// Wait until the queue has finished all jobs and frozen
FrozenCleanupCheck::cv.wait(l, [](){return FrozenCleanupCheck::nFrozen == 1;});
- // Try to get control of the queue a bunch of times
- for (auto x = 0; x < 100 && !fails; ++x) {
- fails = queue->ControlMutex.try_lock();
- }
- // Unfreeze
+ }
+ // Try to get control of the queue a bunch of times
+ for (auto x = 0; x < 100 && !fails; ++x) {
+ fails = queue->ControlMutex.try_lock();
+ }
+ {
+ // Unfreeze (we need lock n case of spurious wakeup)
+ std::unique_lock<std::mutex> l(FrozenCleanupCheck::m);
FrozenCleanupCheck::nFrozen = 0;
}
// Awaken frozen destructor
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index dc358bff95..ed556c07f4 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -2,14 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "coins.h"
-#include "script/standard.h"
-#include "uint256.h"
-#include "undo.h"
-#include "utilstrencodings.h"
-#include "test/test_bitcoin.h"
-#include "validation.h"
-#include "consensus/validation.h"
+#include <coins.h>
+#include <script/standard.h>
+#include <uint256.h>
+#include <undo.h>
+#include <utilstrencodings.h>
+#include <test/test_bitcoin.h>
+#include <validation.h>
+#include <consensus/validation.h>
#include <vector>
#include <map>
@@ -81,8 +81,8 @@ public:
// Manually recompute the dynamic usage of the whole data, and compare it.
size_t ret = memusage::DynamicUsage(cacheCoins);
size_t count = 0;
- for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
- ret += it->second.coin.DynamicMemoryUsage();
+ for (const auto& entry : cacheCoins) {
+ ret += entry.second.coin.DynamicMemoryUsage();
++count;
}
BOOST_CHECK_EQUAL(GetCacheSize(), count);
@@ -189,15 +189,15 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
// Once every 1000 iterations and at the end, verify the full cache.
if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
- for (auto it = result.begin(); it != result.end(); it++) {
- bool have = stack.back()->HaveCoin(it->first);
- const Coin& coin = stack.back()->AccessCoin(it->first);
+ for (const auto& entry : result) {
+ bool have = stack.back()->HaveCoin(entry.first);
+ const Coin& coin = stack.back()->AccessCoin(entry.first);
BOOST_CHECK(have == !coin.IsSpent());
- BOOST_CHECK(coin == it->second);
+ BOOST_CHECK(coin == entry.second);
if (coin.IsSpent()) {
missed_an_entry = true;
} else {
- BOOST_CHECK(stack.back()->HaveCoinInCache(it->first));
+ BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
found_an_entry = true;
}
}
@@ -420,11 +420,11 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
// Once every 1000 iterations and at the end, verify the full cache.
if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
- for (auto it = result.begin(); it != result.end(); it++) {
- bool have = stack.back()->HaveCoin(it->first);
- const Coin& coin = stack.back()->AccessCoin(it->first);
+ for (const auto& entry : result) {
+ bool have = stack.back()->HaveCoin(entry.first);
+ const Coin& coin = stack.back()->AccessCoin(entry.first);
BOOST_CHECK(have == !coin.IsSpent());
- BOOST_CHECK(coin == it->second);
+ BOOST_CHECK(coin == entry.second);
}
}
diff --git a/src/test/compress_tests.cpp b/src/test/compress_tests.cpp
index 35e4458bba..87e75d8020 100644
--- a/src/test/compress_tests.cpp
+++ b/src/test/compress_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "compressor.h"
-#include "util.h"
-#include "test/test_bitcoin.h"
+#include <compressor.h>
+#include <util.h>
+#include <test/test_bitcoin.h>
#include <stdint.h>
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 391ad14ffa..125b68e76d 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -2,17 +2,17 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "crypto/aes.h"
-#include "crypto/chacha20.h"
-#include "crypto/ripemd160.h"
-#include "crypto/sha1.h"
-#include "crypto/sha256.h"
-#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 <crypto/aes.h>
+#include <crypto/chacha20.h>
+#include <crypto/ripemd160.h>
+#include <crypto/sha1.h>
+#include <crypto/sha256.h>
+#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 <vector>
@@ -58,12 +58,12 @@ void TestRIPEMD160(const std::string &in, const std::string &hexout) { TestVecto
void TestHMACSHA256(const std::string &hexkey, const std::string &hexin, const std::string &hexout) {
std::vector<unsigned char> key = ParseHex(hexkey);
- TestVector(CHMAC_SHA256(&key[0], key.size()), ParseHex(hexin), ParseHex(hexout));
+ TestVector(CHMAC_SHA256(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout));
}
void TestHMACSHA512(const std::string &hexkey, const std::string &hexin, const std::string &hexout) {
std::vector<unsigned char> key = ParseHex(hexkey);
- TestVector(CHMAC_SHA512(&key[0], key.size()), ParseHex(hexin), ParseHex(hexout));
+ TestVector(CHMAC_SHA512(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout));
}
void TestAES128(const std::string &hexkey, const std::string &hexin, const std::string &hexout)
@@ -76,13 +76,13 @@ void TestAES128(const std::string &hexkey, const std::string &hexin, const std::
assert(key.size() == 16);
assert(in.size() == 16);
assert(correctout.size() == 16);
- AES128Encrypt enc(&key[0]);
+ AES128Encrypt enc(key.data());
buf.resize(correctout.size());
buf2.resize(correctout.size());
- enc.Encrypt(&buf[0], &in[0]);
+ enc.Encrypt(buf.data(), in.data());
BOOST_CHECK_EQUAL(HexStr(buf), HexStr(correctout));
- AES128Decrypt dec(&key[0]);
- dec.Decrypt(&buf2[0], &buf[0]);
+ AES128Decrypt dec(key.data());
+ dec.Decrypt(buf2.data(), buf.data());
BOOST_CHECK_EQUAL(HexStr(buf2), HexStr(in));
}
@@ -96,12 +96,12 @@ void TestAES256(const std::string &hexkey, const std::string &hexin, const std::
assert(key.size() == 32);
assert(in.size() == 16);
assert(correctout.size() == 16);
- AES256Encrypt enc(&key[0]);
+ AES256Encrypt enc(key.data());
buf.resize(correctout.size());
- enc.Encrypt(&buf[0], &in[0]);
+ enc.Encrypt(buf.data(), in.data());
BOOST_CHECK(buf == correctout);
- AES256Decrypt dec(&key[0]);
- dec.Decrypt(&buf[0], &buf[0]);
+ AES256Decrypt dec(key.data());
+ dec.Decrypt(buf.data(), buf.data());
BOOST_CHECK(buf == in);
}
@@ -114,16 +114,16 @@ void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad
std::vector<unsigned char> realout(in.size() + AES_BLOCKSIZE);
// Encrypt the plaintext and verify that it equals the cipher
- AES128CBCEncrypt enc(&key[0], &iv[0], pad);
- int size = enc.Encrypt(&in[0], in.size(), &realout[0]);
+ AES128CBCEncrypt enc(key.data(), iv.data(), pad);
+ int size = enc.Encrypt(in.data(), in.size(), realout.data());
realout.resize(size);
BOOST_CHECK(realout.size() == correctout.size());
BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout);
// Decrypt the cipher and verify that it equals the plaintext
std::vector<unsigned char> decrypted(correctout.size());
- AES128CBCDecrypt dec(&key[0], &iv[0], pad);
- size = dec.Decrypt(&correctout[0], correctout.size(), &decrypted[0]);
+ AES128CBCDecrypt dec(key.data(), iv.data(), pad);
+ size = dec.Decrypt(correctout.data(), correctout.size(), decrypted.data());
decrypted.resize(size);
BOOST_CHECK(decrypted.size() == in.size());
BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin);
@@ -133,12 +133,12 @@ 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]);
+ int _size = enc.Encrypt(sub.data(), sub.size(), subout.data());
if (_size != 0)
{
subout.resize(_size);
std::vector<unsigned char> subdecrypted(subout.size());
- _size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]);
+ _size = dec.Decrypt(subout.data(), subout.size(), subdecrypted.data());
subdecrypted.resize(_size);
BOOST_CHECK(decrypted.size() == in.size());
BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub));
@@ -155,16 +155,16 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad
std::vector<unsigned char> realout(in.size() + AES_BLOCKSIZE);
// Encrypt the plaintext and verify that it equals the cipher
- AES256CBCEncrypt enc(&key[0], &iv[0], pad);
- int size = enc.Encrypt(&in[0], in.size(), &realout[0]);
+ AES256CBCEncrypt enc(key.data(), iv.data(), pad);
+ int size = enc.Encrypt(in.data(), in.size(), realout.data());
realout.resize(size);
BOOST_CHECK(realout.size() == correctout.size());
BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout);
// Decrypt the cipher and verify that it equals the plaintext
std::vector<unsigned char> decrypted(correctout.size());
- AES256CBCDecrypt dec(&key[0], &iv[0], pad);
- size = dec.Decrypt(&correctout[0], correctout.size(), &decrypted[0]);
+ AES256CBCDecrypt dec(key.data(), iv.data(), pad);
+ size = dec.Decrypt(correctout.data(), correctout.size(), decrypted.data());
decrypted.resize(size);
BOOST_CHECK(decrypted.size() == in.size());
BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin);
@@ -174,12 +174,12 @@ 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]);
+ int _size = enc.Encrypt(sub.data(), sub.size(), subout.data());
if (_size != 0)
{
subout.resize(_size);
std::vector<unsigned char> subdecrypted(subout.size());
- _size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]);
+ _size = dec.Decrypt(subout.data(), subout.size(), subdecrypted.data());
subdecrypted.resize(_size);
BOOST_CHECK(decrypted.size() == in.size());
BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub));
@@ -340,6 +340,22 @@ BOOST_AUTO_TEST_CASE(hmac_sha256_testvectors) {
"647320746f20626520686173686564206265666f7265206265696e6720757365"
"642062792074686520484d414320616c676f726974686d2e",
"9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2");
+ // Test case with key length 63 bytes.
+ TestHMACSHA256("4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566",
+ "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
+ "9de4b546756c83516720a4ad7fe7bdbeac4298c6fdd82b15f895a6d10b0769a6");
+ // Test case with key length 64 bytes.
+ TestHMACSHA256("4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665",
+ "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
+ "528c609a4c9254c274585334946b7c2661bad8f1fc406b20f6892478d19163dd");
+ // Test case with key length 65 bytes.
+ TestHMACSHA256("4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a",
+ "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
+ "d06af337f359a2330deffb8e3cbe4b5b7aa8ca1f208528cdbd245d5dc63c4483");
}
BOOST_AUTO_TEST_CASE(hmac_sha512_testvectors) {
@@ -383,6 +399,31 @@ BOOST_AUTO_TEST_CASE(hmac_sha512_testvectors) {
"642062792074686520484d414320616c676f726974686d2e",
"e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944"
"b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58");
+ // Test case with key length 127 bytes.
+ TestHMACSHA512("4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566",
+ "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
+ "267424dfb8eeb999f3e5ec39a4fe9fd14c923e6187e0897063e5c9e02b2e624a"
+ "c04413e762977df71a9fb5d562b37f89dfdfb930fce2ed1fa783bbc2a203d80e");
+ // Test case with key length 128 bytes.
+ TestHMACSHA512("4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665",
+ "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
+ "43aaac07bb1dd97c82c04df921f83b16a68d76815cd1a30d3455ad43a3d80484"
+ "2bb35462be42cc2e4b5902de4d204c1c66d93b47d1383e3e13a3788687d61258");
+ // Test case with key length 129 bytes.
+ TestHMACSHA512("4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"
+ "4a",
+ "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
+ "0b273325191cfc1b4b71d5075c8fcad67696309d292b1dad2cd23983a35feb8e"
+ "fb29795e79f2ef27f68cb1e16d76178c307a67beaad9456fac5fdffeadb16e2c");
}
BOOST_AUTO_TEST_CASE(aes_testvectors) {
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index 1004482224..32d46da984 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -2,10 +2,10 @@
// 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 "script/sigcache.h"
-#include "test/test_bitcoin.h"
-#include "random.h"
+#include <cuckoocache.h>
+#include <script/sigcache.h>
+#include <test/test_bitcoin.h>
+#include <random.h>
#include <thread>
/** Test Suite for CuckooCache
diff --git a/src/test/data/base58_keys_invalid.json b/src/test/data/base58_keys_invalid.json
index a088620f1b..2056c7491c 100644
--- a/src/test/data/base58_keys_invalid.json
+++ b/src/test/data/base58_keys_invalid.json
@@ -148,5 +148,35 @@
],
[
"2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED"
+ ],
+ [
+ "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"
+ ],
+ [
+ "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"
+ ],
+ [
+ "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"
+ ],
+ [
+ "bc1rw5uspcuh"
+ ],
+ [
+ "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"
+ ],
+ [
+ "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"
+ ],
+ [
+ "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"
+ ],
+ [
+ "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"
+ ],
+ [
+ "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"
+ ],
+ [
+ "bc1gmk9yu"
]
]
diff --git a/src/test/data/base58_keys_valid.json b/src/test/data/base58_keys_valid.json
index e1e252e22d..8418a6002d 100644
--- a/src/test/data/base58_keys_valid.json
+++ b/src/test/data/base58_keys_valid.json
@@ -1,452 +1,533 @@
[
[
- "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
- "65a16059864a2fdbc7c99a4723a8395bc6f188eb",
+ "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
+ "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "main"
}
- ],
+ ],
[
- "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou",
- "74f209f6ea907e2ea48f74fae05782ae8a665257",
+ "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou",
+ "a91474f209f6ea907e2ea48f74fae05782ae8a66525787",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "main"
}
- ],
+ ],
[
- "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",
- "53c0307d6851aa0ce7825ba883c6bd9ad242b486",
+ "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",
+ "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": true
+ "isPrivkey": false,
+ "chain": "test"
}
- ],
+ ],
[
- "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br",
- "6349a418fc4578d10a372b54b45c280cc8c4382f",
+ "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",
+ "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": true
+ "isPrivkey": false,
+ "chain": "regtest"
}
- ],
+ ],
[
- "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr",
- "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19",
+ "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br",
+ "a9146349a418fc4578d10a372b54b45c280cc8c4382f87",
{
- "isCompressed": false,
- "isPrivkey": true,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "test"
}
- ],
+ ],
[
- "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD",
- "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4",
+ "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr",
+ "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19",
{
- "isCompressed": true,
- "isPrivkey": true,
- "isTestnet": false
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "main"
}
- ],
+ ],
[
- "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",
- "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",
+ "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD",
+ "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4",
{
- "isCompressed": false,
- "isPrivkey": true,
- "isTestnet": true
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "main"
}
- ],
+ ],
[
- "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",
- "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",
+ "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",
+ "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",
{
- "isCompressed": true,
- "isPrivkey": true,
- "isTestnet": true
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "test"
}
- ],
+ ],
[
- "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ",
- "6d23156cbbdcc82a5a47eee4c2c7c583c18b6bf4",
+ "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",
+ "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": false
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "regtest"
}
- ],
+ ],
[
- "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy",
- "fcc5460dd6e2487c7d75b1963625da0e8f4c5975",
+ "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",
+ "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": false
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "test"
}
- ],
+ ],
[
- "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ",
- "f1d470f9b02370fdec2e6b708b08ac431bf7a5f7",
+ "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",
+ "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": true
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "regtest"
}
- ],
+ ],
[
- "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n",
- "c579342c2c4c9220205e2cdc285617040c924a0a",
+ "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ",
+ "76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": true
+ "isPrivkey": false,
+ "chain": "main"
}
- ],
+ ],
[
- "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc",
- "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e",
+ "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy",
+ "a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587",
{
- "isCompressed": false,
- "isPrivkey": true,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "main"
}
- ],
+ ],
[
- "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi",
- "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4",
+ "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ",
+ "76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac",
{
- "isCompressed": true,
- "isPrivkey": true,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "test"
}
- ],
+ ],
[
- "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj",
- "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203",
+ "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n",
+ "a914c579342c2c4c9220205e2cdc285617040c924a0a87",
{
- "isCompressed": false,
- "isPrivkey": true,
- "isTestnet": true
+ "isPrivkey": false,
+ "chain": "test"
}
- ],
+ ],
[
- "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN",
- "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9",
+ "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc",
+ "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e",
{
- "isCompressed": true,
- "isPrivkey": true,
- "isTestnet": true
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "main"
}
- ],
+ ],
[
- "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv",
- "7987ccaa53d02c8873487ef919677cd3db7a6912",
+ "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi",
+ "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": false
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "main"
}
- ],
+ ],
[
- "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks",
- "63bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb",
+ "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj",
+ "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": false
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "test"
}
- ],
+ ],
[
- "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk",
- "ef66444b5b17f14e8fae6e7e19b045a78c54fd79",
+ "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN",
+ "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": true
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "test"
}
- ],
+ ],
[
- "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o",
- "c3e55fceceaa4391ed2a9677f4a4d34eacd021a0",
+ "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv",
+ "76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": true
+ "isPrivkey": false,
+ "chain": "main"
}
- ],
+ ],
[
- "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9",
- "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252",
+ "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks",
+ "a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87",
{
- "isCompressed": false,
- "isPrivkey": true,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "main"
}
- ],
+ ],
[
- "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT",
- "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c",
+ "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk",
+ "76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac",
{
- "isCompressed": true,
- "isPrivkey": true,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "test"
}
- ],
+ ],
[
- "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo",
- "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52",
+ "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o",
+ "a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087",
{
- "isCompressed": false,
- "isPrivkey": true,
- "isTestnet": true
+ "isPrivkey": false,
+ "chain": "test"
}
- ],
+ ],
[
- "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7",
- "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69",
+ "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9",
+ "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252",
{
- "isCompressed": true,
- "isPrivkey": true,
- "isTestnet": true
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "main"
}
- ],
+ ],
[
- "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu",
- "adc1cc2081a27206fae25792f28bbc55b831549d",
+ "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT",
+ "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": false
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "main"
}
- ],
+ ],
[
- "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk",
- "188f91a931947eddd7432d6e614387e32b244709",
+ "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo",
+ "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": false
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "test"
}
- ],
+ ],
[
- "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H",
- "1694f5bc1a7295b600f40018a618a6ea48eeb498",
+ "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7",
+ "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": true
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "test"
}
- ],
+ ],
[
- "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN",
- "3b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f3",
+ "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu",
+ "76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": true
+ "isPrivkey": false,
+ "chain": "main"
}
- ],
+ ],
[
- "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR",
- "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0",
+ "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk",
+ "a914188f91a931947eddd7432d6e614387e32b24470987",
{
- "isCompressed": false,
- "isPrivkey": true,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "main"
}
- ],
+ ],
[
- "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8",
- "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af",
+ "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H",
+ "76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac",
{
- "isCompressed": true,
- "isPrivkey": true,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "test"
}
- ],
+ ],
[
- "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",
- "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",
+ "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN",
+ "a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387",
{
- "isCompressed": false,
- "isPrivkey": true,
- "isTestnet": true
+ "isPrivkey": false,
+ "chain": "test"
}
- ],
+ ],
[
- "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA",
- "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef",
+ "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR",
+ "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0",
{
- "isCompressed": true,
- "isPrivkey": true,
- "isTestnet": true
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "main"
}
- ],
+ ],
[
- "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4",
- "c4c1b72491ede1eedaca00618407ee0b772cad0d",
+ "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8",
+ "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": false
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "main"
}
- ],
+ ],
[
- "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y",
- "f6fe69bcb548a829cce4c57bf6fff8af3a5981f9",
+ "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",
+ "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": false
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "test"
}
- ],
+ ],
[
- "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6",
- "261f83568a098a8638844bd7aeca039d5f2352c0",
+ "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",
+ "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": true
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "regtest"
}
- ],
+ ],
[
- "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda",
- "e930e1834a4d234702773951d627cce82fbb5d2e",
+ "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA",
+ "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": true
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "test"
}
- ],
+ ],
[
- "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg",
- "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0",
+ "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4",
+ "76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac",
{
- "isCompressed": false,
- "isPrivkey": true,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "main"
}
- ],
+ ],
[
- "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi",
- "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3",
+ "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y",
+ "a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987",
{
- "isCompressed": true,
- "isPrivkey": true,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "main"
}
- ],
+ ],
[
- "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys",
- "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb",
+ "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6",
+ "76a914261f83568a098a8638844bd7aeca039d5f2352c088ac",
{
- "isCompressed": false,
- "isPrivkey": true,
- "isTestnet": true
+ "isPrivkey": false,
+ "chain": "test"
}
- ],
+ ],
[
- "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw",
- "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de",
+ "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda",
+ "a914e930e1834a4d234702773951d627cce82fbb5d2e87",
{
- "isCompressed": true,
- "isPrivkey": true,
- "isTestnet": true
+ "isPrivkey": false,
+ "chain": "test"
}
- ],
+ ],
[
- "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r",
- "5eadaf9bb7121f0f192561a5a62f5e5f54210292",
+ "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg",
+ "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": false
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "main"
}
- ],
+ ],
[
- "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3",
- "3f210e7277c899c3a155cc1c90f4106cbddeec6e",
+ "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi",
+ "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": false
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "main"
}
- ],
+ ],
[
- "myoqcgYiehufrsnnkqdqbp69dddVDMopJu",
- "c8a3c2a09a298592c3e180f02487cd91ba3400b5",
+ "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys",
+ "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": true
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "test"
}
- ],
+ ],
[
- "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C",
- "99b31df7c9068d1481b596578ddbb4d3bd90baeb",
+ "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw",
+ "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": true
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "test"
}
- ],
+ ],
[
- "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4",
- "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae",
+ "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r",
+ "76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac",
{
- "isCompressed": false,
- "isPrivkey": true,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "main"
}
- ],
+ ],
[
- "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2",
- "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd",
+ "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3",
+ "a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87",
{
- "isCompressed": true,
- "isPrivkey": true,
- "isTestnet": false
+ "isPrivkey": false,
+ "chain": "main"
}
- ],
+ ],
[
- "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV",
- "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801",
+ "myoqcgYiehufrsnnkqdqbp69dddVDMopJu",
+ "76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac",
{
- "isCompressed": false,
- "isPrivkey": true,
- "isTestnet": true
+ "isPrivkey": false,
+ "chain": "test"
}
- ],
+ ],
[
- "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h",
- "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c",
+ "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C",
+ "a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87",
{
- "isCompressed": true,
- "isPrivkey": true,
- "isTestnet": true
+ "isPrivkey": false,
+ "chain": "test"
}
- ],
+ ],
[
- "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE",
- "1ed467017f043e91ed4c44b4e8dd674db211c4e6",
+ "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4",
+ "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae",
{
- "addrType": "pubkey",
- "isPrivkey": false,
- "isTestnet": false
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "main"
}
- ],
+ ],
[
- "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G",
- "5ece0cadddc415b1980f001785947120acdb36fc",
+ "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2",
+ "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd",
{
- "addrType": "script",
- "isPrivkey": false,
- "isTestnet": false
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "main"
+ }
+ ],
+ [
+ "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV",
+ "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801",
+ {
+ "isCompressed": false,
+ "isPrivkey": true,
+ "chain": "test"
+ }
+ ],
+ [
+ "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h",
+ "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c",
+ {
+ "isCompressed": true,
+ "isPrivkey": true,
+ "chain": "test"
+ }
+ ],
+ [
+ "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE",
+ "76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac",
+ {
+ "isPrivkey": false,
+ "chain": "main"
+ }
+ ],
+ [
+ "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G",
+ "a9145ece0cadddc415b1980f001785947120acdb36fc87",
+ {
+ "isPrivkey": false,
+ "chain": "main"
+ }
+ ],
+ [
+ "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
+ "0014751e76e8199196d454941c45d1b3a323f1433bd6",
+ {
+ "isPrivkey": false,
+ "chain": "main",
+ "tryCaseFlip": true
+ }
+ ],
+ [
+ "bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080",
+ "0014751e76e8199196d454941c45d1b3a323f1433bd6",
+ {
+ "isPrivkey": false,
+ "chain": "regtest",
+ "tryCaseFlip": true
+ }
+ ],
+ [
+ "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
+ "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",
+ {
+ "isPrivkey": false,
+ "chain": "test",
+ "tryCaseFlip": true
+ }
+ ],
+ [
+ "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx",
+ "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6",
+ {
+ "isPrivkey": false,
+ "chain": "main",
+ "tryCaseFlip": true
+ }
+ ],
+ [
+ "bc1sw50qa3jx3s",
+ "6002751e",
+ {
+ "isPrivkey": false,
+ "chain": "main",
+ "tryCaseFlip": true
+ }
+ ],
+ [
+ "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj",
+ "5210751e76e8199196d454941c45d1b3a323",
+ {
+ "isPrivkey": false,
+ "chain": "main",
+ "tryCaseFlip": true
+ }
+ ],
+ [
+ "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",
+ "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433",
+ {
+ "isPrivkey": false,
+ "chain": "test",
+ "tryCaseFlip": true
+ }
+ ],
+ [
+ "bcrt1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvseswlauz7",
+ "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433",
+ {
+ "isPrivkey": false,
+ "chain": "regtest",
+ "tryCaseFlip": true
}
]
]
diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json
index 698e898231..63f43c0fc6 100644
--- a/src/test/data/script_tests.json
+++ b/src/test/data/script_tests.json
@@ -862,8 +862,6 @@
["Ensure 100% coverage of discouraged NOPS"],
["1", "NOP1", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"],
-["1", "CHECKLOCKTIMEVERIFY", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"],
-["1", "CHECKSEQUENCEVERIFY", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"],
["1", "NOP4", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"],
["1", "NOP5", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"],
["1", "NOP6", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"],
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 251d5a7142..2e1e4da72f 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -2,10 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "dbwrapper.h"
-#include "uint256.h"
-#include "random.h"
-#include "test/test_bitcoin.h"
+#include <dbwrapper.h>
+#include <uint256.h>
+#include <random.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
@@ -125,7 +125,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
- CDBWrapper* dbw = new CDBWrapper(ph, (1 << 10), false, false, false);
+ std::unique_ptr<CDBWrapper> dbw = MakeUnique<CDBWrapper>(ph, (1 << 10), false, false, false);
char key = 'k';
uint256 in = InsecureRand256();
uint256 res;
@@ -135,8 +135,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
BOOST_CHECK_EQUAL(res.ToString(), in.ToString());
// Call the destructor to free leveldb LOCK
- delete dbw;
- dbw = nullptr;
+ dbw.reset();
// Now, set up another wrapper that wants to obfuscate the same directory
CDBWrapper odbw(ph, (1 << 10), false, false, true);
@@ -167,7 +166,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
- CDBWrapper* dbw = new CDBWrapper(ph, (1 << 10), false, false, false);
+ std::unique_ptr<CDBWrapper> dbw = MakeUnique<CDBWrapper>(ph, (1 << 10), false, false, false);
char key = 'k';
uint256 in = InsecureRand256();
uint256 res;
@@ -177,8 +176,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
BOOST_CHECK_EQUAL(res.ToString(), in.ToString());
// Call the destructor to free leveldb LOCK
- delete dbw;
- dbw = nullptr;
+ dbw.reset();
// Simulate a -reindex by wiping the existing data store
CDBWrapper odbw(ph, (1 << 10), false, true, true);
@@ -204,19 +202,31 @@ BOOST_AUTO_TEST_CASE(iterator_ordering)
for (int x=0x00; x<256; ++x) {
uint8_t key = x;
uint32_t value = x*x;
- BOOST_CHECK(dbw.Write(key, value));
+ if (!(x & 1)) BOOST_CHECK(dbw.Write(key, value));
}
+ // Check that creating an iterator creates a snapshot
std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator());
+
+ for (int x=0x00; x<256; ++x) {
+ uint8_t key = x;
+ uint32_t value = x*x;
+ if (x & 1) BOOST_CHECK(dbw.Write(key, value));
+ }
+
for (int seek_start : {0x00, 0x80}) {
it->Seek((uint8_t)seek_start);
- for (int x=seek_start; x<256; ++x) {
+ for (int x=seek_start; x<255; ++x) {
uint8_t key;
uint32_t value;
BOOST_CHECK(it->Valid());
if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure
break;
BOOST_CHECK(it->GetKey(key));
+ if (x & 1) {
+ BOOST_CHECK_EQUAL(key, x + 1);
+ continue;
+ }
BOOST_CHECK(it->GetValue(value));
BOOST_CHECK_EQUAL(key, x);
BOOST_CHECK_EQUAL(value, x*x);
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 18a7e59933..3c39258693 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "util.h"
-#include "test/test_bitcoin.h"
+#include <util.h>
+#include <test/test_bitcoin.h>
#include <string>
#include <vector>
@@ -27,7 +27,7 @@ static void ResetArgs(const std::string& strArg)
for (std::string& s : vecArg)
vecChar.push_back(s.c_str());
- gArgs.ParseParameters(vecChar.size(), &vecChar[0]);
+ gArgs.ParseParameters(vecChar.size(), vecChar.data());
}
BOOST_AUTO_TEST_CASE(boolarg)
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index 05b6b3b1e6..68f445d142 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "hash.h"
-#include "utilstrencodings.h"
-#include "test/test_bitcoin.h"
+#include <hash.h>
+#include <utilstrencodings.h>
+#include <test/test_bitcoin.h>
#include <vector>
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index 559b3caf1c..7b9d7a6398 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -2,31 +2,30 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "key.h"
+#include <key.h>
-#include "base58.h"
-#include "script/script.h"
-#include "uint256.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "test/test_bitcoin.h"
+#include <base58.h>
+#include <script/script.h>
+#include <uint256.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <test/test_bitcoin.h>
#include <string>
#include <vector>
#include <boost/test/unit_test.hpp>
-static const std::string strSecret1 ("5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj");
-static const std::string strSecret2 ("5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3");
-static const std::string strSecret1C ("Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw");
-static const std::string strSecret2C ("L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g");
-static const CBitcoinAddress addr1 ("1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ");
-static const CBitcoinAddress addr2 ("1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ");
-static const CBitcoinAddress addr1C("1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs");
-static const CBitcoinAddress addr2C("1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs");
+static const std::string strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj";
+static const std::string strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3";
+static const std::string strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw";
+static const std::string strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g";
+static const std::string addr1 = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ";
+static const std::string addr2 = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ";
+static const std::string addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs";
+static const std::string addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs";
-
-static const std::string strAddressBad("1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF");
+static const std::string strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF";
BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup)
@@ -74,10 +73,10 @@ BOOST_AUTO_TEST_CASE(key_test1)
BOOST_CHECK(!key2C.VerifyPubKey(pubkey2));
BOOST_CHECK(key2C.VerifyPubKey(pubkey2C));
- BOOST_CHECK(addr1.Get() == CTxDestination(pubkey1.GetID()));
- BOOST_CHECK(addr2.Get() == CTxDestination(pubkey2.GetID()));
- BOOST_CHECK(addr1C.Get() == CTxDestination(pubkey1C.GetID()));
- BOOST_CHECK(addr2C.Get() == CTxDestination(pubkey2C.GetID()));
+ BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(pubkey1.GetID()));
+ BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(pubkey2.GetID()));
+ BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(pubkey1C.GetID()));
+ BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(pubkey2C.GetID()));
for (int n=0; n<16; n++)
{
diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp
index b071ab117b..90730cd7b5 100644
--- a/src/test/limitedmap_tests.cpp
+++ b/src/test/limitedmap_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "limitedmap.h"
+#include <limitedmap.h>
-#include "test/test_bitcoin.h"
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/main_tests.cpp b/src/test/main_tests.cpp
index 656aec606b..4be32f7628 100644
--- a/src/test/main_tests.cpp
+++ b/src/test/main_tests.cpp
@@ -2,11 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "chainparams.h"
-#include "validation.h"
-#include "net.h"
+#include <chainparams.h>
+#include <validation.h>
+#include <net.h>
-#include "test/test_bitcoin.h"
+#include <test/test_bitcoin.h>
#include <boost/signals2/signal.hpp>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 51b28d09fa..a694b870fe 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -2,11 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "policy/policy.h"
-#include "txmempool.h"
-#include "util.h"
+#include <policy/policy.h>
+#include <txmempool.h>
+#include <util.h>
-#include "test/test_bitcoin.h"
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
#include <list>
@@ -165,6 +165,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
sortedOrder[2] = tx1.GetHash().ToString(); // 10000
sortedOrder[3] = tx4.GetHash().ToString(); // 15000
sortedOrder[4] = tx2.GetHash().ToString(); // 20000
+ LOCK(pool.cs);
CheckSort<descendant_score>(pool, sortedOrder);
/* low fee but with high fee child */
@@ -375,6 +376,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
}
sortedOrder[4] = tx3.GetHash().ToString(); // 0
+ LOCK(pool.cs);
CheckSort<ancestor_score>(pool, sortedOrder);
/* low fee parent with high fee child */
@@ -559,15 +561,15 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
// ... we should keep the same min fee until we get a block
pool.removeForBlock(vtx, 1);
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
- BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/2);
+ BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0));
// ... then feerate should drop 1/2 each halflife
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2);
- BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/4);
+ BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/4.0));
// ... with a 1/2 halflife when mempool is < 1/2 its target size
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
- BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/8);
+ BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/8.0));
// ... with a 1/4 halflife when mempool is < 1/4 its target size
SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp
index 1a1cf4399c..ee48d96ad9 100644
--- a/src/test/merkle_tests.cpp
+++ b/src/test/merkle_tests.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "consensus/merkle.h"
-#include "test/test_bitcoin.h"
+#include <consensus/merkle.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/merkleblock_tests.cpp b/src/test/merkleblock_tests.cpp
new file mode 100644
index 0000000000..37a1a84136
--- /dev/null
+++ b/src/test/merkleblock_tests.cpp
@@ -0,0 +1,78 @@
+// Copyright (c) 2012-2017 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 <merkleblock.h>
+#include <uint256.h>
+#include <test/test_bitcoin.h>
+
+#include <boost/test/unit_test.hpp>
+
+
+BOOST_FIXTURE_TEST_SUITE(merkleblock_tests, BasicTestingSetup)
+
+/**
+ * Create a CMerkleBlock using a list of txids which will be found in the
+ * given block.
+ */
+BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_found)
+{
+ CBlock block = getBlock13b8a();
+
+ std::set<uint256> txids;
+
+ // Last txn in block.
+ uint256 txhash1 = uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20");
+
+ // Second txn in block.
+ uint256 txhash2 = uint256S("0xf9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07");
+
+ txids.insert(txhash1);
+ txids.insert(txhash2);
+
+ CMerkleBlock merkleBlock(block, txids);
+
+ BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex());
+
+ // vMatchedTxn is only used when bloom filter is specified.
+ BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0);
+
+ std::vector<uint256> vMatched;
+ std::vector<unsigned int> vIndex;
+
+ BOOST_CHECK_EQUAL(merkleBlock.txn.ExtractMatches(vMatched, vIndex).GetHex(), block.hashMerkleRoot.GetHex());
+ BOOST_CHECK_EQUAL(vMatched.size(), 2);
+
+ // Ordered by occurrence in depth-first tree traversal.
+ BOOST_CHECK_EQUAL(vMatched[0].ToString(), txhash2.ToString());
+ BOOST_CHECK_EQUAL(vIndex[0], 1);
+
+ BOOST_CHECK_EQUAL(vMatched[1].ToString(), txhash1.ToString());
+ BOOST_CHECK_EQUAL(vIndex[1], 8);
+}
+
+
+/**
+ * Create a CMerkleBlock using a list of txids which will not be found in the
+ * given block.
+ */
+BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_not_found)
+{
+ CBlock block = getBlock13b8a();
+
+ std::set<uint256> txids2;
+ txids2.insert(uint256S("0xc0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
+ CMerkleBlock merkleBlock(block, txids2);
+
+ BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex());
+ BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0);
+
+ std::vector<uint256> vMatched;
+ std::vector<unsigned int> vIndex;
+
+ BOOST_CHECK_EQUAL(merkleBlock.txn.ExtractMatches(vMatched, vIndex).GetHex(), block.hashMerkleRoot.GetHex());
+ BOOST_CHECK_EQUAL(vMatched.size(), 0);
+ BOOST_CHECK_EQUAL(vIndex.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 9fa9a8509c..b1a2032ea8 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -2,23 +2,23 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "chainparams.h"
-#include "coins.h"
-#include "consensus/consensus.h"
-#include "consensus/merkle.h"
-#include "consensus/tx_verify.h"
-#include "consensus/validation.h"
-#include "validation.h"
-#include "miner.h"
-#include "policy/policy.h"
-#include "pubkey.h"
-#include "script/standard.h"
-#include "txmempool.h"
-#include "uint256.h"
-#include "util.h"
-#include "utilstrencodings.h"
-
-#include "test/test_bitcoin.h"
+#include <chainparams.h>
+#include <coins.h>
+#include <consensus/consensus.h>
+#include <consensus/merkle.h>
+#include <consensus/tx_verify.h>
+#include <consensus/validation.h>
+#include <validation.h>
+#include <miner.h>
+#include <policy/policy.h>
+#include <pubkey.h>
+#include <script/standard.h>
+#include <txmempool.h>
+#include <uint256.h>
+#include <util.h>
+#include <utilstrencodings.h>
+
+#include <test/test_bitcoin.h>
#include <memory>
@@ -32,7 +32,6 @@ static BlockAssembler AssemblerForTest(const CChainParams& params) {
BlockAssembler::Options options;
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
- options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE;
options.blockMinFeeRate = blockMinFeeRate;
return BlockAssembler(params, options);
}
@@ -336,23 +335,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
mempool.clear();
- // invalid (pre-p2sh) txn in mempool, template creation fails
- tx.vin[0].prevout.hash = txFirst[0]->GetHash();
- tx.vin[0].prevout.n = 0;
- tx.vin[0].scriptSig = CScript() << OP_1;
- tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE;
- script = CScript() << OP_0;
- tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
- hash = tx.GetHash();
- mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- tx.vin[0].prevout.hash = hash;
- tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
- tx.vout[0].nValue -= LOWFEE;
- hash = tx.GetHash();
- mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
- BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
- mempool.clear();
-
// double spend txn pair in mempool, template creation fails
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vin[0].scriptSig = CScript() << OP_1;
@@ -392,6 +374,24 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
chainActive.SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+
+ // invalid p2sh txn in mempool, template creation fails
+ tx.vin[0].prevout.hash = txFirst[0]->GetHash();
+ tx.vin[0].prevout.n = 0;
+ tx.vin[0].scriptSig = CScript() << OP_1;
+ tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE;
+ script = CScript() << OP_0;
+ tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
+ hash = tx.GetHash();
+ mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ tx.vin[0].prevout.hash = hash;
+ tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
+ tx.vout[0].nValue -= LOWFEE;
+ hash = tx.GetHash();
+ mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
+ mempool.clear();
+
// Delete the dummy blocks again.
while (chainActive.Tip()->nHeight > nHeight) {
CBlockIndex* del = chainActive.Tip();
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index 5e89ef60d2..ffba7d6d22 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -2,22 +2,20 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "key.h"
-#include "keystore.h"
-#include "policy/policy.h"
-#include "script/script.h"
-#include "script/script_error.h"
-#include "script/interpreter.h"
-#include "script/sign.h"
-#include "script/ismine.h"
-#include "uint256.h"
-#include "test/test_bitcoin.h"
+#include <key.h>
+#include <keystore.h>
+#include <policy/policy.h>
+#include <script/script.h>
+#include <script/script_error.h>
+#include <script/interpreter.h>
+#include <script/sign.h>
+#include <script/ismine.h>
+#include <uint256.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
-typedef std::vector<unsigned char> valtype;
-
BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup)
CScript
@@ -173,95 +171,6 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
BOOST_CHECK(!::IsStandard(malformed[i], whichType));
}
-BOOST_AUTO_TEST_CASE(multisig_Solver1)
-{
- // Tests Solver() that returns lists of keys that are
- // required to satisfy a ScriptPubKey
- //
- // Also tests IsMine() and ExtractDestination()
- //
- // Note: ExtractDestination for the multisignature transactions
- // always returns false for this release, even if you have
- // one key that would satisfy an (a|b) or 2-of-3 keys needed
- // to spend an escrow transaction.
- //
- CBasicKeyStore keystore, emptykeystore, partialkeystore;
- CKey key[3];
- CTxDestination keyaddr[3];
- for (int i = 0; i < 3; i++)
- {
- key[i].MakeNewKey(true);
- keystore.AddKey(key[i]);
- keyaddr[i] = key[i].GetPubKey().GetID();
- }
- partialkeystore.AddKey(key[0]);
-
- {
- std::vector<valtype> solutions;
- txnouttype whichType;
- CScript s;
- s << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
- BOOST_CHECK(Solver(s, whichType, solutions));
- BOOST_CHECK(solutions.size() == 1);
- CTxDestination addr;
- BOOST_CHECK(ExtractDestination(s, addr));
- BOOST_CHECK(addr == keyaddr[0]);
- BOOST_CHECK(IsMine(keystore, s));
- BOOST_CHECK(!IsMine(emptykeystore, s));
- }
- {
- std::vector<valtype> solutions;
- txnouttype whichType;
- CScript s;
- s << OP_DUP << OP_HASH160 << ToByteVector(key[0].GetPubKey().GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
- BOOST_CHECK(Solver(s, whichType, solutions));
- BOOST_CHECK(solutions.size() == 1);
- CTxDestination addr;
- BOOST_CHECK(ExtractDestination(s, addr));
- BOOST_CHECK(addr == keyaddr[0]);
- BOOST_CHECK(IsMine(keystore, s));
- BOOST_CHECK(!IsMine(emptykeystore, s));
- }
- {
- std::vector<valtype> solutions;
- txnouttype whichType;
- CScript s;
- s << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;
- BOOST_CHECK(Solver(s, whichType, solutions));
- BOOST_CHECK_EQUAL(solutions.size(), 4U);
- CTxDestination addr;
- BOOST_CHECK(!ExtractDestination(s, addr));
- BOOST_CHECK(IsMine(keystore, s));
- BOOST_CHECK(!IsMine(emptykeystore, s));
- BOOST_CHECK(!IsMine(partialkeystore, s));
- }
- {
- std::vector<valtype> solutions;
- txnouttype whichType;
- CScript s;
- s << OP_1 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;
- BOOST_CHECK(Solver(s, whichType, solutions));
- BOOST_CHECK_EQUAL(solutions.size(), 4U);
- std::vector<CTxDestination> addrs;
- int nRequired;
- BOOST_CHECK(ExtractDestinations(s, whichType, addrs, nRequired));
- BOOST_CHECK(addrs[0] == keyaddr[0]);
- BOOST_CHECK(addrs[1] == keyaddr[1]);
- BOOST_CHECK(nRequired == 1);
- BOOST_CHECK(IsMine(keystore, s));
- BOOST_CHECK(!IsMine(emptykeystore, s));
- BOOST_CHECK(!IsMine(partialkeystore, s));
- }
- {
- std::vector<valtype> solutions;
- txnouttype whichType;
- CScript s;
- s << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << ToByteVector(key[2].GetPubKey()) << OP_3 << OP_CHECKMULTISIG;
- BOOST_CHECK(Solver(s, whichType, solutions));
- BOOST_CHECK(solutions.size() == 5);
- }
-}
-
BOOST_AUTO_TEST_CASE(multisig_Sign)
{
// Test SignSignature() (and therefore the version of Solver() that signs transactions)
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 31b05d868b..b4176a2b85 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -1,17 +1,17 @@
// 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 "addrman.h"
-#include "test/test_bitcoin.h"
+#include <addrman.h>
+#include <test/test_bitcoin.h>
#include <string>
#include <boost/test/unit_test.hpp>
-#include "hash.h"
-#include "serialize.h"
-#include "streams.h"
-#include "net.h"
-#include "netbase.h"
-#include "chainparams.h"
-#include "util.h"
+#include <hash.h>
+#include <serialize.h>
+#include <streams.h>
+#include <net.h>
+#include <netbase.h>
+#include <chainparams.h>
+#include <util.h>
class CAddrManSerializationMock : public CAddrMan
{
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 1baf7643e5..f3a9b1fc65 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "netbase.h"
-#include "test/test_bitcoin.h"
-#include "utilstrencodings.h"
+#include <netbase.h>
+#include <test/test_bitcoin.h>
+#include <utilstrencodings.h>
#include <string>
diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp
index c1d216d094..b7b632be3a 100644
--- a/src/test/pmt_tests.cpp
+++ b/src/test/pmt_tests.cpp
@@ -2,14 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "consensus/merkle.h"
-#include "merkleblock.h"
-#include "serialize.h"
-#include "streams.h"
-#include "uint256.h"
-#include "arith_uint256.h"
-#include "version.h"
-#include "test/test_bitcoin.h"
+#include <consensus/merkle.h>
+#include <merkleblock.h>
+#include <serialize.h>
+#include <streams.h>
+#include <uint256.h>
+#include <arith_uint256.h>
+#include <version.h>
+#include <test/test_bitcoin.h>
#include <vector>
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index fd8f7191f4..d8026e4468 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -2,13 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "policy/policy.h"
-#include "policy/fees.h"
-#include "txmempool.h"
-#include "uint256.h"
-#include "util.h"
+#include <policy/policy.h>
+#include <policy/fees.h>
+#include <txmempool.h>
+#include <uint256.h>
+#include <util.h>
-#include "test/test_bitcoin.h"
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index b13f2625aa..2837537c9a 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -2,12 +2,12 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "chain.h"
-#include "chainparams.h"
-#include "pow.h"
-#include "random.h"
-#include "util.h"
-#include "test/test_bitcoin.h"
+#include <chain.h>
+#include <chainparams.h>
+#include <pow.h>
+#include <random.h>
+#include <util.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index 841282873f..1327d67650 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -3,13 +3,13 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <vector>
-#include "prevector.h"
+#include <prevector.h>
-#include "reverse_iterator.h"
-#include "serialize.h"
-#include "streams.h"
+#include <reverse_iterator.h>
+#include <serialize.h>
+#include <streams.h>
-#include "test/test_bitcoin.h"
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp
index 0d541ec7d4..781b1e242d 100644
--- a/src/test/raii_event_tests.cpp
+++ b/src/test/raii_event_tests.cpp
@@ -10,9 +10,9 @@
#include <map>
#include <stdlib.h>
-#include "support/events.h"
+#include <support/events.h>
-#include "test/test_bitcoin.h"
+#include <test/test_bitcoin.h>
#include <vector>
diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp
index 132e190051..1ca5a53d72 100644
--- a/src/test/random_tests.cpp
+++ b/src/test/random_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "random.h"
+#include <random.h>
-#include "test/test_bitcoin.h"
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/reverselock_tests.cpp b/src/test/reverselock_tests.cpp
index 00dc47e13e..86d6161e20 100644
--- a/src/test/reverselock_tests.cpp
+++ b/src/test/reverselock_tests.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "reverselock.h"
-#include "test/test_bitcoin.h"
+#include <reverselock.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 22de4d8dc4..c69b5f1ca3 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -2,14 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "rpc/server.h"
-#include "rpc/client.h"
+#include <rpc/server.h>
+#include <rpc/client.h>
-#include "base58.h"
-#include "core_io.h"
-#include "netbase.h"
+#include <base58.h>
+#include <core_io.h>
+#include <netbase.h>
-#include "test/test_bitcoin.h"
+#include <test/test_bitcoin.h>
#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp
index 51f9e9f39f..b3ec6d34c7 100644
--- a/src/test/sanity_tests.cpp
+++ b/src/test/sanity_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "compat/sanity.h"
-#include "key.h"
-#include "test/test_bitcoin.h"
+#include <compat/sanity.h>
+#include <key.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index 1de865776e..7e86eed1c5 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -2,10 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "random.h"
-#include "scheduler.h"
+#include <random.h>
+#include <scheduler.h>
-#include "test/test_bitcoin.h"
+#include <test/test_bitcoin.h>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp
index efd0f77d9f..5a64aa3959 100644
--- a/src/test/script_P2SH_tests.cpp
+++ b/src/test/script_P2SH_tests.cpp
@@ -2,17 +2,17 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "consensus/tx_verify.h"
-#include "core_io.h"
-#include "key.h"
-#include "keystore.h"
-#include "validation.h"
-#include "policy/policy.h"
-#include "script/script.h"
-#include "script/script_error.h"
-#include "script/sign.h"
-#include "script/ismine.h"
-#include "test/test_bitcoin.h"
+#include <consensus/tx_verify.h>
+#include <core_io.h>
+#include <key.h>
+#include <keystore.h>
+#include <validation.h>
+#include <policy/policy.h>
+#include <script/script.h>
+#include <script/script_error.h>
+#include <script/sign.h>
+#include <script/ismine.h>
+#include <test/test_bitcoin.h>
#include <vector>
@@ -112,8 +112,7 @@ BOOST_AUTO_TEST_CASE(sign)
{
CScript sigSave = txTo[i].vin[0].scriptSig;
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
- const CTxOut& output = txFrom.vout[txTo[i].vin[0].prevout.n];
- bool sigOK = CScriptCheck(output.scriptPubKey, output.nValue, txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
+ bool sigOK = CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
if (i == j)
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
else
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
new file mode 100644
index 0000000000..19060eccc9
--- /dev/null
+++ b/src/test/script_standard_tests.cpp
@@ -0,0 +1,741 @@
+// Copyright (c) 2017 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 <key.h>
+#include <keystore.h>
+#include <script/ismine.h>
+#include <script/script.h>
+#include <script/script_error.h>
+#include <script/standard.h>
+#include <test/test_bitcoin.h>
+
+#include <boost/test/unit_test.hpp>
+
+
+BOOST_FIXTURE_TEST_SUITE(script_standard_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
+{
+ CKey keys[3];
+ CPubKey pubkeys[3];
+ for (int i = 0; i < 3; i++) {
+ keys[i].MakeNewKey(true);
+ pubkeys[i] = keys[i].GetPubKey();
+ }
+
+ CScript s;
+ txnouttype whichType;
+ std::vector<std::vector<unsigned char> > solutions;
+
+ // TX_PUBKEY
+ s.clear();
+ s << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
+ BOOST_CHECK(Solver(s, whichType, solutions));
+ BOOST_CHECK_EQUAL(whichType, TX_PUBKEY);
+ BOOST_CHECK_EQUAL(solutions.size(), 1);
+ BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0]));
+
+ // TX_PUBKEYHASH
+ s.clear();
+ s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
+ BOOST_CHECK(Solver(s, whichType, solutions));
+ BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH);
+ BOOST_CHECK_EQUAL(solutions.size(), 1);
+ BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID()));
+
+ // TX_SCRIPTHASH
+ CScript redeemScript(s); // initialize with leftover P2PKH script
+ s.clear();
+ s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
+ BOOST_CHECK(Solver(s, whichType, solutions));
+ BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH);
+ BOOST_CHECK_EQUAL(solutions.size(), 1);
+ BOOST_CHECK(solutions[0] == ToByteVector(CScriptID(redeemScript)));
+
+ // TX_MULTISIG
+ s.clear();
+ s << OP_1 <<
+ ToByteVector(pubkeys[0]) <<
+ ToByteVector(pubkeys[1]) <<
+ OP_2 << OP_CHECKMULTISIG;
+ BOOST_CHECK(Solver(s, whichType, solutions));
+ BOOST_CHECK_EQUAL(whichType, TX_MULTISIG);
+ BOOST_CHECK_EQUAL(solutions.size(), 4);
+ BOOST_CHECK(solutions[0] == std::vector<unsigned char>({1}));
+ BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0]));
+ BOOST_CHECK(solutions[2] == ToByteVector(pubkeys[1]));
+ BOOST_CHECK(solutions[3] == std::vector<unsigned char>({2}));
+
+ s.clear();
+ s << OP_2 <<
+ ToByteVector(pubkeys[0]) <<
+ ToByteVector(pubkeys[1]) <<
+ ToByteVector(pubkeys[2]) <<
+ OP_3 << OP_CHECKMULTISIG;
+ BOOST_CHECK(Solver(s, whichType, solutions));
+ BOOST_CHECK_EQUAL(whichType, TX_MULTISIG);
+ BOOST_CHECK_EQUAL(solutions.size(), 5);
+ BOOST_CHECK(solutions[0] == std::vector<unsigned char>({2}));
+ BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0]));
+ BOOST_CHECK(solutions[2] == ToByteVector(pubkeys[1]));
+ BOOST_CHECK(solutions[3] == ToByteVector(pubkeys[2]));
+ BOOST_CHECK(solutions[4] == std::vector<unsigned char>({3}));
+
+ // TX_NULL_DATA
+ s.clear();
+ s << OP_RETURN <<
+ std::vector<unsigned char>({0}) <<
+ std::vector<unsigned char>({75}) <<
+ std::vector<unsigned char>({255});
+ BOOST_CHECK(Solver(s, whichType, solutions));
+ BOOST_CHECK_EQUAL(whichType, TX_NULL_DATA);
+ BOOST_CHECK_EQUAL(solutions.size(), 0);
+
+ // TX_WITNESS_V0_KEYHASH
+ s.clear();
+ s << OP_0 << ToByteVector(pubkeys[0].GetID());
+ BOOST_CHECK(Solver(s, whichType, solutions));
+ BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_KEYHASH);
+ BOOST_CHECK_EQUAL(solutions.size(), 1);
+ BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID()));
+
+ // TX_WITNESS_V0_SCRIPTHASH
+ uint256 scriptHash;
+ CSHA256().Write(&redeemScript[0], redeemScript.size())
+ .Finalize(scriptHash.begin());
+
+ s.clear();
+ s << OP_0 << ToByteVector(scriptHash);
+ BOOST_CHECK(Solver(s, whichType, solutions));
+ BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_SCRIPTHASH);
+ BOOST_CHECK_EQUAL(solutions.size(), 1);
+ BOOST_CHECK(solutions[0] == ToByteVector(scriptHash));
+
+ // TX_NONSTANDARD
+ s.clear();
+ s << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
+ BOOST_CHECK(!Solver(s, whichType, solutions));
+ BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD);
+}
+
+BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
+{
+ CKey key;
+ CPubKey pubkey;
+ key.MakeNewKey(true);
+ pubkey = key.GetPubKey();
+
+ CScript s;
+ txnouttype whichType;
+ std::vector<std::vector<unsigned char> > solutions;
+
+ // TX_PUBKEY with incorrectly sized pubkey
+ s.clear();
+ s << std::vector<unsigned char>(30, 0x01) << OP_CHECKSIG;
+ BOOST_CHECK(!Solver(s, whichType, solutions));
+
+ // TX_PUBKEYHASH with incorrectly sized key hash
+ s.clear();
+ s << OP_DUP << OP_HASH160 << ToByteVector(pubkey) << OP_EQUALVERIFY << OP_CHECKSIG;
+ BOOST_CHECK(!Solver(s, whichType, solutions));
+
+ // TX_SCRIPTHASH with incorrectly sized script hash
+ s.clear();
+ s << OP_HASH160 << std::vector<unsigned char>(21, 0x01) << OP_EQUAL;
+ BOOST_CHECK(!Solver(s, whichType, solutions));
+
+ // TX_MULTISIG 0/2
+ s.clear();
+ s << OP_0 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG;
+ BOOST_CHECK(!Solver(s, whichType, solutions));
+
+ // TX_MULTISIG 2/1
+ s.clear();
+ s << OP_2 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG;
+ BOOST_CHECK(!Solver(s, whichType, solutions));
+
+ // TX_MULTISIG n = 2 with 1 pubkey
+ s.clear();
+ s << OP_1 << ToByteVector(pubkey) << OP_2 << OP_CHECKMULTISIG;
+ BOOST_CHECK(!Solver(s, whichType, solutions));
+
+ // TX_MULTISIG n = 1 with 0 pubkeys
+ s.clear();
+ s << OP_1 << OP_1 << OP_CHECKMULTISIG;
+ BOOST_CHECK(!Solver(s, whichType, solutions));
+
+ // TX_NULL_DATA with other opcodes
+ s.clear();
+ s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD;
+ BOOST_CHECK(!Solver(s, whichType, solutions));
+
+ // TX_WITNESS with incorrect program size
+ s.clear();
+ s << OP_0 << std::vector<unsigned char>(19, 0x01);
+ BOOST_CHECK(!Solver(s, whichType, solutions));
+}
+
+BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
+{
+ CKey key;
+ CPubKey pubkey;
+ key.MakeNewKey(true);
+ pubkey = key.GetPubKey();
+
+ CScript s;
+ CTxDestination address;
+
+ // TX_PUBKEY
+ s.clear();
+ s << ToByteVector(pubkey) << OP_CHECKSIG;
+ BOOST_CHECK(ExtractDestination(s, address));
+ BOOST_CHECK(boost::get<CKeyID>(&address) &&
+ *boost::get<CKeyID>(&address) == pubkey.GetID());
+
+ // TX_PUBKEYHASH
+ s.clear();
+ s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
+ BOOST_CHECK(ExtractDestination(s, address));
+ BOOST_CHECK(boost::get<CKeyID>(&address) &&
+ *boost::get<CKeyID>(&address) == pubkey.GetID());
+
+ // TX_SCRIPTHASH
+ CScript redeemScript(s); // initialize with leftover P2PKH script
+ s.clear();
+ s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
+ BOOST_CHECK(ExtractDestination(s, address));
+ BOOST_CHECK(boost::get<CScriptID>(&address) &&
+ *boost::get<CScriptID>(&address) == CScriptID(redeemScript));
+
+ // TX_MULTISIG
+ s.clear();
+ s << OP_1 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG;
+ BOOST_CHECK(!ExtractDestination(s, address));
+
+ // TX_NULL_DATA
+ s.clear();
+ s << OP_RETURN << std::vector<unsigned char>({75});
+ BOOST_CHECK(!ExtractDestination(s, address));
+
+ // TX_WITNESS_V0_KEYHASH
+ s.clear();
+ s << OP_0 << ToByteVector(pubkey.GetID());
+ BOOST_CHECK(ExtractDestination(s, address));
+ WitnessV0KeyHash keyhash;
+ CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(keyhash.begin());
+ BOOST_CHECK(boost::get<WitnessV0KeyHash>(&address) && *boost::get<WitnessV0KeyHash>(&address) == keyhash);
+
+ // TX_WITNESS_V0_SCRIPTHASH
+ s.clear();
+ WitnessV0ScriptHash scripthash;
+ CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin());
+ s << OP_0 << ToByteVector(scripthash);
+ BOOST_CHECK(ExtractDestination(s, address));
+ BOOST_CHECK(boost::get<WitnessV0ScriptHash>(&address) && *boost::get<WitnessV0ScriptHash>(&address) == scripthash);
+
+ // TX_WITNESS with unknown version
+ s.clear();
+ s << OP_1 << ToByteVector(pubkey);
+ BOOST_CHECK(ExtractDestination(s, address));
+ WitnessUnknown unk;
+ unk.length = 33;
+ unk.version = 1;
+ std::copy(pubkey.begin(), pubkey.end(), unk.program);
+ BOOST_CHECK(boost::get<WitnessUnknown>(&address) && *boost::get<WitnessUnknown>(&address) == unk);
+}
+
+BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
+{
+ CKey keys[3];
+ CPubKey pubkeys[3];
+ for (int i = 0; i < 3; i++) {
+ keys[i].MakeNewKey(true);
+ pubkeys[i] = keys[i].GetPubKey();
+ }
+
+ CScript s;
+ txnouttype whichType;
+ std::vector<CTxDestination> addresses;
+ int nRequired;
+
+ // TX_PUBKEY
+ s.clear();
+ s << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
+ BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
+ BOOST_CHECK_EQUAL(whichType, TX_PUBKEY);
+ BOOST_CHECK_EQUAL(addresses.size(), 1);
+ BOOST_CHECK_EQUAL(nRequired, 1);
+ BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) &&
+ *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID());
+
+ // TX_PUBKEYHASH
+ s.clear();
+ s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
+ BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
+ BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH);
+ BOOST_CHECK_EQUAL(addresses.size(), 1);
+ BOOST_CHECK_EQUAL(nRequired, 1);
+ BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) &&
+ *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID());
+
+ // TX_SCRIPTHASH
+ CScript redeemScript(s); // initialize with leftover P2PKH script
+ s.clear();
+ s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
+ BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
+ BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH);
+ BOOST_CHECK_EQUAL(addresses.size(), 1);
+ BOOST_CHECK_EQUAL(nRequired, 1);
+ BOOST_CHECK(boost::get<CScriptID>(&addresses[0]) &&
+ *boost::get<CScriptID>(&addresses[0]) == CScriptID(redeemScript));
+
+ // TX_MULTISIG
+ s.clear();
+ s << OP_2 <<
+ ToByteVector(pubkeys[0]) <<
+ ToByteVector(pubkeys[1]) <<
+ OP_2 << OP_CHECKMULTISIG;
+ BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
+ BOOST_CHECK_EQUAL(whichType, TX_MULTISIG);
+ BOOST_CHECK_EQUAL(addresses.size(), 2);
+ BOOST_CHECK_EQUAL(nRequired, 2);
+ BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) &&
+ *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID());
+ BOOST_CHECK(boost::get<CKeyID>(&addresses[1]) &&
+ *boost::get<CKeyID>(&addresses[1]) == pubkeys[1].GetID());
+
+ // TX_NULL_DATA
+ s.clear();
+ s << OP_RETURN << std::vector<unsigned char>({75});
+ BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
+}
+
+BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
+{
+ CKey keys[3];
+ CPubKey pubkeys[3];
+ for (int i = 0; i < 3; i++) {
+ keys[i].MakeNewKey(true);
+ pubkeys[i] = keys[i].GetPubKey();
+ }
+
+ CScript expected, result;
+
+ // CKeyID
+ expected.clear();
+ expected << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
+ result = GetScriptForDestination(pubkeys[0].GetID());
+ BOOST_CHECK(result == expected);
+
+ // CScriptID
+ CScript redeemScript(result);
+ expected.clear();
+ expected << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
+ result = GetScriptForDestination(CScriptID(redeemScript));
+ BOOST_CHECK(result == expected);
+
+ // CNoDestination
+ expected.clear();
+ result = GetScriptForDestination(CNoDestination());
+ BOOST_CHECK(result == expected);
+
+ // GetScriptForRawPubKey
+ expected.clear();
+ expected << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
+ result = GetScriptForRawPubKey(pubkeys[0]);
+ BOOST_CHECK(result == expected);
+
+ // GetScriptForMultisig
+ expected.clear();
+ expected << OP_2 <<
+ ToByteVector(pubkeys[0]) <<
+ ToByteVector(pubkeys[1]) <<
+ ToByteVector(pubkeys[2]) <<
+ OP_3 << OP_CHECKMULTISIG;
+ result = GetScriptForMultisig(2, std::vector<CPubKey>(pubkeys, pubkeys + 3));
+ BOOST_CHECK(result == expected);
+
+ // GetScriptForWitness
+ CScript witnessScript;
+
+ witnessScript << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
+ expected.clear();
+ expected << OP_0 << ToByteVector(pubkeys[0].GetID());
+ result = GetScriptForWitness(witnessScript);
+ BOOST_CHECK(result == expected);
+
+ witnessScript.clear();
+ witnessScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
+ result = GetScriptForWitness(witnessScript);
+ BOOST_CHECK(result == expected);
+
+ witnessScript.clear();
+ witnessScript << OP_1 << ToByteVector(pubkeys[0]) << OP_1 << OP_CHECKMULTISIG;
+
+ uint256 scriptHash;
+ CSHA256().Write(&witnessScript[0], witnessScript.size())
+ .Finalize(scriptHash.begin());
+
+ expected.clear();
+ expected << OP_0 << ToByteVector(scriptHash);
+ result = GetScriptForWitness(witnessScript);
+ BOOST_CHECK(result == expected);
+}
+
+BOOST_AUTO_TEST_CASE(script_standard_IsMine)
+{
+ CKey keys[2];
+ CPubKey pubkeys[2];
+ for (int i = 0; i < 2; i++) {
+ keys[i].MakeNewKey(true);
+ pubkeys[i] = keys[i].GetPubKey();
+ }
+
+ CKey uncompressedKey;
+ uncompressedKey.MakeNewKey(false);
+ CPubKey uncompressedPubkey = uncompressedKey.GetPubKey();
+
+ CScript scriptPubKey;
+ isminetype result;
+ bool isInvalid;
+
+ // P2PK compressed
+ {
+ CBasicKeyStore keystore;
+ scriptPubKey.clear();
+ scriptPubKey << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
+
+ // Keystore does not have key
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has key
+ keystore.AddKey(keys[0]);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ BOOST_CHECK(!isInvalid);
+ }
+
+ // P2PK uncompressed
+ {
+ CBasicKeyStore keystore;
+ scriptPubKey.clear();
+ scriptPubKey << ToByteVector(uncompressedPubkey) << OP_CHECKSIG;
+
+ // Keystore does not have key
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has key
+ keystore.AddKey(uncompressedKey);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ BOOST_CHECK(!isInvalid);
+ }
+
+ // P2PKH compressed
+ {
+ CBasicKeyStore keystore;
+ scriptPubKey.clear();
+ scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
+
+ // Keystore does not have key
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has key
+ keystore.AddKey(keys[0]);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ BOOST_CHECK(!isInvalid);
+ }
+
+ // P2PKH uncompressed
+ {
+ CBasicKeyStore keystore;
+ scriptPubKey.clear();
+ scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(uncompressedPubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
+
+ // Keystore does not have key
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has key
+ keystore.AddKey(uncompressedKey);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ BOOST_CHECK(!isInvalid);
+ }
+
+ // P2SH
+ {
+ CBasicKeyStore keystore;
+
+ CScript redeemScript;
+ redeemScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
+
+ // Keystore does not have redeemScript or key
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has redeemScript but no key
+ keystore.AddCScript(redeemScript);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has redeemScript and key
+ keystore.AddKey(keys[0]);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ BOOST_CHECK(!isInvalid);
+ }
+
+ // P2WPKH compressed
+ {
+ CBasicKeyStore keystore;
+ keystore.AddKey(keys[0]);
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_0 << ToByteVector(pubkeys[0].GetID());
+
+ // Keystore has key, but no P2SH redeemScript
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has key and P2SH redeemScript
+ keystore.AddCScript(scriptPubKey);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ BOOST_CHECK(!isInvalid);
+ }
+
+ // P2WPKH uncompressed
+ {
+ CBasicKeyStore keystore;
+ keystore.AddKey(uncompressedKey);
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_0 << ToByteVector(uncompressedPubkey.GetID());
+
+ // Keystore has key, but no P2SH redeemScript
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has key and P2SH redeemScript
+ keystore.AddCScript(scriptPubKey);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(isInvalid);
+ }
+
+ // scriptPubKey multisig
+ {
+ CBasicKeyStore keystore;
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_2 <<
+ ToByteVector(uncompressedPubkey) <<
+ ToByteVector(pubkeys[1]) <<
+ OP_2 << OP_CHECKMULTISIG;
+
+ // Keystore does not have any keys
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has 1/2 keys
+ keystore.AddKey(uncompressedKey);
+
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has 2/2 keys
+ keystore.AddKey(keys[1]);
+
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ BOOST_CHECK(!isInvalid);
+ }
+
+ // P2SH multisig
+ {
+ CBasicKeyStore keystore;
+ keystore.AddKey(uncompressedKey);
+ keystore.AddKey(keys[1]);
+
+ CScript redeemScript;
+ redeemScript << OP_2 <<
+ ToByteVector(uncompressedPubkey) <<
+ ToByteVector(pubkeys[1]) <<
+ OP_2 << OP_CHECKMULTISIG;
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
+
+ // Keystore has no redeemScript
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has redeemScript
+ keystore.AddCScript(redeemScript);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ BOOST_CHECK(!isInvalid);
+ }
+
+ // P2WSH multisig with compressed keys
+ {
+ CBasicKeyStore keystore;
+ keystore.AddKey(keys[0]);
+ keystore.AddKey(keys[1]);
+
+ CScript witnessScript;
+ witnessScript << OP_2 <<
+ ToByteVector(pubkeys[0]) <<
+ ToByteVector(pubkeys[1]) <<
+ OP_2 << OP_CHECKMULTISIG;
+
+ uint256 scriptHash;
+ CSHA256().Write(&witnessScript[0], witnessScript.size())
+ .Finalize(scriptHash.begin());
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_0 << ToByteVector(scriptHash);
+
+ // Keystore has keys, but no witnessScript or P2SH redeemScript
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has keys and witnessScript, but no P2SH redeemScript
+ keystore.AddCScript(witnessScript);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has keys, witnessScript, P2SH redeemScript
+ keystore.AddCScript(scriptPubKey);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ BOOST_CHECK(!isInvalid);
+ }
+
+ // P2WSH multisig with uncompressed key
+ {
+ CBasicKeyStore keystore;
+ keystore.AddKey(uncompressedKey);
+ keystore.AddKey(keys[1]);
+
+ CScript witnessScript;
+ witnessScript << OP_2 <<
+ ToByteVector(uncompressedPubkey) <<
+ ToByteVector(pubkeys[1]) <<
+ OP_2 << OP_CHECKMULTISIG;
+
+ uint256 scriptHash;
+ CSHA256().Write(&witnessScript[0], witnessScript.size())
+ .Finalize(scriptHash.begin());
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_0 << ToByteVector(scriptHash);
+
+ // Keystore has keys, but no witnessScript or P2SH redeemScript
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has keys and witnessScript, but no P2SH redeemScript
+ keystore.AddCScript(witnessScript);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has keys, witnessScript, P2SH redeemScript
+ keystore.AddCScript(scriptPubKey);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(isInvalid);
+ }
+
+ // P2WSH multisig wrapped in P2SH
+ {
+ CBasicKeyStore keystore;
+
+ CScript witnessScript;
+ witnessScript << OP_2 <<
+ ToByteVector(pubkeys[0]) <<
+ ToByteVector(pubkeys[1]) <<
+ OP_2 << OP_CHECKMULTISIG;
+
+ uint256 scriptHash;
+ CSHA256().Write(&witnessScript[0], witnessScript.size())
+ .Finalize(scriptHash.begin());
+
+ CScript redeemScript;
+ redeemScript << OP_0 << ToByteVector(scriptHash);
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
+
+ // Keystore has no witnessScript, P2SH redeemScript, or keys
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has witnessScript and P2SH redeemScript, but no keys
+ keystore.AddCScript(redeemScript);
+ keystore.AddCScript(witnessScript);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has keys, witnessScript, P2SH redeemScript
+ keystore.AddKey(keys[0]);
+ keystore.AddKey(keys[1]);
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ BOOST_CHECK(!isInvalid);
+ }
+
+ // OP_RETURN
+ {
+ CBasicKeyStore keystore;
+ keystore.AddKey(keys[0]);
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]);
+
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+ }
+
+ // Nonstandard
+ {
+ CBasicKeyStore keystore;
+ keystore.AddKey(keys[0]);
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
+
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 17374edcc4..f96d867bc6 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -2,21 +2,21 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "data/script_tests.json.h"
-
-#include "core_io.h"
-#include "key.h"
-#include "keystore.h"
-#include "script/script.h"
-#include "script/script_error.h"
-#include "script/sign.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "test/test_bitcoin.h"
-#include "rpc/server.h"
+#include <test/data/script_tests.json.h>
+
+#include <core_io.h>
+#include <key.h>
+#include <keystore.h>
+#include <script/script.h>
+#include <script/script_error.h>
+#include <script/sign.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <test/test_bitcoin.h>
+#include <rpc/server.h>
#if defined(HAVE_CONSENSUS_LIB)
-#include "script/bitcoinconsensus.h"
+#include <script/bitcoinconsensus.h>
#endif
#include <fstream>
@@ -166,6 +166,17 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
CMutableTransaction tx2 = tx;
BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue), &err) == expect, message);
BOOST_CHECK_MESSAGE(err == scriptError, std::string(FormatScriptError(err)) + " where " + std::string(FormatScriptError((ScriptError_t)scriptError)) + " expected: " + message);
+
+ // Verify that removing flags from a passing test or adding flags to a failing test does not change the result.
+ for (int i = 0; i < 16; ++i) {
+ int extra_flags = InsecureRandBits(16);
+ int combined_flags = expect ? (flags & ~extra_flags) : (flags | extra_flags);
+ // Weed out some invalid flag combinations.
+ if (combined_flags & SCRIPT_VERIFY_CLEANSTACK && ~combined_flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) continue;
+ if (combined_flags & SCRIPT_VERIFY_WITNESS && ~combined_flags & SCRIPT_VERIFY_P2SH) continue;
+ BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, combined_flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue), &err) == expect, message + strprintf(" (with flags %x)", combined_flags));
+ }
+
#if defined(HAVE_CONSENSUS_LIB)
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << tx2;
@@ -1451,4 +1462,21 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps)
BOOST_CHECK(!script.HasValidOps());
}
+BOOST_AUTO_TEST_CASE(script_can_append_self)
+{
+ CScript s, d;
+
+ s = ScriptFromHex("00");
+ s += s;
+ d = ScriptFromHex("0000");
+ BOOST_CHECK(s == d);
+
+ // check doubling a script that's large enough to require reallocation
+ static const char hex[] = "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f";
+ s = CScript() << ParseHex(hex) << OP_CHECKSIG;
+ d = CScript() << ParseHex(hex) << OP_CHECKSIG << ParseHex(hex) << OP_CHECKSIG;
+ s += s;
+ BOOST_CHECK(s == d);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/scriptnum10.h b/src/test/scriptnum10.h
index 94dd58526c..ad15427a9b 100644
--- a/src/test/scriptnum10.h
+++ b/src/test/scriptnum10.h
@@ -12,7 +12,7 @@
#include <stdint.h>
#include <string>
#include <vector>
-#include "assert.h"
+#include <assert.h>
class scriptnum10_error : public std::runtime_error
{
diff --git a/src/test/scriptnum_tests.cpp b/src/test/scriptnum_tests.cpp
index 280eb59ce8..aef500174a 100644
--- a/src/test/scriptnum_tests.cpp
+++ b/src/test/scriptnum_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "scriptnum10.h"
-#include "script/script.h"
-#include "test/test_bitcoin.h"
+#include <test/scriptnum10.h>
+#include <script/script.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
#include <limits.h>
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index 9661a66514..28ed2bfeb0 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -2,10 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "serialize.h"
-#include "streams.h"
-#include "hash.h"
-#include "test/test_bitcoin.h"
+#include <serialize.h>
+#include <streams.h>
+#include <hash.h>
+#include <test/test_bitcoin.h>
#include <stdint.h>
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index ecbdf57788..d97e106287 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -2,18 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "consensus/tx_verify.h"
-#include "consensus/validation.h"
-#include "data/sighash.json.h"
-#include "hash.h"
-#include "script/interpreter.h"
-#include "script/script.h"
-#include "serialize.h"
-#include "streams.h"
-#include "test/test_bitcoin.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "version.h"
+#include <consensus/tx_verify.h>
+#include <consensus/validation.h>
+#include <test/data/sighash.json.h>
+#include <hash.h>
+#include <script/interpreter.h>
+#include <script/script.h>
+#include <serialize.h>
+#include <streams.h>
+#include <test/test_bitcoin.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <version.h>
#include <iostream>
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index d3b8b07228..891cd1e1d8 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -2,14 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "consensus/tx_verify.h"
-#include "consensus/validation.h"
-#include "pubkey.h"
-#include "key.h"
-#include "script/script.h"
-#include "script/standard.h"
-#include "uint256.h"
-#include "test/test_bitcoin.h"
+#include <consensus/tx_verify.h>
+#include <consensus/validation.h>
+#include <pubkey.h>
+#include <key.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <uint256.h>
+#include <test/test_bitcoin.h>
#include <vector>
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index e3654e67ad..1ffd9f2b42 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "chain.h"
-#include "util.h"
-#include "test/test_bitcoin.h"
+#include <chain.h>
+#include <util.h>
+#include <test/test_bitcoin.h>
#include <vector>
@@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(skiplist_test)
BOOST_CHECK(vIndex[SKIPLIST_LENGTH - 1].GetAncestor(from) == &vIndex[from]);
BOOST_CHECK(vIndex[from].GetAncestor(to) == &vIndex[to]);
- BOOST_CHECK(vIndex[from].GetAncestor(0) == &vIndex[0]);
+ BOOST_CHECK(vIndex[from].GetAncestor(0) == vIndex.data());
}
}
@@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(getlocator_test)
for (unsigned int i=0; i<vBlocksSide.size(); i++) {
vHashSide[i] = ArithToUint256(i + 50000 + (arith_uint256(1) << 128)); // Add 1<<128 to the hashes, so GetLow64() still returns the height.
vBlocksSide[i].nHeight = i + 50000;
- vBlocksSide[i].pprev = i ? &vBlocksSide[i - 1] : &vBlocksMain[49999];
+ vBlocksSide[i].pprev = i ? &vBlocksSide[i - 1] : (vBlocksMain.data()+49999);
vBlocksSide[i].phashBlock = &vHashSide[i];
vBlocksSide[i].BuildSkip();
BOOST_CHECK_EQUAL((int)UintToArith256(vBlocksSide[i].GetBlockHash()).GetLow64(), vBlocksSide[i].nHeight);
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index af2a152aa5..3636e6c332 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "streams.h"
-#include "support/allocators/zeroafterfree.h"
-#include "test/test_bitcoin.h"
+#include <streams.h>
+#include <support/allocators/zeroafterfree.h>
+#include <test/test_bitcoin.h>
#include <boost/assign/std/vector.hpp> // for 'operator+=()'
#include <boost/test/unit_test.hpp>
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 194f62ca11..f52c8ccc21 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -2,28 +2,35 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "test_bitcoin.h"
-
-#include "chainparams.h"
-#include "consensus/consensus.h"
-#include "consensus/validation.h"
-#include "crypto/sha256.h"
-#include "fs.h"
-#include "key.h"
-#include "validation.h"
-#include "miner.h"
-#include "net_processing.h"
-#include "pubkey.h"
-#include "random.h"
-#include "txdb.h"
-#include "txmempool.h"
-#include "ui_interface.h"
-#include "rpc/server.h"
-#include "rpc/register.h"
-#include "script/sigcache.h"
+#include <test/test_bitcoin.h>
+
+#include <chainparams.h>
+#include <consensus/consensus.h>
+#include <consensus/validation.h>
+#include <crypto/sha256.h>
+#include <validation.h>
+#include <miner.h>
+#include <net_processing.h>
+#include <ui_interface.h>
+#include <streams.h>
+#include <rpc/server.h>
+#include <rpc/register.h>
+#include <script/sigcache.h>
#include <memory>
+void CConnmanTest::AddNode(CNode& node)
+{
+ LOCK(g_connman->cs_vNodes);
+ g_connman->vNodes.push_back(&node);
+}
+
+void CConnmanTest::ClearNodes()
+{
+ LOCK(g_connman->cs_vNodes);
+ g_connman->vNodes.clear();
+}
+
uint256 insecure_rand_seed = GetRandHash();
FastRandomContext insecure_rand_ctx(insecure_rand_seed);
@@ -48,7 +55,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
BasicTestingSetup::~BasicTestingSetup()
{
ECC_Stop();
- g_connman.reset();
}
TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
@@ -69,9 +75,9 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
mempool.setSanityCheck(1.0);
- pblocktree = new CBlockTreeDB(1 << 20, true);
- pcoinsdbview = new CCoinsViewDB(1 << 23, true);
- pcoinsTip = new CCoinsViewCache(pcoinsdbview);
+ pblocktree.reset(new CBlockTreeDB(1 << 20, true));
+ pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
+ pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
if (!LoadGenesisBlock(chainparams)) {
throw std::runtime_error("LoadGenesisBlock failed.");
}
@@ -86,25 +92,29 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
threadGroup.create_thread(&ThreadScriptCheck);
g_connman = std::unique_ptr<CConnman>(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests.
connman = g_connman.get();
- RegisterNodeSignals(GetNodeSignals());
+ peerLogic.reset(new PeerLogicValidation(connman, scheduler));
}
TestingSetup::~TestingSetup()
{
- UnregisterNodeSignals(GetNodeSignals());
threadGroup.interrupt_all();
threadGroup.join_all();
GetMainSignals().FlushBackgroundCallbacks();
GetMainSignals().UnregisterBackgroundSignalScheduler();
+ g_connman.reset();
+ peerLogic.reset();
UnloadBlockIndex();
- delete pcoinsTip;
- delete pcoinsdbview;
- delete pblocktree;
+ pcoinsTip.reset();
+ pcoinsdbview.reset();
+ pblocktree.reset();
fs::remove_all(pathTemp);
}
TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
+ // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
+ // TODO: fix the code to support SegWit blocks.
+ UpdateVersionBitsParameters(Consensus::DEPLOYMENT_SEGWIT, 0, Consensus::BIP9Deployment::NO_TIMEOUT);
// Generate a 100-block chain:
coinbaseKey.MakeNewKey(true);
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
@@ -133,7 +143,10 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
block.vtx.push_back(MakeTransactionRef(tx));
// IncrementExtraNonce creates a valid coinbase and merkleRoot
unsigned int extraNonce = 0;
- IncrementExtraNonce(&block, chainActive.Tip(), extraNonce);
+ {
+ LOCK(cs_main);
+ IncrementExtraNonce(&block, chainActive.Tip(), extraNonce);
+ }
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
@@ -158,3 +171,15 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn) {
return CTxMemPoolEntry(MakeTransactionRef(txn), nFee, nTime, nHeight,
spendsCoinbase, sigOpCost, lp);
}
+
+/**
+ * @returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
+ * with 9 txs.
+ */
+CBlock getBlock13b8a()
+{
+ CBlock block;
+ CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION);
+ stream >> block;
+ return block;
+}
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index 2ddac2f076..80a9cbaeea 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -5,14 +5,14 @@
#ifndef BITCOIN_TEST_TEST_BITCOIN_H
#define BITCOIN_TEST_TEST_BITCOIN_H
-#include "chainparamsbase.h"
-#include "fs.h"
-#include "key.h"
-#include "pubkey.h"
-#include "random.h"
-#include "scheduler.h"
-#include "txdb.h"
-#include "txmempool.h"
+#include <chainparamsbase.h>
+#include <fs.h>
+#include <key.h>
+#include <pubkey.h>
+#include <random.h>
+#include <scheduler.h>
+#include <txdb.h>
+#include <txmempool.h>
#include <boost/thread.hpp>
@@ -49,12 +49,19 @@ struct BasicTestingSetup {
* Included are data directory, coins database, script check threads setup.
*/
class CConnman;
+class CNode;
+struct CConnmanTest {
+ static void AddNode(CNode& node);
+ static void ClearNodes();
+};
+
+class PeerLogicValidation;
struct TestingSetup: public BasicTestingSetup {
- CCoinsViewDB *pcoinsdbview;
fs::path pathTemp;
boost::thread_group threadGroup;
CConnman* connman;
CScheduler scheduler;
+ std::unique_ptr<PeerLogicValidation> peerLogic;
explicit TestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~TestingSetup();
@@ -97,7 +104,7 @@ struct TestMemPoolEntryHelper
TestMemPoolEntryHelper() :
nFee(0), nTime(0), nHeight(1),
spendsCoinbase(false), sigOpCost(4) { }
-
+
CTxMemPoolEntry FromTx(const CMutableTransaction &tx);
CTxMemPoolEntry FromTx(const CTransaction &tx);
@@ -108,4 +115,7 @@ struct TestMemPoolEntryHelper
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) { sigOpCost = _sigopsCost; return *this; }
};
+
+CBlock getBlock13b8a();
+
#endif
diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp
index de14251601..319d81187a 100644
--- a/src/test/test_bitcoin_fuzzy.cpp
+++ b/src/test/test_bitcoin_fuzzy.cpp
@@ -3,22 +3,23 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-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 "pubkey.h"
+#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 <pubkey.h>
+#include <blockencodings.h>
#include <stdint.h>
#include <unistd.h>
@@ -45,11 +46,13 @@ enum TEST_ID {
CBLOOMFILTER_DESERIALIZE,
CDISKBLOCKINDEX_DESERIALIZE,
CTXOUTCOMPRESSOR_DESERIALIZE,
+ BLOCKTRANSACTIONS_DESERIALIZE,
+ BLOCKTRANSACTIONSREQUEST_DESERIALIZE,
TEST_ID_END
};
-bool read_stdin(std::vector<char> &data) {
- char buffer[1024];
+bool read_stdin(std::vector<uint8_t> &data) {
+ uint8_t buffer[1024];
ssize_t length=0;
while((length = read(STDIN_FILENO, buffer, 1024)) > 0) {
data.insert(data.end(), buffer, buffer+length);
@@ -59,15 +62,11 @@ bool read_stdin(std::vector<char> &data) {
return length==0;
}
-int do_fuzz()
-{
- std::vector<char> buffer;
- if (!read_stdin(buffer)) return 0;
-
+int test_one_input(std::vector<uint8_t> buffer) {
if (buffer.size() < sizeof(uint32_t)) return 0;
uint32_t test_id = 0xffffffff;
- memcpy(&test_id, &buffer[0], sizeof(uint32_t));
+ memcpy(&test_id, buffer.data(), sizeof(uint32_t));
buffer.erase(buffer.begin(), buffer.begin() + sizeof(uint32_t));
if (test_id >= TEST_ID_END) return 0;
@@ -249,15 +248,58 @@ int do_fuzz()
break;
}
+ case BLOCKTRANSACTIONS_DESERIALIZE:
+ {
+ try
+ {
+ BlockTransactions bt;
+ ds >> bt;
+ } catch (const std::ios_base::failure& e) {return 0;}
+
+ break;
+ }
+ case BLOCKTRANSACTIONSREQUEST_DESERIALIZE:
+ {
+ try
+ {
+ BlockTransactionsRequest btr;
+ ds >> btr;
+ } catch (const std::ios_base::failure& e) {return 0;}
+
+ break;
+ }
default:
return 0;
}
return 0;
}
+static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
+void initialize() {
+ globalVerifyHandle = std::unique_ptr<ECCVerifyHandle>(new ECCVerifyHandle());
+}
+
+// This function is used by libFuzzer
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ test_one_input(std::vector<uint8_t>(data, data + size));
+ return 0;
+}
+
+// This function is used by libFuzzer
+extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
+ initialize();
+ return 0;
+}
+
+// Disabled under WIN32 due to clash with Cygwin's WinMain.
+#ifndef WIN32
+// Declare main(...) "weak" to allow for libFuzzer linking. libFuzzer provides
+// the main(...) function.
+__attribute__((weak))
+#endif
int main(int argc, char **argv)
{
- ECCVerifyHandle globalVerifyHandle;
+ initialize();
#ifdef __AFL_INIT
// Enable AFL deferred forkserver mode. Requires compilation using
// afl-clang-fast++. See fuzzing.md for details.
@@ -267,11 +309,20 @@ int main(int argc, char **argv)
#ifdef __AFL_LOOP
// Enable AFL persistent mode. Requires compilation using afl-clang-fast++.
// See fuzzing.md for details.
+ int ret = 0;
while (__AFL_LOOP(1000)) {
- do_fuzz();
+ std::vector<uint8_t> buffer;
+ if (!read_stdin(buffer)) {
+ continue;
+ }
+ ret = test_one_input(buffer);
}
- return 0;
+ return ret;
#else
- return do_fuzz();
+ std::vector<uint8_t> buffer;
+ if (!read_stdin(buffer)) {
+ return 0;
+ }
+ return test_one_input(buffer);
#endif
}
diff --git a/src/test/test_bitcoin_main.cpp b/src/test/test_bitcoin_main.cpp
index b556c953b9..8d81ff6fc6 100644
--- a/src/test/test_bitcoin_main.cpp
+++ b/src/test/test_bitcoin_main.cpp
@@ -4,7 +4,7 @@
#define BOOST_TEST_MODULE Bitcoin Test Suite
-#include "net.h"
+#include <net.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp
index 34863fd9d0..9ff95bd2b0 100644
--- a/src/test/timedata_tests.cpp
+++ b/src/test/timedata_tests.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
-#include "timedata.h"
-#include "test/test_bitcoin.h"
+#include <timedata.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/torcontrol_tests.cpp b/src/test/torcontrol_tests.cpp
index b7affaacde..d0aa8659c2 100644
--- a/src/test/torcontrol_tests.cpp
+++ b/src/test/torcontrol_tests.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
-#include "test/test_bitcoin.h"
-#include "torcontrol.cpp"
+#include <test/test_bitcoin.h>
+#include <torcontrol.cpp>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 6654634bf1..34a182722d 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -2,24 +2,24 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "data/tx_invalid.json.h"
-#include "data/tx_valid.json.h"
-#include "test/test_bitcoin.h"
-
-#include "clientversion.h"
-#include "checkqueue.h"
-#include "consensus/tx_verify.h"
-#include "consensus/validation.h"
-#include "core_io.h"
-#include "key.h"
-#include "keystore.h"
-#include "validation.h"
-#include "policy/policy.h"
-#include "script/script.h"
-#include "script/sign.h"
-#include "script/script_error.h"
-#include "script/standard.h"
-#include "utilstrencodings.h"
+#include <test/data/tx_invalid.json.h>
+#include <test/data/tx_valid.json.h>
+#include <test/test_bitcoin.h>
+
+#include <clientversion.h>
+#include <checkqueue.h>
+#include <consensus/tx_verify.h>
+#include <consensus/validation.h>
+#include <core_io.h>
+#include <key.h>
+#include <keystore.h>
+#include <validation.h>
+#include <policy/policy.h>
+#include <script/script.h>
+#include <script/sign.h>
+#include <script/script_error.h>
+#include <script/standard.h>
+#include <utilstrencodings.h>
#include <map>
#include <string>
@@ -480,8 +480,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
std::vector<CScriptCheck> vChecks;
- const CTxOut& output = coins[tx.vin[i].prevout.n].out;
- CScriptCheck check(output.scriptPubKey, output.nValue, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata);
+ CScriptCheck check(coins[tx.vin[i].prevout.n].out, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata);
vChecks.push_back(CScriptCheck());
check.swap(vChecks.back());
control.Add(vChecks);
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
new file mode 100644
index 0000000000..2d1eb7b772
--- /dev/null
+++ b/src/test/txvalidation_tests.cpp
@@ -0,0 +1,61 @@
+// Copyright (c) 2017 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 <validation.h>
+#include <txmempool.h>
+#include <amount.h>
+#include <consensus/validation.h>
+#include <primitives/transaction.h>
+#include <script/script.h>
+#include <test/test_bitcoin.h>
+
+#include <boost/test/unit_test.hpp>
+
+
+BOOST_AUTO_TEST_SUITE(txvalidation_tests)
+
+/**
+ * Ensure that the mempool won't accept coinbase transactions.
+ */
+BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
+{
+ CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+ CMutableTransaction coinbaseTx;
+
+ coinbaseTx.nVersion = 1;
+ coinbaseTx.vin.resize(1);
+ coinbaseTx.vout.resize(1);
+ coinbaseTx.vin[0].scriptSig = CScript() << OP_11 << OP_EQUAL;
+ coinbaseTx.vout[0].nValue = 1 * CENT;
+ coinbaseTx.vout[0].scriptPubKey = scriptPubKey;
+
+ assert(CTransaction(coinbaseTx).IsCoinBase());
+
+ CValidationState state;
+
+ LOCK(cs_main);
+
+ unsigned int initialPoolSize = mempool.size();
+
+ BOOST_CHECK_EQUAL(
+ false,
+ AcceptToMemoryPool(mempool, state, MakeTransactionRef(coinbaseTx),
+ nullptr /* pfMissingInputs */,
+ nullptr /* plTxnReplaced */,
+ true /* bypass_limits */,
+ 0 /* nAbsurdFee */));
+
+ // Check that the transaction hasn't been added to mempool.
+ BOOST_CHECK_EQUAL(mempool.size(), initialPoolSize);
+
+ // Check that the validation state reflects the unsuccessful attempt.
+ BOOST_CHECK(state.IsInvalid());
+ BOOST_CHECK_EQUAL(state.GetRejectReason(), "coinbase");
+
+ int nDoS;
+ BOOST_CHECK_EQUAL(state.IsInvalid(nDoS), true);
+ BOOST_CHECK_EQUAL(nDoS, 100);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 2d25cb96c8..fe8cb6126e 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -2,20 +2,20 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "consensus/validation.h"
-#include "key.h"
-#include "validation.h"
-#include "miner.h"
-#include "pubkey.h"
-#include "txmempool.h"
-#include "random.h"
-#include "script/standard.h"
-#include "script/sign.h"
-#include "test/test_bitcoin.h"
-#include "utiltime.h"
-#include "core_io.h"
-#include "keystore.h"
-#include "policy/policy.h"
+#include <consensus/validation.h>
+#include <key.h>
+#include <validation.h>
+#include <miner.h>
+#include <pubkey.h>
+#include <txmempool.h>
+#include <random.h>
+#include <script/standard.h>
+#include <script/sign.h>
+#include <test/test_bitcoin.h>
+#include <utiltime.h>
+#include <core_io.h>
+#include <keystore.h>
+#include <policy/policy.h>
#include <boost/test/unit_test.hpp>
@@ -29,7 +29,8 @@ ToMemPool(CMutableTransaction& tx)
LOCK(cs_main);
CValidationState state;
- return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), false, nullptr, nullptr, true, 0);
+ return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), nullptr /* pfMissingInputs */,
+ nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */);
}
BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
@@ -65,6 +66,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
// Test 1: block with both of those transactions should be rejected.
block = CreateAndProcessBlock(spends, scriptPubKey);
+ LOCK(cs_main);
BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
// Test 2: ... and should be rejected if spend1 is in the memory pool
@@ -101,7 +103,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
// should fail.
// Capture this interaction with the upgraded_nop argument: set it when evaluating
// any script flag that is implemented as an upgraded NOP code.
-void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t failing_flags, bool add_to_cache, bool upgraded_nop)
+void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t failing_flags, bool add_to_cache)
{
PrecomputedTransactionData txdata(tx);
// If we add many more flags, this loop can get too expensive, but we can
@@ -118,29 +120,23 @@ void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t failing_fl
// WITNESS requires P2SH
test_flags |= SCRIPT_VERIFY_P2SH;
}
- bool ret = CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, nullptr);
+ bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, nullptr);
// CheckInputs should succeed iff test_flags doesn't intersect with
// failing_flags
bool expected_return_value = !(test_flags & failing_flags);
- if (expected_return_value && upgraded_nop) {
- // If the script flag being tested corresponds to an upgraded NOP,
- // then script execution should fail if DISCOURAGE_UPGRADABLE_NOPS
- // is set.
- expected_return_value = !(test_flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS);
- }
BOOST_CHECK_EQUAL(ret, expected_return_value);
// Test the caching
if (ret && add_to_cache) {
// Check that we get a cache hit if the tx was valid
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK(scriptchecks.empty());
} else {
// Check that we get script executions to check, if the transaction
// was invalid, or we didn't add to cache.
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
}
}
@@ -150,7 +146,10 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
{
// Test that passing CheckInputs with one set of script flags doesn't imply
// that we would pass again with a different set of flags.
- InitScriptExecutionCache();
+ {
+ LOCK(cs_main);
+ InitScriptExecutionCache();
+ }
CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
CScript p2sh_scriptPubKey = GetScriptForDestination(CScriptID(p2pk_scriptPubKey));
@@ -200,20 +199,20 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CValidationState state;
PrecomputedTransactionData ptd_spend_tx(spend_tx);
- BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
+ BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
// If we call again asking for scriptchecks (as happens in
// ConnectBlock), we should add a script check object for this -- we're
// not caching invalidity (if that changes, delete this test case).
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
+ BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1);
// Test that CheckInputs returns true iff DERSIG-enforcing flags are
// not present. Don't add these checks to the cache, so that we can
// test later that block validation works fine in the absence of cached
// successes.
- ValidateCheckInputsForAllFlags(spend_tx, SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, false);
+ ValidateCheckInputsForAllFlags(spend_tx, SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false);
// And if we produce a block with this tx, it should be valid (DERSIG not
// enabled yet), even though there's no cache entry.
@@ -238,7 +237,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
- ValidateCheckInputsForAllFlags(invalid_under_p2sh_tx, SCRIPT_VERIFY_P2SH, true, false);
+ ValidateCheckInputsForAllFlags(invalid_under_p2sh_tx, SCRIPT_VERIFY_P2SH, true);
}
// Test CHECKLOCKTIMEVERIFY
@@ -261,13 +260,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(invalid_with_cltv_tx, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true);
+ ValidateCheckInputsForAllFlags(invalid_with_cltv_tx, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true);
// Make it valid, and check again
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state;
PrecomputedTransactionData txdata(invalid_with_cltv_tx);
- BOOST_CHECK(CheckInputs(invalid_with_cltv_tx, state, pcoinsTip, true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputs(invalid_with_cltv_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
}
// TEST CHECKSEQUENCEVERIFY
@@ -289,13 +288,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(invalid_with_csv_tx, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true);
+ ValidateCheckInputsForAllFlags(invalid_with_csv_tx, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true);
// Make it valid, and check again
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state;
PrecomputedTransactionData txdata(invalid_with_csv_tx);
- BOOST_CHECK(CheckInputs(invalid_with_csv_tx, state, pcoinsTip, true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputs(invalid_with_csv_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
}
// TODO: add tests for remaining script flags
@@ -318,11 +317,11 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
UpdateTransaction(valid_with_witness_tx, 0, sigdata);
// This should be valid under all script flags.
- ValidateCheckInputsForAllFlags(valid_with_witness_tx, 0, true, false);
+ ValidateCheckInputsForAllFlags(valid_with_witness_tx, 0, true);
// Remove the witness, and check that it is now invalid.
valid_with_witness_tx.vin[0].scriptWitness.SetNull();
- ValidateCheckInputsForAllFlags(valid_with_witness_tx, SCRIPT_VERIFY_WITNESS, true, false);
+ ValidateCheckInputsForAllFlags(valid_with_witness_tx, SCRIPT_VERIFY_WITNESS, true);
}
{
@@ -347,7 +346,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
}
// This should be valid under all script flags
- ValidateCheckInputsForAllFlags(tx, 0, true, false);
+ ValidateCheckInputsForAllFlags(tx, 0, true);
// Check that if the second input is invalid, but the first input is
// valid, the transaction is not cached.
@@ -357,12 +356,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CValidationState state;
PrecomputedTransactionData txdata(tx);
// This transaction is now invalid under segwit, because of the second input.
- BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
+ BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
std::vector<CScriptCheck> scriptchecks;
// Make sure this transaction was not cached (ie because the first
// input was valid)
- BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
// Should get 2 script checks back -- caching is on a whole-transaction basis.
BOOST_CHECK_EQUAL(scriptchecks.size(), 2);
}
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index 70d83a2e54..40ef9adb2e 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -1,10 +1,10 @@
// 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.
-#include "arith_uint256.h"
-#include "uint256.h"
-#include "version.h"
-#include "test/test_bitcoin.h"
+#include <arith_uint256.h>
+#include <uint256.h>
+#include <version.h>
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
#include <stdint.h>
diff --git a/src/test/univalue_tests.cpp b/src/test/univalue_tests.cpp
index dffe8e55a8..7386204437 100644
--- a/src/test/univalue_tests.cpp
+++ b/src/test/univalue_tests.cpp
@@ -8,7 +8,7 @@
#include <string>
#include <map>
#include <univalue.h>
-#include "test/test_bitcoin.h"
+#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 5679086969..998090cd9b 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -2,14 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "util.h"
+#include <util.h>
-#include "clientversion.h"
-#include "primitives/transaction.h"
-#include "sync.h"
-#include "utilstrencodings.h"
-#include "utilmoneystr.h"
-#include "test/test_bitcoin.h"
+#include <clientversion.h>
+#include <primitives/transaction.h>
+#include <sync.h>
+#include <utilstrencodings.h>
+#include <utilmoneystr.h>
+#include <test/test_bitcoin.h>
#include <stdint.h>
#include <vector>
@@ -253,6 +253,31 @@ BOOST_AUTO_TEST_CASE(util_IsHex)
BOOST_CHECK(!IsHex("0x0000"));
}
+BOOST_AUTO_TEST_CASE(util_IsHexNumber)
+{
+ BOOST_CHECK(IsHexNumber("0x0"));
+ BOOST_CHECK(IsHexNumber("0"));
+ BOOST_CHECK(IsHexNumber("0x10"));
+ BOOST_CHECK(IsHexNumber("10"));
+ BOOST_CHECK(IsHexNumber("0xff"));
+ BOOST_CHECK(IsHexNumber("ff"));
+ BOOST_CHECK(IsHexNumber("0xFfa"));
+ BOOST_CHECK(IsHexNumber("Ffa"));
+ BOOST_CHECK(IsHexNumber("0x00112233445566778899aabbccddeeffAABBCCDDEEFF"));
+ BOOST_CHECK(IsHexNumber("00112233445566778899aabbccddeeffAABBCCDDEEFF"));
+
+ BOOST_CHECK(!IsHexNumber("")); // empty string not allowed
+ BOOST_CHECK(!IsHexNumber("0x")); // empty string after prefix not allowed
+ BOOST_CHECK(!IsHexNumber("0x0 ")); // no spaces at end,
+ BOOST_CHECK(!IsHexNumber(" 0x0")); // or beginning,
+ BOOST_CHECK(!IsHexNumber("0x 0")); // or middle,
+ BOOST_CHECK(!IsHexNumber(" ")); // etc.
+ BOOST_CHECK(!IsHexNumber("0x0ga")); // invalid character
+ BOOST_CHECK(!IsHexNumber("x0")); // broken prefix
+ BOOST_CHECK(!IsHexNumber("0x0x00")); // two prefixes not allowed
+
+}
+
BOOST_AUTO_TEST_CASE(util_seed_insecure_rand)
{
SeedInsecureRand(true);
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 882afb2e20..7029b347dd 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -2,12 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "chain.h"
-#include "versionbits.h"
-#include "test/test_bitcoin.h"
-#include "chainparams.h"
-#include "validation.h"
-#include "consensus/params.h"
+#include <chain.h>
+#include <versionbits.h>
+#include <test/test_bitcoin.h>
+#include <chainparams.h>
+#include <validation.h>
+#include <consensus/params.h>
#include <boost/test/unit_test.hpp>
@@ -32,6 +32,12 @@ public:
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); }
};
+class TestAlwaysActiveConditionChecker : public TestConditionChecker
+{
+public:
+ int64_t BeginTime(const Consensus::Params& params) const override { return Consensus::BIP9Deployment::ALWAYS_ACTIVE; }
+};
+
#define CHECKERS 6
class VersionBitsTester
@@ -43,6 +49,8 @@ class VersionBitsTester
// The first one performs all checks, the second only 50%, the third only 25%, etc...
// This is to test whether lack of cached information leads to the same results.
TestConditionChecker checker[CHECKERS];
+ // Another 6 that assume always active activation
+ TestAlwaysActiveConditionChecker checker_always[CHECKERS];
// Test counter (to identify failures)
int num;
@@ -56,6 +64,7 @@ public:
}
for (unsigned int i = 0; i < CHECKERS; i++) {
checker[i] = TestConditionChecker();
+ checker_always[i] = TestAlwaysActiveConditionChecker();
}
vpblock.clear();
return *this;
@@ -82,6 +91,7 @@ public:
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == 0, strprintf("Test %i for StateSinceHeight (always active)", num));
}
}
num++;
@@ -92,6 +102,7 @@ public:
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for DEFINED", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
@@ -102,6 +113,7 @@ public:
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_STARTED, strprintf("Test %i for STARTED", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
@@ -112,6 +124,7 @@ public:
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_LOCKED_IN, strprintf("Test %i for LOCKED_IN", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
@@ -122,6 +135,7 @@ public:
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
@@ -132,6 +146,7 @@ public:
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_FAILED, strprintf("Test %i for FAILED", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
diff --git a/src/threadinterrupt.cpp b/src/threadinterrupt.cpp
index 9d691079ed..7bcacc3014 100644
--- a/src/threadinterrupt.cpp
+++ b/src/threadinterrupt.cpp
@@ -3,7 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "threadinterrupt.h"
+#include <threadinterrupt.h>
CThreadInterrupt::operator bool() const
{
diff --git a/src/timedata.cpp b/src/timedata.cpp
index 5113bb60db..b91cf01723 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -3,17 +3,17 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "timedata.h"
+#include <timedata.h>
-#include "netaddress.h"
-#include "sync.h"
-#include "ui_interface.h"
-#include "util.h"
-#include "utilstrencodings.h"
-#include "warnings.h"
+#include <netaddress.h>
+#include <sync.h>
+#include <ui_interface.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <warnings.h>
static CCriticalSection cs_nTimeOffset;
diff --git a/src/tinyformat.h b/src/tinyformat.h
index 2e453e56bb..d34cfaa94f 100644
--- a/src/tinyformat.h
+++ b/src/tinyformat.h
@@ -495,7 +495,11 @@ namespace detail {
class FormatArg
{
public:
- FormatArg() {}
+ FormatArg()
+ : m_value(nullptr),
+ m_formatImpl(nullptr),
+ m_toIntImpl(nullptr)
+ { }
template<typename T>
explicit FormatArg(const T& value)
@@ -507,11 +511,15 @@ class FormatArg
void format(std::ostream& out, const char* fmtBegin,
const char* fmtEnd, int ntrunc) const
{
+ assert(m_value);
+ assert(m_formatImpl);
m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
}
int toInt() const
{
+ assert(m_value);
+ assert(m_toIntImpl);
return m_toIntImpl(m_value);
}
@@ -712,23 +720,27 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
break;
case 'X':
out.setf(std::ios::uppercase);
+ // Falls through
case 'x': case 'p':
out.setf(std::ios::hex, std::ios::basefield);
intConversion = true;
break;
case 'E':
out.setf(std::ios::uppercase);
+ // Falls through
case 'e':
out.setf(std::ios::scientific, std::ios::floatfield);
out.setf(std::ios::dec, std::ios::basefield);
break;
case 'F':
out.setf(std::ios::uppercase);
+ // Falls through
case 'f':
out.setf(std::ios::fixed, std::ios::floatfield);
break;
case 'G':
out.setf(std::ios::uppercase);
+ // Falls through
case 'g':
out.setf(std::ios::dec, std::ios::basefield);
// As in boost::format, let stream decide float format.
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 1cea197666..46f39d65a0 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -3,12 +3,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "torcontrol.h"
-#include "utilstrencodings.h"
-#include "netbase.h"
-#include "net.h"
-#include "util.h"
-#include "crypto/hmac_sha256.h"
+#include <torcontrol.h>
+#include <utilstrencodings.h>
+#include <netbase.h>
+#include <net.h>
+#include <util.h>
+#include <crypto/hmac_sha256.h>
#include <vector>
#include <deque>
diff --git a/src/torcontrol.h b/src/torcontrol.h
index 72dc82c5b1..a01eeefeb8 100644
--- a/src/torcontrol.h
+++ b/src/torcontrol.h
@@ -8,7 +8,7 @@
#ifndef BITCOIN_TORCONTROL_H
#define BITCOIN_TORCONTROL_H
-#include "scheduler.h"
+#include <scheduler.h>
extern const std::string DEFAULT_TOR_CONTROL;
static const bool DEFAULT_LISTEN_ONION = true;
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 797ae5713f..7a8e854b55 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -3,16 +3,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "txdb.h"
+#include <txdb.h>
-#include "chainparams.h"
-#include "hash.h"
-#include "random.h"
-#include "pow.h"
-#include "uint256.h"
-#include "util.h"
-#include "ui_interface.h"
-#include "init.h"
+#include <chainparams.h>
+#include <hash.h>
+#include <random.h>
+#include <pow.h>
+#include <uint256.h>
+#include <util.h>
+#include <ui_interface.h>
+#include <init.h>
#include <stdint.h>
@@ -371,9 +371,9 @@ bool CCoinsViewDB::Upgrade() {
int64_t count = 0;
LogPrintf("Upgrading utxo-set database...\n");
LogPrintf("[0%%]...");
+ uiInterface.ShowProgress(_("Upgrading UTXO database"), 0, true);
size_t batch_size = 1 << 24;
CDBBatch batch(db);
- uiInterface.SetProgressBreakAction(StartShutdown);
int reportDone = 0;
std::pair<unsigned char, uint256> key;
std::pair<unsigned char, uint256> prev_key = {DB_COINS, uint256()};
@@ -386,7 +386,7 @@ bool CCoinsViewDB::Upgrade() {
if (count++ % 256 == 0) {
uint32_t high = 0x100 * *key.second.begin() + *(key.second.begin() + 1);
int percentageDone = (int)(high * 100.0 / 65536.0 + 0.5);
- uiInterface.ShowProgress(_("Upgrading UTXO database") + "\n"+ _("(press q to shutdown and continue later)") + "\n", percentageDone);
+ uiInterface.ShowProgress(_("Upgrading UTXO database"), percentageDone, true);
if (reportDone < percentageDone/10) {
// report max. every 10% step
LogPrintf("[%d%%]...", percentageDone);
@@ -420,7 +420,7 @@ bool CCoinsViewDB::Upgrade() {
}
db.WriteBatch(batch);
db.CompactRange({DB_COINS, uint256()}, key);
- uiInterface.SetProgressBreakAction(std::function<void(void)>());
+ uiInterface.ShowProgress("", 100, false);
LogPrintf("[%s].\n", ShutdownRequested() ? "CANCELLED" : "DONE");
return !ShutdownRequested();
}
diff --git a/src/txdb.h b/src/txdb.h
index d1cd5a4250..2e8095e3cd 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -6,9 +6,9 @@
#ifndef BITCOIN_TXDB_H
#define BITCOIN_TXDB_H
-#include "coins.h"
-#include "dbwrapper.h"
-#include "chain.h"
+#include <coins.h>
+#include <dbwrapper.h>
+#include <chain.h>
#include <map>
#include <string>
@@ -110,17 +110,17 @@ class CBlockTreeDB : public CDBWrapper
{
public:
explicit CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
-private:
- CBlockTreeDB(const CBlockTreeDB&);
- void operator=(const CBlockTreeDB&);
-public:
+
+ CBlockTreeDB(const CBlockTreeDB&) = delete;
+ CBlockTreeDB& operator=(const CBlockTreeDB&) = delete;
+
bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo);
- bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo);
+ bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info);
bool ReadLastBlockFile(int &nFile);
- bool WriteReindexing(bool fReindex);
- bool ReadReindexing(bool &fReindex);
+ bool WriteReindexing(bool fReindexing);
+ bool ReadReindexing(bool &fReindexing);
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
- bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
+ bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &vect);
bool WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue);
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex);
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index f68d677646..392323808f 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -3,20 +3,20 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "txmempool.h"
-
-#include "consensus/consensus.h"
-#include "consensus/tx_verify.h"
-#include "consensus/validation.h"
-#include "validation.h"
-#include "policy/policy.h"
-#include "policy/fees.h"
-#include "reverse_iterator.h"
-#include "streams.h"
-#include "timedata.h"
-#include "util.h"
-#include "utilmoneystr.h"
-#include "utiltime.h"
+#include <txmempool.h>
+
+#include <consensus/consensus.h>
+#include <consensus/tx_verify.h>
+#include <consensus/validation.h>
+#include <validation.h>
+#include <policy/policy.h>
+#include <policy/fees.h>
+#include <reverse_iterator.h>
+#include <streams.h>
+#include <timedata.h>
+#include <util.h>
+#include <utilmoneystr.h>
+#include <utiltime.h>
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
int64_t _nTime, unsigned int _entryHeight,
@@ -317,7 +317,7 @@ void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, CAmount modifyFe
assert(int64_t(nCountWithDescendants) > 0);
}
-void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int modifySigOps)
+void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps)
{
nSizeWithAncestors += modifySize;
assert(int64_t(nSizeWithAncestors) > 0);
@@ -607,6 +607,15 @@ void CTxMemPool::clear()
_clear();
}
+static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight)
+{
+ CValidationState state;
+ CAmount txfee = 0;
+ bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee);
+ assert(fCheckResult);
+ UpdateCoins(tx, mempoolDuplicate, 1000000);
+}
+
void CTxMemPool::check(const CCoinsViewCache *pcoins) const
{
if (nCheckFrequency == 0)
@@ -621,7 +630,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
uint64_t innerUsage = 0;
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins));
- const int64_t nSpendHeight = GetSpendHeight(mempoolDuplicate);
+ const int64_t spendheight = GetSpendHeight(mempoolDuplicate);
LOCK(cs);
std::list<const CTxMemPoolEntry*> waitingOnDependants;
@@ -700,11 +709,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
if (fDependsWait)
waitingOnDependants.push_back(&(*it));
else {
- CValidationState state;
- bool fCheckResult = tx.IsCoinBase() ||
- Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight);
- assert(fCheckResult);
- UpdateCoins(tx, mempoolDuplicate, 1000000);
+ CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight);
}
}
unsigned int stepsSinceLastRemove = 0;
@@ -717,10 +722,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
stepsSinceLastRemove++;
assert(stepsSinceLastRemove < waitingOnDependants.size());
} else {
- bool fCheckResult = entry->GetTx().IsCoinBase() ||
- Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight);
- assert(fCheckResult);
- UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000);
+ CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight);
stepsSinceLastRemove = 0;
}
}
@@ -981,7 +983,7 @@ const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) cons
CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const {
LOCK(cs);
if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0)
- return CFeeRate(rollingMinimumFeeRate);
+ return CFeeRate(llround(rollingMinimumFeeRate));
int64_t time = GetTime();
if (time > lastRollingFeeUpdate + 10) {
@@ -999,7 +1001,7 @@ CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const {
return CFeeRate(0);
}
}
- return std::max(CFeeRate(rollingMinimumFeeRate), incrementalRelayFee);
+ return std::max(CFeeRate(llround(rollingMinimumFeeRate)), incrementalRelayFee);
}
void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) {
diff --git a/src/txmempool.h b/src/txmempool.h
index b07886579c..86a008d7b2 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -13,13 +13,13 @@
#include <utility>
#include <string>
-#include "amount.h"
-#include "coins.h"
-#include "indirectmap.h"
-#include "policy/feerate.h"
-#include "primitives/transaction.h"
-#include "sync.h"
-#include "random.h"
+#include <amount.h>
+#include <coins.h>
+#include <indirectmap.h>
+#include <policy/feerate.h>
+#include <primitives/transaction.h>
+#include <sync.h>
+#include <random.h>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
@@ -109,7 +109,7 @@ public:
// Adjusts the descendant state.
void UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount);
// Adjusts the ancestor state
- void UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int modifySigOps);
+ void UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps);
// Updates the fee delta used for mining priority score, and the
// modified fees with descendants.
void UpdateFeeDelta(int64_t feeDelta);
@@ -204,7 +204,7 @@ struct mempoolentry_txid
class CompareTxMemPoolEntryByDescendantScore
{
public:
- bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b)
+ bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const
{
bool fUseADescendants = UseDescendantScore(a);
bool fUseBDescendants = UseDescendantScore(b);
@@ -226,7 +226,7 @@ public:
}
// Calculate which score to use for an entry (avoiding division).
- bool UseDescendantScore(const CTxMemPoolEntry &a)
+ bool UseDescendantScore(const CTxMemPoolEntry &a) const
{
double f1 = (double)a.GetModifiedFee() * a.GetSizeWithDescendants();
double f2 = (double)a.GetModFeesWithDescendants() * a.GetTxSize();
@@ -241,7 +241,7 @@ public:
class CompareTxMemPoolEntryByScore
{
public:
- bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b)
+ bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const
{
double f1 = (double)a.GetModifiedFee() * b.GetTxSize();
double f2 = (double)b.GetModifiedFee() * a.GetTxSize();
@@ -255,7 +255,7 @@ public:
class CompareTxMemPoolEntryByEntryTime
{
public:
- bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b)
+ bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const
{
return a.GetTime() < b.GetTime();
}
@@ -264,7 +264,7 @@ public:
class CompareTxMemPoolEntryByAncestorFee
{
public:
- bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b)
+ bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const
{
double aFees = a.GetModFeesWithAncestors();
double aSize = a.GetSizeWithAncestors();
@@ -507,12 +507,15 @@ public:
* check does nothing.
*/
void check(const CCoinsViewCache *pcoins) const;
- void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = dFrequency * 4294967295.0; }
+ void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = static_cast<uint32_t>(dFrequency * 4294967295.0); }
// addUnchecked must updated state for all ancestors of a given transaction,
// to track size/count of descendant transactions. First version of
// addUnchecked can be used to have it call CalculateMemPoolAncestors(), and
// then invoke the second version.
+ // Note that addUnchecked is ONLY called from ATMP outside of tests
+ // and any other callers may break wallet's in-mempool tracking (due to
+ // lack of CValidationInterface::TransactionAddedToMempool callbacks).
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool validFeeEstimate = true);
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate = true);
diff --git a/src/ui_interface.cpp b/src/ui_interface.cpp
index 74a13e0e05..c0f7defa46 100644
--- a/src/ui_interface.cpp
+++ b/src/ui_interface.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "ui_interface.h"
-#include "util.h"
+#include <ui_interface.h>
+#include <util.h>
CClientUIInterface uiInterface;
diff --git a/src/ui_interface.h b/src/ui_interface.h
index 762dd19b19..7f68c578ee 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -94,11 +94,11 @@ public:
/** A wallet has been loaded. */
boost::signals2::signal<void (CWallet* wallet)> LoadWallet;
- /** Show progress e.g. for verifychain */
- boost::signals2::signal<void (const std::string &title, int nProgress)> ShowProgress;
-
- /** Set progress break action (possible "cancel button" triggers that action) */
- boost::signals2::signal<void (std::function<void(void)> action)> SetProgressBreakAction;
+ /**
+ * Show progress e.g. for verifychain.
+ * resume_possible indicates shutting down now will result in the current progress action resuming upon restart.
+ */
+ boost::signals2::signal<void (const std::string &title, int nProgress, bool resume_possible)> ShowProgress;
/** New block has been accepted */
boost::signals2::signal<void (bool, const CBlockIndex *)> NotifyBlockTip;
diff --git a/src/uint256.cpp b/src/uint256.cpp
index c4c7b716fe..b7255231c1 100644
--- a/src/uint256.cpp
+++ b/src/uint256.cpp
@@ -3,9 +3,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "uint256.h"
+#include <uint256.h>
-#include "utilstrencodings.h"
+#include <utilstrencodings.h>
#include <stdio.h>
#include <string.h>
@@ -14,7 +14,7 @@ template <unsigned int BITS>
base_blob<BITS>::base_blob(const std::vector<unsigned char>& vch)
{
assert(vch.size() == sizeof(data));
- memcpy(data, &vch[0], sizeof(data));
+ memcpy(data, vch.data(), sizeof(data));
}
template <unsigned int BITS>
diff --git a/src/uint256.h b/src/uint256.h
index 3ed694d723..e45c990dae 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -12,14 +12,14 @@
#include <stdint.h>
#include <string>
#include <vector>
-#include "crypto/common.h"
+#include <crypto/common.h>
/** Template base class for fixed-sized opaque blobs. */
template<unsigned int BITS>
class base_blob
{
protected:
- enum { WIDTH=BITS/8 };
+ static constexpr int WIDTH = BITS / 8;
uint8_t data[WIDTH];
public:
base_blob()
@@ -111,7 +111,6 @@ public:
class uint160 : public base_blob<160> {
public:
uint160() {}
- explicit uint160(const base_blob<160>& b) : base_blob<160>(b) {}
explicit uint160(const std::vector<unsigned char>& vch) : base_blob<160>(vch) {}
};
@@ -123,7 +122,6 @@ public:
class uint256 : public base_blob<256> {
public:
uint256() {}
- explicit uint256(const base_blob<256>& b) : base_blob<256>(b) {}
explicit uint256(const std::vector<unsigned char>& vch) : base_blob<256>(vch) {}
/** A cheap hash function that just returns 64 bits from the result, it can be
diff --git a/src/undo.h b/src/undo.h
index a720de4ac5..b482fd3089 100644
--- a/src/undo.h
+++ b/src/undo.h
@@ -6,10 +6,10 @@
#ifndef BITCOIN_UNDO_H
#define BITCOIN_UNDO_H
-#include "compressor.h"
-#include "consensus/consensus.h"
-#include "primitives/transaction.h"
-#include "serialize.h"
+#include <compressor.h>
+#include <consensus/consensus.h>
+#include <primitives/transaction.h>
+#include <serialize.h>
/** Undo information for a CTxIn
*
diff --git a/src/univalue/Makefile.am b/src/univalue/Makefile.am
index 6c1ec81e63..e283fc890e 100644
--- a/src/univalue/Makefile.am
+++ b/src/univalue/Makefile.am
@@ -12,6 +12,7 @@ pkgconfig_DATA = pc/libunivalue.pc
libunivalue_la_SOURCES = \
lib/univalue.cpp \
+ lib/univalue_get.cpp \
lib/univalue_read.cpp \
lib/univalue_write.cpp
@@ -20,7 +21,7 @@ libunivalue_la_LDFLAGS = \
-no-undefined
libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include
-TESTS = test/unitester
+TESTS = test/object test/unitester test/no_nul
GENBIN = gen/gen$(BUILD_EXEEXT)
GEN_SRCS = gen/gen.cpp
@@ -33,7 +34,7 @@ gen: lib/univalue_escapes.h $(GENBIN)
@echo Updating $<
$(AM_V_at)$(GENBIN) > lib/univalue_escapes.h
-noinst_PROGRAMS = $(TESTS)
+noinst_PROGRAMS = $(TESTS) test/test_json
TEST_DATA_DIR=test
@@ -42,6 +43,21 @@ test_unitester_LDADD = libunivalue.la
test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\"
test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
+test_test_json_SOURCES = test/test_json.cpp
+test_test_json_LDADD = libunivalue.la
+test_test_json_CXXFLAGS = -I$(top_srcdir)/include
+test_test_json_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
+
+test_no_nul_SOURCES = test/no_nul.cpp
+test_no_nul_LDADD = libunivalue.la
+test_no_nul_CXXFLAGS = -I$(top_srcdir)/include
+test_no_nul_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
+
+test_object_SOURCES = test/object.cpp
+test_object_LDADD = libunivalue.la
+test_object_CXXFLAGS = -I$(top_srcdir)/include
+test_object_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
+
TEST_FILES = \
$(TEST_DATA_DIR)/fail10.json \
$(TEST_DATA_DIR)/fail11.json \
@@ -77,6 +93,8 @@ TEST_FILES = \
$(TEST_DATA_DIR)/fail39.json \
$(TEST_DATA_DIR)/fail40.json \
$(TEST_DATA_DIR)/fail41.json \
+ $(TEST_DATA_DIR)/fail42.json \
+ $(TEST_DATA_DIR)/fail44.json \
$(TEST_DATA_DIR)/fail3.json \
$(TEST_DATA_DIR)/fail4.json \
$(TEST_DATA_DIR)/fail5.json \
@@ -88,6 +106,11 @@ TEST_FILES = \
$(TEST_DATA_DIR)/pass2.json \
$(TEST_DATA_DIR)/pass3.json \
$(TEST_DATA_DIR)/round1.json \
- $(TEST_DATA_DIR)/round2.json
+ $(TEST_DATA_DIR)/round2.json \
+ $(TEST_DATA_DIR)/round3.json \
+ $(TEST_DATA_DIR)/round4.json \
+ $(TEST_DATA_DIR)/round5.json \
+ $(TEST_DATA_DIR)/round6.json \
+ $(TEST_DATA_DIR)/round7.json
EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS)
diff --git a/src/univalue/README b/src/univalue/README
deleted file mode 100644
index 48167b083b..0000000000
--- a/src/univalue/README
+++ /dev/null
@@ -1,7 +0,0 @@
-
- UniValue
-
-A universal value object, with JSON encoding (output) and decoding (input).
-
-Built as a single dynamic RAII C++ object class, and no templates.
-
diff --git a/src/univalue/README.md b/src/univalue/README.md
new file mode 100644
index 0000000000..36aa786a4c
--- /dev/null
+++ b/src/univalue/README.md
@@ -0,0 +1,32 @@
+
+# UniValue
+
+## Summary
+
+A universal value class, with JSON encoding and decoding.
+
+UniValue is an abstract data type that may be a null, boolean, string,
+number, array container, or a key/value dictionary container, nested to
+an arbitrary depth.
+
+This class is aligned with the JSON standard, [RFC
+7159](https://tools.ietf.org/html/rfc7159.html).
+
+## Installation
+
+This project is a standard GNU
+[autotools](https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html)
+project. Build and install instructions are available in the `INSTALL`
+file provided with GNU autotools.
+
+```
+$ ./autogen.sh
+$ ./configure
+$ make
+```
+
+## Design
+
+UniValue provides a single dynamic RAII C++ object class,
+and minimizes template use (contra json_spirit).
+
diff --git a/src/univalue/configure.ac b/src/univalue/configure.ac
index 93d3ba945d..8298332ac1 100644
--- a/src/univalue/configure.ac
+++ b/src/univalue/configure.ac
@@ -1,7 +1,7 @@
m4_define([libunivalue_major_version], [1])
m4_define([libunivalue_minor_version], [1])
-m4_define([libunivalue_micro_version], [2])
-m4_define([libunivalue_interface_age], [2])
+m4_define([libunivalue_micro_version], [3])
+m4_define([libunivalue_interface_age], [3])
# If you need a modifier for the version number.
# Normally empty, but can be used to make "fixup" releases.
m4_define([libunivalue_extraversion], [])
@@ -14,7 +14,7 @@ m4_define([libunivalue_age], [m4_eval(libunivalue_binary_age - libunivalue_inter
m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()])
-AC_INIT([univalue], [1.0.2],
+AC_INIT([univalue], [1.0.3],
[http://github.com/jgarzik/univalue/])
dnl make the compilation flags quiet unless V=1 is used
diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h
index e8ce283519..4fd2223b30 100644
--- a/src/univalue/include/univalue.h
+++ b/src/univalue/include/univalue.h
@@ -7,6 +7,7 @@
#define __UNIVALUE_H__
#include <stdint.h>
+#include <string.h>
#include <string>
#include <vector>
@@ -69,10 +70,11 @@ public:
size_t size() const { return values.size(); }
bool getBool() const { return isTrue(); }
- bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes);
+ void getObjMap(std::map<std::string,UniValue>& kv) const;
+ bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes) const;
const UniValue& operator[](const std::string& key) const;
- const UniValue& operator[](unsigned int index) const;
- bool exists(const std::string& key) const { return (findKey(key) >= 0); }
+ const UniValue& operator[](size_t index) const;
+ bool exists(const std::string& key) const { size_t i; return findKey(key, i); }
bool isNull() const { return (typ == VNULL); }
bool isTrue() const { return (typ == VBOOL) && (val == "1"); }
@@ -92,8 +94,25 @@ public:
std::string s(val_);
return push_back(s);
}
+ bool push_back(uint64_t val_) {
+ UniValue tmpVal(val_);
+ return push_back(tmpVal);
+ }
+ bool push_back(int64_t val_) {
+ UniValue tmpVal(val_);
+ return push_back(tmpVal);
+ }
+ bool push_back(int val_) {
+ UniValue tmpVal(val_);
+ return push_back(tmpVal);
+ }
+ bool push_back(double val_) {
+ UniValue tmpVal(val_);
+ return push_back(tmpVal);
+ }
bool push_backV(const std::vector<UniValue>& vec);
+ void __pushKV(const std::string& key, const UniValue& val);
bool pushKV(const std::string& key, const UniValue& val);
bool pushKV(const std::string& key, const std::string& val_) {
UniValue tmpVal(VSTR, val_);
@@ -124,9 +143,10 @@ public:
std::string write(unsigned int prettyIndent = 0,
unsigned int indentLevel = 0) const;
- bool read(const char *raw);
+ bool read(const char *raw, size_t len);
+ bool read(const char *raw) { return read(raw, strlen(raw)); }
bool read(const std::string& rawStr) {
- return read(rawStr.c_str());
+ return read(rawStr.data(), rawStr.size());
}
private:
@@ -135,7 +155,7 @@ private:
std::vector<std::string> keys;
std::vector<UniValue> values;
- int findKey(const std::string& key) const;
+ bool findKey(const std::string& key, size_t& retIdx) const;
void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const;
void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const;
@@ -240,7 +260,7 @@ enum jtokentype {
};
extern enum jtokentype getJsonToken(std::string& tokenVal,
- unsigned int& consumed, const char *raw);
+ unsigned int& consumed, const char *raw, const char *end);
extern const char *uvTypeName(UniValue::VType t);
static inline bool jsonTokenIsValue(enum jtokentype jtt)
diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp
index 5a2860c13f..d8ad7c4b90 100644
--- a/src/univalue/lib/univalue.cpp
+++ b/src/univalue/lib/univalue.cpp
@@ -4,75 +4,12 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <stdint.h>
-#include <errno.h>
#include <iomanip>
-#include <limits>
#include <sstream>
-#include <stdexcept>
#include <stdlib.h>
-#include <string.h>
#include "univalue.h"
-namespace
-{
-static bool ParsePrechecks(const std::string& str)
-{
- if (str.empty()) // No empty string allowed
- return false;
- if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed
- return false;
- if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed
- return false;
- return true;
-}
-
-bool ParseInt32(const std::string& str, int32_t *out)
-{
- if (!ParsePrechecks(str))
- return false;
- char *endp = NULL;
- errno = 0; // strtol will not set errno if valid
- long int n = strtol(str.c_str(), &endp, 10);
- if(out) *out = (int32_t)n;
- // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow
- // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
- // platforms the size of these types may be different.
- return endp && *endp == 0 && !errno &&
- n >= std::numeric_limits<int32_t>::min() &&
- n <= std::numeric_limits<int32_t>::max();
-}
-
-bool ParseInt64(const std::string& str, int64_t *out)
-{
- if (!ParsePrechecks(str))
- return false;
- char *endp = NULL;
- errno = 0; // strtoll will not set errno if valid
- long long int n = strtoll(str.c_str(), &endp, 10);
- if(out) *out = (int64_t)n;
- // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow
- // we still have to check that the returned value is within the range of an *int64_t*.
- return endp && *endp == 0 && !errno &&
- n >= std::numeric_limits<int64_t>::min() &&
- n <= std::numeric_limits<int64_t>::max();
-}
-
-bool ParseDouble(const std::string& str, double *out)
-{
- if (!ParsePrechecks(str))
- return false;
- if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
- return false;
- std::istringstream text(str);
- text.imbue(std::locale::classic());
- double result;
- text >> result;
- if(out) *out = result;
- return text.eof() && !text.fail();
-}
-}
-
using namespace std;
const UniValue NullUniValue;
@@ -104,7 +41,7 @@ static bool validNumStr(const string& s)
{
string tokenVal;
unsigned int consumed;
- enum jtokentype tt = getJsonToken(tokenVal, consumed, s.c_str());
+ enum jtokentype tt = getJsonToken(tokenVal, consumed, s.data(), s.data() + s.size());
return (tt == JTOK_NUMBER);
}
@@ -189,13 +126,22 @@ bool UniValue::push_backV(const std::vector<UniValue>& vec)
return true;
}
+void UniValue::__pushKV(const std::string& key, const UniValue& val_)
+{
+ keys.push_back(key);
+ values.push_back(val_);
+}
+
bool UniValue::pushKV(const std::string& key, const UniValue& val_)
{
if (typ != VOBJ)
return false;
- keys.push_back(key);
- values.push_back(val_);
+ size_t idx;
+ if (findKey(key, idx))
+ values[idx] = val_;
+ else
+ __pushKV(key, val_);
return true;
}
@@ -204,30 +150,43 @@ bool UniValue::pushKVs(const UniValue& obj)
if (typ != VOBJ || obj.typ != VOBJ)
return false;
- for (unsigned int i = 0; i < obj.keys.size(); i++) {
- keys.push_back(obj.keys[i]);
- values.push_back(obj.values.at(i));
- }
+ for (size_t i = 0; i < obj.keys.size(); i++)
+ __pushKV(obj.keys[i], obj.values.at(i));
return true;
}
-int UniValue::findKey(const std::string& key) const
+void UniValue::getObjMap(std::map<std::string,UniValue>& kv) const
+{
+ if (typ != VOBJ)
+ return;
+
+ kv.clear();
+ for (size_t i = 0; i < keys.size(); i++)
+ kv[keys[i]] = values[i];
+}
+
+bool UniValue::findKey(const std::string& key, size_t& retIdx) const
{
- for (unsigned int i = 0; i < keys.size(); i++) {
- if (keys[i] == key)
- return (int) i;
+ for (size_t i = 0; i < keys.size(); i++) {
+ if (keys[i] == key) {
+ retIdx = i;
+ return true;
+ }
}
- return -1;
+ return false;
}
-bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t)
+bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t) const
{
+ if (typ != VOBJ)
+ return false;
+
for (std::map<std::string,UniValue::VType>::const_iterator it = t.begin();
it != t.end(); ++it) {
- int idx = findKey(it->first);
- if (idx < 0)
+ size_t idx = 0;
+ if (!findKey(it->first, idx))
return false;
if (values.at(idx).getType() != it->second)
@@ -242,14 +201,14 @@ const UniValue& UniValue::operator[](const std::string& key) const
if (typ != VOBJ)
return NullUniValue;
- int index = findKey(key);
- if (index < 0)
+ size_t index = 0;
+ if (!findKey(key, index))
return NullUniValue;
return values.at(index);
}
-const UniValue& UniValue::operator[](unsigned int index) const
+const UniValue& UniValue::operator[](size_t index) const
{
if (typ != VOBJ && typ != VARR)
return NullUniValue;
@@ -283,75 +242,3 @@ const UniValue& find_value(const UniValue& obj, const std::string& name)
return NullUniValue;
}
-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;
-}
-
-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");
- return values;
-}
-
-bool UniValue::get_bool() const
-{
- if (typ != VBOOL)
- throw std::runtime_error("JSON value is not a boolean as expected");
- return getBool();
-}
-
-const std::string& UniValue::get_str() const
-{
- if (typ != VSTR)
- throw std::runtime_error("JSON value is not a string as expected");
- return getValStr();
-}
-
-int UniValue::get_int() const
-{
- if (typ != VNUM)
- throw std::runtime_error("JSON value is not an integer as expected");
- int32_t retval;
- if (!ParseInt32(getValStr(), &retval))
- throw std::runtime_error("JSON integer out of range");
- return retval;
-}
-
-int64_t UniValue::get_int64() const
-{
- if (typ != VNUM)
- throw std::runtime_error("JSON value is not an integer as expected");
- int64_t retval;
- if (!ParseInt64(getValStr(), &retval))
- throw std::runtime_error("JSON integer out of range");
- return retval;
-}
-
-double UniValue::get_real() const
-{
- if (typ != VNUM)
- throw std::runtime_error("JSON value is not a number as expected");
- double retval;
- if (!ParseDouble(getValStr(), &retval))
- throw std::runtime_error("JSON double out of range");
- return retval;
-}
-
-const UniValue& UniValue::get_obj() const
-{
- if (typ != VOBJ)
- throw std::runtime_error("JSON value is not an object as expected");
- return *this;
-}
-
-const UniValue& UniValue::get_array() const
-{
- if (typ != VARR)
- throw std::runtime_error("JSON value is not an array as expected");
- return *this;
-}
-
diff --git a/src/univalue/lib/univalue_get.cpp b/src/univalue/lib/univalue_get.cpp
new file mode 100644
index 0000000000..eabcf2dad1
--- /dev/null
+++ b/src/univalue/lib/univalue_get.cpp
@@ -0,0 +1,147 @@
+// Copyright 2014 BitPay Inc.
+// Copyright 2015 Bitcoin Core Developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdexcept>
+#include <vector>
+#include <limits>
+#include <string>
+
+#include "univalue.h"
+
+namespace
+{
+static bool ParsePrechecks(const std::string& str)
+{
+ if (str.empty()) // No empty string allowed
+ return false;
+ if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed
+ return false;
+ if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed
+ return false;
+ return true;
+}
+
+bool ParseInt32(const std::string& str, int32_t *out)
+{
+ if (!ParsePrechecks(str))
+ return false;
+ char *endp = NULL;
+ errno = 0; // strtol will not set errno if valid
+ long int n = strtol(str.c_str(), &endp, 10);
+ if(out) *out = (int32_t)n;
+ // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow
+ // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
+ // platforms the size of these types may be different.
+ return endp && *endp == 0 && !errno &&
+ n >= std::numeric_limits<int32_t>::min() &&
+ n <= std::numeric_limits<int32_t>::max();
+}
+
+bool ParseInt64(const std::string& str, int64_t *out)
+{
+ if (!ParsePrechecks(str))
+ return false;
+ char *endp = NULL;
+ errno = 0; // strtoll will not set errno if valid
+ long long int n = strtoll(str.c_str(), &endp, 10);
+ if(out) *out = (int64_t)n;
+ // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow
+ // we still have to check that the returned value is within the range of an *int64_t*.
+ return endp && *endp == 0 && !errno &&
+ n >= std::numeric_limits<int64_t>::min() &&
+ n <= std::numeric_limits<int64_t>::max();
+}
+
+bool ParseDouble(const std::string& str, double *out)
+{
+ if (!ParsePrechecks(str))
+ return false;
+ if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
+ return false;
+ std::istringstream text(str);
+ text.imbue(std::locale::classic());
+ double result;
+ text >> result;
+ if(out) *out = result;
+ return text.eof() && !text.fail();
+}
+}
+
+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;
+}
+
+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");
+ return values;
+}
+
+bool UniValue::get_bool() const
+{
+ if (typ != VBOOL)
+ throw std::runtime_error("JSON value is not a boolean as expected");
+ return getBool();
+}
+
+const std::string& UniValue::get_str() const
+{
+ if (typ != VSTR)
+ throw std::runtime_error("JSON value is not a string as expected");
+ return getValStr();
+}
+
+int UniValue::get_int() const
+{
+ if (typ != VNUM)
+ throw std::runtime_error("JSON value is not an integer as expected");
+ int32_t retval;
+ if (!ParseInt32(getValStr(), &retval))
+ throw std::runtime_error("JSON integer out of range");
+ return retval;
+}
+
+int64_t UniValue::get_int64() const
+{
+ if (typ != VNUM)
+ throw std::runtime_error("JSON value is not an integer as expected");
+ int64_t retval;
+ if (!ParseInt64(getValStr(), &retval))
+ throw std::runtime_error("JSON integer out of range");
+ return retval;
+}
+
+double UniValue::get_real() const
+{
+ if (typ != VNUM)
+ throw std::runtime_error("JSON value is not a number as expected");
+ double retval;
+ if (!ParseDouble(getValStr(), &retval))
+ throw std::runtime_error("JSON double out of range");
+ return retval;
+}
+
+const UniValue& UniValue::get_obj() const
+{
+ if (typ != VOBJ)
+ throw std::runtime_error("JSON value is not an object as expected");
+ return *this;
+}
+
+const UniValue& UniValue::get_array() const
+{
+ if (typ != VARR)
+ throw std::runtime_error("JSON value is not an array as expected");
+ return *this;
+}
+
diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp
index 95bac6958d..ae75cb462a 100644
--- a/src/univalue/lib/univalue_read.cpp
+++ b/src/univalue/lib/univalue_read.cpp
@@ -43,21 +43,21 @@ static const char *hatoui(const char *first, const char *last,
}
enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
- const char *raw)
+ const char *raw, const char *end)
{
tokenVal.clear();
consumed = 0;
const char *rawStart = raw;
- while ((*raw) && (json_isspace(*raw))) // skip whitespace
+ while (raw < end && (json_isspace(*raw))) // skip whitespace
raw++;
- switch (*raw) {
-
- case 0:
+ if (raw >= end)
return JTOK_NONE;
+ switch (*raw) {
+
case '{':
raw++;
consumed = (raw - rawStart);
@@ -127,40 +127,40 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
numStr += *raw; // copy first char
raw++;
- if ((*first == '-') && (!json_isdigit(*raw)))
+ if ((*first == '-') && (raw < end) && (!json_isdigit(*raw)))
return JTOK_ERR;
- while ((*raw) && json_isdigit(*raw)) { // copy digits
+ while (raw < end && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
// part 2: frac
- if (*raw == '.') {
+ if (raw < end && *raw == '.') {
numStr += *raw; // copy .
raw++;
- if (!json_isdigit(*raw))
+ if (raw >= end || !json_isdigit(*raw))
return JTOK_ERR;
- while ((*raw) && json_isdigit(*raw)) { // copy digits
+ while (raw < end && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
}
// part 3: exp
- if (*raw == 'e' || *raw == 'E') {
+ if (raw < end && (*raw == 'e' || *raw == 'E')) {
numStr += *raw; // copy E
raw++;
- if (*raw == '-' || *raw == '+') { // copy +/-
+ if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/-
numStr += *raw;
raw++;
}
- if (!json_isdigit(*raw))
+ if (raw >= end || !json_isdigit(*raw))
return JTOK_ERR;
- while ((*raw) && json_isdigit(*raw)) { // copy digits
+ while (raw < end && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
@@ -177,13 +177,16 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
string valStr;
JSONUTF8StringFilter writer(valStr);
- while (*raw) {
- if ((unsigned char)*raw < 0x20)
+ while (true) {
+ if (raw >= end || (unsigned char)*raw < 0x20)
return JTOK_ERR;
else if (*raw == '\\') {
raw++; // skip backslash
+ if (raw >= end)
+ return JTOK_ERR;
+
switch (*raw) {
case '"': writer.push_back('\"'); break;
case '\\': writer.push_back('\\'); break;
@@ -196,7 +199,8 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
case 'u': {
unsigned int codepoint;
- if (hatoui(raw + 1, raw + 1 + 4, codepoint) !=
+ if (raw + 1 + 4 >= end ||
+ hatoui(raw + 1, raw + 1 + 4, codepoint) !=
raw + 1 + 4)
return JTOK_ERR;
writer.push_back_u(codepoint);
@@ -246,7 +250,7 @@ enum expect_bits {
#define setExpect(bit) (expectMask |= EXP_##bit)
#define clearExpect(bit) (expectMask &= ~EXP_##bit)
-bool UniValue::read(const char *raw)
+bool UniValue::read(const char *raw, size_t size)
{
clear();
@@ -257,10 +261,11 @@ bool UniValue::read(const char *raw)
unsigned int consumed;
enum jtokentype tok = JTOK_NONE;
enum jtokentype last_tok = JTOK_NONE;
+ const char* end = raw + size;
do {
last_tok = tok;
- tok = getJsonToken(tokenVal, consumed, raw);
+ tok = getJsonToken(tokenVal, consumed, raw, end);
if (tok == JTOK_NONE || tok == JTOK_ERR)
return false;
raw += consumed;
@@ -371,9 +376,6 @@ bool UniValue::read(const char *raw)
case JTOK_KW_NULL:
case JTOK_KW_TRUE:
case JTOK_KW_FALSE: {
- if (!stack.size())
- return false;
-
UniValue tmpVal;
switch (tok) {
case JTOK_KW_NULL:
@@ -388,6 +390,11 @@ bool UniValue::read(const char *raw)
default: /* impossible */ break;
}
+ if (!stack.size()) {
+ *this = tmpVal;
+ break;
+ }
+
UniValue *top = stack.back();
top->values.push_back(tmpVal);
@@ -396,10 +403,12 @@ bool UniValue::read(const char *raw)
}
case JTOK_NUMBER: {
- if (!stack.size())
- return false;
-
UniValue tmpVal(VNUM, tokenVal);
+ if (!stack.size()) {
+ *this = tmpVal;
+ break;
+ }
+
UniValue *top = stack.back();
top->values.push_back(tmpVal);
@@ -408,17 +417,18 @@ bool UniValue::read(const char *raw)
}
case JTOK_STRING: {
- if (!stack.size())
- return false;
-
- UniValue *top = stack.back();
-
if (expect(OBJ_NAME)) {
+ UniValue *top = stack.back();
top->keys.push_back(tokenVal);
clearExpect(OBJ_NAME);
setExpect(COLON);
} else {
UniValue tmpVal(VSTR, tokenVal);
+ if (!stack.size()) {
+ *this = tmpVal;
+ break;
+ }
+ UniValue *top = stack.back();
top->values.push_back(tmpVal);
}
@@ -432,7 +442,7 @@ bool UniValue::read(const char *raw)
} while (!stack.empty ());
/* Check that nothing follows the initial construct (parsed above). */
- tok = getJsonToken(tokenVal, consumed, raw);
+ tok = getJsonToken(tokenVal, consumed, raw, end);
if (tok != JTOK_NONE)
return false;
diff --git a/src/univalue/lib/univalue_utffilter.h b/src/univalue/lib/univalue_utffilter.h
index 2fb6a492d1..20d4043009 100644
--- a/src/univalue/lib/univalue_utffilter.h
+++ b/src/univalue/lib/univalue_utffilter.h
@@ -46,19 +46,19 @@ public:
}
}
// Write codepoint directly, possibly collating surrogate pairs
- void push_back_u(unsigned int codepoint)
+ void push_back_u(unsigned int codepoint_)
{
if (state) // Only accept full codepoints in open state
is_valid = false;
- if (codepoint >= 0xD800 && codepoint < 0xDC00) { // First half of surrogate pair
+ if (codepoint_ >= 0xD800 && codepoint_ < 0xDC00) { // First half of surrogate pair
if (surpair) // Two subsequent surrogate pair openers - fail
is_valid = false;
else
- surpair = codepoint;
- } else if (codepoint >= 0xDC00 && codepoint < 0xE000) { // Second half of surrogate pair
+ surpair = codepoint_;
+ } else if (codepoint_ >= 0xDC00 && codepoint_ < 0xE000) { // Second half of surrogate pair
if (surpair) { // Open surrogate pair, expect second half
// Compute code point from UTF-16 surrogate pair
- append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint - 0xDC00));
+ append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint_ - 0xDC00));
surpair = 0;
} else // Second half doesn't follow a first half - fail
is_valid = false;
@@ -66,7 +66,7 @@ public:
if (surpair) // First half of surrogate pair not followed by second - fail
is_valid = false;
else
- append_codepoint(codepoint);
+ append_codepoint(codepoint_);
}
}
// Check that we're in a state where the string can be ended
@@ -96,22 +96,22 @@ private:
// Two subsequent \u.... may have to be replaced with one actual codepoint.
unsigned int surpair; // First half of open UTF-16 surrogate pair, or 0
- void append_codepoint(unsigned int codepoint)
+ void append_codepoint(unsigned int codepoint_)
{
- if (codepoint <= 0x7f)
- str.push_back((char)codepoint);
- else if (codepoint <= 0x7FF) {
- str.push_back((char)(0xC0 | (codepoint >> 6)));
- str.push_back((char)(0x80 | (codepoint & 0x3F)));
- } else if (codepoint <= 0xFFFF) {
- str.push_back((char)(0xE0 | (codepoint >> 12)));
- str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F)));
- str.push_back((char)(0x80 | (codepoint & 0x3F)));
- } else if (codepoint <= 0x1FFFFF) {
- str.push_back((char)(0xF0 | (codepoint >> 18)));
- str.push_back((char)(0x80 | ((codepoint >> 12) & 0x3F)));
- str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F)));
- str.push_back((char)(0x80 | (codepoint & 0x3F)));
+ if (codepoint_ <= 0x7f)
+ str.push_back((char)codepoint_);
+ else if (codepoint_ <= 0x7FF) {
+ str.push_back((char)(0xC0 | (codepoint_ >> 6)));
+ str.push_back((char)(0x80 | (codepoint_ & 0x3F)));
+ } else if (codepoint_ <= 0xFFFF) {
+ str.push_back((char)(0xE0 | (codepoint_ >> 12)));
+ str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F)));
+ str.push_back((char)(0x80 | (codepoint_ & 0x3F)));
+ } else if (codepoint_ <= 0x1FFFFF) {
+ str.push_back((char)(0xF0 | (codepoint_ >> 18)));
+ str.push_back((char)(0x80 | ((codepoint_ >> 12) & 0x3F)));
+ str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F)));
+ str.push_back((char)(0x80 | (codepoint_ & 0x3F)));
}
}
};
diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp
index cfbdad3284..cf27835991 100644
--- a/src/univalue/lib/univalue_write.cpp
+++ b/src/univalue/lib/univalue_write.cpp
@@ -79,8 +79,6 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s
s += values[i].write(prettyIndent, indentLevel + 1);
if (i != (values.size() - 1)) {
s += ",";
- if (prettyIndent)
- s += " ";
}
if (prettyIndent)
s += "\n";
diff --git a/src/univalue/test/.gitignore b/src/univalue/test/.gitignore
index 3d9347fe7e..7b27cf0da2 100644
--- a/src/univalue/test/.gitignore
+++ b/src/univalue/test/.gitignore
@@ -1,4 +1,8 @@
+
+object
unitester
+test_json
+no_nul
*.trs
*.log
diff --git a/src/univalue/test/fail1.json b/src/univalue/test/fail1.json
index 6216b865f1..8feb01a6d0 100644
--- a/src/univalue/test/fail1.json
+++ b/src/univalue/test/fail1.json
@@ -1 +1 @@
-"A JSON payload should be an object or array, not a string." \ No newline at end of file
+"This is a string that never ends, yes it goes on and on, my friends.
diff --git a/src/univalue/test/fail42.json b/src/univalue/test/fail42.json
new file mode 100644
index 0000000000..9c7565adbd
--- /dev/null
+++ b/src/univalue/test/fail42.json
Binary files differ
diff --git a/src/univalue/test/fail44.json b/src/univalue/test/fail44.json
new file mode 100644
index 0000000000..80edceddf1
--- /dev/null
+++ b/src/univalue/test/fail44.json
@@ -0,0 +1 @@
+"This file ends without a newline or close-quote. \ No newline at end of file
diff --git a/src/univalue/test/no_nul.cpp b/src/univalue/test/no_nul.cpp
new file mode 100644
index 0000000000..83d292200b
--- /dev/null
+++ b/src/univalue/test/no_nul.cpp
@@ -0,0 +1,8 @@
+#include "univalue.h"
+
+int main (int argc, char *argv[])
+{
+ char buf[] = "___[1,2,3]___";
+ UniValue val;
+ return val.read(buf + 3, 7) ? 0 : 1;
+}
diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp
new file mode 100644
index 0000000000..02446292a1
--- /dev/null
+++ b/src/univalue/test/object.cpp
@@ -0,0 +1,395 @@
+// Copyright (c) 2014 BitPay Inc.
+// 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 <stdint.h>
+#include <vector>
+#include <string>
+#include <map>
+#include <cassert>
+#include <stdexcept>
+#include <univalue.h>
+
+#define BOOST_FIXTURE_TEST_SUITE(a, b)
+#define BOOST_AUTO_TEST_CASE(funcName) void funcName()
+#define BOOST_AUTO_TEST_SUITE_END()
+#define BOOST_CHECK(expr) assert(expr)
+#define BOOST_CHECK_EQUAL(v1, v2) assert((v1) == (v2))
+#define BOOST_CHECK_THROW(stmt, excMatch) { \
+ try { \
+ (stmt); \
+ } catch (excMatch & e) { \
+ } catch (...) { \
+ assert(0); \
+ } \
+ }
+#define BOOST_CHECK_NO_THROW(stmt) { \
+ try { \
+ (stmt); \
+ } catch (...) { \
+ assert(0); \
+ } \
+ }
+
+BOOST_FIXTURE_TEST_SUITE(univalue_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(univalue_constructor)
+{
+ UniValue v1;
+ BOOST_CHECK(v1.isNull());
+
+ UniValue v2(UniValue::VSTR);
+ BOOST_CHECK(v2.isStr());
+
+ UniValue v3(UniValue::VSTR, "foo");
+ BOOST_CHECK(v3.isStr());
+ BOOST_CHECK_EQUAL(v3.getValStr(), "foo");
+
+ UniValue numTest;
+ BOOST_CHECK(numTest.setNumStr("82"));
+ BOOST_CHECK(numTest.isNum());
+ BOOST_CHECK_EQUAL(numTest.getValStr(), "82");
+
+ uint64_t vu64 = 82;
+ UniValue v4(vu64);
+ BOOST_CHECK(v4.isNum());
+ BOOST_CHECK_EQUAL(v4.getValStr(), "82");
+
+ int64_t vi64 = -82;
+ UniValue v5(vi64);
+ BOOST_CHECK(v5.isNum());
+ BOOST_CHECK_EQUAL(v5.getValStr(), "-82");
+
+ int vi = -688;
+ UniValue v6(vi);
+ BOOST_CHECK(v6.isNum());
+ BOOST_CHECK_EQUAL(v6.getValStr(), "-688");
+
+ double vd = -7.21;
+ UniValue v7(vd);
+ BOOST_CHECK(v7.isNum());
+ BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21");
+
+ std::string vs("yawn");
+ UniValue v8(vs);
+ BOOST_CHECK(v8.isStr());
+ BOOST_CHECK_EQUAL(v8.getValStr(), "yawn");
+
+ const char *vcs = "zappa";
+ UniValue v9(vcs);
+ BOOST_CHECK(v9.isStr());
+ BOOST_CHECK_EQUAL(v9.getValStr(), "zappa");
+}
+
+BOOST_AUTO_TEST_CASE(univalue_typecheck)
+{
+ UniValue v1;
+ BOOST_CHECK(v1.setNumStr("1"));
+ BOOST_CHECK(v1.isNum());
+ BOOST_CHECK_THROW(v1.get_bool(), std::runtime_error);
+
+ UniValue v2;
+ BOOST_CHECK(v2.setBool(true));
+ BOOST_CHECK_EQUAL(v2.get_bool(), true);
+ BOOST_CHECK_THROW(v2.get_int(), std::runtime_error);
+
+ UniValue v3;
+ BOOST_CHECK(v3.setNumStr("32482348723847471234"));
+ BOOST_CHECK_THROW(v3.get_int64(), std::runtime_error);
+ BOOST_CHECK(v3.setNumStr("1000"));
+ BOOST_CHECK_EQUAL(v3.get_int64(), 1000);
+
+ UniValue v4;
+ BOOST_CHECK(v4.setNumStr("2147483648"));
+ BOOST_CHECK_EQUAL(v4.get_int64(), 2147483648);
+ BOOST_CHECK_THROW(v4.get_int(), std::runtime_error);
+ BOOST_CHECK(v4.setNumStr("1000"));
+ BOOST_CHECK_EQUAL(v4.get_int(), 1000);
+ BOOST_CHECK_THROW(v4.get_str(), std::runtime_error);
+ BOOST_CHECK_EQUAL(v4.get_real(), 1000);
+ BOOST_CHECK_THROW(v4.get_array(), std::runtime_error);
+ BOOST_CHECK_THROW(v4.getKeys(), std::runtime_error);
+ BOOST_CHECK_THROW(v4.getValues(), std::runtime_error);
+ BOOST_CHECK_THROW(v4.get_obj(), std::runtime_error);
+
+ UniValue v5;
+ BOOST_CHECK(v5.read("[true, 10]"));
+ BOOST_CHECK_NO_THROW(v5.get_array());
+ std::vector<UniValue> vals = v5.getValues();
+ BOOST_CHECK_THROW(vals[0].get_int(), std::runtime_error);
+ BOOST_CHECK_EQUAL(vals[0].get_bool(), true);
+
+ BOOST_CHECK_EQUAL(vals[1].get_int(), 10);
+ BOOST_CHECK_THROW(vals[1].get_bool(), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_CASE(univalue_set)
+{
+ UniValue v(UniValue::VSTR, "foo");
+ v.clear();
+ BOOST_CHECK(v.isNull());
+ BOOST_CHECK_EQUAL(v.getValStr(), "");
+
+ BOOST_CHECK(v.setObject());
+ BOOST_CHECK(v.isObject());
+ BOOST_CHECK_EQUAL(v.size(), 0);
+ BOOST_CHECK_EQUAL(v.getType(), UniValue::VOBJ);
+ BOOST_CHECK(v.empty());
+
+ BOOST_CHECK(v.setArray());
+ BOOST_CHECK(v.isArray());
+ BOOST_CHECK_EQUAL(v.size(), 0);
+
+ BOOST_CHECK(v.setStr("zum"));
+ BOOST_CHECK(v.isStr());
+ BOOST_CHECK_EQUAL(v.getValStr(), "zum");
+
+ BOOST_CHECK(v.setFloat(-1.01));
+ BOOST_CHECK(v.isNum());
+ BOOST_CHECK_EQUAL(v.getValStr(), "-1.01");
+
+ BOOST_CHECK(v.setInt((int)1023));
+ BOOST_CHECK(v.isNum());
+ BOOST_CHECK_EQUAL(v.getValStr(), "1023");
+
+ BOOST_CHECK(v.setInt((int64_t)-1023LL));
+ BOOST_CHECK(v.isNum());
+ BOOST_CHECK_EQUAL(v.getValStr(), "-1023");
+
+ BOOST_CHECK(v.setInt((uint64_t)1023ULL));
+ BOOST_CHECK(v.isNum());
+ BOOST_CHECK_EQUAL(v.getValStr(), "1023");
+
+ BOOST_CHECK(v.setNumStr("-688"));
+ BOOST_CHECK(v.isNum());
+ BOOST_CHECK_EQUAL(v.getValStr(), "-688");
+
+ BOOST_CHECK(v.setBool(false));
+ BOOST_CHECK_EQUAL(v.isBool(), true);
+ BOOST_CHECK_EQUAL(v.isTrue(), false);
+ BOOST_CHECK_EQUAL(v.isFalse(), true);
+ BOOST_CHECK_EQUAL(v.getBool(), false);
+
+ BOOST_CHECK(v.setBool(true));
+ BOOST_CHECK_EQUAL(v.isBool(), true);
+ BOOST_CHECK_EQUAL(v.isTrue(), true);
+ BOOST_CHECK_EQUAL(v.isFalse(), false);
+ BOOST_CHECK_EQUAL(v.getBool(), true);
+
+ BOOST_CHECK(!v.setNumStr("zombocom"));
+
+ BOOST_CHECK(v.setNull());
+ BOOST_CHECK(v.isNull());
+}
+
+BOOST_AUTO_TEST_CASE(univalue_array)
+{
+ UniValue arr(UniValue::VARR);
+
+ UniValue v((int64_t)1023LL);
+ BOOST_CHECK(arr.push_back(v));
+
+ std::string vStr("zippy");
+ BOOST_CHECK(arr.push_back(vStr));
+
+ const char *s = "pippy";
+ BOOST_CHECK(arr.push_back(s));
+
+ std::vector<UniValue> vec;
+ v.setStr("boing");
+ vec.push_back(v);
+
+ v.setStr("going");
+ vec.push_back(v);
+
+ BOOST_CHECK(arr.push_backV(vec));
+
+ BOOST_CHECK(arr.push_back((uint64_t) 400ULL));
+ BOOST_CHECK(arr.push_back((int64_t) -400LL));
+ BOOST_CHECK(arr.push_back((int) -401));
+ BOOST_CHECK(arr.push_back(-40.1));
+
+ BOOST_CHECK_EQUAL(arr.empty(), false);
+ BOOST_CHECK_EQUAL(arr.size(), 9);
+
+ BOOST_CHECK_EQUAL(arr[0].getValStr(), "1023");
+ BOOST_CHECK_EQUAL(arr[1].getValStr(), "zippy");
+ BOOST_CHECK_EQUAL(arr[2].getValStr(), "pippy");
+ BOOST_CHECK_EQUAL(arr[3].getValStr(), "boing");
+ BOOST_CHECK_EQUAL(arr[4].getValStr(), "going");
+ BOOST_CHECK_EQUAL(arr[5].getValStr(), "400");
+ BOOST_CHECK_EQUAL(arr[6].getValStr(), "-400");
+ BOOST_CHECK_EQUAL(arr[7].getValStr(), "-401");
+ BOOST_CHECK_EQUAL(arr[8].getValStr(), "-40.1");
+
+ BOOST_CHECK_EQUAL(arr[999].getValStr(), "");
+
+ arr.clear();
+ BOOST_CHECK(arr.empty());
+ BOOST_CHECK_EQUAL(arr.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(univalue_object)
+{
+ UniValue obj(UniValue::VOBJ);
+ std::string strKey, strVal;
+ UniValue v;
+
+ strKey = "age";
+ v.setInt(100);
+ BOOST_CHECK(obj.pushKV(strKey, v));
+
+ strKey = "first";
+ strVal = "John";
+ BOOST_CHECK(obj.pushKV(strKey, strVal));
+
+ strKey = "last";
+ const char *cVal = "Smith";
+ BOOST_CHECK(obj.pushKV(strKey, cVal));
+
+ strKey = "distance";
+ BOOST_CHECK(obj.pushKV(strKey, (int64_t) 25));
+
+ strKey = "time";
+ BOOST_CHECK(obj.pushKV(strKey, (uint64_t) 3600));
+
+ strKey = "calories";
+ BOOST_CHECK(obj.pushKV(strKey, (int) 12));
+
+ strKey = "temperature";
+ BOOST_CHECK(obj.pushKV(strKey, (double) 90.012));
+
+ UniValue obj2(UniValue::VOBJ);
+ BOOST_CHECK(obj2.pushKV("cat1", 9000));
+ BOOST_CHECK(obj2.pushKV("cat2", 12345));
+
+ BOOST_CHECK(obj.pushKVs(obj2));
+
+ BOOST_CHECK_EQUAL(obj.empty(), false);
+ BOOST_CHECK_EQUAL(obj.size(), 9);
+
+ BOOST_CHECK_EQUAL(obj["age"].getValStr(), "100");
+ BOOST_CHECK_EQUAL(obj["first"].getValStr(), "John");
+ BOOST_CHECK_EQUAL(obj["last"].getValStr(), "Smith");
+ BOOST_CHECK_EQUAL(obj["distance"].getValStr(), "25");
+ BOOST_CHECK_EQUAL(obj["time"].getValStr(), "3600");
+ BOOST_CHECK_EQUAL(obj["calories"].getValStr(), "12");
+ BOOST_CHECK_EQUAL(obj["temperature"].getValStr(), "90.012");
+ BOOST_CHECK_EQUAL(obj["cat1"].getValStr(), "9000");
+ BOOST_CHECK_EQUAL(obj["cat2"].getValStr(), "12345");
+
+ BOOST_CHECK_EQUAL(obj["nyuknyuknyuk"].getValStr(), "");
+
+ BOOST_CHECK(obj.exists("age"));
+ BOOST_CHECK(obj.exists("first"));
+ BOOST_CHECK(obj.exists("last"));
+ BOOST_CHECK(obj.exists("distance"));
+ BOOST_CHECK(obj.exists("time"));
+ BOOST_CHECK(obj.exists("calories"));
+ BOOST_CHECK(obj.exists("temperature"));
+ BOOST_CHECK(obj.exists("cat1"));
+ BOOST_CHECK(obj.exists("cat2"));
+
+ BOOST_CHECK(!obj.exists("nyuknyuknyuk"));
+
+ std::map<std::string, UniValue::VType> objTypes;
+ objTypes["age"] = UniValue::VNUM;
+ objTypes["first"] = UniValue::VSTR;
+ objTypes["last"] = UniValue::VSTR;
+ objTypes["distance"] = UniValue::VNUM;
+ objTypes["time"] = UniValue::VNUM;
+ objTypes["calories"] = UniValue::VNUM;
+ objTypes["temperature"] = UniValue::VNUM;
+ objTypes["cat1"] = UniValue::VNUM;
+ objTypes["cat2"] = UniValue::VNUM;
+ BOOST_CHECK(obj.checkObject(objTypes));
+
+ objTypes["cat2"] = UniValue::VSTR;
+ BOOST_CHECK(!obj.checkObject(objTypes));
+
+ obj.clear();
+ BOOST_CHECK(obj.empty());
+ BOOST_CHECK_EQUAL(obj.size(), 0);
+ BOOST_CHECK_EQUAL(obj.getType(), UniValue::VNULL);
+
+ BOOST_CHECK_EQUAL(obj.setObject(), true);
+ UniValue uv;
+ uv.setInt(42);
+ obj.__pushKV("age", uv);
+ BOOST_CHECK_EQUAL(obj.size(), 1);
+ BOOST_CHECK_EQUAL(obj["age"].getValStr(), "42");
+
+ uv.setInt(43);
+ obj.pushKV("age", uv);
+ BOOST_CHECK_EQUAL(obj.size(), 1);
+ BOOST_CHECK_EQUAL(obj["age"].getValStr(), "43");
+
+ obj.pushKV("name", "foo bar");
+
+ std::map<std::string,UniValue> kv;
+ obj.getObjMap(kv);
+ BOOST_CHECK_EQUAL(kv["age"].getValStr(), "43");
+ BOOST_CHECK_EQUAL(kv["name"].getValStr(), "foo bar");
+
+}
+
+static const char *json1 =
+"[1.10000000,{\"key1\":\"str\\u0000\",\"key2\":800,\"key3\":{\"name\":\"martian http://test.com\"}}]";
+
+BOOST_AUTO_TEST_CASE(univalue_readwrite)
+{
+ UniValue v;
+ BOOST_CHECK(v.read(json1));
+
+ std::string strJson1(json1);
+ BOOST_CHECK(v.read(strJson1));
+
+ BOOST_CHECK(v.isArray());
+ BOOST_CHECK_EQUAL(v.size(), 2);
+
+ BOOST_CHECK_EQUAL(v[0].getValStr(), "1.10000000");
+
+ UniValue obj = v[1];
+ BOOST_CHECK(obj.isObject());
+ BOOST_CHECK_EQUAL(obj.size(), 3);
+
+ BOOST_CHECK(obj["key1"].isStr());
+ std::string correctValue("str");
+ correctValue.push_back('\0');
+ BOOST_CHECK_EQUAL(obj["key1"].getValStr(), correctValue);
+ BOOST_CHECK(obj["key2"].isNum());
+ BOOST_CHECK_EQUAL(obj["key2"].getValStr(), "800");
+ BOOST_CHECK(obj["key3"].isObject());
+
+ BOOST_CHECK_EQUAL(strJson1, v.write());
+
+ /* Check for (correctly reporting) a parsing error if the initial
+ JSON construct is followed by more stuff. Note that whitespace
+ is, of course, exempt. */
+
+ BOOST_CHECK(v.read(" {}\n "));
+ BOOST_CHECK(v.isObject());
+ BOOST_CHECK(v.read(" []\n "));
+ BOOST_CHECK(v.isArray());
+
+ BOOST_CHECK(!v.read("@{}"));
+ BOOST_CHECK(!v.read("{} garbage"));
+ BOOST_CHECK(!v.read("[]{}"));
+ BOOST_CHECK(!v.read("{}[]"));
+ BOOST_CHECK(!v.read("{} 42"));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+int main (int argc, char *argv[])
+{
+ univalue_constructor();
+ univalue_typecheck();
+ univalue_set();
+ univalue_array();
+ univalue_object();
+ univalue_readwrite();
+ return 0;
+}
+
diff --git a/src/univalue/test/round3.json b/src/univalue/test/round3.json
new file mode 100644
index 0000000000..7182dc2f9b
--- /dev/null
+++ b/src/univalue/test/round3.json
@@ -0,0 +1 @@
+"abcdefghijklmnopqrstuvwxyz"
diff --git a/src/univalue/test/round4.json b/src/univalue/test/round4.json
new file mode 100644
index 0000000000..7f8f011eb7
--- /dev/null
+++ b/src/univalue/test/round4.json
@@ -0,0 +1 @@
+7
diff --git a/src/univalue/test/round5.json b/src/univalue/test/round5.json
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/src/univalue/test/round5.json
@@ -0,0 +1 @@
+true
diff --git a/src/univalue/test/round6.json b/src/univalue/test/round6.json
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/src/univalue/test/round6.json
@@ -0,0 +1 @@
+false
diff --git a/src/univalue/test/round7.json b/src/univalue/test/round7.json
new file mode 100644
index 0000000000..19765bd501
--- /dev/null
+++ b/src/univalue/test/round7.json
@@ -0,0 +1 @@
+null
diff --git a/src/univalue/test/test_json.cpp b/src/univalue/test/test_json.cpp
new file mode 100644
index 0000000000..2943bae2b1
--- /dev/null
+++ b/src/univalue/test/test_json.cpp
@@ -0,0 +1,24 @@
+// Test program that can be called by the JSON test suite at
+// https://github.com/nst/JSONTestSuite.
+//
+// It reads JSON input from stdin and exits with code 0 if it can be parsed
+// successfully. It also pretty prints the parsed JSON value to stdout.
+
+#include <iostream>
+#include <string>
+#include "univalue.h"
+
+using namespace std;
+
+int main (int argc, char *argv[])
+{
+ UniValue val;
+ if (val.read(string(istreambuf_iterator<char>(cin),
+ istreambuf_iterator<char>()))) {
+ cout << val.write(1 /* prettyIndent */, 4 /* indentLevel */) << endl;
+ return 0;
+ } else {
+ cerr << "JSON Parse Error." << endl;
+ return 1;
+ }
+}
diff --git a/src/univalue/test/unitester.cpp b/src/univalue/test/unitester.cpp
index 05f3842cd1..2c37794a4b 100644
--- a/src/univalue/test/unitester.cpp
+++ b/src/univalue/test/unitester.cpp
@@ -113,6 +113,8 @@ static const char *filenames[] = {
"fail39.json", // invalid unicode: only second half of surrogate pair
"fail40.json", // invalid unicode: broken UTF-8
"fail41.json", // invalid unicode: unfinished UTF-8
+ "fail42.json", // valid json with garbage following a nul byte
+ "fail44.json", // unterminated string
"fail3.json",
"fail4.json", // extra comma
"fail5.json",
@@ -125,6 +127,11 @@ static const char *filenames[] = {
"pass3.json",
"round1.json", // round-trip test
"round2.json", // unicode
+ "round3.json", // bare string
+ "round4.json", // bare number
+ "round5.json", // bare true
+ "round6.json", // bare false
+ "round7.json", // bare null
};
// Test \u handling
diff --git a/src/util.cpp b/src/util.cpp
index 4659ff73c6..a3e4b0acfb 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -3,18 +3,12 @@
// 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 "util.h"
+#include <util.h>
-#include "chainparamsbase.h"
-#include "fs.h"
-#include "random.h"
-#include "serialize.h"
-#include "utilstrencodings.h"
-#include "utiltime.h"
+#include <chainparamsbase.h>
+#include <random.h>
+#include <serialize.h>
+#include <utilstrencodings.h>
#include <stdarg.h>
@@ -89,6 +83,7 @@ const int64_t nStartupTime = GetTime();
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
const char * const BITCOIN_PID_FILENAME = "bitcoind.pid";
+const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
ArgsManager gArgs;
bool fPrintToConsole = false;
@@ -189,26 +184,40 @@ static void DebugPrintInit()
vMsgsBeforeOpenLog = new std::list<std::string>;
}
-void OpenDebugLog()
+fs::path GetDebugLogPath()
+{
+ fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
+ if (logfile.is_absolute()) {
+ return logfile;
+ } else {
+ return GetDataDir() / logfile;
+ }
+}
+
+bool OpenDebugLog()
{
boost::call_once(&DebugPrintInit, debugPrintInitFlag);
boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);
assert(fileout == nullptr);
assert(vMsgsBeforeOpenLog);
- fs::path pathDebug = GetDataDir() / "debug.log";
+ fs::path pathDebug = GetDebugLogPath();
+
fileout = fsbridge::fopen(pathDebug, "a");
- if (fileout) {
- setbuf(fileout, nullptr); // unbuffered
- // dump buffered messages from before we opened the log
- while (!vMsgsBeforeOpenLog->empty()) {
- FileWriteStr(vMsgsBeforeOpenLog->front(), fileout);
- vMsgsBeforeOpenLog->pop_front();
- }
+ if (!fileout) {
+ return false;
+ }
+
+ setbuf(fileout, nullptr); // unbuffered
+ // dump buffered messages from before we opened the log
+ while (!vMsgsBeforeOpenLog->empty()) {
+ FileWriteStr(vMsgsBeforeOpenLog->front(), fileout);
+ vMsgsBeforeOpenLog->pop_front();
}
delete vMsgsBeforeOpenLog;
vMsgsBeforeOpenLog = nullptr;
+ return true;
}
struct CLogCategoryDesc
@@ -220,6 +229,7 @@ struct CLogCategoryDesc
const CLogCategoryDesc LogCategories[] =
{
{BCLog::NONE, "0"},
+ {BCLog::NONE, "none"},
{BCLog::NET, "net"},
{BCLog::TOR, "tor"},
{BCLog::MEMPOOL, "mempool"},
@@ -354,7 +364,7 @@ int LogPrintStr(const std::string &str)
// reopen the log file, if requested
if (fReopenDebugLog) {
fReopenDebugLog = false;
- fs::path pathDebug = GetDataDir() / "debug.log";
+ fs::path pathDebug = GetDebugLogPath();
if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr)
setbuf(fileout, nullptr); // unbuffered
}
@@ -574,7 +584,10 @@ const fs::path &GetDataDir(bool fNetSpecific)
if (fNetSpecific)
path /= BaseParams().DataDir();
- fs::create_directories(path);
+ if (fs::create_directories(path)) {
+ // This is the first run, create wallets subdirectory too
+ fs::create_directories(path / "wallets");
+ }
return path;
}
@@ -620,6 +633,9 @@ void ArgsManager::ReadConfigFile(const std::string& confPath)
}
// If datadir is changed in .conf file:
ClearDatadirCache();
+ if (!fs::is_directory(GetDataDir(false))) {
+ throw std::runtime_error(strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", "").c_str()));
+ }
}
#ifndef WIN32
@@ -770,7 +786,7 @@ void ShrinkDebugFile()
// Amount of debug.log to save at end when shrinking (must fit in memory)
constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
// Scroll debug.log if it's getting too big
- fs::path pathLog = GetDataDir() / "debug.log";
+ fs::path pathLog = GetDebugLogPath();
FILE* file = fsbridge::fopen(pathLog, "r");
// If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
// trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
@@ -810,6 +826,7 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
void runCommand(const std::string& strCommand)
{
+ if (strCommand.empty()) return;
int nErr = ::system(strCommand.c_str());
if (nErr)
LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
diff --git a/src/util.h b/src/util.h
index 480a80c0a3..3cc4c26817 100644
--- a/src/util.h
+++ b/src/util.h
@@ -11,14 +11,14 @@
#define BITCOIN_UTIL_H
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "compat.h"
-#include "fs.h"
-#include "sync.h"
-#include "tinyformat.h"
-#include "utiltime.h"
+#include <compat.h>
+#include <fs.h>
+#include <sync.h>
+#include <tinyformat.h>
+#include <utiltime.h>
#include <atomic>
#include <exception>
@@ -28,6 +28,7 @@
#include <vector>
#include <boost/signals2/signal.hpp>
+#include <boost/thread/condition_variable.hpp> // for boost::thread_interrupted
// Application startup time (used for uptime calculation)
int64_t GetStartupTime();
@@ -35,6 +36,7 @@ int64_t GetStartupTime();
static const bool DEFAULT_LOGTIMEMICROS = false;
static const bool DEFAULT_LOGIPS = false;
static const bool DEFAULT_LOGTIMESTAMPS = true;
+extern const char * const DEFAULT_DEBUGLOGFILE;
/** Signals for translation. */
class CTranslationInterface
@@ -132,6 +134,10 @@ template<typename T, typename... Args> static inline void MarkUsed(const T& t, c
MarkUsed(args...);
}
+// Be conservative when using LogPrintf/error or other things which
+// unconditionally log to debug.log! It should not be the case that an inbound
+// peer can fill up a users disk with debug.log entries.
+
#ifdef USE_COVERAGE
#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0)
#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0)
@@ -179,7 +185,8 @@ void CreatePidFile(const fs::path &path, pid_t pid);
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
-void OpenDebugLog();
+fs::path GetDebugLogPath();
+bool OpenDebugLog();
void ShrinkDebugFile();
void runCommand(const std::string& strCommand);
@@ -326,4 +333,11 @@ template <typename Callable> void TraceThread(const char* name, Callable func)
std::string CopyrightHolders(const std::string& strPrefix);
+//! Substitute for C++14 std::make_unique.
+template <typename T, typename... Args>
+std::unique_ptr<T> MakeUnique(Args&&... args)
+{
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
#endif // BITCOIN_UTIL_H
diff --git a/src/utilmoneystr.cpp b/src/utilmoneystr.cpp
index 6e6e33184e..656d7c51b7 100644
--- a/src/utilmoneystr.cpp
+++ b/src/utilmoneystr.cpp
@@ -3,11 +3,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "utilmoneystr.h"
+#include <utilmoneystr.h>
-#include "primitives/transaction.h"
-#include "tinyformat.h"
-#include "utilstrencodings.h"
+#include <primitives/transaction.h>
+#include <tinyformat.h>
+#include <utilstrencodings.h>
std::string FormatMoney(const CAmount& n)
{
diff --git a/src/utilmoneystr.h b/src/utilmoneystr.h
index bc885ee167..b1a4499e90 100644
--- a/src/utilmoneystr.h
+++ b/src/utilmoneystr.h
@@ -12,7 +12,7 @@
#include <stdint.h>
#include <string>
-#include "amount.h"
+#include <amount.h>
/* Do not use these functions to represent or parse monetary amounts to or from
* JSON but use AmountFromValue and ValueFromAmount for that.
diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp
index fd233f6757..d90713ed6a 100644
--- a/src/utilstrencodings.cpp
+++ b/src/utilstrencodings.cpp
@@ -3,9 +3,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "utilstrencodings.h"
+#include <utilstrencodings.h>
-#include "tinyformat.h"
+#include <tinyformat.h>
#include <cstdlib>
#include <cstring>
@@ -65,6 +65,19 @@ bool IsHex(const std::string& str)
return (str.size() > 0) && (str.size()%2 == 0);
}
+bool IsHexNumber(const std::string& str)
+{
+ size_t starting_location = 0;
+ if (str.size() > 2 && *str.begin() == '0' && *(str.begin()+1) == 'x') {
+ starting_location = 2;
+ }
+ for (auto c : str.substr(starting_location)) {
+ if (HexDigit(c) < 0) return false;
+ }
+ // Return false for empty string or "0x".
+ return (str.size() > starting_location);
+}
+
std::vector<unsigned char> ParseHex(const char* psz)
{
// convert hex dump to vector
diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h
index 53da60e8f1..af33f0e5f8 100644
--- a/src/utilstrencodings.h
+++ b/src/utilstrencodings.h
@@ -38,7 +38,13 @@ std::string SanitizeString(const std::string& str, int rule = SAFE_CHARS_DEFAULT
std::vector<unsigned char> ParseHex(const char* psz);
std::vector<unsigned char> ParseHex(const std::string& str);
signed char HexDigit(char c);
+/* Returns true if each character in str is a hex character, and has an even
+ * number of hex digits.*/
bool IsHex(const std::string& str);
+/**
+* Return true if the string is a hex number, optionally prefixed with "0x"
+*/
+bool IsHexNumber(const std::string& str);
std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid = nullptr);
std::string DecodeBase64(const std::string& str);
std::string EncodeBase64(const unsigned char* pch, size_t len);
@@ -143,4 +149,28 @@ bool TimingResistantEqual(const T& a, const T& b)
*/
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
+/** Convert from one power-of-2 number base to another. */
+template<int frombits, int tobits, bool pad, typename O, typename I>
+bool ConvertBits(O& out, I it, I end) {
+ size_t acc = 0;
+ size_t bits = 0;
+ constexpr size_t maxv = (1 << tobits) - 1;
+ constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1;
+ while (it != end) {
+ acc = ((acc << frombits) | *it) & max_acc;
+ bits += frombits;
+ while (bits >= tobits) {
+ bits -= tobits;
+ out.push_back((acc >> bits) & maxv);
+ }
+ ++it;
+ }
+ if (pad) {
+ if (bits) out.push_back((acc << (tobits - bits)) & maxv);
+ } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
+ return false;
+ }
+ return true;
+}
+
#endif // BITCOIN_UTILSTRENCODINGS_H
diff --git a/src/utiltime.cpp b/src/utiltime.cpp
index 4cc77dbfeb..ca11cf341f 100644
--- a/src/utiltime.cpp
+++ b/src/utiltime.cpp
@@ -4,10 +4,10 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "utiltime.h"
+#include <utiltime.h>
#include <atomic>
diff --git a/src/validation.cpp b/src/validation.cpp
index 3b9636839d..75c40b22fc 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -3,46 +3,43 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "validation.h"
-
-#include "arith_uint256.h"
-#include "chain.h"
-#include "chainparams.h"
-#include "checkpoints.h"
-#include "checkqueue.h"
-#include "consensus/consensus.h"
-#include "consensus/merkle.h"
-#include "consensus/tx_verify.h"
-#include "consensus/validation.h"
-#include "cuckoocache.h"
-#include "fs.h"
-#include "hash.h"
-#include "init.h"
-#include "policy/fees.h"
-#include "policy/policy.h"
-#include "policy/rbf.h"
-#include "pow.h"
-#include "primitives/block.h"
-#include "primitives/transaction.h"
-#include "random.h"
-#include "reverse_iterator.h"
-#include "script/script.h"
-#include "script/sigcache.h"
-#include "script/standard.h"
-#include "timedata.h"
-#include "tinyformat.h"
-#include "txdb.h"
-#include "txmempool.h"
-#include "ui_interface.h"
-#include "undo.h"
-#include "util.h"
-#include "utilmoneystr.h"
-#include "utilstrencodings.h"
-#include "validationinterface.h"
-#include "versionbits.h"
-#include "warnings.h"
-
-#include <atomic>
+#include <validation.h>
+
+#include <arith_uint256.h>
+#include <chain.h>
+#include <chainparams.h>
+#include <checkpoints.h>
+#include <checkqueue.h>
+#include <consensus/consensus.h>
+#include <consensus/merkle.h>
+#include <consensus/tx_verify.h>
+#include <consensus/validation.h>
+#include <cuckoocache.h>
+#include <hash.h>
+#include <init.h>
+#include <policy/fees.h>
+#include <policy/policy.h>
+#include <policy/rbf.h>
+#include <pow.h>
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <random.h>
+#include <reverse_iterator.h>
+#include <script/script.h>
+#include <script/sigcache.h>
+#include <script/standard.h>
+#include <timedata.h>
+#include <tinyformat.h>
+#include <txdb.h>
+#include <txmempool.h>
+#include <ui_interface.h>
+#include <undo.h>
+#include <util.h>
+#include <utilmoneystr.h>
+#include <utilstrencodings.h>
+#include <validationinterface.h>
+#include <warnings.h>
+
#include <sstream>
#include <boost/algorithm/string/replace.hpp>
@@ -59,17 +56,156 @@
/**
* Global state
*/
+namespace {
+ struct CBlockIndexWorkComparator
+ {
+ bool operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
+ // First sort by most total work, ...
+ if (pa->nChainWork > pb->nChainWork) return false;
+ if (pa->nChainWork < pb->nChainWork) return true;
+
+ // ... then by earliest time received, ...
+ if (pa->nSequenceId < pb->nSequenceId) return false;
+ if (pa->nSequenceId > pb->nSequenceId) return true;
+
+ // Use pointer address as tie breaker (should only happen with blocks
+ // loaded from disk, as those all have id 0).
+ if (pa < pb) return false;
+ if (pa > pb) return true;
+
+ // Identical blocks.
+ return false;
+ }
+ };
+} // anon namespace
+
+enum DisconnectResult
+{
+ DISCONNECT_OK, // All good.
+ DISCONNECT_UNCLEAN, // Rolled back, but UTXO set was inconsistent with block.
+ DISCONNECT_FAILED // Something else went wrong.
+};
+
+class ConnectTrace;
+
+/**
+ * CChainState stores and provides an API to update our local knowledge of the
+ * current best chain and header tree.
+ *
+ * It generally provides access to the current block tree, as well as functions
+ * to provide new data, which it will appropriately validate and incorporate in
+ * its state as necessary.
+ *
+ * Eventually, the API here is targeted at being exposed externally as a
+ * consumable libconsensus library, so any functions added must only call
+ * other class member functions, pure functions in other parts of the consensus
+ * library, callbacks via the validation interface, or read/write-to-disk
+ * functions (eventually this will also be via callbacks).
+ */
+class CChainState {
+private:
+ /**
+ * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
+ * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
+ * missing the data for the block.
+ */
+ std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
+
+ /**
+ * Every received block is assigned a unique and increasing identifier, so we
+ * know which one to give priority in case of a fork.
+ */
+ CCriticalSection cs_nBlockSequenceId;
+ /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */
+ 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;
+
+ /** In order to efficiently track invalidity of headers, we keep the set of
+ * blocks which we tried to connect and found to be invalid here (ie which
+ * were set to BLOCK_FAILED_VALID since the last restart). We can then
+ * walk this set and check if a new header is a descendant of something in
+ * this set, preventing us from having to walk mapBlockIndex when we try
+ * to connect a bad block and fail.
+ *
+ * While this is more complicated than marking everything which descends
+ * from an invalid block as invalid at the time we discover it to be
+ * invalid, doing so would require walking all of mapBlockIndex to find all
+ * descendants. Since this case should be very rare, keeping track of all
+ * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
+ * well.
+ *
+ * Because we already walk mapBlockIndex in height-order at startup, we go
+ * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
+ * instead of putting things in this set.
+ */
+ std::set<CBlockIndex*> g_failed_blocks;
+
+public:
+ CChain chainActive;
+ BlockMap mapBlockIndex;
+ std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
+ CBlockIndex *pindexBestInvalid = nullptr;
+
+ bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree);
+
+ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock);
+
+ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex);
+ bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock);
+
+ // Block (dis)connection on a given view:
+ DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view);
+ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
+ CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false);
+
+ // Block disconnection on our pcoinsTip:
+ bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool);
+
+ // Manual block validity manipulation:
+ bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex);
+ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex);
+ bool ResetBlockFailureFlags(CBlockIndex *pindex);
+
+ bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
+ bool RewindBlockIndex(const CChainParams& params);
+ bool LoadGenesisBlock(const CChainParams& chainparams);
+
+ void PruneBlockIndexCandidates();
+
+ void UnloadBlockIndex();
+
+private:
+ bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace);
+ bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool);
+
+ CBlockIndex* AddToBlockIndex(const CBlockHeader& block);
+ /** Create a new block index entry for a given block hash */
+ CBlockIndex * InsertBlockIndex(const uint256& hash);
+ void CheckBlockIndex(const Consensus::Params& consensusParams);
+
+ void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state);
+ CBlockIndex* FindMostWorkChain();
+ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
+
+
+ bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params);
+} g_chainstate;
+
+
CCriticalSection cs_main;
-BlockMap mapBlockIndex;
-CChain chainActive;
+BlockMap& mapBlockIndex = g_chainstate.mapBlockIndex;
+CChain& chainActive = g_chainstate.chainActive;
CBlockIndex *pindexBestHeader = nullptr;
CWaitableCriticalSection csBestBlock;
CConditionVariable cvBlockChange;
int nScriptCheckThreads = 0;
std::atomic_bool fImporting(false);
-bool fReindex = false;
+std::atomic_bool fReindex(false);
bool fTxIndex = false;
bool fHavePruned = false;
bool fPruneMode = false;
@@ -83,6 +219,7 @@ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT;
uint256 hashAssumeValid;
+arith_uint256 nMinimumChainWork;
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
@@ -90,8 +227,6 @@ CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
CBlockPolicyEstimator feeEstimator;
CTxMemPool mempool(&feeEstimator);
-static void CheckBlockIndex(const Consensus::Params& consensusParams);
-
/** Constant stuff for coinbase transactions we create: */
CScript COINBASE_FLAGS;
@@ -99,40 +234,12 @@ const std::string strMessageMagic = "Bitcoin Signed Message:\n";
// Internal stuff
namespace {
+ CBlockIndex *&pindexBestInvalid = g_chainstate.pindexBestInvalid;
- struct CBlockIndexWorkComparator
- {
- bool operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
- // First sort by most total work, ...
- if (pa->nChainWork > pb->nChainWork) return false;
- if (pa->nChainWork < pb->nChainWork) return true;
-
- // ... then by earliest time received, ...
- if (pa->nSequenceId < pb->nSequenceId) return false;
- if (pa->nSequenceId > pb->nSequenceId) return true;
-
- // Use pointer address as tie breaker (should only happen with blocks
- // loaded from disk, as those all have id 0).
- if (pa < pb) return false;
- if (pa > pb) return true;
-
- // Identical blocks.
- return false;
- }
- };
-
- CBlockIndex *pindexBestInvalid;
-
- /**
- * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
- * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
- * missing the data for the block.
- */
- std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
/** 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.
*/
- std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
+ std::multimap<CBlockIndex*, CBlockIndex*>& mapBlocksUnlinked = g_chainstate.mapBlocksUnlinked;
CCriticalSection cs_LastBlockFile;
std::vector<CBlockFileInfo> vinfoBlockFile;
@@ -143,18 +250,6 @@ namespace {
*/
bool fCheckForPruning = false;
- /**
- * Every received block is assigned a unique and increasing identifier, so we
- * know which one to give priority in case of a fork.
- */
- CCriticalSection cs_nBlockSequenceId;
- /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */
- 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. */
std::set<CBlockIndex*> setDirtyBlockIndex;
@@ -180,9 +275,9 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
return chain.Genesis();
}
-CCoinsViewDB *pcoinsdbview = nullptr;
-CCoinsViewCache *pcoinsTip = nullptr;
-CBlockTreeDB *pblocktree = nullptr;
+std::unique_ptr<CCoinsViewDB> pcoinsdbview;
+std::unique_ptr<CCoinsViewCache> pcoinsTip;
+std::unique_ptr<CBlockTreeDB> pblocktree;
enum FlushStateMode {
FLUSH_STATE_NONE,
@@ -218,7 +313,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags)
// IsFinalTx() with one more than chainActive.Height().
const int nBlockHeight = chainActive.Height() + 1;
- // BIP113 will require that time-locked transactions have nLockTime set to
+ // BIP113 requires that time-locked transactions have nLockTime set to
// less than the median time of the previous block they're contained in.
// When the next block is created its previous block will be the current
// chain tip, so we use that to calculate the median time passed to
@@ -254,6 +349,8 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
AssertLockHeld(mempool.cs);
CBlockIndex* tip = chainActive.Tip();
+ assert(tip != nullptr);
+
CBlockIndex index;
index.pprev = tip;
// CheckSequenceLocks() uses chainActive.Height()+1 to evaluate
@@ -272,7 +369,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
}
else {
// pcoinsTip contains the UTXO set for chainActive.Tip()
- CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
+ CCoinsViewMemPool viewMemPool(pcoinsTip.get(), mempool);
std::vector<int> prevheights;
prevheights.resize(tx.vin.size());
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
@@ -381,7 +478,9 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) {
// ignore validation errors in resurrected transactions
CValidationState stateDummy;
- if (!fAddToMempool || (*it)->IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, *it, false, nullptr, nullptr, true)) {
+ if (!fAddToMempool || (*it)->IsCoinBase() ||
+ !AcceptToMemoryPool(mempool, stateDummy, *it, nullptr /* pfMissingInputs */,
+ nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */)) {
// If the transaction doesn't make it in to the mempool, remove any
// transactions that depend on it (which would now be orphans).
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
@@ -399,7 +498,7 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f
mempool.UpdateTransactionsFromBlock(vHashUpdate);
// We also need to remove any now-immature transactions
- mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
+ mempool.removeForReorg(pcoinsTip.get(), chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
// Re-limit mempool size, in case we added any transactions
LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
}
@@ -440,9 +539,9 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata);
}
-static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree,
+static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
- bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache)
+ bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache)
{
const CTransaction& tx = *ptx;
const uint256 hash = tx.GetHash();
@@ -529,11 +628,10 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
CCoinsView dummy;
CCoinsViewCache view(&dummy);
- CAmount nValueIn = 0;
LockPoints lp;
{
LOCK(pool.cs);
- CCoinsViewMemPool viewMemPool(pcoinsTip, pool);
+ CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
view.SetBackend(viewMemPool);
// do all inputs exist?
@@ -560,8 +658,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// Bring the best block into scope
view.GetBestBlock();
- nValueIn = view.GetValueIn(tx);
-
// we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool
view.SetBackend(dummy);
@@ -572,6 +668,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// CoinsViewCache instead of create its own
if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
+
+ } // end LOCK(pool.cs)
+
+ CAmount nFees = 0;
+ if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) {
+ return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
}
// Check for non-standard pay-to-script-hash in inputs
@@ -584,8 +686,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
- CAmount nValueOut = tx.GetValueOut();
- CAmount nFees = nValueIn-nValueOut;
// nModifiedFees includes any fee deltas from PrioritiseTransaction
CAmount nModifiedFees = nFees;
pool.ApplyDelta(hash, nModifiedFees);
@@ -615,12 +715,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
strprintf("%d", nSigOpsCost));
CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
- if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
+ if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee));
}
// No transactions are allowed below minRelayTxFee except from disconnected blocks
- if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) {
+ if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) {
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met");
}
@@ -852,17 +952,18 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}
pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED);
- // This transaction should only count for fee estimation if it isn't a
- // BIP 125 replacement transaction (may not be widely supported), the
- // node is not behind, and the transaction is not dependent on any other
- // transactions in the mempool.
- bool validForFeeEstimation = !fReplacementTransaction && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx);
+ // This transaction should only count for fee estimation if:
+ // - it isn't a BIP 125 replacement transaction (may not be widely supported)
+ // - it's not being readded during a reorg which bypasses typical mempool fee limits
+ // - the node is not behind
+ // - the transaction is not dependent on any other transactions in the mempool
+ bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx);
// Store transaction in memory
pool.addUnchecked(hash, entry, setAncestors, validForFeeEstimation);
// trim mempool and check if tx was trimmed
- if (!fOverrideMempoolLimit) {
+ if (!bypass_limits) {
LimitMempoolSize(pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
if (!pool.exists(hash))
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full");
@@ -875,12 +976,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}
/** (try to) add transaction to memory pool with a specified acceptance time **/
-static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree,
+static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
- bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
+ bool bypass_limits, const CAmount nAbsurdFee)
{
std::vector<COutPoint> coins_to_uncache;
- bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, coins_to_uncache);
+ bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache);
if (!res) {
for (const COutPoint& hashTx : coins_to_uncache)
pcoinsTip->Uncache(hashTx);
@@ -891,52 +992,59 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
return res;
}
-bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree,
+bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
- bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
+ bool bypass_limits, const CAmount nAbsurdFee)
{
const CChainParams& chainparams = Params();
- return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee);
+ return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, 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)
+/**
+ * Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock.
+ * If blockIndex is provided, the transaction is fetched from the corresponding block.
+ */
+bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus::Params& consensusParams, uint256& hashBlock, bool fAllowSlow, CBlockIndex* blockIndex)
{
- CBlockIndex *pindexSlow = nullptr;
+ CBlockIndex* pindexSlow = blockIndex;
LOCK(cs_main);
- CTransactionRef ptx = mempool.get(hash);
- if (ptx)
- {
- txOut = ptx;
- return true;
- }
+ if (!blockIndex) {
+ CTransactionRef ptx = mempool.get(hash);
+ if (ptx) {
+ txOut = ptx;
+ return true;
+ }
- if (fTxIndex) {
- CDiskTxPos postx;
- if (pblocktree->ReadTxIndex(hash, postx)) {
- CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
- if (file.IsNull())
- return error("%s: OpenBlockFile failed", __func__);
- CBlockHeader header;
- try {
- file >> header;
- fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
- file >> txOut;
- } catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ if (fTxIndex) {
+ CDiskTxPos postx;
+ if (pblocktree->ReadTxIndex(hash, postx)) {
+ CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
+ if (file.IsNull())
+ return error("%s: OpenBlockFile failed", __func__);
+ CBlockHeader header;
+ try {
+ file >> header;
+ fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
+ file >> txOut;
+ } catch (const std::exception& e) {
+ return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ }
+ hashBlock = header.GetHash();
+ if (txOut->GetHash() != hash)
+ return error("%s: txid mismatch", __func__);
+ return true;
}
- hashBlock = header.GetHash();
- if (txOut->GetHash() != hash)
- return error("%s: txid mismatch", __func__);
- return true;
+
+ // transaction not found in index, nothing more can be done
+ return false;
}
- }
- if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
- const Coin& coin = AccessByTxid(*pcoinsTip, hash);
- if (!coin.IsSpent()) pindexSlow = chainActive[coin.nHeight];
+ if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
+ const Coin& coin = AccessByTxid(*pcoinsTip, hash);
+ if (!coin.IsSpent()) pindexSlow = chainActive[coin.nHeight];
+ }
}
if (pindexSlow) {
@@ -1035,8 +1143,6 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
bool IsInitialBlockDownload()
{
- const CChainParams& chainParams = Params();
-
// Once this function has returned false, it must remain false.
static std::atomic<bool> latchToFalse{false};
// Optimization: pre-test latch before taking the lock.
@@ -1050,7 +1156,7 @@ bool IsInitialBlockDownload()
return true;
if (chainActive.Tip() == nullptr)
return true;
- if (chainActive.Tip()->nChainWork < UintToArith256(chainParams.GetConsensus().nMinimumChainWork))
+ if (chainActive.Tip()->nChainWork < nMinimumChainWork)
return true;
if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
return true;
@@ -1169,9 +1275,10 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
CheckForkWarningConditions();
}
-void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
+void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
if (!state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
+ g_failed_blocks.insert(pindex);
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
InvalidChainFound(pindex);
@@ -1202,7 +1309,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
- return VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error);
+ return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *txdata), &error);
}
int GetSpendHeight(const CCoinsViewCache& inputs)
@@ -1243,9 +1350,6 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
{
if (!tx.IsCoinBase())
{
- if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs)))
- return false;
-
if (pvChecks)
pvChecks->reserve(tx.vin.size());
@@ -1284,11 +1388,9 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
// a sanity check that our caching is not introducing consensus
// failures through additional data in, eg, the coins being
// spent being checked as a part of CScriptCheck.
- const CScript& scriptPubKey = coin.out.scriptPubKey;
- const CAmount amount = coin.out.nValue;
// Verify signature
- CScriptCheck check(scriptPubKey, amount, tx, i, flags, cacheSigStore, &txdata);
+ CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata);
if (pvChecks) {
pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back());
@@ -1300,7 +1402,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
// arguments; if so, don't trigger DoS protection to
// avoid splitting the network between upgraded and
// non-upgraded nodes.
- CScriptCheck check2(scriptPubKey, amount, tx, i,
+ CScriptCheck check2(coin.out, tx, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
if (check2())
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
@@ -1356,8 +1458,13 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint
return true;
}
-bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock)
+static bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex *pindex)
{
+ CDiskBlockPos pos = pindex->GetUndoPos();
+ if (pos.IsNull()) {
+ return error("%s: no undo data available", __func__);
+ }
+
// Open history file to read
CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
@@ -1367,7 +1474,7 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin
uint256 hashChecksum;
CHashVerifier<CAutoFile> verifier(&filein); // We need a CHashVerifier as reserializing may lose data
try {
- verifier << hashBlock;
+ verifier << pindex->pprev->GetBlockHash();
verifier >> blockundo;
filein >> hashChecksum;
}
@@ -1402,13 +1509,6 @@ bool AbortNode(CValidationState& state, const std::string& strMessage, const std
} // namespace
-enum DisconnectResult
-{
- DISCONNECT_OK, // All good.
- DISCONNECT_UNCLEAN, // Rolled back, but UTXO set was inconsistent with block.
- DISCONNECT_FAILED // Something else went wrong.
-};
-
/**
* Restore the UTXO in a Coin at a given COutPoint
* @param undo The Coin to be restored.
@@ -1445,17 +1545,12 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
* When FAILED is returned, view is left in an indeterminate state. */
-static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view)
+DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view)
{
bool fClean = true;
CBlockUndo blockUndo;
- CDiskBlockPos pos = pindex->GetUndoPos();
- if (pos.IsNull()) {
- error("DisconnectBlock(): no undo data available");
- return DISCONNECT_FAILED;
- }
- if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash())) {
+ if (!UndoReadFromDisk(blockUndo, pindex)) {
error("DisconnectBlock(): failure reading undo data");
return DISCONNECT_FAILED;
}
@@ -1532,6 +1627,45 @@ void static FlushBlockFile(bool fFinalize = false)
static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
+static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, CValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
+{
+ // Write undo information to disk
+ if (pindex->GetUndoPos().IsNull()) {
+ CDiskBlockPos _pos;
+ if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40))
+ return error("ConnectBlock(): FindUndoPos failed");
+ if (!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->nStatus |= BLOCK_HAVE_UNDO;
+ setDirtyBlockIndex.insert(pindex);
+ }
+
+ return true;
+}
+
+static bool WriteTxIndexDataForBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex)
+{
+ if (!fTxIndex) return true;
+
+ CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
+ std::vector<std::pair<uint256, CDiskTxPos> > vPos;
+ vPos.reserve(block.vtx.size());
+ for (const CTransactionRef& tx : block.vtx)
+ {
+ vPos.push_back(std::make_pair(tx->GetHash(), pos));
+ pos.nTxOffset += ::GetSerializeSize(*tx, SER_DISK, CLIENT_VERSION);
+ }
+
+ if (!pblocktree->WriteTxIndex(vPos)) {
+ return AbortNode(state, "Failed to write transaction index");
+ }
+
+ return true;
+}
+
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
void ThreadScriptCheck() {
@@ -1587,11 +1721,12 @@ static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS];
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) {
AssertLockHeld(cs_main);
- // BIP16 didn't become active until Apr 1 2012
- int64_t nBIP16SwitchTime = 1333238400;
- bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime);
+ unsigned int flags = SCRIPT_VERIFY_NONE;
- unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE;
+ // Start enforcing P2SH (BIP16)
+ if (pindex->nHeight >= consensusparams.BIP16Height) {
+ flags |= SCRIPT_VERIFY_P2SH;
+ }
// Start enforcing the DERSIG (BIP66) rule
if (pindex->nHeight >= consensusparams.BIP66Height) {
@@ -1631,8 +1766,8 @@ static int64_t nBlocksTotal = 0;
/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
* can fail if those validity checks fail (among other reasons). */
-static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
- CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false)
+bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
+ CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck)
{
AssertLockHeld(cs_main);
assert(pindex);
@@ -1642,6 +1777,18 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
int64_t nTimeStart = GetTimeMicros();
// Check it again in case a previous version let a bad block in
+ // NOTE: We don't currently (re-)invoke ContextualCheckBlock() or
+ // ContextualCheckBlockHeader() here. This means that if we add a new
+ // consensus rule that is enforced in one of those two functions, then we
+ // may have let in a block that violates the rule prior to updating the
+ // software, and we would NOT be enforcing the rule here. Fully solving
+ // upgrade from one software version to the next after a consensus rule
+ // change is potentially tricky and issue-specific (see RewindBlockIndex()
+ // for one general approach that was used for BIP 141 deployment).
+ // Also, currently the rule against blocks more than 2 hours in the future
+ // is enforced in ContextualCheckBlockHeader(); we wouldn't want to
+ // re-enforce that rule here (at least until we make it impossible for
+ // GetAdjustedTime() to go backward).
if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck))
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
@@ -1670,7 +1817,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
if (it != mapBlockIndex.end()) {
if (it->second->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
- pindexBestHeader->nChainWork >= UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) {
+ pindexBestHeader->nChainWork >= nMinimumChainWork) {
// This block is a member of the assumed verified chain and an ancestor of the best header.
// The equivalent time check discourages hash power from extorting the network via DOS attack
// into accepting an invalid block through telling users they must manually set assumevalid.
@@ -1710,6 +1857,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
// before the first had been spent. Since those coinbases are sufficiently buried its no longer possible to create further
// duplicate transactions descending from the known pairs either.
// If we're on the known chain at height greater than where BIP34 activated, we can save the db accesses needed for the BIP30 check.
+ assert(pindex->pprev);
CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(chainparams.GetConsensus().BIP34Height);
//Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond.
fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash));
@@ -1745,9 +1893,6 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
CAmount nFees = 0;
int nInputs = 0;
int64_t nSigOpsCost = 0;
- CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
- std::vector<std::pair<uint256, CDiskTxPos> > vPos;
- vPos.reserve(block.vtx.size());
blockundo.vtxundo.reserve(block.vtx.size() - 1);
std::vector<PrecomputedTransactionData> txdata;
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
@@ -1759,9 +1904,15 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
if (!tx.IsCoinBase())
{
- if (!view.HaveInputs(tx))
- return state.DoS(100, error("ConnectBlock(): inputs missing/spent"),
- REJECT_INVALID, "bad-txns-inputs-missingorspent");
+ CAmount txfee = 0;
+ if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee)) {
+ return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
+ }
+ nFees += txfee;
+ if (!MoneyRange(nFees)) {
+ return state.DoS(100, error("%s: accumulated fee in the block out of range.", __func__),
+ REJECT_INVALID, "bad-txns-accumulated-fee-outofrange");
+ }
// Check that transaction is BIP68 final
// BIP68 lock checks (as opposed to nLockTime checks) must
@@ -1789,8 +1940,6 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
txdata.emplace_back(tx);
if (!tx.IsCoinBase())
{
- nFees += view.GetValueIn(tx)-tx.GetValueOut();
-
std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr))
@@ -1804,9 +1953,6 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
blockundo.vtxundo.push_back(CTxUndo());
}
UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
-
- vPos.push_back(std::make_pair(tx.GetHash(), pos));
- pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
}
int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2;
LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal);
@@ -1826,29 +1972,18 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
if (fJustCheck)
return true;
- // Write undo information to disk
- 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))
- return error("ConnectBlock(): FindUndoPos failed");
- 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->nStatus |= BLOCK_HAVE_UNDO;
- }
+ if (!WriteUndoDataForBlock(blockundo, state, pindex, chainparams))
+ return false;
+ if (!pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
setDirtyBlockIndex.insert(pindex);
}
- if (fTxIndex)
- if (!pblocktree->WriteTxIndex(vPos))
- return AbortNode(state, "Failed to write transaction index");
+ if (!WriteTxIndexDataForBlock(block, state, pindex))
+ return false;
+ assert(pindex->phashBlock);
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
@@ -1998,10 +2133,8 @@ static void DoWarning(const std::string& strWarning)
}
}
-/** Update chainActive and related internal data structures. */
-void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
- chainActive.SetTip(pindexNew);
-
+/** Check warning conditions and do some notifications on new chain tip set. */
+void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainParams) {
// New best block
mempool.AddTransactionsUpdated(1);
@@ -2011,7 +2144,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
if (!IsInitialBlockDownload())
{
int nUpgraded = 0;
- const CBlockIndex* pindex = chainActive.Tip();
+ const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(bit);
ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
@@ -2042,10 +2175,10 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
}
}
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__,
- chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nVersion,
- log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx,
- DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
- GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
+ pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
+ log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
+ DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime()),
+ GuessVerificationProgress(chainParams.TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
if (!warningMessages.empty())
LogPrintf(" warning='%s'", boost::algorithm::join(warningMessages, ", "));
LogPrintf("\n");
@@ -2062,7 +2195,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
* disconnectpool (note that the caller is responsible for mempool consistency
* in any case).
*/
-bool static DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool)
+bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool)
{
CBlockIndex *pindexDelete = chainActive.Tip();
assert(pindexDelete);
@@ -2074,7 +2207,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
// Apply the block atomically to the chain state.
int64_t nStart = GetTimeMicros();
{
- CCoinsViewCache view(pcoinsTip);
+ CCoinsViewCache view(pcoinsTip.get());
assert(view.GetBestBlock() == pindexDelete->GetBlockHash());
if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK)
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
@@ -2099,7 +2232,8 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
}
}
- // Update chainActive and related variables.
+ chainActive.SetTip(pindexDelete->pprev);
+
UpdateTip(pindexDelete->pprev, chainparams);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
@@ -2184,7 +2318,7 @@ public:
*
* The block is added to connectTrace if connection succeeds.
*/
-bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool)
+bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool)
{
assert(pindexNew->pprev == chainActive.Tip());
// Read block from disk.
@@ -2204,7 +2338,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
int64_t nTime3;
LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO);
{
- CCoinsViewCache view(pcoinsTip);
+ CCoinsViewCache view(pcoinsTip.get());
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams);
GetMainSignals().BlockChecked(blockConnecting, state);
if (!rv) {
@@ -2228,6 +2362,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
disconnectpool.removeForBlock(blockConnecting.vtx);
// Update chainActive & related variables.
+ chainActive.SetTip(pindexNew);
UpdateTip(pindexNew, chainparams);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
@@ -2242,7 +2377,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
* Return the tip of the chain with the most work in it, that isn't
* known to be invalid (it's however far from certain to be valid).
*/
-static CBlockIndex* FindMostWorkChain() {
+CBlockIndex* CChainState::FindMostWorkChain() {
do {
CBlockIndex *pindexNew = nullptr;
@@ -2297,7 +2432,7 @@ static CBlockIndex* FindMostWorkChain() {
}
/** Delete all entries in setBlockIndexCandidates that are worse than the current tip. */
-static void PruneBlockIndexCandidates() {
+void CChainState::PruneBlockIndexCandidates() {
// Note that we can't delete the current block itself, as we may need to return to it later in case a
// reorganization to a better block fails.
std::set<CBlockIndex*, CBlockIndexWorkComparator>::iterator it = setBlockIndexCandidates.begin();
@@ -2312,7 +2447,7 @@ static void PruneBlockIndexCandidates() {
* Try to make some progress towards making pindexMostWork the active block.
* pblock is either nullptr or a pointer to a CBlock corresponding to pindexMostWork.
*/
-static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
+bool CChainState::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();
@@ -2382,7 +2517,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
// any disconnected transactions back to the mempool.
UpdateMempoolForReorg(disconnectpool, true);
}
- mempool.check(pcoinsTip);
+ mempool.check(pcoinsTip.get());
// Callbacks/notifications for a new best chain.
if (fInvalidFound)
@@ -2419,7 +2554,7 @@ static void NotifyHeaderTip() {
* or an activated best chain. pblock is either nullptr or a pointer to a block
* that is already loaded (to avoid loading it again from disk).
*/
-bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
+bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
// Note that while we're often called here from ProcessNewBlock, this is
// far from a guarantee. Things in the P2P/RPC will often end up calling
// us in the middle of ProcessNewBlock - do not assume pblock is set
@@ -2463,7 +2598,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
assert(trace.pblock && trace.pindex);
- GetMainSignals().BlockConnected(trace.pblock, trace.pindex, *trace.conflictedTxs);
+ GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
}
}
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
@@ -2489,9 +2624,11 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
return true;
}
+bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
+ return g_chainstate.ActivateBestChain(state, chainparams, std::move(pblock));
+}
-
-bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex)
+bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex)
{
{
LOCK(cs_main);
@@ -2517,24 +2654,28 @@ bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIn
}
}
- return ActivateBestChain(state, params);
+ return ActivateBestChain(state, params, std::shared_ptr<const CBlock>());
+}
+bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) {
+ return g_chainstate.PreciousBlock(state, params, pindex);
}
-bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
+bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
{
AssertLockHeld(cs_main);
- // Mark the block itself as invalid.
- pindex->nStatus |= BLOCK_FAILED_VALID;
- setDirtyBlockIndex.insert(pindex);
- setBlockIndexCandidates.erase(pindex);
+ // We first disconnect backwards and then mark the blocks as invalid.
+ // This prevents a case where pruned nodes may fail to invalidateblock
+ // and be left unable to start as they have no tip candidates (as there
+ // are no blocks that meet the "have data and are not invalid per
+ // nStatus" criteria for inclusion in setBlockIndexCandidates).
+
+ bool pindex_was_in_chain = false;
+ CBlockIndex *invalid_walk_tip = chainActive.Tip();
DisconnectedBlockTransactions disconnectpool;
while (chainActive.Contains(pindex)) {
- CBlockIndex *pindexWalk = chainActive.Tip();
- pindexWalk->nStatus |= BLOCK_FAILED_CHILD;
- setDirtyBlockIndex.insert(pindexWalk);
- setBlockIndexCandidates.erase(pindexWalk);
+ pindex_was_in_chain = true;
// ActivateBestChain considers blocks already in chainActive
// unconditionally valid already, so force disconnect away from it.
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
@@ -2545,6 +2686,21 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
}
}
+ // Now mark the blocks we just disconnected as descendants invalid
+ // (note this may not be all descendants).
+ while (pindex_was_in_chain && invalid_walk_tip != pindex) {
+ invalid_walk_tip->nStatus |= BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(invalid_walk_tip);
+ setBlockIndexCandidates.erase(invalid_walk_tip);
+ invalid_walk_tip = invalid_walk_tip->pprev;
+ }
+
+ // Mark the block itself as invalid.
+ pindex->nStatus |= BLOCK_FAILED_VALID;
+ setDirtyBlockIndex.insert(pindex);
+ setBlockIndexCandidates.erase(pindex);
+ g_failed_blocks.insert(pindex);
+
// DisconnectTip will add transactions to disconnectpool; try to add these
// back to the mempool.
UpdateMempoolForReorg(disconnectpool, true);
@@ -2563,8 +2719,11 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev);
return true;
}
+bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) {
+ return g_chainstate.InvalidateBlock(state, chainparams, pindex);
+}
-bool ResetBlockFailureFlags(CBlockIndex *pindex) {
+bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
AssertLockHeld(cs_main);
int nHeight = pindex->nHeight;
@@ -2582,6 +2741,7 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) {
// Reset invalid block marker if it was pointing to one of those.
pindexBestInvalid = nullptr;
}
+ g_failed_blocks.erase(it->second);
}
it++;
}
@@ -2596,8 +2756,11 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) {
}
return true;
}
+bool ResetBlockFailureFlags(CBlockIndex *pindex) {
+ return g_chainstate.ResetBlockFailureFlags(pindex);
+}
-static CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
+CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block)
{
// Check for duplicate
uint256 hash = block.GetHash();
@@ -2607,7 +2770,6 @@ static CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
// Construct new block index object
CBlockIndex* pindexNew = new CBlockIndex(block);
- assert(pindexNew);
// We assign the sequence id to blocks only when the full data is available,
// to avoid miners withholding blocks but broadcasting headers, to get a
// competitive advantage.
@@ -2633,7 +2795,7 @@ static CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
}
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
-static bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
+bool CChainState::ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
pindexNew->nTx = block.vtx.size();
pindexNew->nChainTx = 0;
@@ -2681,7 +2843,7 @@ static bool ReceivedBlockTransactions(const CBlock &block, CValidationState& sta
return true;
}
-static bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
+static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
{
LOCK(cs_LastBlockFile);
@@ -2730,7 +2892,7 @@ static bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned i
}
}
else
- return state.Error("out of disk space");
+ return error("out of disk space");
}
}
@@ -2905,7 +3067,13 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
/** Context-dependent validity checks.
* By "context", we mean only the previous block headers, but not the UTXO
- * set; UTXO-related validity checks are done in ConnectBlock(). */
+ * set; UTXO-related validity checks are done in ConnectBlock().
+ * NOTE: This function is not currently invoked by ConnectBlock(), so we
+ * should consider upgrade issues if we change which consensus rules are
+ * enforced in this function (eg by adding a new consensus rule). See comment
+ * in ConnectBlock().
+ * Note that -reindex-chainstate skips the validation that happens here!
+ */
static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime)
{
assert(pindexPrev != nullptr);
@@ -2945,6 +3113,12 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
return true;
}
+/** NOTE: This function is not currently invoked by ConnectBlock(), so we
+ * should consider upgrade issues if we change which consensus rules are
+ * enforced in this function (eg by adding a new consensus rule). See comment
+ * in ConnectBlock().
+ * Note that -reindex-chainstate skips the validation that happens here!
+ */
static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
{
const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
@@ -3026,7 +3200,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
return true;
}
-static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex)
+bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex)
{
AssertLockHeld(cs_main);
// Check for duplicate
@@ -3058,6 +3232,21 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
+
+ if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) {
+ for (const CBlockIndex* failedit : g_failed_blocks) {
+ if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) {
+ assert(failedit->nStatus & BLOCK_FAILED_VALID);
+ CBlockIndex* invalid_walk = pindexPrev;
+ while (invalid_walk != failedit) {
+ invalid_walk->nStatus |= BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(invalid_walk);
+ invalid_walk = invalid_walk->pprev;
+ }
+ return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
+ }
+ }
+ }
}
if (pindex == nullptr)
pindex = AddToBlockIndex(block);
@@ -3071,13 +3260,15 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
}
// Exposed wrapper for AcceptBlockHeader
-bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
+bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex, CBlockHeader *first_invalid)
{
+ if (first_invalid != nullptr) first_invalid->SetNull();
{
LOCK(cs_main);
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
- if (!AcceptBlockHeader(header, state, chainparams, &pindex)) {
+ if (!g_chainstate.AcceptBlockHeader(header, state, chainparams, &pindex)) {
+ if (first_invalid) *first_invalid = header;
return false;
}
if (ppindex) {
@@ -3090,7 +3281,26 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
}
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
-static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock)
+static CDiskBlockPos SaveBlockToDisk(const CBlock& block, int nHeight, const CChainParams& chainparams, const CDiskBlockPos* dbp) {
+ unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
+ CDiskBlockPos blockPos;
+ if (dbp != nullptr)
+ blockPos = *dbp;
+ if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != nullptr)) {
+ error("%s: FindBlockPos failed", __func__);
+ return CDiskBlockPos();
+ }
+ if (dbp == nullptr) {
+ if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) {
+ AbortNode("Failed to write block");
+ return CDiskBlockPos();
+ }
+ }
+ return blockPos;
+}
+
+/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
+bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock)
{
const CBlock& block = *pblock;
@@ -3107,7 +3317,7 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
// process an unrequested block if it's new and has enough work to
// advance our tip, and isn't too many blocks ahead.
bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA;
- bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true);
+ bool fHasMoreOrSameWork = (chainActive.Tip() ? pindex->nChainWork >= chainActive.Tip()->nChainWork : true);
// Blocks that are too out-of-order needlessly limit the effectiveness of
// pruning, because pruning will not delete block files that contain any
// blocks which are too close in height to the tip. Apply this test
@@ -3124,9 +3334,15 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
// and unrequested blocks.
if (fAlreadyHave) return true;
if (!fRequested) { // If we didn't ask for it:
- if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned
- if (!fHasMoreWork) return true; // Don't process less-work chains
- if (fTooFarAhead) return true; // Block height is too high
+ if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned
+ if (!fHasMoreOrSameWork) return true; // Don't process less-work chains
+ if (fTooFarAhead) return true; // Block height is too high
+
+ // Protect against DoS attacks from low-work chains.
+ // If our tip is behind, a peer could try to send us
+ // low-work blocks on a fake chain that we would never
+ // request; don't process these.
+ if (pindex->nChainWork < nMinimumChainWork) return true;
}
if (fNewBlock) *fNewBlock = true;
@@ -3144,19 +3360,13 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
if (!IsInitialBlockDownload() && chainActive.Tip() == pindex->pprev)
GetMainSignals().NewPoWValidBlock(pindex, pblock);
- int nHeight = pindex->nHeight;
-
// Write block to history file
try {
- unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
- CDiskBlockPos blockPos;
- if (dbp != nullptr)
- blockPos = *dbp;
- if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != nullptr))
- return error("AcceptBlock(): FindBlockPos failed");
- if (dbp == nullptr)
- if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart()))
- AbortNode(state, "Failed to write block");
+ CDiskBlockPos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp);
+ if (blockPos.IsNull()) {
+ state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
+ return false;
+ }
if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
return error("AcceptBlock(): ReceivedBlockTransactions failed");
} catch (const std::runtime_error& e) {
@@ -3166,6 +3376,8 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
if (fCheckForPruning)
FlushStateToDisk(chainparams, state, FLUSH_STATE_NONE); // we just allocated more disk space for block files
+ CheckBlockIndex(chainparams.GetConsensus());
+
return true;
}
@@ -3183,19 +3395,18 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
if (ret) {
// Store to disk
- ret = AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
+ ret = g_chainstate.AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
}
- CheckBlockIndex(chainparams.GetConsensus());
if (!ret) {
GetMainSignals().BlockChecked(*pblock, state);
- return error("%s: AcceptBlock FAILED", __func__);
+ return error("%s: AcceptBlock FAILED (%s)", __func__, state.GetDebugMessage());
}
}
NotifyHeaderTip();
CValidationState state; // Only used to report errors, not invalidity - ignore it
- if (!ActivateBestChain(state, chainparams, pblock))
+ if (!g_chainstate.ActivateBestChain(state, chainparams, pblock))
return error("%s: ActivateBestChain failed", __func__);
return true;
@@ -3205,7 +3416,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
{
AssertLockHeld(cs_main);
assert(pindexPrev && pindexPrev == chainActive.Tip());
- CCoinsViewCache viewNew(pcoinsTip);
+ CCoinsViewCache viewNew(pcoinsTip.get());
CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1;
@@ -3217,7 +3428,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev))
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state));
- if (!ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
+ if (!g_chainstate.ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
return false;
assert(state.IsValid());
@@ -3229,8 +3440,10 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
*/
/* Calculate the amount of disk space the block & undo files currently use */
-static uint64_t CalculateCurrentUsage()
+uint64_t CalculateCurrentUsage()
{
+ LOCK(cs_LastBlockFile);
+
uint64_t retval = 0;
for (const CBlockFileInfo &file : vinfoBlockFile) {
retval += file.nSize + file.nUndoSize;
@@ -3241,8 +3454,10 @@ static uint64_t CalculateCurrentUsage()
/* Prune a block file (modify associated database entries)*/
void PruneOneBlockFile(const int fileNumber)
{
- for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); ++it) {
- CBlockIndex* pindex = it->second;
+ LOCK(cs_LastBlockFile);
+
+ for (const auto& entry : mapBlockIndex) {
+ CBlockIndex* pindex = entry.second;
if (pindex->nFile == fileNumber) {
pindex->nStatus &= ~BLOCK_HAVE_DATA;
pindex->nStatus &= ~BLOCK_HAVE_UNDO;
@@ -3390,7 +3605,7 @@ static FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fRe
return nullptr;
fs::path path = GetBlockPosFilename(pos, prefix);
fs::create_directories(path.parent_path());
- FILE* file = fsbridge::fopen(path, "rb+");
+ FILE* file = fsbridge::fopen(path, fReadOnly ? "rb": "rb+");
if (!file && !fReadOnly)
file = fsbridge::fopen(path, "wb+");
if (!file) {
@@ -3421,7 +3636,7 @@ fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix)
return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile);
}
-CBlockIndex * InsertBlockIndex(uint256 hash)
+CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash)
{
if (hash.IsNull())
return nullptr;
@@ -3433,17 +3648,15 @@ CBlockIndex * InsertBlockIndex(uint256 hash)
// Create new
CBlockIndex* pindexNew = new CBlockIndex();
- if (!pindexNew)
- throw std::runtime_error(std::string(__func__) + ": new CBlockIndex failed");
mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
return pindexNew;
}
-bool static LoadBlockIndexDB(const CChainParams& chainparams)
+bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree)
{
- if (!pblocktree->LoadBlockIndexGuts(chainparams.GetConsensus(), InsertBlockIndex))
+ if (!blocktree.LoadBlockIndexGuts(consensus_params, [this](const uint256& hash){ return this->InsertBlockIndex(hash); }))
return false;
boost::this_thread::interruption_point();
@@ -3476,6 +3689,10 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
pindex->nChainTx = pindex->nTx;
}
}
+ if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) {
+ pindex->nStatus |= BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(pindex);
+ }
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr))
setBlockIndexCandidates.insert(pindex);
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
@@ -3486,6 +3703,14 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
pindexBestHeader = pindex;
}
+ return true;
+}
+
+bool static LoadBlockIndexDB(const CChainParams& chainparams)
+{
+ if (!g_chainstate.LoadBlockIndex(chainparams.GetConsensus(), *pblocktree))
+ return false;
+
// Load block file info
pblocktree->ReadLastBlockFile(nLastBlockFile);
vinfoBlockFile.resize(nLastBlockFile + 1);
@@ -3529,7 +3754,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
// Check whether we need to continue reindexing
bool fReindexing = false;
pblocktree->ReadReindexing(fReindexing);
- fReindex |= fReindexing;
+ if(fReindexing) fReindex = true;
// Check whether we have a transaction index
pblocktree->ReadFlag("txindex", fTxIndex);
@@ -3558,7 +3783,7 @@ bool LoadChainTip(const CChainParams& chainparams)
return false;
chainActive.SetTip(it->second);
- PruneBlockIndexCandidates();
+ g_chainstate.PruneBlockIndexCandidates();
LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n",
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(),
@@ -3569,12 +3794,12 @@ bool LoadChainTip(const CChainParams& chainparams)
CVerifyDB::CVerifyDB()
{
- uiInterface.ShowProgress(_("Verifying blocks..."), 0);
+ uiInterface.ShowProgress(_("Verifying blocks..."), 0, false);
}
CVerifyDB::~CVerifyDB()
{
- uiInterface.ShowProgress("", 100);
+ uiInterface.ShowProgress("", 100, false);
}
bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
@@ -3604,7 +3829,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
LogPrintf("[%d%%]...", percentageDone);
reportDone = percentageDone/10;
}
- uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone);
+ uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false);
if (pindex->nHeight < chainActive.Height()-nCheckDepth)
break;
if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
@@ -3623,16 +3848,16 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {
CBlockUndo undo;
- CDiskBlockPos pos = pindex->GetUndoPos();
- if (!pos.IsNull()) {
- if (!UndoReadFromDisk(undo, pos, pindex->pprev->GetBlockHash()))
+ if (!pindex->GetUndoPos().IsNull()) {
+ if (!UndoReadFromDisk(undo, pindex)) {
return error("VerifyDB(): *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
+ }
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
- DisconnectResult res = DisconnectBlock(block, pindex, coins);
+ DisconnectResult res = g_chainstate.DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
@@ -3655,12 +3880,12 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
CBlockIndex *pindex = pindexState;
while (pindex != chainActive.Tip()) {
boost::this_thread::interruption_point();
- uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))));
+ uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))), false);
pindex = chainActive.Next(pindex);
CBlock block;
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
- if (!ConnectBlock(block, state, pindex, coins, chainparams))
+ if (!g_chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
}
@@ -3672,7 +3897,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
}
/** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */
-static bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params)
+bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params)
{
// TODO: merge with ConnectBlock
CBlock block;
@@ -3692,7 +3917,7 @@ static bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs,
return true;
}
-bool ReplayBlocks(const CChainParams& params, CCoinsView* view)
+bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
{
LOCK(cs_main);
@@ -3702,7 +3927,7 @@ bool ReplayBlocks(const CChainParams& params, CCoinsView* view)
if (hashHeads.empty()) return true; // We're already in a consistent state.
if (hashHeads.size() != 2) return error("ReplayBlocks(): unknown inconsistent state");
- uiInterface.ShowProgress(_("Replaying blocks..."), 0);
+ uiInterface.ShowProgress(_("Replaying blocks..."), 0, false);
LogPrintf("Replaying blocks\n");
const CBlockIndex* pindexOld = nullptr; // Old tip during the interrupted flush.
@@ -3753,11 +3978,15 @@ bool ReplayBlocks(const CChainParams& params, CCoinsView* view)
cache.SetBestBlock(pindexNew->GetBlockHash());
cache.Flush();
- uiInterface.ShowProgress("", 100);
+ uiInterface.ShowProgress("", 100, false);
return true;
}
-bool RewindBlockIndex(const CChainParams& params)
+bool ReplayBlocks(const CChainParams& params, CCoinsView* view) {
+ return g_chainstate.ReplayBlocks(params, view);
+}
+
+bool CChainState::RewindBlockIndex(const CChainParams& params)
{
LOCK(cs_main);
@@ -3794,8 +4023,8 @@ bool RewindBlockIndex(const CChainParams& params)
// Reduce validity flag and have-data flags.
// We do this after actual disconnecting, otherwise we'll end up writing the lack of data
// to disk before writing the chainstate, resulting in a failure to continue if interrupted.
- for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) {
- CBlockIndex* pindexIter = it->second;
+ for (const auto& entry : mapBlockIndex) {
+ CBlockIndex* pindexIter = entry.second;
// Note: If we encounter an insufficiently validated block that
// is on chainActive, it must be because we are a pruning node, and
@@ -3838,10 +4067,21 @@ bool RewindBlockIndex(const CChainParams& params)
PruneBlockIndexCandidates();
CheckBlockIndex(params.GetConsensus());
+ }
+
+ return true;
+}
+bool RewindBlockIndex(const CChainParams& params) {
+ if (!g_chainstate.RewindBlockIndex(params)) {
+ return false;
+ }
+
+ if (chainActive.Tip() != nullptr) {
// FlushStateToDisk can possibly read chainActive. Be conservative
// and skip it here, we're about to -reindex-chainstate anyway, so
// it'll get called a bunch real soon.
+ CValidationState state;
if (!FlushStateToDisk(params, state, FLUSH_STATE_ALWAYS)) {
return false;
}
@@ -3850,13 +4090,18 @@ bool RewindBlockIndex(const CChainParams& params)
return true;
}
+void CChainState::UnloadBlockIndex() {
+ nBlockSequenceId = 1;
+ g_failed_blocks.clear();
+ setBlockIndexCandidates.clear();
+}
+
// 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);
- setBlockIndexCandidates.clear();
chainActive.SetTip(nullptr);
pindexBestInvalid = nullptr;
pindexBestHeader = nullptr;
@@ -3864,7 +4109,6 @@ void UnloadBlockIndex()
mapBlocksUnlinked.clear();
vinfoBlockFile.clear();
nLastBlockFile = 0;
- nBlockSequenceId = 1;
setDirtyBlockIndex.clear();
setDirtyFileInfo.clear();
versionbitscache.Clear();
@@ -3877,6 +4121,8 @@ void UnloadBlockIndex()
}
mapBlockIndex.clear();
fHavePruned = false;
+
+ g_chainstate.UnloadBlockIndex();
}
bool LoadBlockIndex(const CChainParams& chainparams)
@@ -3904,7 +4150,7 @@ bool LoadBlockIndex(const CChainParams& chainparams)
return true;
}
-bool LoadGenesisBlock(const CChainParams& chainparams)
+bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
{
LOCK(cs_main);
@@ -3917,15 +4163,11 @@ bool LoadGenesisBlock(const CChainParams& chainparams)
try {
CBlock &block = const_cast<CBlock&>(chainparams.GenesisBlock());
- // Start new block file
- unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
- CDiskBlockPos blockPos;
- CValidationState state;
- if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.GetBlockTime()))
- return error("%s: FindBlockPos failed", __func__);
- if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart()))
+ CDiskBlockPos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr);
+ if (blockPos.IsNull())
return error("%s: writing genesis block to disk failed", __func__);
CBlockIndex *pindex = AddToBlockIndex(block);
+ CValidationState state;
if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
return error("%s: genesis block not accepted", __func__);
} catch (const std::runtime_error& e) {
@@ -3935,6 +4177,11 @@ bool LoadGenesisBlock(const CChainParams& chainparams)
return true;
}
+bool LoadGenesisBlock(const CChainParams& chainparams)
+{
+ return g_chainstate.LoadGenesisBlock(chainparams);
+}
+
bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp)
{
// Map of disk positions for blocks with unknown parent (only used for reindex)
@@ -3995,7 +4242,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) {
LOCK(cs_main);
CValidationState state;
- if (AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr))
+ if (g_chainstate.AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr))
nLoaded++;
if (state.IsError())
break;
@@ -4029,7 +4276,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
head.ToString());
LOCK(cs_main);
CValidationState dummy;
- if (AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
+ if (g_chainstate.AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
{
nLoaded++;
queue.push_back(pblockrecursive->GetHash());
@@ -4052,7 +4299,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
return nLoaded > 0;
}
-void static CheckBlockIndex(const Consensus::Params& consensusParams)
+void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
{
if (!fCheckBlockIndex) {
return;
@@ -4070,8 +4317,8 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams)
// Build forward-pointing map of the entire block tree.
std::multimap<CBlockIndex*,CBlockIndex*> forward;
- for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) {
- forward.insert(std::make_pair(it->second->pprev, it->second));
+ for (auto& entry : mapBlockIndex) {
+ forward.insert(std::make_pair(entry.second->pprev, entry.second));
}
assert(forward.size() == mapBlockIndex.size());
@@ -4243,6 +4490,8 @@ std::string CBlockFileInfo::ToString() const
CBlockFileInfo* GetBlockFileInfo(size_t n)
{
+ LOCK(cs_LastBlockFile);
+
return &vinfoBlockFile.at(n);
}
@@ -4278,8 +4527,9 @@ bool LoadMempool(void)
}
int64_t count = 0;
- int64_t skipped = 0;
+ int64_t expired = 0;
int64_t failed = 0;
+ int64_t already_there = 0;
int64_t nNow = GetTime();
try {
@@ -4305,14 +4555,23 @@ bool LoadMempool(void)
CValidationState state;
if (nTime + nExpiryTimeout > nNow) {
LOCK(cs_main);
- AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, true, nullptr, nTime, nullptr, false, 0);
+ AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, nullptr /* pfMissingInputs */, nTime,
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */);
if (state.IsValid()) {
++count;
} else {
- ++failed;
+ // mempool may contain the transaction already, e.g. from
+ // wallet(s) having loaded it while we were processing
+ // mempool transactions; consider these as valid, instead of
+ // failed, but mark them as 'already there'
+ if (mempool.exists(tx->GetHash())) {
+ ++already_there;
+ } else {
+ ++failed;
+ }
}
} else {
- ++skipped;
+ ++expired;
}
if (ShutdownRequested())
return false;
@@ -4328,11 +4587,11 @@ bool LoadMempool(void)
return false;
}
- LogPrintf("Imported mempool transactions from disk: %i successes, %i failed, %i expired\n", count, failed, skipped);
+ LogPrintf("Imported mempool transactions from disk: %i succeeded, %i failed, %i expired, %i already there\n", count, failed, expired, already_there);
return true;
}
-void DumpMempool(void)
+bool DumpMempool(void)
{
int64_t start = GetTimeMicros();
@@ -4352,7 +4611,7 @@ void DumpMempool(void)
try {
FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb");
if (!filestr) {
- return;
+ return false;
}
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
@@ -4376,11 +4635,13 @@ void DumpMempool(void)
LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*MICRO, (last-mid)*MICRO);
} catch (const std::exception& e) {
LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
+ return false;
}
+ return true;
}
//! Guess how far we are in the verification process at the given block index
-double GuessVerificationProgress(const ChainTxData& data, CBlockIndex *pindex) {
+double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pindex) {
if (pindex == nullptr)
return 0.0;
diff --git a/src/validation.h b/src/validation.h
index d0f6cdc135..4c8e041af0 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -7,17 +7,17 @@
#define BITCOIN_VALIDATION_H
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
-#include "amount.h"
-#include "coins.h"
-#include "fs.h"
-#include "protocol.h" // For CMessageHeader::MessageStartChars
-#include "policy/feerate.h"
-#include "script/script_error.h"
-#include "sync.h"
-#include "versionbits.h"
+#include <amount.h>
+#include <coins.h>
+#include <fs.h>
+#include <protocol.h> // For CMessageHeader::MessageStartChars
+#include <policy/feerate.h>
+#include <script/script_error.h>
+#include <sync.h>
+#include <versionbits.h>
#include <algorithm>
#include <exception>
@@ -45,9 +45,9 @@ struct ChainTxData;
struct PrecomputedTransactionData;
struct LockPoints;
-/** Default for DEFAULT_WHITELISTRELAY. */
+/** Default for -whitelistrelay. */
static const bool DEFAULT_WHITELISTRELAY = true;
-/** Default for DEFAULT_WHITELISTFORCERELAY. */
+/** Default for -whitelistforcerelay. */
static const bool DEFAULT_WHITELISTFORCERELAY = true;
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
@@ -94,8 +94,8 @@ static const int MAX_CMPCTBLOCK_DEPTH = 5;
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
- * harder). We'll probably want to make this a per-peer adaptive value at some point. */
+ * degree of disordering of blocks on disk (which make reindexing and pruning harder). We'll probably
+ * want to make this a per-peer adaptive value at some point. */
static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024;
/** Time to wait (in seconds) between writing blocks/block index to disk. */
static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
@@ -159,15 +159,14 @@ extern CCriticalSection cs_main;
extern CBlockPolicyEstimator feeEstimator;
extern CTxMemPool mempool;
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
-extern BlockMap mapBlockIndex;
+extern BlockMap& mapBlockIndex;
extern uint64_t nLastBlockTx;
-extern uint64_t nLastBlockSize;
extern uint64_t nLastBlockWeight;
extern const std::string strMessageMagic;
extern CWaitableCriticalSection csBestBlock;
extern CConditionVariable cvBlockChange;
extern std::atomic_bool fImporting;
-extern bool fReindex;
+extern std::atomic_bool fReindex;
extern int nScriptCheckThreads;
extern bool fTxIndex;
extern bool fIsBareMultisigStd;
@@ -186,6 +185,9 @@ extern bool fEnableReplacement;
/** Block hash whose ancestors we will assume to have valid scripts without checking them. */
extern uint256 hashAssumeValid;
+/** Minimum work we will assume exists on some valid chain. */
+extern arith_uint256 nMinimumChainWork;
+
/** Best header we've seen so far (used for getheaders queries' starting points). */
extern CBlockIndex *pindexBestHeader;
@@ -201,6 +203,8 @@ extern bool fPruneMode;
extern uint64_t nPruneTarget;
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */
static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
+/** Minimum blocks required to signal NODE_NETWORK_LIMITED */
+static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288;
static const signed int DEFAULT_CHECKBLOCKS = 6;
static const unsigned int DEFAULT_CHECKLEVEL = 3;
@@ -245,8 +249,9 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
* @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
+ * @param[out] first_invalid First header that fails validation, if one exists
*/
-bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=nullptr);
+bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=nullptr, CBlockHeader *first_invalid=nullptr);
/** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
@@ -270,13 +275,16 @@ void ThreadScriptCheck();
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
bool IsInitialBlockDownload();
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
-bool GetTransaction(const uint256 &hash, CTransactionRef &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, CBlockIndex* blockIndex = nullptr);
/** Find the best known block, and make it the tip of the block chain */
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);
/** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */
-double GuessVerificationProgress(const ChainTxData& data, CBlockIndex* pindex);
+double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex* pindex);
+
+/** Calculate the amount of disk space the block & undo files currently use */
+uint64_t CalculateCurrentUsage();
/**
* Mark one block file as pruned.
@@ -288,8 +296,6 @@ void PruneOneBlockFile(const int fileNumber);
*/
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
-/** Create a new block index entry for a given block hash */
-CBlockIndex * InsertBlockIndex(uint256 hash);
/** Flush all state, indexes and buffers to disk. */
void FlushStateToDisk();
/** Prune block files and flush state to disk. */
@@ -299,9 +305,9 @@ void PruneBlockFilesManual(int nManualPruneHeight);
/** (try to) add transaction to memory pool
* plTxnReplaced will be appended to with all transactions replaced from mempool **/
-bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree,
- bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced = nullptr,
- bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0);
+bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
+ bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
+ bool bypass_limits, const CAmount nAbsurdFee);
/** Convert CValidationState to a human-readable message for logging */
std::string FormatStateMessage(const CValidationState &state);
@@ -355,8 +361,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = null
class CScriptCheck
{
private:
- CScript scriptPubKey;
- CAmount amount;
+ CTxOut m_tx_out;
const CTransaction *ptxTo;
unsigned int nIn;
unsigned int nFlags;
@@ -365,17 +370,15 @@ private:
PrecomputedTransactionData *txdata;
public:
- CScriptCheck(): amount(0), ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
- CScriptCheck(const CScript& scriptPubKeyIn, const CAmount amountIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) :
- scriptPubKey(scriptPubKeyIn), amount(amountIn),
- ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { }
+ CScriptCheck(): ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
+ CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) :
+ m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { }
bool operator()();
void swap(CScriptCheck &check) {
- scriptPubKey.swap(check.scriptPubKey);
std::swap(ptxTo, check.ptxTo);
- std::swap(amount, check.amount);
+ std::swap(m_tx_out, check.m_tx_out);
std::swap(nIn, check.nIn);
std::swap(nFlags, check.nFlags);
std::swap(cacheStore, check.cacheStore);
@@ -438,16 +441,16 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
bool ResetBlockFailureFlags(CBlockIndex *pindex);
/** The currently-connected chain of blocks (protected by cs_main). */
-extern CChain chainActive;
+extern CChain& chainActive;
/** Global variable that points to the coins database (protected by cs_main) */
-extern CCoinsViewDB *pcoinsdbview;
+extern std::unique_ptr<CCoinsViewDB> pcoinsdbview;
/** Global variable that points to the active CCoinsView (protected by cs_main) */
-extern CCoinsViewCache *pcoinsTip;
+extern std::unique_ptr<CCoinsViewCache> pcoinsTip;
/** Global variable that points to the active block tree (protected by cs_main) */
-extern CBlockTreeDB *pblocktree;
+extern std::unique_ptr<CBlockTreeDB> pblocktree;
/**
* Return the spend height, which is one more than the inputs.GetBestBlock().
@@ -475,7 +478,7 @@ static const unsigned int REJECT_HIGHFEE = 0x100;
CBlockFileInfo* GetBlockFileInfo(size_t n);
/** Dump the mempool to disk. */
-void DumpMempool();
+bool DumpMempool();
/** Load the mempool from disk. */
bool LoadMempool();
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index be5029dec3..abbd8cc4d2 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -3,11 +3,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "validationinterface.h"
-#include "init.h"
-#include "scheduler.h"
-#include "sync.h"
-#include "util.h"
+#include <validationinterface.h>
+
+#include <init.h>
+#include <primitives/block.h>
+#include <scheduler.h>
+#include <sync.h>
+#include <txmempool.h>
+#include <util.h>
#include <list>
#include <atomic>
@@ -19,6 +22,7 @@ struct MainSignalsInstance {
boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef>&)> BlockConnected;
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
+ boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool;
boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
boost::signals2::signal<void (const uint256 &)> Inventory;
boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
@@ -45,7 +49,17 @@ void CMainSignals::UnregisterBackgroundSignalScheduler() {
}
void CMainSignals::FlushBackgroundCallbacks() {
- m_internals->m_schedulerClient.EmptyQueue();
+ if (m_internals) {
+ m_internals->m_schedulerClient.EmptyQueue();
+ }
+}
+
+void CMainSignals::RegisterWithMempoolSignals(CTxMemPool& pool) {
+ pool.NotifyEntryRemoved.connect(boost::bind(&CMainSignals::MempoolEntryRemoved, this, _1, _2));
+}
+
+void CMainSignals::UnregisterWithMempoolSignals(CTxMemPool& pool) {
+ pool.NotifyEntryRemoved.disconnect(boost::bind(&CMainSignals::MempoolEntryRemoved, this, _1, _2));
}
CMainSignals& GetMainSignals()
@@ -58,6 +72,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.m_internals->TransactionAddedToMempool.connect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
g_signals.m_internals->BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
g_signals.m_internals->BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
+ g_signals.m_internals->TransactionRemovedFromMempool.connect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1));
g_signals.m_internals->SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.m_internals->Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.m_internals->Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
@@ -73,11 +88,15 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.m_internals->TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
g_signals.m_internals->BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
g_signals.m_internals->BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
+ g_signals.m_internals->TransactionRemovedFromMempool.disconnect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1));
g_signals.m_internals->UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
g_signals.m_internals->NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
}
void UnregisterAllValidationInterfaces() {
+ if (!g_signals.m_internals) {
+ return;
+ }
g_signals.m_internals->BlockChecked.disconnect_all_slots();
g_signals.m_internals->Broadcast.disconnect_all_slots();
g_signals.m_internals->Inventory.disconnect_all_slots();
@@ -85,32 +104,57 @@ void UnregisterAllValidationInterfaces() {
g_signals.m_internals->TransactionAddedToMempool.disconnect_all_slots();
g_signals.m_internals->BlockConnected.disconnect_all_slots();
g_signals.m_internals->BlockDisconnected.disconnect_all_slots();
+ g_signals.m_internals->TransactionRemovedFromMempool.disconnect_all_slots();
g_signals.m_internals->UpdatedBlockTip.disconnect_all_slots();
g_signals.m_internals->NewPoWValidBlock.disconnect_all_slots();
}
+void CallFunctionInValidationInterfaceQueue(std::function<void ()> func) {
+ g_signals.m_internals->m_schedulerClient.AddToProcessQueue(std::move(func));
+}
+
+void CMainSignals::MempoolEntryRemoved(CTransactionRef ptx, MemPoolRemovalReason reason) {
+ if (reason != MemPoolRemovalReason::BLOCK && reason != MemPoolRemovalReason::CONFLICT) {
+ m_internals->m_schedulerClient.AddToProcessQueue([ptx, this] {
+ m_internals->TransactionRemovedFromMempool(ptx);
+ });
+ }
+}
+
void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
- m_internals->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload);
+ m_internals->m_schedulerClient.AddToProcessQueue([pindexNew, pindexFork, fInitialDownload, this] {
+ m_internals->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload);
+ });
}
void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) {
- m_internals->TransactionAddedToMempool(ptx);
+ m_internals->m_schedulerClient.AddToProcessQueue([ptx, this] {
+ m_internals->TransactionAddedToMempool(ptx);
+ });
}
-void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) {
- m_internals->BlockConnected(pblock, pindex, vtxConflicted);
+void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>>& pvtxConflicted) {
+ m_internals->m_schedulerClient.AddToProcessQueue([pblock, pindex, pvtxConflicted, this] {
+ m_internals->BlockConnected(pblock, pindex, *pvtxConflicted);
+ });
}
void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) {
- m_internals->BlockDisconnected(pblock);
+ m_internals->m_schedulerClient.AddToProcessQueue([pblock, this] {
+ m_internals->BlockDisconnected(pblock);
+ });
}
void CMainSignals::SetBestChain(const CBlockLocator &locator) {
- m_internals->SetBestChain(locator);
+ m_internals->m_schedulerClient.AddToProcessQueue([locator, this] {
+ m_internals->SetBestChain(locator);
+ });
}
void CMainSignals::Inventory(const uint256 &hash) {
- m_internals->Inventory(hash);
+ m_internals->m_schedulerClient.AddToProcessQueue([hash, this] {
+ m_internals->Inventory(hash);
+ });
}
void CMainSignals::Broadcast(int64_t nBestBlockTime, CConnman* connman) {
diff --git a/src/validationinterface.h b/src/validationinterface.h
index d6da2bc1fd..7b5d138414 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -6,9 +6,10 @@
#ifndef BITCOIN_VALIDATIONINTERFACE_H
#define BITCOIN_VALIDATIONINTERFACE_H
-#include <memory>
+#include <primitives/transaction.h> // CTransaction(Ref)
-#include "primitives/transaction.h" // CTransaction(Ref)
+#include <functional>
+#include <memory>
class CBlock;
class CBlockIndex;
@@ -20,6 +21,8 @@ class CValidationInterface;
class CValidationState;
class uint256;
class CScheduler;
+class CTxMemPool;
+enum class MemPoolRemovalReason;
// These functions dispatch to one or all registered wallets
@@ -29,23 +32,66 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn);
void UnregisterValidationInterface(CValidationInterface* pwalletIn);
/** Unregister all wallets from core */
void UnregisterAllValidationInterfaces();
+/**
+ * Pushes a function to callback onto the notification queue, guaranteeing any
+ * callbacks generated prior to now are finished when the function is called.
+ *
+ * Be very careful blocking on func to be called if any locks are held -
+ * validation interface clients may not be able to make progress as they often
+ * wait for things like cs_main, so blocking until func is called with cs_main
+ * will result in a deadlock (that DEBUG_LOCKORDER will miss).
+ */
+void CallFunctionInValidationInterfaceQueue(std::function<void ()> func);
class CValidationInterface {
protected:
- /** Notifies listeners of updated block chain tip */
+ /**
+ * Notifies listeners of updated block chain tip
+ *
+ * Called on a background thread.
+ */
virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
- /** Notifies listeners of a transaction having been added to mempool. */
+ /**
+ * Notifies listeners of a transaction having been added to mempool.
+ *
+ * Called on a background thread.
+ */
virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {}
/**
+ * Notifies listeners of a transaction leaving mempool.
+ *
+ * This only fires for transactions which leave mempool because of expiry,
+ * size limiting, reorg (changes in lock times/coinbase maturity), or
+ * replacement. This does not include any transactions which are included
+ * in BlockConnectedDisconnected either in block->vtx or in txnConflicted.
+ *
+ * Called on a background thread.
+ */
+ virtual void TransactionRemovedFromMempool(const CTransactionRef &ptx) {}
+ /**
* Notifies listeners of a block being connected.
* Provides a vector of transactions evicted from the mempool as a result.
+ *
+ * Called on a background thread.
*/
virtual void BlockConnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex, const std::vector<CTransactionRef> &txnConflicted) {}
- /** Notifies listeners of a block being disconnected */
+ /**
+ * Notifies listeners of a block being disconnected
+ *
+ * Called on a background thread.
+ */
virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block) {}
- /** Notifies listeners of the new active block chain on-disk. */
+ /**
+ * Notifies listeners of the new active block chain on-disk.
+ *
+ * Called on a background thread.
+ */
virtual void SetBestChain(const CBlockLocator &locator) {}
- /** Notifies listeners about an inventory item being seen on the network. */
+ /**
+ * Notifies listeners about an inventory item being seen on the network.
+ *
+ * Called on a background thread.
+ */
virtual void Inventory(const uint256 &hash) {}
/** Tells listeners to broadcast their data. */
virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
@@ -73,6 +119,9 @@ private:
friend void ::RegisterValidationInterface(CValidationInterface*);
friend void ::UnregisterValidationInterface(CValidationInterface*);
friend void ::UnregisterAllValidationInterfaces();
+ friend void ::CallFunctionInValidationInterfaceQueue(std::function<void ()> func);
+
+ void MempoolEntryRemoved(CTransactionRef tx, MemPoolRemovalReason reason);
public:
/** Register a CScheduler to give callbacks which should run in the background (may only be called once) */
@@ -82,9 +131,14 @@ public:
/** Call any remaining callbacks on the calling thread */
void FlushBackgroundCallbacks();
+ /** Register with mempool to call TransactionRemovedFromMempool callbacks */
+ void RegisterWithMempoolSignals(CTxMemPool& pool);
+ /** Unregister with mempool */
+ void UnregisterWithMempoolSignals(CTxMemPool& pool);
+
void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
void TransactionAddedToMempool(const CTransactionRef &);
- void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef> &);
+ void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &);
void BlockDisconnected(const std::shared_ptr<const CBlock> &);
void SetBestChain(const CBlockLocator &);
void Inventory(const uint256 &);
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index 64ae939672..3bd00ccb23 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "versionbits.h"
-#include "consensus/params.h"
+#include <versionbits.h>
+#include <consensus/params.h>
const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = {
{
@@ -27,6 +27,11 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
int64_t nTimeStart = BeginTime(params);
int64_t nTimeTimeout = EndTime(params);
+ // Check if this deployment is always active.
+ if (nTimeStart == Consensus::BIP9Deployment::ALWAYS_ACTIVE) {
+ return THRESHOLD_ACTIVE;
+ }
+
// 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.
if (pindexPrev != nullptr) {
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
@@ -136,6 +141,11 @@ BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockI
int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
{
+ int64_t start_time = BeginTime(params);
+ if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE) {
+ return 0;
+ }
+
const ThresholdState initialState = GetStateFor(pindexPrev, params, cache);
// BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment."
diff --git a/src/versionbits.h b/src/versionbits.h
index 71dc0c8500..0e111ed7a1 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_CONSENSUS_VERSIONBITS
#define BITCOIN_CONSENSUS_VERSIONBITS
-#include "chain.h"
+#include <chain.h>
#include <map>
/** What block version to use for new blocks (pre versionbits) */
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index fc0e7c519e..15fd105779 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -5,10 +5,10 @@
#ifndef BITCOIN_WALLET_COINCONTROL_H
#define BITCOIN_WALLET_COINCONTROL_H
-#include "policy/feerate.h"
-#include "policy/fees.h"
-#include "primitives/transaction.h"
-#include "wallet/wallet.h"
+#include <policy/feerate.h>
+#include <policy/fees.h>
+#include <primitives/transaction.h>
+#include <wallet/wallet.h>
#include <boost/optional.hpp>
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index dcce88cedc..4cd7db048b 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -2,13 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "crypter.h"
+#include <wallet/crypter.h>
-#include "crypto/aes.h"
-#include "crypto/sha512.h"
-#include "script/script.h"
-#include "script/standard.h"
-#include "util.h"
+#include <crypto/aes.h>
+#include <crypto/sha512.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <util.h>
#include <string>
#include <vector>
@@ -27,8 +27,7 @@ int CCrypter::BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, cons
CSHA512 di;
di.Write((const unsigned char*)strKeyData.c_str(), strKeyData.size());
- if(chSalt.size())
- di.Write(&chSalt[0], chSalt.size());
+ di.Write(chSalt.data(), chSalt.size());
di.Finalize(buf);
for(int i = 0; i != count - 1; i++)
@@ -82,7 +81,7 @@ bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned
vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE);
AES256CBCEncrypt enc(vchKey.data(), vchIV.data(), true);
- size_t nLen = enc.Encrypt(&vchPlaintext[0], vchPlaintext.size(), &vchCiphertext[0]);
+ size_t nLen = enc.Encrypt(&vchPlaintext[0], vchPlaintext.size(), vchCiphertext.data());
if(nLen < vchPlaintext.size())
return false;
vchCiphertext.resize(nLen);
@@ -101,7 +100,7 @@ bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingM
vchPlaintext.resize(nLen);
AES256CBCDecrypt dec(vchKey.data(), vchIV.data(), true);
- nLen = dec.Decrypt(&vchCiphertext[0], vchCiphertext.size(), &vchPlaintext[0]);
+ nLen = dec.Decrypt(vchCiphertext.data(), vchCiphertext.size(), &vchPlaintext[0]);
if(nLen == 0)
return false;
vchPlaintext.resize(nLen);
@@ -113,7 +112,7 @@ static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMateri
{
CCrypter cKeyCrypter;
std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
- memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE);
+ memcpy(chIV.data(), &nIV, WALLET_CRYPTO_IV_SIZE);
if(!cKeyCrypter.SetKey(vMasterKey, chIV))
return false;
return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext);
@@ -123,7 +122,7 @@ static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<u
{
CCrypter cKeyCrypter;
std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
- memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE);
+ memcpy(chIV.data(), &nIV, WALLET_CRYPTO_IV_SIZE);
if(!cKeyCrypter.SetKey(vMasterKey, chIV))
return false;
return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext));
@@ -153,6 +152,15 @@ bool CCryptoKeyStore::SetCrypted()
return true;
}
+bool CCryptoKeyStore::IsLocked() const
+{
+ if (!IsCrypted()) {
+ return false;
+ }
+ LOCK(cs_KeyStore);
+ return vMasterKey.empty();
+}
+
bool CCryptoKeyStore::Lock()
{
if (!SetCrypted())
@@ -207,21 +215,23 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
{
- {
- LOCK(cs_KeyStore);
- if (!IsCrypted())
- return CBasicKeyStore::AddKeyPubKey(key, pubkey);
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ return CBasicKeyStore::AddKeyPubKey(key, pubkey);
+ }
- if (IsLocked())
- return false;
+ if (IsLocked()) {
+ return false;
+ }
- std::vector<unsigned char> vchCryptedSecret;
- CKeyingMaterial vchSecret(key.begin(), key.end());
- if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret))
- return false;
+ std::vector<unsigned char> vchCryptedSecret;
+ CKeyingMaterial vchSecret(key.begin(), key.end());
+ if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) {
+ return false;
+ }
- if (!AddCryptedKey(pubkey, vchCryptedSecret))
- return false;
+ if (!AddCryptedKey(pubkey, vchCryptedSecret)) {
+ return false;
}
return true;
}
@@ -229,72 +239,88 @@ bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
- {
- LOCK(cs_KeyStore);
- if (!SetCrypted())
- return false;
-
- mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
+ LOCK(cs_KeyStore);
+ if (!SetCrypted()) {
+ return false;
}
+
+ mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
return true;
}
+bool CCryptoKeyStore::HaveKey(const CKeyID &address) const
+{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ return CBasicKeyStore::HaveKey(address);
+ }
+ return mapCryptedKeys.count(address) > 0;
+}
+
bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const
{
- {
- LOCK(cs_KeyStore);
- if (!IsCrypted())
- return CBasicKeyStore::GetKey(address, keyOut);
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ return CBasicKeyStore::GetKey(address, keyOut);
+ }
- CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
- if (mi != mapCryptedKeys.end())
- {
- const CPubKey &vchPubKey = (*mi).second.first;
- const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
- return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut);
- }
+ CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
+ if (mi != mapCryptedKeys.end())
+ {
+ const CPubKey &vchPubKey = (*mi).second.first;
+ const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
+ return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut);
}
return false;
}
bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted())
+ return CBasicKeyStore::GetPubKey(address, vchPubKeyOut);
+
+ CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
+ if (mi != mapCryptedKeys.end())
{
- LOCK(cs_KeyStore);
- if (!IsCrypted())
- return CBasicKeyStore::GetPubKey(address, vchPubKeyOut);
+ vchPubKeyOut = (*mi).second.first;
+ return true;
+ }
+ // Check for watch-only pubkeys
+ return CBasicKeyStore::GetPubKey(address, vchPubKeyOut);
+}
- CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
- if (mi != mapCryptedKeys.end())
- {
- vchPubKeyOut = (*mi).second.first;
- return true;
- }
- // Check for watch-only pubkeys
- return CBasicKeyStore::GetPubKey(address, vchPubKeyOut);
+std::set<CKeyID> CCryptoKeyStore::GetKeys() const
+{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ return CBasicKeyStore::GetKeys();
}
+ std::set<CKeyID> set_address;
+ for (const auto& mi : mapCryptedKeys) {
+ set_address.insert(mi.first);
+ }
+ return set_address;
}
bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
{
+ LOCK(cs_KeyStore);
+ if (!mapCryptedKeys.empty() || IsCrypted())
+ return false;
+
+ fUseCrypto = true;
+ for (KeyMap::value_type& mKey : mapKeys)
{
- LOCK(cs_KeyStore);
- if (!mapCryptedKeys.empty() || IsCrypted())
+ const CKey &key = mKey.second;
+ CPubKey vchPubKey = key.GetPubKey();
+ CKeyingMaterial vchSecret(key.begin(), key.end());
+ std::vector<unsigned char> vchCryptedSecret;
+ if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret))
+ return false;
+ if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
return false;
-
- fUseCrypto = true;
- for (KeyMap::value_type& mKey : mapKeys)
- {
- const CKey &key = mKey.second;
- CPubKey vchPubKey = key.GetPubKey();
- CKeyingMaterial vchSecret(key.begin(), key.end());
- std::vector<unsigned char> vchCryptedSecret;
- if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret))
- return false;
- if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
- return false;
- }
- mapKeys.clear();
}
+ mapKeys.clear();
return true;
}
diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h
index f1e8a25650..7b0936ba0d 100644
--- a/src/wallet/crypter.h
+++ b/src/wallet/crypter.h
@@ -5,9 +5,11 @@
#ifndef BITCOIN_WALLET_CRYPTER_H
#define BITCOIN_WALLET_CRYPTER_H
-#include "keystore.h"
-#include "serialize.h"
-#include "support/allocators/secure.h"
+#include <keystore.h>
+#include <serialize.h>
+#include <support/allocators/secure.h>
+
+#include <atomic>
const unsigned int WALLET_CRYPTO_KEY_SIZE = 32;
const unsigned int WALLET_CRYPTO_SALT_SIZE = 8;
@@ -16,13 +18,13 @@ const unsigned int WALLET_CRYPTO_IV_SIZE = 16;
/**
* Private key encryption is done based on a CMasterKey,
* which holds a salt and random encryption key.
- *
+ *
* CMasterKeys are encrypted using AES-256-CBC using a key
* derived using derivation method nDerivationMethod
* (0 == EVP_sha512()) and derivation iterations nDeriveIterations.
* vchOtherDerivationParameters is provided for alternative algorithms
* which may require more parameters (such as scrypt).
- *
+ *
* Wallet Private Keys are then encrypted using AES-256-CBC
* with the double-sha256 of the public key as the IV, and the
* master key's key as the encryption key (see keystore.[ch]).
@@ -118,7 +120,7 @@ private:
//! if fUseCrypto is true, mapKeys must be empty
//! if fUseCrypto is false, vMasterKey must be empty
- bool fUseCrypto;
+ std::atomic<bool> fUseCrypto;
//! keeps track of whether Unlock has run a thorough check before
bool fDecryptionThoroughlyChecked;
@@ -137,54 +139,16 @@ public:
{
}
- bool IsCrypted() const
- {
- return fUseCrypto;
- }
-
- bool IsLocked() const
- {
- if (!IsCrypted())
- return false;
- bool result;
- {
- LOCK(cs_KeyStore);
- result = vMasterKey.empty();
- }
- return result;
- }
-
+ bool IsCrypted() const { return fUseCrypto; }
+ bool IsLocked() const;
bool Lock();
virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
- bool HaveKey(const CKeyID &address) const override
- {
- {
- LOCK(cs_KeyStore);
- if (!IsCrypted())
- return CBasicKeyStore::HaveKey(address);
- return mapCryptedKeys.count(address) > 0;
- }
- return false;
- }
+ bool HaveKey(const CKeyID &address) const override;
bool GetKey(const CKeyID &address, CKey& keyOut) const override;
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
- void GetKeys(std::set<CKeyID> &setAddress) const override
- {
- if (!IsCrypted())
- {
- CBasicKeyStore::GetKeys(setAddress);
- return;
- }
- setAddress.clear();
- CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
- while (mi != mapCryptedKeys.end())
- {
- setAddress.insert((*mi).first);
- mi++;
- }
- }
+ std::set<CKeyID> GetKeys() const override;
/**
* Wallet status (encrypted, locked) changed.
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index d2fe4866fa..d4cd30dfac 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -3,14 +3,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "db.h"
+#include <wallet/db.h>
-#include "addrman.h"
-#include "fs.h"
-#include "hash.h"
-#include "protocol.h"
-#include "util.h"
-#include "utilstrencodings.h"
+#include <addrman.h>
+#include <hash.h>
+#include <protocol.h>
+#include <util.h>
+#include <utilstrencodings.h>
+#include <wallet/walletutil.h>
#include <stdint.h>
@@ -20,6 +20,40 @@
#include <boost/thread.hpp>
+namespace {
+//! Make sure database has a unique fileid within the environment. If it
+//! doesn't, throw an error. BDB caches do not work properly when more than one
+//! open database has the same fileid (values written to one database may show
+//! up in reads to other databases).
+//!
+//! BerkeleyDB generates unique fileids by default
+//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
+//! so bitcoin should never create different databases with the same fileid, but
+//! this error can be triggered if users manually copy database files.
+void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db)
+{
+ if (env.IsMock()) return;
+
+ u_int8_t fileid[DB_FILE_ID_LEN];
+ int ret = db.get_mpf()->get_fileid(fileid);
+ if (ret != 0) {
+ throw std::runtime_error(strprintf("CDB: Can't open database %s (get_fileid failed with %d)", filename, ret));
+ }
+
+ for (const auto& item : env.mapDb) {
+ u_int8_t item_fileid[DB_FILE_ID_LEN];
+ if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 &&
+ memcmp(fileid, item_fileid, sizeof(fileid)) == 0) {
+ const char* item_filename = nullptr;
+ item.second->get_dbname(&item_filename, nullptr);
+ throw std::runtime_error(strprintf("CDB: Can't open database %s (duplicates fileid %s from %s)", filename,
+ HexStr(std::begin(item_fileid), std::end(item_fileid)),
+ item_filename ? item_filename : "(unknown database)"));
+ }
+ }
+}
+} // namespace
+
//
// CDB
//
@@ -41,13 +75,12 @@ void CDBEnv::EnvShutdown()
void CDBEnv::Reset()
{
- delete dbenv;
- dbenv = new DbEnv(DB_CXX_NO_EXCEPTIONS);
+ dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
fDbEnvInit = false;
fMockDb = false;
}
-CDBEnv::CDBEnv() : dbenv(nullptr)
+CDBEnv::CDBEnv()
{
Reset();
}
@@ -55,8 +88,6 @@ CDBEnv::CDBEnv() : dbenv(nullptr)
CDBEnv::~CDBEnv()
{
EnvShutdown();
- delete dbenv;
- dbenv = nullptr;
}
void CDBEnv::Close()
@@ -148,7 +179,7 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type
LOCK(cs_db);
assert(mapFileUseCount.count(strFile) == 0);
- Db db(dbenv, 0);
+ Db db(dbenv.get(), 0);
int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
if (result == 0)
return VERIFY_OK;
@@ -191,7 +222,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
}
LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
- std::unique_ptr<Db> pdbCopy(new Db(bitdb.dbenv, 0));
+ std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(bitdb.dbenv.get(), 0);
int ret = pdbCopy->open(nullptr, // Txn pointer
filename.c_str(), // Filename
"main", // Logical db name
@@ -226,7 +257,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
return fSuccess;
}
-bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr)
+bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr)
{
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
LogPrintf("Using wallet %s\n", walletFile);
@@ -234,15 +265,15 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataD
// Wallet file must be a plain filename without a directory
if (walletFile != fs::basename(walletFile) + fs::extension(walletFile))
{
- errorStr = strprintf(_("Wallet %s resides outside data directory %s"), walletFile, dataDir.string());
+ errorStr = strprintf(_("Wallet %s resides outside wallet directory %s"), walletFile, walletDir.string());
return false;
}
- if (!bitdb.Open(dataDir))
+ if (!bitdb.Open(walletDir))
{
// try moving the database env out of the way
- fs::path pathDatabase = dataDir / "database";
- fs::path pathDatabaseBak = dataDir / strprintf("database.%d.bak", GetTime());
+ fs::path pathDatabase = walletDir / "database";
+ fs::path pathDatabaseBak = walletDir / strprintf("database.%d.bak", GetTime());
try {
fs::rename(pathDatabase, pathDatabaseBak);
LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string());
@@ -251,18 +282,18 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataD
}
// try again
- if (!bitdb.Open(dataDir)) {
+ if (!bitdb.Open(walletDir)) {
// if it still fails, it probably means we can't even create the database env
- errorStr = strprintf(_("Error initializing wallet database environment %s!"), GetDataDir());
+ errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
return false;
}
}
return true;
}
-bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc)
+bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc)
{
- if (fs::exists(dataDir / walletFile))
+ if (fs::exists(walletDir / walletFile))
{
std::string backup_filename;
CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc, backup_filename);
@@ -272,7 +303,7 @@ bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& data
" Original %s saved as %s in %s; if"
" your balance or transactions are incorrect you should"
" restore from a backup."),
- walletFile, backup_filename, dataDir);
+ walletFile, backup_filename, walletDir);
}
if (r == CDBEnv::RECOVER_FAIL)
{
@@ -300,7 +331,7 @@ bool CDBEnv::Salvage(const std::string& strFile, bool fAggressive, std::vector<C
std::stringstream strDump;
- Db db(dbenv, 0);
+ Db db(dbenv.get(), 0);
int result = db.verify(strFile.c_str(), nullptr, &strDump, flags);
if (result == DB_VERIFY_BAD) {
LogPrintf("CDBEnv::Salvage: Database salvage found errors, all data may not be recoverable.\n");
@@ -376,38 +407,37 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb
{
LOCK(env->cs_db);
- if (!env->Open(GetDataDir()))
+ if (!env->Open(GetWalletDir()))
throw std::runtime_error("CDB: Failed to open database environment.");
- strFile = strFilename;
- ++env->mapFileUseCount[strFile];
- pdb = env->mapDb[strFile];
+ pdb = env->mapDb[strFilename];
if (pdb == nullptr) {
int ret;
- pdb = new Db(env->dbenv, 0);
+ std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0);
bool fMockDb = env->IsMock();
if (fMockDb) {
- DbMpoolFile* mpf = pdb->get_mpf();
+ DbMpoolFile* mpf = pdb_temp->get_mpf();
ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
- if (ret != 0)
- throw std::runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFile));
+ if (ret != 0) {
+ throw std::runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFilename));
+ }
}
- ret = pdb->open(nullptr, // Txn pointer
- fMockDb ? nullptr : strFile.c_str(), // Filename
- fMockDb ? strFile.c_str() : "main", // Logical db name
- DB_BTREE, // Database type
- nFlags, // Flags
+ ret = pdb_temp->open(nullptr, // Txn pointer
+ fMockDb ? nullptr : strFilename.c_str(), // Filename
+ fMockDb ? strFilename.c_str() : "main", // Logical db name
+ DB_BTREE, // Database type
+ nFlags, // Flags
0);
if (ret != 0) {
- delete pdb;
- pdb = nullptr;
- --env->mapFileUseCount[strFile];
- strFile = "";
throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename));
}
+ CheckUniqueFileid(*env, strFilename, *pdb_temp);
+
+ pdb = pdb_temp.release();
+ env->mapDb[strFilename] = pdb;
if (fCreate && !Exists(std::string("version"))) {
bool fTmp = fReadOnly;
@@ -415,9 +445,9 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb
WriteVersion(CLIENT_VERSION);
fReadOnly = fTmp;
}
-
- env->mapDb[strFile] = pdb;
}
+ ++env->mapFileUseCount[strFilename];
+ strFile = strFilename;
}
}
@@ -492,7 +522,7 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip)
std::string strFileRes = strFile + ".rewrite";
{ // surround usage of db with extra {}
CDB db(dbw, "r");
- Db* pdbCopy = new Db(env->dbenv, 0);
+ std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
int ret = pdbCopy->open(nullptr, // Txn pointer
strFileRes.c_str(), // Filename
@@ -541,13 +571,12 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip)
} else {
pdbCopy->close(0);
}
- delete pdbCopy;
}
if (fSuccess) {
- Db dbA(env->dbenv, 0);
+ Db dbA(env->dbenv.get(), 0);
if (dbA.remove(strFile.c_str(), nullptr, 0))
fSuccess = false;
- Db dbB(env->dbenv, 0);
+ Db dbB(env->dbenv.get(), 0);
if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0))
fSuccess = false;
}
@@ -558,7 +587,6 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip)
}
MilliSleep(100);
}
- return false;
}
@@ -667,12 +695,17 @@ bool CWalletDBWrapper::Backup(const std::string& strDest)
env->mapFileUseCount.erase(strFile);
// Copy wallet file
- fs::path pathSrc = GetDataDir() / strFile;
+ fs::path pathSrc = GetWalletDir() / strFile;
fs::path pathDest(strDest);
if (fs::is_directory(pathDest))
pathDest /= strFile;
try {
+ if (fs::equivalent(pathSrc, pathDest)) {
+ LogPrintf("cannot backup to wallet source file %s\n", pathDest.string());
+ return false;
+ }
+
fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
LogPrintf("copied %s to %s\n", strFile, pathDest.string());
return true;
@@ -684,7 +717,6 @@ bool CWalletDBWrapper::Backup(const std::string& strDest)
}
MilliSleep(100);
}
- return false;
}
void CWalletDBWrapper::Flush(bool shutdown)
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 6f3cfe9557..ed2ee65cac 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -6,12 +6,12 @@
#ifndef BITCOIN_WALLET_DB_H
#define BITCOIN_WALLET_DB_H
-#include "clientversion.h"
-#include "fs.h"
-#include "serialize.h"
-#include "streams.h"
-#include "sync.h"
-#include "version.h"
+#include <clientversion.h>
+#include <fs.h>
+#include <serialize.h>
+#include <streams.h>
+#include <sync.h>
+#include <version.h>
#include <atomic>
#include <map>
@@ -36,7 +36,7 @@ private:
public:
mutable CCriticalSection cs_db;
- DbEnv *dbenv;
+ std::unique_ptr<DbEnv> dbenv;
std::map<std::string, int> mapFileUseCount;
std::map<std::string, Db*> mapDb;
@@ -156,6 +156,9 @@ public:
explicit CDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
~CDB() { Close(); }
+ CDB(const CDB&) = delete;
+ CDB& operator=(const CDB&) = delete;
+
void Flush();
void Close();
static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
@@ -164,13 +167,9 @@ public:
ideal to be called periodically */
static bool PeriodicFlush(CWalletDBWrapper& dbw);
/* verifies the database environment */
- static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
+ static bool VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc);
-
-private:
- CDB(const CDB&);
- void operator=(const CDB&);
+ static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc);
public:
template <typename K, typename T>
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 6abd060714..9bfcab54a5 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -2,19 +2,19 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "consensus/validation.h"
-#include "wallet/coincontrol.h"
-#include "wallet/feebumper.h"
-#include "wallet/fees.h"
-#include "wallet/wallet.h"
-#include "policy/fees.h"
-#include "policy/policy.h"
-#include "policy/rbf.h"
-#include "validation.h" //for mempool access
-#include "txmempool.h"
-#include "utilmoneystr.h"
-#include "util.h"
-#include "net.h"
+#include <consensus/validation.h>
+#include <wallet/coincontrol.h>
+#include <wallet/feebumper.h>
+#include <wallet/fees.h>
+#include <wallet/wallet.h>
+#include <policy/fees.h>
+#include <policy/policy.h>
+#include <policy/rbf.h>
+#include <validation.h> //for mempool access
+#include <txmempool.h>
+#include <utilmoneystr.h>
+#include <util.h>
+#include <net.h>
// Calculate the size of the transaction assuming all signatures are max size
// Use DummySignatureCreator, which inserts 72 byte signatures everywhere.
@@ -23,7 +23,7 @@
// calculation, but we should be able to refactor after priority is removed).
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
// be IsAllFromMe).
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWallet)
+static int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet)
{
CMutableTransaction txNew(tx);
std::vector<CInputCoin> vCoins;
@@ -31,11 +31,11 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWal
// IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
// wallet, with a valid index into the vout array.
for (auto& input : tx.vin) {
- const auto mi = pWallet->mapWallet.find(input.prevout.hash);
- assert(mi != pWallet->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
+ const auto mi = wallet->mapWallet.find(input.prevout.hash);
+ assert(mi != wallet->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
vCoins.emplace_back(CInputCoin(&(mi->second), input.prevout.n));
}
- if (!pWallet->DummySignTx(txNew, vCoins)) {
+ if (!wallet->DummySignTx(txNew, vCoins)) {
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
// implies that we can sign for every input.
return -1;
@@ -43,103 +43,102 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWal
return GetVirtualTransactionSize(txNew);
}
-bool CFeeBumper::preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx) {
- if (pWallet->HasWalletSpend(wtx.GetHash())) {
- vErrors.push_back("Transaction has descendants in the wallet");
- currentResult = BumpFeeResult::INVALID_PARAMETER;
- return false;
+//! Check whether transaction has descendant in wallet or mempool, or has been
+//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
+static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors)
+{
+ if (wallet->HasWalletSpend(wtx.GetHash())) {
+ errors.push_back("Transaction has descendants in the wallet");
+ return feebumper::Result::INVALID_PARAMETER;
}
{
LOCK(mempool.cs);
auto it_mp = mempool.mapTx.find(wtx.GetHash());
if (it_mp != mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1) {
- vErrors.push_back("Transaction has descendants in the mempool");
- currentResult = BumpFeeResult::INVALID_PARAMETER;
- return false;
+ errors.push_back("Transaction has descendants in the mempool");
+ return feebumper::Result::INVALID_PARAMETER;
}
}
if (wtx.GetDepthInMainChain() != 0) {
- vErrors.push_back("Transaction has been mined, or is conflicted with a mined transaction");
- currentResult = BumpFeeResult::WALLET_ERROR;
- return false;
+ errors.push_back("Transaction has been mined, or is conflicted with a mined transaction");
+ return feebumper::Result::WALLET_ERROR;
}
- return true;
+ return feebumper::Result::OK;
}
-CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoinControl& coin_control, CAmount totalFee)
- :
- txid(std::move(txidIn)),
- nOldFee(0),
- nNewFee(0)
+namespace feebumper {
+
+bool TransactionCanBeBumped(CWallet* wallet, const uint256& txid)
{
- vErrors.clear();
- bumpedTxid.SetNull();
- AssertLockHeld(pWallet->cs_wallet);
- auto it = pWallet->mapWallet.find(txid);
- if (it == pWallet->mapWallet.end()) {
- vErrors.push_back("Invalid or non-wallet transaction id");
- currentResult = BumpFeeResult::INVALID_ADDRESS_OR_KEY;
- return;
+ LOCK2(cs_main, wallet->cs_wallet);
+ const CWalletTx* wtx = wallet->GetWalletTx(txid);
+ return wtx && SignalsOptInRBF(*wtx->tx) && !wtx->mapValue.count("replaced_by_txid");
+}
+
+Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors,
+ CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
+{
+ LOCK2(cs_main, wallet->cs_wallet);
+ errors.clear();
+ auto it = wallet->mapWallet.find(txid);
+ if (it == wallet->mapWallet.end()) {
+ errors.push_back("Invalid or non-wallet transaction id");
+ return Result::INVALID_ADDRESS_OR_KEY;
}
const CWalletTx& wtx = it->second;
- if (!preconditionChecks(pWallet, wtx)) {
- return;
+ Result result = PreconditionChecks(wallet, wtx, errors);
+ if (result != Result::OK) {
+ return result;
}
- if (!SignalsOptInRBF(wtx)) {
- vErrors.push_back("Transaction is not BIP 125 replaceable");
- currentResult = BumpFeeResult::WALLET_ERROR;
- return;
+ if (!SignalsOptInRBF(*wtx.tx)) {
+ errors.push_back("Transaction is not BIP 125 replaceable");
+ return Result::WALLET_ERROR;
}
if (wtx.mapValue.count("replaced_by_txid")) {
- vErrors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", txid.ToString(), wtx.mapValue.at("replaced_by_txid")));
- currentResult = BumpFeeResult::WALLET_ERROR;
- return;
+ errors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", txid.ToString(), wtx.mapValue.at("replaced_by_txid")));
+ return Result::WALLET_ERROR;
}
// check that original tx consists entirely of our inputs
// if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
- if (!pWallet->IsAllFromMe(wtx, ISMINE_SPENDABLE)) {
- vErrors.push_back("Transaction contains inputs that don't belong to this wallet");
- currentResult = BumpFeeResult::WALLET_ERROR;
- return;
+ if (!wallet->IsAllFromMe(*wtx.tx, ISMINE_SPENDABLE)) {
+ errors.push_back("Transaction contains inputs that don't belong to this wallet");
+ return Result::WALLET_ERROR;
}
// figure out which output was change
// if there was no change output or multiple change outputs, fail
int nOutput = -1;
for (size_t i = 0; i < wtx.tx->vout.size(); ++i) {
- if (pWallet->IsChange(wtx.tx->vout[i])) {
+ if (wallet->IsChange(wtx.tx->vout[i])) {
if (nOutput != -1) {
- vErrors.push_back("Transaction has multiple change outputs");
- currentResult = BumpFeeResult::WALLET_ERROR;
- return;
+ errors.push_back("Transaction has multiple change outputs");
+ return Result::WALLET_ERROR;
}
nOutput = i;
}
}
if (nOutput == -1) {
- vErrors.push_back("Transaction does not have a change output");
- currentResult = BumpFeeResult::WALLET_ERROR;
- return;
+ errors.push_back("Transaction does not have a change output");
+ return Result::WALLET_ERROR;
}
// Calculate the expected size of the new transaction.
int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
- const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx, pWallet);
+ const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx, wallet);
if (maxNewTxSize < 0) {
- vErrors.push_back("Transaction contains inputs that cannot be signed");
- currentResult = BumpFeeResult::INVALID_ADDRESS_OR_KEY;
- return;
+ errors.push_back("Transaction contains inputs that cannot be signed");
+ return Result::INVALID_ADDRESS_OR_KEY;
}
// calculate the old fee and fee-rate
- nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
- CFeeRate nOldFeeRate(nOldFee, txSize);
+ old_fee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
+ CFeeRate nOldFeeRate(old_fee, txSize);
CFeeRate nNewFeeRate;
// The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
// future proof against changes to network wide policy for incremental relay
@@ -149,26 +148,24 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin
walletIncrementalRelayFee = ::incrementalRelayFee;
}
- if (totalFee > 0) {
+ if (total_fee > 0) {
CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize);
- if (totalFee < minTotalFee) {
- vErrors.push_back(strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)",
+ if (total_fee < minTotalFee) {
+ errors.push_back(strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)",
FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize))));
- currentResult = BumpFeeResult::INVALID_PARAMETER;
- return;
+ return Result::INVALID_PARAMETER;
}
CAmount requiredFee = GetRequiredFee(maxNewTxSize);
- if (totalFee < requiredFee) {
- vErrors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)",
+ if (total_fee < requiredFee) {
+ errors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)",
FormatMoney(requiredFee)));
- currentResult = BumpFeeResult::INVALID_PARAMETER;
- return;
+ return Result::INVALID_PARAMETER;
}
- nNewFee = totalFee;
- nNewFeeRate = CFeeRate(totalFee, maxNewTxSize);
+ new_fee = total_fee;
+ nNewFeeRate = CFeeRate(total_fee, maxNewTxSize);
} else {
- nNewFee = GetMinimumFee(maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */);
- nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
+ new_fee = GetMinimumFee(maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */);
+ nNewFeeRate = CFeeRate(new_fee, maxNewTxSize);
// New fee rate must be at least old rate + minimum incremental relay rate
// walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized
@@ -177,47 +174,50 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin
// add 1 satoshi to the result, because it may have been rounded down.
if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()) {
nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK());
- nNewFee = nNewFeeRate.GetFee(maxNewTxSize);
+ new_fee = nNewFeeRate.GetFee(maxNewTxSize);
}
}
// Check that in all cases the new fee doesn't violate maxTxFee
- if (nNewFee > maxTxFee) {
- vErrors.push_back(strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)",
- FormatMoney(nNewFee), FormatMoney(maxTxFee)));
- currentResult = BumpFeeResult::WALLET_ERROR;
- return;
+ if (new_fee > maxTxFee) {
+ errors.push_back(strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)",
+ FormatMoney(new_fee), FormatMoney(maxTxFee)));
+ return Result::WALLET_ERROR;
}
// check that fee rate is higher than mempool's minimum fee
// (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
// This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps,
// in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
- // moment earlier. In this case, we report an error to the user, who may use totalFee to make an adjustment.
+ // moment earlier. In this case, we report an error to the user, who may use total_fee to make an adjustment.
CFeeRate minMempoolFeeRate = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {
- vErrors.push_back(strprintf("New fee rate (%s) is less than the minimum fee rate (%s) to get into the mempool. totalFee value should to be at least %s or settxfee value should be at least %s to add transaction.", FormatMoney(nNewFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)), FormatMoney(minMempoolFeeRate.GetFeePerK())));
- currentResult = BumpFeeResult::WALLET_ERROR;
- return;
+ errors.push_back(strprintf(
+ "New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- "
+ "the totalFee value should be at least %s or the settxfee value should be at least %s to add transaction",
+ FormatMoney(nNewFeeRate.GetFeePerK()),
+ FormatMoney(minMempoolFeeRate.GetFeePerK()),
+ FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)),
+ FormatMoney(minMempoolFeeRate.GetFeePerK())));
+ return Result::WALLET_ERROR;
}
// Now modify the output to increase the fee.
// If the output is not large enough to pay the fee, fail.
- CAmount nDelta = nNewFee - nOldFee;
+ CAmount nDelta = new_fee - old_fee;
assert(nDelta > 0);
mtx = *wtx.tx;
CTxOut* poutput = &(mtx.vout[nOutput]);
if (poutput->nValue < nDelta) {
- vErrors.push_back("Change output is too small to bump the fee");
- currentResult = BumpFeeResult::WALLET_ERROR;
- return;
+ errors.push_back("Change output is too small to bump the fee");
+ return Result::WALLET_ERROR;
}
// If the output would become dust, discard it (converting the dust to fee)
poutput->nValue -= nDelta;
if (poutput->nValue <= GetDustThreshold(*poutput, ::dustRelayFee)) {
LogPrint(BCLog::RPC, "Bumping fee and discarding dust output\n");
- nNewFee += poutput->nValue;
+ new_fee += poutput->nValue;
mtx.vout.erase(mtx.vout.begin() + nOutput);
}
@@ -228,36 +228,36 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin
}
}
- currentResult = BumpFeeResult::OK;
+ return Result::OK;
}
-bool CFeeBumper::signTransaction(CWallet *pWallet)
-{
- return pWallet->SignTransaction(mtx);
+bool SignTransaction(CWallet* wallet, CMutableTransaction& mtx) {
+ LOCK2(cs_main, wallet->cs_wallet);
+ return wallet->SignTransaction(mtx);
}
-bool CFeeBumper::commit(CWallet *pWallet)
+Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<std::string>& errors, uint256& bumped_txid)
{
- AssertLockHeld(pWallet->cs_wallet);
- if (!vErrors.empty() || currentResult != BumpFeeResult::OK) {
- return false;
+ LOCK2(cs_main, wallet->cs_wallet);
+ if (!errors.empty()) {
+ return Result::MISC_ERROR;
}
- auto it = txid.IsNull() ? pWallet->mapWallet.end() : pWallet->mapWallet.find(txid);
- if (it == pWallet->mapWallet.end()) {
- vErrors.push_back("Invalid or non-wallet transaction id");
- currentResult = BumpFeeResult::MISC_ERROR;
- return false;
+ auto it = txid.IsNull() ? wallet->mapWallet.end() : wallet->mapWallet.find(txid);
+ if (it == wallet->mapWallet.end()) {
+ errors.push_back("Invalid or non-wallet transaction id");
+ return Result::MISC_ERROR;
}
CWalletTx& oldWtx = it->second;
// make sure the transaction still has no descendants and hasn't been mined in the meantime
- if (!preconditionChecks(pWallet, oldWtx)) {
- return false;
+ Result result = PreconditionChecks(wallet, oldWtx, errors);
+ if (result != Result::OK) {
+ return result;
}
- CWalletTx wtxBumped(pWallet, MakeTransactionRef(std::move(mtx)));
+ CWalletTx wtxBumped(wallet, MakeTransactionRef(std::move(mtx)));
// commit/broadcast the tx
- CReserveKey reservekey(pWallet);
+ CReserveKey reservekey(wallet);
wtxBumped.mapValue = oldWtx.mapValue;
wtxBumped.mapValue["replaces_txid"] = oldWtx.GetHash().ToString();
wtxBumped.vOrderForm = oldWtx.vOrderForm;
@@ -265,27 +265,29 @@ bool CFeeBumper::commit(CWallet *pWallet)
wtxBumped.fTimeReceivedIsTxTime = true;
wtxBumped.fFromMe = true;
CValidationState state;
- if (!pWallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) {
+ if (!wallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) {
// NOTE: CommitTransaction never returns false, so this should never happen.
- vErrors.push_back(strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()));
- return false;
+ errors.push_back(strprintf("The transaction was rejected: %s", state.GetRejectReason()));
+ return Result::WALLET_ERROR;
}
- bumpedTxid = wtxBumped.GetHash();
+ bumped_txid = wtxBumped.GetHash();
if (state.IsInvalid()) {
// This can happen if the mempool rejected the transaction. Report
// what happened in the "errors" response.
- vErrors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state)));
+ errors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state)));
}
// mark the original tx as bumped
- if (!pWallet->MarkReplaced(oldWtx.GetHash(), wtxBumped.GetHash())) {
+ if (!wallet->MarkReplaced(oldWtx.GetHash(), wtxBumped.GetHash())) {
// TODO: see if JSON-RPC has a standard way of returning a response
// along with an exception. It would be good to return information about
// wtxBumped to the caller even if marking the original transaction
// replaced does not succeed for some reason.
- vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced.");
+ errors.push_back("Created new bumpfee transaction but could not mark the original transaction as replaced");
}
- return true;
+ return Result::OK;
}
+} // namespace feebumper
+
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index 3d64e53c15..8eec30440c 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -13,7 +13,9 @@ class uint256;
class CCoinControl;
enum class FeeEstimateMode;
-enum class BumpFeeResult
+namespace feebumper {
+
+enum class Result
{
OK,
INVALID_ADDRESS_OR_KEY,
@@ -23,39 +25,34 @@ enum class BumpFeeResult
MISC_ERROR,
};
-class CFeeBumper
-{
-public:
- CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, const CCoinControl& coin_control, CAmount totalFee);
- BumpFeeResult getResult() const { return currentResult; }
- const std::vector<std::string>& getErrors() const { return vErrors; }
- CAmount getOldFee() const { return nOldFee; }
- CAmount getNewFee() const { return nNewFee; }
- uint256 getBumpedTxId() const { return bumpedTxid; }
-
- /* signs the new transaction,
- * returns false if the tx couldn't be found or if it was
- * impossible to create the signature(s)
- */
- bool signTransaction(CWallet *pWallet);
-
- /* commits the fee bump,
- * returns true, in case of CWallet::CommitTransaction was successful
- * but, eventually sets vErrors if the tx could not be added to the mempool (will try later)
- * or if the old transaction could not be marked as replaced
- */
- bool commit(CWallet *pWalletNonConst);
-
-private:
- bool preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx);
-
- const uint256 txid;
- uint256 bumpedTxid;
- CMutableTransaction mtx;
- std::vector<std::string> vErrors;
- BumpFeeResult currentResult;
- CAmount nOldFee;
- CAmount nNewFee;
-};
+//! Return whether transaction can be bumped.
+bool TransactionCanBeBumped(CWallet* wallet, const uint256& txid);
+
+//! Create bumpfee transaction.
+Result CreateTransaction(const CWallet* wallet,
+ const uint256& txid,
+ const CCoinControl& coin_control,
+ CAmount total_fee,
+ std::vector<std::string>& errors,
+ CAmount& old_fee,
+ CAmount& new_fee,
+ CMutableTransaction& mtx);
+
+//! Sign the new transaction,
+//! @return false if the tx couldn't be found or if it was
+//! impossible to create the signature(s)
+bool SignTransaction(CWallet* wallet, CMutableTransaction& mtx);
+
+//! Commit the bumpfee transaction.
+//! @return success in case of CWallet::CommitTransaction was successful,
+//! but sets errors if the tx could not be added to the mempool (will try later)
+//! or if the old transaction could not be marked as replaced.
+Result CommitTransaction(CWallet* wallet,
+ const uint256& txid,
+ CMutableTransaction&& mtx,
+ std::vector<std::string>& errors,
+ uint256& bumped_txid);
+
+} // namespace feebumper
#endif // BITCOIN_WALLET_FEEBUMPER_H
diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp
index 76eeeeda05..73985dcf25 100644
--- a/src/wallet/fees.cpp
+++ b/src/wallet/fees.cpp
@@ -3,14 +3,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "wallet/fees.h"
+#include <wallet/fees.h>
-#include "policy/policy.h"
-#include "txmempool.h"
-#include "util.h"
-#include "validation.h"
-#include "wallet/coincontrol.h"
-#include "wallet/wallet.h"
+#include <policy/policy.h>
+#include <txmempool.h>
+#include <util.h>
+#include <validation.h>
+#include <wallet/coincontrol.h>
+#include <wallet/wallet.h>
CAmount GetRequiredFee(unsigned int nTxBytes)
diff --git a/src/wallet/fees.h b/src/wallet/fees.h
index 7b8a7dc868..225aff08ad 100644
--- a/src/wallet/fees.h
+++ b/src/wallet/fees.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_WALLET_FEES_H
#define BITCOIN_WALLET_FEES_H
-#include "amount.h"
+#include <amount.h>
class CBlockPolicyEstimator;
class CCoinControl;
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 18365b1b72..67c46df87d 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -3,13 +3,15 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "wallet/init.h"
+#include <wallet/init.h>
-#include "net.h"
-#include "util.h"
-#include "utilmoneystr.h"
-#include "validation.h"
-#include "wallet/wallet.h"
+#include <net.h>
+#include <util.h>
+#include <utilmoneystr.h>
+#include <validation.h>
+#include <wallet/rpcwallet.h>
+#include <wallet/wallet.h>
+#include <wallet/walletutil.h>
std::string GetWalletHelpString(bool showDebug)
{
@@ -29,11 +31,11 @@ std::string GetWalletHelpString(bool showDebug)
strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup"));
strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE));
strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET));
- strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET));
strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF));
strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup"));
strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT));
strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST));
+ strUsage += HelpMessageOpt("-walletdir=<dir>", _("Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)"));
strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)"));
strUsage += HelpMessageOpt("-zapwallettxes=<mode>", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") +
" " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)"));
@@ -53,11 +55,16 @@ std::string GetWalletHelpString(bool showDebug)
bool WalletParameterInteraction()
{
- gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT);
- const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
+ if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
+ for (const std::string& wallet : gArgs.GetArgs("-wallet")) {
+ LogPrintf("%s: parameter interaction: -disablewallet -> ignoring -wallet=%s\n", __func__, wallet);
+ }
- if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
return true;
+ }
+
+ gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT);
+ const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
@@ -171,10 +178,26 @@ bool WalletParameterInteraction()
return true;
}
-bool WalletVerify()
+void RegisterWalletRPC(CRPCTable &t)
{
- if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
+ if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
+ return;
+ }
+
+ RegisterWalletRPCCommands(t);
+}
+
+bool VerifyWallets()
+{
+ if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
return true;
+ }
+
+ if (gArgs.IsArgSet("-walletdir") && !fs::is_directory(GetWalletDir())) {
+ return InitError(strprintf(_("Error: Specified wallet directory \"%s\" does not exist."), gArgs.GetArg("-walletdir", "").c_str()));
+ }
+
+ LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
uiInterface.InitMessage(_("Verifying wallet(s)..."));
@@ -190,7 +213,7 @@ bool WalletVerify()
return InitError(strprintf(_("Error loading wallet %s. Invalid characters in -wallet filename."), walletFile));
}
- fs::path wallet_path = fs::absolute(walletFile, GetDataDir());
+ fs::path wallet_path = fs::absolute(walletFile, GetWalletDir());
if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) {
return InitError(strprintf(_("Error loading wallet %s. -wallet filename must be a regular file."), walletFile));
@@ -201,7 +224,7 @@ bool WalletVerify()
}
std::string strError;
- if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) {
+ if (!CWalletDB::VerifyEnvironment(walletFile, GetWalletDir().string(), strError)) {
return InitError(strError);
}
@@ -215,7 +238,7 @@ bool WalletVerify()
}
std::string strWarning;
- bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError);
+ bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetWalletDir().string(), strWarning, strError);
if (!strWarning.empty()) {
InitWarning(strWarning);
}
@@ -228,7 +251,7 @@ bool WalletVerify()
return true;
}
-bool InitLoadWallet()
+bool OpenWallets()
{
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
LogPrintf("Wallet disabled!\n");
@@ -245,3 +268,28 @@ bool InitLoadWallet()
return true;
}
+
+void StartWallets(CScheduler& scheduler) {
+ for (CWalletRef pwallet : vpwallets) {
+ pwallet->postInitProcess(scheduler);
+ }
+}
+
+void FlushWallets() {
+ for (CWalletRef pwallet : vpwallets) {
+ pwallet->Flush(false);
+ }
+}
+
+void StopWallets() {
+ for (CWalletRef pwallet : vpwallets) {
+ pwallet->Flush(true);
+ }
+}
+
+void CloseWallets() {
+ for (CWalletRef pwallet : vpwallets) {
+ delete pwallet;
+ }
+ vpwallets.clear();
+}
diff --git a/src/wallet/init.h b/src/wallet/init.h
index fa2251506d..0b3ee2dda2 100644
--- a/src/wallet/init.h
+++ b/src/wallet/init.h
@@ -8,18 +8,36 @@
#include <string>
+class CRPCTable;
+class CScheduler;
+
//! Return the wallets help message.
std::string GetWalletHelpString(bool showDebug);
//! Wallets parameter interaction
bool WalletParameterInteraction();
+//! Register wallet RPCs.
+void RegisterWalletRPC(CRPCTable &tableRPC);
+
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
// This function will perform salvage on the wallet if requested, as long as only one wallet is
-// being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
-bool WalletVerify();
+// being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
+bool VerifyWallets();
//! Load wallet databases.
-bool InitLoadWallet();
+bool OpenWallets();
+
+//! Complete startup of wallets.
+void StartWallets(CScheduler& scheduler);
+
+//! Flush all wallets in preparation for shutdown.
+void FlushWallets();
+
+//! Stop all wallets. Wallets will be flushed first.
+void StopWallets();
+
+//! Close all wallets.
+void CloseWallets();
#endif // BITCOIN_WALLET_INIT_H
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index db6f6b48ac..6ca947bf1b 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -2,22 +2,22 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "base58.h"
-#include "chain.h"
-#include "rpc/safemode.h"
-#include "rpc/server.h"
-#include "init.h"
-#include "validation.h"
-#include "script/script.h"
-#include "script/standard.h"
-#include "sync.h"
-#include "util.h"
-#include "utiltime.h"
-#include "wallet.h"
-#include "merkleblock.h"
-#include "core_io.h"
-
-#include "rpcwallet.h"
+#include <base58.h>
+#include <chain.h>
+#include <rpc/safemode.h>
+#include <rpc/server.h>
+#include <wallet/init.h>
+#include <validation.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <sync.h>
+#include <util.h>
+#include <utiltime.h>
+#include <wallet/wallet.h>
+#include <merkleblock.h>
+#include <core_io.h>
+
+#include <wallet/rpcwallet.h>
#include <fstream>
#include <stdint.h>
@@ -80,10 +80,10 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "importprivkey \"bitcoinprivkey\" ( \"label\" ) ( rescan )\n"
- "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
+ "importprivkey \"privkey\" ( \"label\" ) ( rescan )\n"
+ "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
"\nArguments:\n"
- "1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n"
+ "1. \"privkey\" (string, required) The private key (see dumpprivkey)\n"
"2. \"label\" (string, optional, default=\"\") An optional label\n"
"3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
"\nNote: This call can take minutes to complete if rescan is true.\n"
@@ -165,7 +165,7 @@ UniValue abortrescan(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
"abortrescan\n"
- "\nStops current wallet rescan triggered e.g. by an importprivkey call.\n"
+ "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
"\nExamples:\n"
"\nImport a private key\n"
+ HelpExampleCli("importprivkey", "\"mykey\"") +
@@ -181,7 +181,7 @@ UniValue abortrescan(const JSONRPCRequest& request)
return true;
}
-void ImportAddress(CWallet*, const CBitcoinAddress& address, const std::string& strLabel);
+void ImportAddress(CWallet*, const CTxDestination& dest, const std::string& strLabel);
void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript)
{
if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
@@ -198,7 +198,7 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri
if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
}
- ImportAddress(pwallet, CBitcoinAddress(CScriptID(script)), strLabel);
+ ImportAddress(pwallet, CScriptID(script), strLabel);
} else {
CTxDestination destination;
if (ExtractDestination(script, destination)) {
@@ -207,13 +207,13 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri
}
}
-void ImportAddress(CWallet* const pwallet, const CBitcoinAddress& address, const std::string& strLabel)
+void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel)
{
- CScript script = GetScriptForDestination(address.Get());
+ CScript script = GetScriptForDestination(dest);
ImportScript(pwallet, script, strLabel, false);
// add to address book or update label
- if (address.IsValid())
- pwallet->SetAddressBook(address.Get(), strLabel, "receive");
+ if (IsValidDestination(dest))
+ pwallet->SetAddressBook(dest, strLabel, "receive");
}
UniValue importaddress(const JSONRPCRequest& request)
@@ -226,7 +226,7 @@ UniValue importaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw std::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"
+ "\nAdds a script (in hex) or address that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"\nArguments:\n"
"1. \"script\" (string, required) The hex-encoded script (or address)\n"
"2. \"label\" (string, optional, default=\"\") An optional label\n"
@@ -265,11 +265,12 @@ UniValue importaddress(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
- CBitcoinAddress address(request.params[0].get_str());
- if (address.IsValid()) {
- if (fP2SH)
+ CTxDestination dest = DecodeDestination(request.params[0].get_str());
+ if (IsValidDestination(dest)) {
+ if (fP2SH) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
- ImportAddress(pwallet, address, strLabel);
+ }
+ ImportAddress(pwallet, dest, strLabel);
} else if (IsHex(request.params[0].get_str())) {
std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel, fP2SH);
@@ -339,7 +340,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
- if (pwallet->IsMine(wtx)) {
+ if (pwallet->IsMine(*wtx.tx)) {
pwallet->AddToWallet(wtx, false);
return NullUniValue;
}
@@ -395,7 +396,7 @@ UniValue importpubkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw std::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"
+ "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"\nArguments:\n"
"1. \"pubkey\" (string, required) The hex-encoded public key\n"
"2. \"label\" (string, optional, default=\"\") An optional label\n"
@@ -432,7 +433,7 @@ UniValue importpubkey(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
- ImportAddress(pwallet, CBitcoinAddress(pubKey.GetID()), strLabel);
+ ImportAddress(pwallet, pubKey.GetID(), strLabel);
ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false);
if (fRescan)
@@ -455,7 +456,7 @@ UniValue importwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"importwallet \"filename\"\n"
- "\nImports keys from a wallet dump file (see dumpwallet).\n"
+ "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
"\nArguments:\n"
"1. \"filename\" (string, required) The wallet file\n"
"\nExamples:\n"
@@ -506,7 +507,7 @@ UniValue importwallet(const JSONRPCRequest& request)
assert(key.VerifyPubKey(pubkey));
CKeyID keyid = pubkey.GetID();
if (pwallet->HaveKey(keyid)) {
- LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString());
+ LogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid));
continue;
}
int64_t nTime = DecodeDumpTime(vstr[1]);
@@ -524,7 +525,7 @@ UniValue importwallet(const JSONRPCRequest& request)
fLabel = true;
}
}
- LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString());
+ LogPrintf("Importing %s...\n", EncodeDestination(keyid));
if (!pwallet->AddKeyPubKey(key, pubkey)) {
fGood = false;
continue;
@@ -573,14 +574,16 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
std::string strAddress = request.params[0].get_str();
- CBitcoinAddress address;
- if (!address.SetString(strAddress))
+ CTxDestination dest = DecodeDestination(strAddress);
+ if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
- CKeyID keyID;
- if (!address.GetKeyID(keyID))
+ }
+ const CKeyID *keyID = boost::get<CKeyID>(&dest);
+ if (!keyID) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
+ }
CKey vchSecret;
- if (!pwallet->GetKey(keyID, vchSecret)) {
+ if (!pwallet->GetKey(*keyID, vchSecret)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
}
return CBitcoinSecret(vchSecret).ToString();
@@ -597,7 +600,10 @@ UniValue dumpwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"dumpwallet \"filename\"\n"
- "\nDumps all wallet keys in a human-readable format.\n"
+ "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
+ "Imported scripts are not currently included in wallet dumps, these must be backed up separately.\n"
+ "Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n"
+ "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n"
"\nArguments:\n"
"1. \"filename\" (string, required) The filename with path (either absolute or relative to bitcoind)\n"
"\nResult:\n"
@@ -613,9 +619,19 @@ UniValue dumpwallet(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
- std::ofstream file;
boost::filesystem::path filepath = request.params[0].get_str();
filepath = boost::filesystem::absolute(filepath);
+
+ /* Prevent arbitrary files from being overwritten. There have been reports
+ * that users have overwritten wallet files this way:
+ * https://github.com/bitcoin/bitcoin/issues/9934
+ * It may also avoid other security issues.
+ */
+ if (boost::filesystem::exists(filepath)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.string() + " already exists. If you are sure this is what you want, move it out of the way first");
+ }
+
+ std::ofstream file;
file.open(filepath.string().c_str());
if (!file.is_open())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
@@ -659,7 +675,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
const CKeyID &keyid = it->second;
std::string strTime = EncodeDumpTime(it->first);
- std::string strAddr = CBitcoinAddress(keyid).ToString();
+ std::string strAddr = EncodeDestination(keyid);
CKey key;
if (pwallet->GetKey(keyid, key)) {
file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime);
@@ -705,8 +721,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
const std::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 bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
+ const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
const std::string& label = data.exists("label") && !internal ? data["label"].get_str() : "";
bool isScript = scriptPubKey.getType() == UniValue::VSTR;
@@ -715,14 +731,14 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
// Parse the output.
CScript script;
- CBitcoinAddress address;
+ CTxDestination dest;
if (!isScript) {
- address = CBitcoinAddress(output);
- if (!address.IsValid()) {
+ dest = DecodeDestination(output);
+ if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
}
- script = GetScriptForDestination(address.Get());
+ script = GetScriptForDestination(dest);
} else {
if (!IsHex(output)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey");
@@ -780,8 +796,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
}
- CBitcoinAddress redeemAddress = CBitcoinAddress(CScriptID(redeemScript));
- CScript redeemDestination = GetScriptForDestination(redeemAddress.Get());
+ CTxDestination redeem_dest = CScriptID(redeemScript);
+ CScript redeemDestination = GetScriptForDestination(redeem_dest);
if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
@@ -794,8 +810,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
}
// add to address book or update label
- if (address.IsValid()) {
- pwallet->SetAddressBook(address.Get(), label, "receive");
+ if (IsValidDestination(dest)) {
+ pwallet->SetAddressBook(dest, label, "receive");
}
// Import private keys.
@@ -854,27 +870,25 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
}
- CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID());
+ CTxDestination pubkey_dest = pubKey.GetID();
// Consistency check.
- if (!isScript && !(pubKeyAddress.Get() == address.Get())) {
+ if (!isScript && !(pubkey_dest == dest)) {
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())) {
+ if (!(destination == pubkey_dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
}
}
}
- CScript pubKeyScript = GetScriptForDestination(pubKeyAddress.Get());
+ CScript pubKeyScript = GetScriptForDestination(pubkey_dest);
if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
@@ -887,8 +901,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
}
// add to address book or update label
- if (pubKeyAddress.IsValid()) {
- pwallet->SetAddressBook(pubKeyAddress.Get(), label, "receive");
+ if (IsValidDestination(pubkey_dest)) {
+ pwallet->SetAddressBook(pubkey_dest, label, "receive");
}
// TODO Is this necessary?
@@ -927,21 +941,19 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
CPubKey pubKey = key.GetPubKey();
assert(key.VerifyPubKey(pubKey));
- CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID());
+ CTxDestination pubkey_dest = pubKey.GetID();
// Consistency check.
- if (!isScript && !(pubKeyAddress.Get() == address.Get())) {
+ if (!isScript && !(pubkey_dest == dest)) {
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())) {
+ if (!(destination == pubkey_dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
}
}
@@ -952,7 +964,7 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
pwallet->SetAddressBook(vchAddress, label, "receive");
if (pwallet->HaveKey(vchAddress)) {
- return false;
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
}
pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
@@ -980,8 +992,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
if (scriptPubKey.getType() == UniValue::VOBJ) {
// add to address book or update label
- if (address.IsValid()) {
- pwallet->SetAddressBook(address.Get(), label, "receive");
+ if (IsValidDestination(dest)) {
+ pwallet->SetAddressBook(dest, label, "receive");
}
}
@@ -1030,7 +1042,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
throw std::runtime_error(
"importmulti \"requests\" ( \"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"
+ "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n\n"
"Arguments:\n"
"1. requests (array, required) Data to be imported\n"
" [ (array of json objects)\n"
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 5f80c57d13..f839a18612 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3,29 +3,30 @@
// 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 "httpserver.h"
-#include "validation.h"
-#include "net.h"
-#include "policy/feerate.h"
-#include "policy/fees.h"
-#include "policy/policy.h"
-#include "policy/rbf.h"
-#include "rpc/mining.h"
-#include "rpc/safemode.h"
-#include "rpc/server.h"
-#include "script/sign.h"
-#include "timedata.h"
-#include "util.h"
-#include "utilmoneystr.h"
-#include "wallet/coincontrol.h"
-#include "wallet/feebumper.h"
-#include "wallet/wallet.h"
-#include "wallet/walletdb.h"
+#include <amount.h>
+#include <base58.h>
+#include <chain.h>
+#include <consensus/validation.h>
+#include <core_io.h>
+#include <httpserver.h>
+#include <validation.h>
+#include <net.h>
+#include <policy/feerate.h>
+#include <policy/fees.h>
+#include <policy/policy.h>
+#include <policy/rbf.h>
+#include <rpc/mining.h>
+#include <rpc/safemode.h>
+#include <rpc/server.h>
+#include <script/sign.h>
+#include <timedata.h>
+#include <util.h>
+#include <utilmoneystr.h>
+#include <wallet/coincontrol.h>
+#include <wallet/feebumper.h>
+#include <wallet/wallet.h>
+#include <wallet/walletdb.h>
+#include <wallet/walletutil.h>
#include <init.h> // For StartShutdown
@@ -108,7 +109,7 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
std::string rbfStatus = "no";
if (confirms <= 0) {
LOCK(mempool.cs);
- RBFTransactionState rbfState = IsRBFOptIn(wtx, mempool);
+ RBFTransactionState rbfState = IsRBFOptIn(*wtx.tx, mempool);
if (rbfState == RBF_TRANSACTIONSTATE_UNKNOWN)
rbfStatus = "unknown";
else if (rbfState == RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125)
@@ -170,18 +171,18 @@ UniValue getnewaddress(const JSONRPCRequest& request)
pwallet->SetAddressBook(keyID, strAccount, "receive");
- return CBitcoinAddress(keyID).ToString();
+ return EncodeDestination(keyID);
}
-CBitcoinAddress GetAccountAddress(CWallet* const pwallet, std::string strAccount, bool bForceNew=false)
+CTxDestination GetAccountAddress(CWallet* const pwallet, std::string strAccount, bool bForceNew=false)
{
CPubKey pubKey;
if (!pwallet->GetAccountPubkey(pubKey, strAccount, bForceNew)) {
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
}
- return CBitcoinAddress(pubKey.GetID());
+ return pubKey.GetID();
}
UniValue getaccountaddress(const JSONRPCRequest& request)
@@ -213,7 +214,7 @@ UniValue getaccountaddress(const JSONRPCRequest& request)
UniValue ret(UniValue::VSTR);
- ret = GetAccountAddress(pwallet, strAccount).ToString();
+ ret = EncodeDestination(GetAccountAddress(pwallet, strAccount));
return ret;
}
@@ -252,7 +253,7 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request)
CKeyID keyID = vchPubKey.GetID();
- return CBitcoinAddress(keyID).ToString();
+ return EncodeDestination(keyID);
}
@@ -277,24 +278,25 @@ UniValue setaccount(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
- CBitcoinAddress address(request.params[0].get_str());
- if (!address.IsValid())
+ CTxDestination dest = DecodeDestination(request.params[0].get_str());
+ if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
+ }
std::string strAccount;
if (!request.params[1].isNull())
strAccount = AccountFromValue(request.params[1]);
// Only add the account if the address is yours.
- if (IsMine(*pwallet, address.Get())) {
+ if (IsMine(*pwallet, dest)) {
// Detect when changing the account of an address that is the 'unused current key' of another account:
- if (pwallet->mapAddressBook.count(address.Get())) {
- std::string strOldAccount = pwallet->mapAddressBook[address.Get()].name;
- if (address == GetAccountAddress(pwallet, strOldAccount)) {
+ if (pwallet->mapAddressBook.count(dest)) {
+ std::string strOldAccount = pwallet->mapAddressBook[dest].name;
+ if (dest == GetAccountAddress(pwallet, strOldAccount)) {
GetAccountAddress(pwallet, strOldAccount, true);
}
}
- pwallet->SetAddressBook(address.Get(), strAccount, "receive");
+ pwallet->SetAddressBook(dest, strAccount, "receive");
}
else
throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address");
@@ -325,12 +327,13 @@ UniValue getaccount(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
- CBitcoinAddress address(request.params[0].get_str());
- if (!address.IsValid())
+ CTxDestination dest = DecodeDestination(request.params[0].get_str());
+ if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
+ }
std::string strAccount;
- std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(address.Get());
+ std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(dest);
if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) {
strAccount = (*mi).second.name;
}
@@ -367,11 +370,12 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request)
// Find all addresses that have the given account
UniValue ret(UniValue::VARR);
- for (const std::pair<CBitcoinAddress, CAddressBookData>& item : pwallet->mapAddressBook) {
- const CBitcoinAddress& address = item.first;
+ for (const std::pair<CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) {
+ const CTxDestination& dest = item.first;
const std::string& strName = item.second.name;
- if (strName == strAccount)
- ret.push_back(address.ToString());
+ if (strName == strAccount) {
+ ret.push_back(EncodeDestination(dest));
+ }
}
return ret;
}
@@ -452,11 +456,17 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
- CBitcoinAddress address(request.params[0].get_str());
- if (!address.IsValid())
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
+ CTxDestination dest = DecodeDestination(request.params[0].get_str());
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
+ }
// Amount
CAmount nAmount = AmountFromValue(request.params[1]);
@@ -493,7 +503,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
- SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx, coin_control);
+ SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, wtx, coin_control);
return wtx.GetHash().GetHex();
}
@@ -529,20 +539,25 @@ UniValue listaddressgroupings(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
UniValue jsonGroupings(UniValue::VARR);
std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances();
- for (std::set<CTxDestination> grouping : pwallet->GetAddressGroupings()) {
+ for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) {
UniValue jsonGrouping(UniValue::VARR);
- for (CTxDestination address : grouping)
+ for (const CTxDestination& address : grouping)
{
UniValue addressInfo(UniValue::VARR);
- addressInfo.push_back(CBitcoinAddress(address).ToString());
+ addressInfo.push_back(EncodeDestination(address));
addressInfo.push_back(ValueFromAmount(balances[address]));
{
- if (pwallet->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwallet->mapAddressBook.end()) {
- addressInfo.push_back(pwallet->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name);
+ if (pwallet->mapAddressBook.find(address) != pwallet->mapAddressBook.end()) {
+ addressInfo.push_back(pwallet->mapAddressBook.find(address)->second.name);
}
}
jsonGrouping.push_back(addressInfo);
@@ -587,16 +602,18 @@ UniValue signmessage(const JSONRPCRequest& request)
std::string strAddress = request.params[0].get_str();
std::string strMessage = request.params[1].get_str();
- CBitcoinAddress addr(strAddress);
- if (!addr.IsValid())
+ CTxDestination dest = DecodeDestination(strAddress);
+ if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
+ }
- CKeyID keyID;
- if (!addr.GetKeyID(keyID))
+ const CKeyID *keyID = boost::get<CKeyID>(&dest);
+ if (!keyID) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
+ }
CKey key;
- if (!pwallet->GetKey(keyID, key)) {
+ if (!pwallet->GetKey(*keyID, key)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
}
@@ -608,7 +625,7 @@ UniValue signmessage(const JSONRPCRequest& request)
if (!key.SignCompact(ss.GetHash(), vchSig))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
- return EncodeBase64(&vchSig[0], vchSig.size());
+ return EncodeBase64(vchSig.data(), vchSig.size());
}
UniValue getreceivedbyaddress(const JSONRPCRequest& request)
@@ -639,15 +656,21 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
// Bitcoin address
- CBitcoinAddress address = CBitcoinAddress(request.params[0].get_str());
- if (!address.IsValid())
+ CTxDestination dest = DecodeDestination(request.params[0].get_str());
+ if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
- CScript scriptPubKey = GetScriptForDestination(address.Get());
+ }
+ CScript scriptPubKey = GetScriptForDestination(dest);
if (!IsMine(*pwallet, scriptPubKey)) {
- return ValueFromAmount(0);
+ throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
}
// Minimum confirmations
@@ -700,6 +723,11 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
// Minimum confirmations
@@ -743,6 +771,8 @@ UniValue getbalance(const JSONRPCRequest& request)
throw std::runtime_error(
"getbalance ( \"account\" minconf include_watchonly )\n"
"\nIf account is not specified, returns the server's total available balance.\n"
+ "The available balance is what the wallet considers currently spendable, and is\n"
+ "thus affected by options which limit spendability such as -spendzeroconfchange.\n"
"If account is specified (DEPRECATED), returns the balance in the account.\n"
"Note that the account \"\" is not the same as leaving the parameter out.\n"
"The server total may be different to the balance in the default \"\" account.\n"
@@ -773,6 +803,11 @@ UniValue getbalance(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
const UniValue& account_value = request.params[0];
@@ -818,6 +853,11 @@ UniValue getunconfirmedbalance(const JSONRPCRequest &request)
"Returns the server's total unconfirmed balance\n");
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
return ValueFromAmount(pwallet->GetUnconfirmedBalance());
@@ -912,12 +952,18 @@ UniValue sendfrom(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount = AccountFromValue(request.params[0]);
- CBitcoinAddress address(request.params[1].get_str());
- if (!address.IsValid())
+ CTxDestination dest = DecodeDestination(request.params[1].get_str());
+ if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
+ }
CAmount nAmount = AmountFromValue(request.params[2]);
if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
@@ -940,7 +986,7 @@ UniValue sendfrom(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
CCoinControl no_coin_control; // This is a deprecated API
- SendMoney(pwallet, address.Get(), nAmount, false, wtx, no_coin_control);
+ SendMoney(pwallet, dest, nAmount, false, wtx, no_coin_control);
return wtx.GetHash().GetHex();
}
@@ -996,6 +1042,11 @@ UniValue sendmany(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
if (pwallet->GetBroadcastTransactions() && !g_connman) {
@@ -1032,22 +1083,23 @@ UniValue sendmany(const JSONRPCRequest& request)
}
}
- std::set<CBitcoinAddress> setAddress;
+ std::set<CTxDestination> destinations;
std::vector<CRecipient> vecSend;
CAmount totalAmount = 0;
std::vector<std::string> keys = sendTo.getKeys();
- for (const std::string& name_ : keys)
- {
- CBitcoinAddress address(name_);
- if (!address.IsValid())
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+name_);
+ for (const std::string& name_ : keys) {
+ CTxDestination dest = DecodeDestination(name_);
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
+ }
- if (setAddress.count(address))
- throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+name_);
- setAddress.insert(address);
+ if (destinations.count(dest)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
+ }
+ destinations.insert(dest);
- CScript scriptPubKey = GetScriptForDestination(address.Get());
+ CScript scriptPubKey = GetScriptForDestination(dest);
CAmount nAmount = AmountFromValue(sendTo[name_]);
if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
@@ -1101,7 +1153,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
{
std::string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n"
- "\nAdd a nrequired-to-sign multisignature address to the wallet.\n"
+ "\nAdd a nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
"If 'account' is specified (DEPRECATED), assign address to that account.\n"
@@ -1138,18 +1190,17 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
pwallet->AddCScript(inner);
pwallet->SetAddressBook(innerID, strAccount, "send");
- return CBitcoinAddress(innerID).ToString();
+ return EncodeDestination(innerID);
}
class Witnessifier : public boost::static_visitor<bool>
{
public:
CWallet * const pwallet;
- CScriptID result;
-
- explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {}
+ CTxDestination result;
+ bool already_witness;
- bool operator()(const CNoDestination &dest) const { return false; }
+ explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {}
bool operator()(const CKeyID &keyID) {
if (pwallet) {
@@ -1163,9 +1214,7 @@ public:
!VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) {
return false;
}
- pwallet->AddCScript(witscript);
- result = CScriptID(witscript);
- return true;
+ return ExtractDestination(witscript, result);
}
return false;
}
@@ -1176,7 +1225,8 @@ public:
int witnessversion;
std::vector<unsigned char> witprog;
if (subscript.IsWitnessProgram(witnessversion, witprog)) {
- result = scriptID;
+ ExtractDestination(subscript, result);
+ already_witness = true;
return true;
}
CScript witscript = GetScriptForWitness(subscript);
@@ -1188,12 +1238,27 @@ public:
!VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) {
return false;
}
- pwallet->AddCScript(witscript);
- result = CScriptID(witscript);
- return true;
+ return ExtractDestination(witscript, result);
}
return false;
}
+
+ bool operator()(const WitnessV0KeyHash& id)
+ {
+ already_witness = true;
+ result = id;
+ return true;
+ }
+
+ bool operator()(const WitnessV0ScriptHash& id)
+ {
+ already_witness = true;
+ result = id;
+ return true;
+ }
+
+ template<typename T>
+ bool operator()(const T& dest) { return false; }
};
UniValue addwitnessaddress(const JSONRPCRequest& request)
@@ -1203,17 +1268,18 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 1)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
{
- std::string msg = "addwitnessaddress \"address\"\n"
- "\nAdd a witness address for a script (with pubkey or redeemscript known).\n"
+ std::string msg = "addwitnessaddress \"address\" ( p2sh )\n"
+ "\nAdd a witness address for a script (with pubkey or redeemscript known). Requires a new wallet backup.\n"
"It returns the witness script.\n"
"\nArguments:\n"
"1. \"address\" (string, required) An address known to the wallet\n"
+ "2. p2sh (bool, optional, default=true) Embed inside P2SH\n"
"\nResult:\n"
- "\"witnessaddress\", (string) The value of the new address (P2SH of witness script).\n"
+ "\"witnessaddress\", (string) The value of the new address (P2SH or BIP173).\n"
"}\n"
;
throw std::runtime_error(msg);
@@ -1226,20 +1292,38 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
}
}
- CBitcoinAddress address(request.params[0].get_str());
- if (!address.IsValid())
+ CTxDestination dest = DecodeDestination(request.params[0].get_str());
+ if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
+ }
+
+ bool p2sh = true;
+ if (!request.params[1].isNull()) {
+ p2sh = request.params[1].get_bool();
+ }
Witnessifier w(pwallet);
- 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, or the key is uncompressed");
}
- pwallet->SetAddressBook(w.result, "", "receive");
+ CScript witprogram = GetScriptForDestination(w.result);
- return CBitcoinAddress(w.result).ToString();
+ if (p2sh) {
+ w.result = CScriptID(witprogram);
+ }
+
+ if (w.already_witness) {
+ if (!(dest == w.result)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types");
+ }
+ } else {
+ pwallet->AddCScript(witprogram);
+ pwallet->SetAddressBook(w.result, "", "receive");
+ }
+
+ return EncodeDestination(w.result);
}
struct tallyitem
@@ -1274,7 +1358,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
filter = filter | ISMINE_WATCH_ONLY;
// Tally
- std::map<CBitcoinAddress, tallyitem> mapTally;
+ std::map<CTxDestination, tallyitem> mapTally;
for (const std::pair<uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second;
@@ -1307,10 +1391,10 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
// Reply
UniValue ret(UniValue::VARR);
std::map<std::string, tallyitem> mapAccountTally;
- for (const std::pair<CBitcoinAddress, CAddressBookData>& item : pwallet->mapAddressBook) {
- const CBitcoinAddress& address = item.first;
+ for (const std::pair<CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) {
+ const CTxDestination& dest = item.first;
const std::string& strAccount = item.second.name;
- std::map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
+ std::map<CTxDestination, tallyitem>::iterator it = mapTally.find(dest);
if (it == mapTally.end() && !fIncludeEmpty)
continue;
@@ -1336,7 +1420,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
UniValue obj(UniValue::VOBJ);
if(fIsWatchonly)
obj.push_back(Pair("involvesWatchonly", true));
- obj.push_back(Pair("address", address.ToString()));
+ obj.push_back(Pair("address", EncodeDestination(dest)));
obj.push_back(Pair("account", strAccount));
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
@@ -1357,14 +1441,14 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
if (fByAccounts)
{
- for (std::map<std::string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
+ for (const auto& entry : mapAccountTally)
{
- CAmount nAmount = (*it).second.nAmount;
- int nConf = (*it).second.nConf;
+ CAmount nAmount = entry.second.nAmount;
+ int nConf = entry.second.nConf;
UniValue obj(UniValue::VOBJ);
- if((*it).second.fIsWatchonly)
+ if (entry.second.fIsWatchonly)
obj.push_back(Pair("involvesWatchonly", true));
- obj.push_back(Pair("account", (*it).first));
+ obj.push_back(Pair("account", entry.first));
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
ret.push_back(obj);
@@ -1414,6 +1498,11 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
return ListReceived(pwallet, request.params, false);
@@ -1454,6 +1543,11 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
return ListReceived(pwallet, request.params, true);
@@ -1461,9 +1555,9 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request)
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
{
- CBitcoinAddress addr;
- if (addr.Set(dest))
- entry.push_back(Pair("address", addr.ToString()));
+ if (IsValidDestination(dest)) {
+ entry.push_back(Pair("address", EncodeDestination(dest)));
+ }
}
/**
@@ -1642,6 +1736,11 @@ UniValue listtransactions(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount = "*";
@@ -1736,6 +1835,11 @@ UniValue listaccounts(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
int nMinDepth = 1;
@@ -1845,6 +1949,11 @@ UniValue listsinceblock(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
const CBlockIndex* pindex = nullptr; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain.
@@ -1852,19 +1961,20 @@ UniValue listsinceblock(const JSONRPCRequest& request)
int target_confirms = 1;
isminefilter filter = ISMINE_SPENDABLE;
- if (!request.params[0].isNull()) {
+ if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
uint256 blockId;
blockId.SetHex(request.params[0].get_str());
BlockMap::iterator it = mapBlockIndex.find(blockId);
- if (it != mapBlockIndex.end()) {
- paltindex = pindex = it->second;
- if (chainActive[pindex->nHeight] != pindex) {
- // the block being asked for is a part of a deactivated chain;
- // we don't want to depend on its perceived height in the block
- // chain, we want to instead use the last common ancestor
- pindex = chainActive.FindFork(pindex);
- }
+ if (it == mapBlockIndex.end()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+ paltindex = pindex = it->second;
+ if (chainActive[pindex->nHeight] != pindex) {
+ // the block being asked for is a part of a deactivated chain;
+ // we don't want to depend on its perceived height in the block
+ // chain, we want to instead use the last common ancestor
+ pindex = chainActive.FindFork(pindex);
}
}
@@ -1977,6 +2087,11 @@ UniValue gettransaction(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
uint256 hash;
@@ -2009,7 +2124,7 @@ UniValue gettransaction(const JSONRPCRequest& request)
ListTransactions(pwallet, wtx, "*", 0, false, details, filter);
entry.push_back(Pair("details", details));
- std::string strHex = EncodeHexTx(static_cast<CTransaction>(wtx), RPCSerializationFlags());
+ std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags());
entry.push_back(Pair("hex", strHex));
return entry;
@@ -2039,6 +2154,11 @@ UniValue abandontransaction(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
uint256 hash;
@@ -2073,6 +2193,10 @@ UniValue backupwallet(const JSONRPCRequest& request)
+ HelpExampleRpc("backupwallet", "\"backup.dat\"")
);
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
std::string strDest = request.params[0].get_str();
@@ -2138,7 +2262,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
return NullUniValue;
}
- if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) {
+ if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"walletpassphrase \"passphrase\" timeout\n"
"\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
@@ -2202,7 +2326,7 @@ UniValue walletpassphrasechange(const JSONRPCRequest& request)
return NullUniValue;
}
- if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) {
+ if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n"
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n"
@@ -2253,7 +2377,7 @@ UniValue walletlock(const JSONRPCRequest& request)
return NullUniValue;
}
- if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 0)) {
+ if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"walletlock\n"
"\nRemoves the wallet encryption key from memory, locking the wallet.\n"
@@ -2293,7 +2417,7 @@ UniValue encryptwallet(const JSONRPCRequest& request)
return NullUniValue;
}
- if (!pwallet->IsCrypted() && (request.fHelp || request.params.size() != 1)) {
+ if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"encryptwallet \"passphrase\"\n"
"\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
@@ -2392,6 +2516,10 @@ UniValue lockunspent(const JSONRPCRequest& request)
+ HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
);
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);
@@ -2406,12 +2534,15 @@ UniValue lockunspent(const JSONRPCRequest& request)
RPCTypeCheckArgument(request.params[1], UniValue::VARR);
- UniValue outputs = request.params[1].get_array();
- for (unsigned int idx = 0; idx < outputs.size(); idx++) {
- const UniValue& output = outputs[idx];
- if (!output.isObject())
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object");
- const UniValue& o = output.get_obj();
+ const UniValue& output_params = request.params[1];
+
+ // Create and validate the COutPoints first.
+
+ std::vector<COutPoint> outputs;
+ outputs.reserve(output_params.size());
+
+ for (unsigned int idx = 0; idx < output_params.size(); idx++) {
+ const UniValue& o = output_params[idx].get_obj();
RPCTypeCheckObj(o,
{
@@ -2419,20 +2550,50 @@ UniValue lockunspent(const JSONRPCRequest& request)
{"vout", UniValueType(UniValue::VNUM)},
});
- std::string txid = find_value(o, "txid").get_str();
- if (!IsHex(txid))
+ const std::string& txid = find_value(o, "txid").get_str();
+ if (!IsHex(txid)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");
+ }
- int nOutput = find_value(o, "vout").get_int();
- if (nOutput < 0)
+ const int nOutput = find_value(o, "vout").get_int();
+ if (nOutput < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
+ }
- COutPoint outpt(uint256S(txid), nOutput);
+ const COutPoint outpt(uint256S(txid), nOutput);
- if (fUnlock)
- pwallet->UnlockCoin(outpt);
- else
- pwallet->LockCoin(outpt);
+ const auto it = pwallet->mapWallet.find(outpt.hash);
+ if (it == pwallet->mapWallet.end()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction");
+ }
+
+ const CWalletTx& trans = it->second;
+
+ if (outpt.n >= trans.tx->vout.size()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
+ }
+
+ if (pwallet->IsSpent(outpt.hash, outpt.n)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
+ }
+
+ const bool is_locked = pwallet->IsLockedCoin(outpt.hash, outpt.n);
+
+ if (fUnlock && !is_locked) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output");
+ }
+
+ if (!fUnlock && is_locked) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked");
+ }
+
+ outputs.push_back(outpt);
+ }
+
+ // Atomically set (un)locked status for the outputs.
+ for (const COutPoint& outpt : outputs) {
+ if (fUnlock) pwallet->UnlockCoin(outpt);
+ else pwallet->LockCoin(outpt);
}
return true;
@@ -2551,6 +2712,11 @@ UniValue getwalletinfo(const JSONRPCRequest& request)
);
ObserveSafeMode();
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
UniValue obj(UniValue::VOBJ);
@@ -2717,18 +2883,19 @@ UniValue listunspent(const JSONRPCRequest& request)
nMaxDepth = request.params[1].get_int();
}
- std::set<CBitcoinAddress> setAddress;
+ std::set<CTxDestination> destinations;
if (!request.params[2].isNull()) {
RPCTypeCheckArgument(request.params[2], UniValue::VARR);
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());
- if (!address.IsValid())
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+input.get_str());
- if (setAddress.count(address))
- throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+input.get_str());
- setAddress.insert(address);
+ CTxDestination dest = DecodeDestination(input.get_str());
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + input.get_str());
+ }
+ if (!destinations.insert(dest).second) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str());
+ }
}
}
@@ -2759,9 +2926,12 @@ UniValue listunspent(const JSONRPCRequest& request)
nMaximumCount = options["maximumCount"].get_int64();
}
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
UniValue results(UniValue::VARR);
std::vector<COutput> vecOutputs;
- assert(pwallet != nullptr);
LOCK2(cs_main, pwallet->cs_wallet);
pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
@@ -2770,7 +2940,7 @@ UniValue listunspent(const JSONRPCRequest& request)
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
bool fValidAddress = ExtractDestination(scriptPubKey, address);
- if (setAddress.size() && (!fValidAddress || !setAddress.count(address)))
+ if (destinations.size() && (!fValidAddress || !destinations.count(address)))
continue;
UniValue entry(UniValue::VOBJ);
@@ -2778,7 +2948,7 @@ UniValue listunspent(const JSONRPCRequest& request)
entry.push_back(Pair("vout", out.i));
if (fValidAddress) {
- entry.push_back(Pair("address", CBitcoinAddress(address).ToString()));
+ entry.push_back(Pair("address", EncodeDestination(address)));
if (pwallet->mapAddressBook.count(address)) {
entry.push_back(Pair("account", pwallet->mapAddressBook[address].name));
@@ -2872,6 +3042,10 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
ObserveSafeMode();
RPCTypeCheck(request.params, {UniValue::VSTR});
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
CCoinControl coinControl;
int changePosition = -1;
bool lockUnspents = false;
@@ -2904,12 +3078,13 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
true, true);
if (options.exists("changeAddress")) {
- CBitcoinAddress address(options["changeAddress"].get_str());
+ CTxDestination dest = DecodeDestination(options["changeAddress"].get_str());
- if (!address.IsValid())
+ if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address");
+ }
- coinControl.destChange = address.Get();
+ coinControl.destChange = dest;
}
if (options.exists("changePosition"))
@@ -3084,48 +3259,57 @@ UniValue bumpfee(const JSONRPCRequest& request)
}
}
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
LOCK2(cs_main, pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
- CFeeBumper feeBump(pwallet, hash, coin_control, totalFee);
- BumpFeeResult res = feeBump.getResult();
- if (res != BumpFeeResult::OK)
- {
+
+ std::vector<std::string> errors;
+ CAmount old_fee;
+ CAmount new_fee;
+ CMutableTransaction mtx;
+ feebumper::Result res = feebumper::CreateTransaction(pwallet, hash, coin_control, totalFee, errors, old_fee, new_fee, mtx);
+ if (res != feebumper::Result::OK) {
switch(res) {
- case BumpFeeResult::INVALID_ADDRESS_OR_KEY:
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, feeBump.getErrors()[0]);
+ case feebumper::Result::INVALID_ADDRESS_OR_KEY:
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errors[0]);
break;
- case BumpFeeResult::INVALID_REQUEST:
- throw JSONRPCError(RPC_INVALID_REQUEST, feeBump.getErrors()[0]);
+ case feebumper::Result::INVALID_REQUEST:
+ throw JSONRPCError(RPC_INVALID_REQUEST, errors[0]);
break;
- case BumpFeeResult::INVALID_PARAMETER:
- throw JSONRPCError(RPC_INVALID_PARAMETER, feeBump.getErrors()[0]);
+ case feebumper::Result::INVALID_PARAMETER:
+ throw JSONRPCError(RPC_INVALID_PARAMETER, errors[0]);
break;
- case BumpFeeResult::WALLET_ERROR:
- throw JSONRPCError(RPC_WALLET_ERROR, feeBump.getErrors()[0]);
+ case feebumper::Result::WALLET_ERROR:
+ throw JSONRPCError(RPC_WALLET_ERROR, errors[0]);
break;
default:
- throw JSONRPCError(RPC_MISC_ERROR, feeBump.getErrors()[0]);
+ throw JSONRPCError(RPC_MISC_ERROR, errors[0]);
break;
}
}
// sign bumped transaction
- if (!feeBump.signTransaction(pwallet)) {
+ if (!feebumper::SignTransaction(pwallet, mtx)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
}
// commit the bumped transaction
- if(!feeBump.commit(pwallet)) {
- throw JSONRPCError(RPC_WALLET_ERROR, feeBump.getErrors()[0]);
+ uint256 txid;
+ if (feebumper::CommitTransaction(pwallet, hash, std::move(mtx), errors, txid) != feebumper::Result::OK) {
+ throw JSONRPCError(RPC_WALLET_ERROR, errors[0]);
}
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("txid", feeBump.getBumpedTxId().GetHex()));
- result.push_back(Pair("origfee", ValueFromAmount(feeBump.getOldFee())));
- result.push_back(Pair("fee", ValueFromAmount(feeBump.getNewFee())));
- UniValue errors(UniValue::VARR);
- for (const std::string& err: feeBump.getErrors())
- errors.push_back(err);
- result.push_back(Pair("errors", errors));
+ result.push_back(Pair("txid", txid.GetHex()));
+ result.push_back(Pair("origfee", ValueFromAmount(old_fee)));
+ result.push_back(Pair("fee", ValueFromAmount(new_fee)));
+ UniValue result_errors(UniValue::VARR);
+ for (const std::string& error : errors) {
+ result_errors.push_back(error);
+ }
+ result.push_back(Pair("errors", result_errors));
return result;
}
@@ -3175,6 +3359,81 @@ UniValue generate(const JSONRPCRequest& request)
return generateBlocks(coinbase_script, num_generate, max_tries, true);
}
+UniValue rescanblockchain(const JSONRPCRequest& request)
+{
+ CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() > 2) {
+ throw std::runtime_error(
+ "rescanblockchain (\"start_height\") (\"stop_height\")\n"
+ "\nRescan the local blockchain for wallet related transactions.\n"
+ "\nArguments:\n"
+ "1. \"start_height\" (numeric, optional) block height where the rescan should start\n"
+ "2. \"stop_height\" (numeric, optional) the last block height that should be scanned\n"
+ "\nResult:\n"
+ "{\n"
+ " \"start_height\" (numeric) The block height where the rescan has started. If omitted, rescan started from the genesis block.\n"
+ " \"stop_height\" (numeric) The height of the last rescanned block. If omitted, rescan stopped at the chain tip.\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("rescanblockchain", "100000 120000")
+ + HelpExampleRpc("rescanblockchain", "100000, 120000")
+ );
+ }
+
+ LOCK2(cs_main, pwallet->cs_wallet);
+
+ CBlockIndex *pindexStart = chainActive.Genesis();
+ CBlockIndex *pindexStop = nullptr;
+ if (!request.params[0].isNull()) {
+ pindexStart = chainActive[request.params[0].get_int()];
+ if (!pindexStart) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height");
+ }
+ }
+
+ if (!request.params[1].isNull()) {
+ pindexStop = chainActive[request.params[1].get_int()];
+ if (!pindexStop) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
+ }
+ else if (pindexStop->nHeight < pindexStart->nHeight) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater then start_height");
+ }
+ }
+
+ // We can't rescan beyond non-pruned blocks, stop and throw an error
+ if (fPruneMode) {
+ CBlockIndex *block = pindexStop ? pindexStop : chainActive.Tip();
+ while (block && block->nHeight >= pindexStart->nHeight) {
+ if (!(block->nStatus & BLOCK_HAVE_DATA)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
+ }
+ block = block->pprev;
+ }
+ }
+
+ CBlockIndex *stopBlock = pwallet->ScanForWalletTransactions(pindexStart, pindexStop, true);
+ if (!stopBlock) {
+ if (pwallet->IsAbortingRescan()) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
+ }
+ // if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex
+ stopBlock = pindexStop ? pindexStop : chainActive.Tip();
+ }
+ else {
+ throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
+ }
+
+ UniValue response(UniValue::VOBJ);
+ response.pushKV("start_height", pindexStart->nHeight);
+ response.pushKV("stop_height", stopBlock->nHeight);
+ return response;
+}
+
extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue importprivkey(const JSONRPCRequest& request);
@@ -3185,6 +3444,7 @@ extern UniValue importwallet(const JSONRPCRequest& request);
extern UniValue importprunedfunds(const JSONRPCRequest& request);
extern UniValue removeprunedfunds(const JSONRPCRequest& request);
extern UniValue importmulti(const JSONRPCRequest& request);
+extern UniValue rescanblockchain(const JSONRPCRequest& request);
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -3194,7 +3454,7 @@ static const CRPCCommand commands[] =
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
{ "wallet", "abortrescan", &abortrescan, {} },
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} },
- { "wallet", "addwitnessaddress", &addwitnessaddress, {"address"} },
+ { "wallet", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
@@ -3239,15 +3499,13 @@ static const CRPCCommand commands[] =
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
{ "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} },
{ "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
+ { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
{ "generating", "generate", &generate, {"nblocks","maxtries"} },
};
void RegisterWalletRPCCommands(CRPCTable &t)
{
- if (gArgs.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/rpcwallet.h b/src/wallet/rpcwallet.h
index db0808b93b..14e51610d9 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -5,7 +5,10 @@
#ifndef BITCOIN_WALLET_RPCWALLET_H
#define BITCOIN_WALLET_RPCWALLET_H
+#include <string>
+
class CRPCTable;
+class CWallet;
class JSONRPCRequest;
void RegisterWalletRPCCommands(CRPCTable &t);
diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp
index 330878ceb5..b95bb14335 100644
--- a/src/wallet/test/accounting_tests.cpp
+++ b/src/wallet/test/accounting_tests.cpp
@@ -2,26 +2,24 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "wallet/wallet.h"
+#include <wallet/wallet.h>
-#include "wallet/test/wallet_test_fixture.h"
+#include <wallet/test/wallet_test_fixture.h>
#include <stdint.h>
#include <boost/test/unit_test.hpp>
-extern CWallet* pwalletMain;
-
BOOST_FIXTURE_TEST_SUITE(accounting_tests, WalletTestingSetup)
static void
-GetResults(std::map<CAmount, CAccountingEntry>& results)
+GetResults(CWallet *wallet, std::map<CAmount, CAccountingEntry>& results)
{
std::list<CAccountingEntry> aes;
results.clear();
- BOOST_CHECK(pwalletMain->ReorderTransactions() == DB_LOAD_OK);
- pwalletMain->ListAccountCreditDebit("", aes);
+ BOOST_CHECK(wallet->ReorderTransactions() == DB_LOAD_OK);
+ wallet->ListAccountCreditDebit("", aes);
for (CAccountingEntry& ae : aes)
{
results[ae.nOrderPos] = ae;
@@ -54,7 +52,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.strOtherAccount = "c";
pwalletMain->AddAccountingEntry(ae);
- GetResults(results);
+ GetResults(pwalletMain.get(), results);
BOOST_CHECK(pwalletMain->nOrderPosNext == 3);
BOOST_CHECK(2 == results.size());
@@ -70,7 +68,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nOrderPos = pwalletMain->IncOrderPosNext();
pwalletMain->AddAccountingEntry(ae);
- GetResults(results);
+ GetResults(pwalletMain.get(), results);
BOOST_CHECK(results.size() == 3);
BOOST_CHECK(pwalletMain->nOrderPosNext == 4);
@@ -83,7 +81,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
wtx.mapValue["comment"] = "y";
{
- CMutableTransaction tx(wtx);
+ CMutableTransaction tx(*wtx.tx);
--tx.nLockTime; // Just to change the hash :)
wtx.SetTx(MakeTransactionRef(std::move(tx)));
}
@@ -93,7 +91,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
wtx.mapValue["comment"] = "x";
{
- CMutableTransaction tx(wtx);
+ CMutableTransaction tx(*wtx.tx);
--tx.nLockTime; // Just to change the hash :)
wtx.SetTx(MakeTransactionRef(std::move(tx)));
}
@@ -102,7 +100,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
vpwtx[2]->nTimeReceived = (unsigned int)1333333329;
vpwtx[2]->nOrderPos = -1;
- GetResults(results);
+ GetResults(pwalletMain.get(), results);
BOOST_CHECK(results.size() == 3);
BOOST_CHECK(pwalletMain->nOrderPosNext == 6);
@@ -120,7 +118,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nOrderPos = -1;
pwalletMain->AddAccountingEntry(ae);
- GetResults(results);
+ GetResults(pwalletMain.get(), 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 cbd74b6f96..3ff8c6d224 100644
--- a/src/wallet/test/crypto_tests.cpp
+++ b/src/wallet/test/crypto_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "test/test_bitcoin.h"
-#include "utilstrencodings.h"
-#include "wallet/crypter.h"
+#include <test/test_bitcoin.h>
+#include <utilstrencodings.h>
+#include <wallet/crypter.h>
#include <vector>
@@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(passphrase) {
std::string hash(GetRandHash().ToString());
std::vector<unsigned char> vchSalt(8);
- GetRandBytes(&vchSalt[0], vchSalt.size());
+ GetRandBytes(vchSalt.data(), vchSalt.size());
uint32_t rounds = InsecureRand32();
if (rounds > 30000)
rounds = 30000;
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index e2f48c45ab..3ee83d2d7c 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -2,13 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "wallet/test/wallet_test_fixture.h"
+#include <wallet/test/wallet_test_fixture.h>
-#include "rpc/server.h"
-#include "wallet/db.h"
-#include "wallet/wallet.h"
-
-CWallet *pwalletMain;
+#include <rpc/server.h>
+#include <wallet/db.h>
WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
TestingSetup(chainName)
@@ -17,18 +14,16 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
bool fFirstRun;
std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
- pwalletMain = new CWallet(std::move(dbw));
+ pwalletMain = MakeUnique<CWallet>(std::move(dbw));
pwalletMain->LoadWallet(fFirstRun);
- RegisterValidationInterface(pwalletMain);
+ RegisterValidationInterface(pwalletMain.get());
RegisterWalletRPCCommands(tableRPC);
}
WalletTestingSetup::~WalletTestingSetup()
{
- UnregisterValidationInterface(pwalletMain);
- delete pwalletMain;
- pwalletMain = nullptr;
+ UnregisterValidationInterface(pwalletMain.get());
bitdb.Flush(true);
bitdb.Reset();
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index 9373b7907c..292d654438 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -5,13 +5,17 @@
#ifndef BITCOIN_WALLET_TEST_FIXTURE_H
#define BITCOIN_WALLET_TEST_FIXTURE_H
-#include "test/test_bitcoin.h"
+#include <test/test_bitcoin.h>
+
+#include <wallet/wallet.h>
/** Testing setup and teardown for wallet.
*/
struct WalletTestingSetup: public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~WalletTestingSetup();
+
+ std::unique_ptr<CWallet> pwalletMain;
};
#endif
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 5ebacd57d3..80e31a1ce0 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -2,25 +2,23 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "wallet/wallet.h"
+#include <wallet/wallet.h>
#include <set>
#include <stdint.h>
#include <utility>
#include <vector>
-#include "consensus/validation.h"
-#include "rpc/server.h"
-#include "test/test_bitcoin.h"
-#include "validation.h"
-#include "wallet/coincontrol.h"
-#include "wallet/test/wallet_test_fixture.h"
+#include <consensus/validation.h>
+#include <rpc/server.h>
+#include <test/test_bitcoin.h>
+#include <validation.h>
+#include <wallet/coincontrol.h>
+#include <wallet/test/wallet_test_fixture.h>
#include <boost/test/unit_test.hpp>
#include <univalue.h>
-extern CWallet* pwalletMain;
-
extern UniValue importmulti(const JSONRPCRequest& request);
extern UniValue dumpwallet(const JSONRPCRequest& request);
extern UniValue importwallet(const JSONRPCRequest& request);
@@ -386,7 +384,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
{
CWallet wallet;
AddKey(wallet, coinbaseKey);
- BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip));
+ BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip, nullptr));
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
}
@@ -399,7 +397,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
{
CWallet wallet;
AddKey(wallet, coinbaseKey);
- BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip));
+ BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip, nullptr));
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
}
@@ -489,6 +487,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
vpwallets[0] = &wallet;
::importwallet(request);
+ LOCK(wallet.cs_wallet);
BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3);
BOOST_CHECK_EQUAL(coinbaseTxns.size(), 103);
for (size_t i = 0; i < coinbaseTxns.size(); ++i) {
@@ -534,6 +533,7 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
SetMockTime(mockTime);
CBlockIndex* block = nullptr;
if (blockTime > 0) {
+ LOCK(cs_main);
auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second);
const uint256& hash = inserted.first->first;
@@ -547,6 +547,7 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
wtx.SetMerkleBranch(block, 0);
}
wallet.AddToWallet(wtx);
+ LOCK(wallet.cs_wallet);
return wallet.mapWallet.at(wtx.GetHash()).nTimeSmart;
}
@@ -583,6 +584,7 @@ BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
{
CTxDestination dest = CKeyID();
+ LOCK(pwalletMain->cs_wallet);
pwalletMain->AddDestData(dest, "misc", "val_misc");
pwalletMain->AddDestData(dest, "rr0", "val_rr0");
pwalletMain->AddDestData(dest, "rr1", "val_rr1");
@@ -604,7 +606,7 @@ public:
bool firstRun;
wallet->LoadWallet(firstRun);
AddKey(*wallet, coinbaseKey);
- wallet->ScanForWalletTransactions(chainActive.Genesis());
+ wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr);
}
~ListCoinsTestingSetup()
@@ -625,6 +627,7 @@ public:
BOOST_CHECK(wallet->CreateTransaction({recipient}, wtx, reservekey, fee, changePos, error, dummy));
CValidationState state;
BOOST_CHECK(wallet->CommitTransaction(wtx, reservekey, nullptr, state));
+ LOCK(wallet->cs_wallet);
auto it = wallet->mapWallet.find(wtx.GetHash());
BOOST_CHECK(it != wallet->mapWallet.end());
CreateAndProcessBlock({CMutableTransaction(*it->second.tx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index d1d2060b0c..dafd708d09 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -3,36 +3,35 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "wallet/wallet.h"
-
-#include "base58.h"
-#include "checkpoints.h"
-#include "chain.h"
-#include "wallet/coincontrol.h"
-#include "consensus/consensus.h"
-#include "consensus/validation.h"
-#include "fs.h"
-#include "init.h"
-#include "key.h"
-#include "keystore.h"
-#include "validation.h"
-#include "net.h"
-#include "policy/fees.h"
-#include "policy/policy.h"
-#include "policy/rbf.h"
-#include "primitives/block.h"
-#include "primitives/transaction.h"
-#include "script/script.h"
-#include "script/sign.h"
-#include "scheduler.h"
-#include "timedata.h"
-#include "txmempool.h"
-#include "util.h"
-#include "ui_interface.h"
-#include "utilmoneystr.h"
-#include "wallet/fees.h"
+#include <wallet/wallet.h>
+
+#include <base58.h>
+#include <checkpoints.h>
+#include <chain.h>
+#include <wallet/coincontrol.h>
+#include <consensus/consensus.h>
+#include <consensus/validation.h>
+#include <fs.h>
+#include <wallet/init.h>
+#include <key.h>
+#include <keystore.h>
+#include <validation.h>
+#include <net.h>
+#include <policy/fees.h>
+#include <policy/policy.h>
+#include <policy/rbf.h>
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <script/script.h>
+#include <scheduler.h>
+#include <timedata.h>
+#include <txmempool.h>
+#include <util.h>
+#include <utilmoneystr.h>
+#include <wallet/fees.h>
#include <assert.h>
+#include <future>
#include <boost/algorithm/string/replace.hpp>
#include <boost/thread.hpp>
@@ -111,7 +110,26 @@ public:
Process(script);
}
- void operator()(const CNoDestination &none) {}
+ void operator()(const WitnessV0ScriptHash& scriptID)
+ {
+ CScriptID id;
+ CRIPEMD160().Write(scriptID.begin(), 32).Finalize(id.begin());
+ CScript script;
+ if (keystore.GetCScript(id, script)) {
+ Process(script);
+ }
+ }
+
+ void operator()(const WitnessV0KeyHash& keyid)
+ {
+ CKeyID id(keyid);
+ if (keystore.HaveKey(id)) {
+ vKeys.push_back(id);
+ }
+ }
+
+ template<typename X>
+ void operator()(const X &none) {}
};
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
@@ -264,7 +282,7 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
}
}
-bool CWallet::LoadKeyMetadata(const CTxDestination& keyID, const CKeyMetadata &meta)
+bool CWallet::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &meta)
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
UpdateTimeFirstKey(meta.nCreateTime);
@@ -272,6 +290,14 @@ bool CWallet::LoadKeyMetadata(const CTxDestination& keyID, const CKeyMetadata &m
return true;
}
+bool CWallet::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &meta)
+{
+ AssertLockHeld(cs_wallet); // m_script_metadata
+ UpdateTimeFirstKey(meta.nCreateTime);
+ m_script_metadata[script_id] = meta;
+ return true;
+}
+
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
@@ -307,7 +333,7 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
* these. Do not add them to the wallet and warn. */
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
{
- std::string strAddr = CBitcoinAddress(CScriptID(redeemScript)).ToString();
+ std::string strAddr = EncodeDestination(CScriptID(redeemScript));
LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n",
__func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr);
return true;
@@ -320,7 +346,7 @@ bool CWallet::AddWatchOnly(const CScript& dest)
{
if (!CCryptoKeyStore::AddWatchOnly(dest))
return false;
- const CKeyMetadata& meta = mapKeyMetadata[CScriptID(dest)];
+ const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)];
UpdateTimeFirstKey(meta.nCreateTime);
NotifyWatchonlyChanged(true);
return CWalletDB(*dbw).WriteWatchOnly(dest, meta);
@@ -328,7 +354,7 @@ bool CWallet::AddWatchOnly(const CScript& dest)
bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime)
{
- mapKeyMetadata[CScriptID(dest)].nCreateTime = nCreateTime;
+ m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime;
return AddWatchOnly(dest);
}
@@ -390,11 +416,11 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
{
int64_t nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
- pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)));
+ pMasterKey.second.nDeriveIterations = static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))));
nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
- pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
+ pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime)))) / 2;
if (pMasterKey.second.nDeriveIterations < 25000)
pMasterKey.second.nDeriveIterations = 25000;
@@ -513,12 +539,16 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran
copyFrom = &mapWallet[hash];
}
}
+
+ assert(copyFrom);
+
// Now copy data from copyFrom to rest:
for (TxSpends::iterator it = range.first; it != range.second; ++it)
{
const uint256& hash = it->second;
CWalletTx* copyTo = &mapWallet[hash];
if (copyFrom == copyTo) continue;
+ assert(copyFrom && "Oldest wallet transaction in range assumed to have been found.");
if (!copyFrom->IsEquivalentTo(*copyTo)) continue;
copyTo->mapValue = copyFrom->mapValue;
copyTo->vOrderForm = copyFrom->vOrderForm;
@@ -595,11 +625,11 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
CCrypter crypter;
int64_t nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
- kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime));
+ kMasterKey.nDeriveIterations = static_cast<unsigned int>(2500000 / ((double)(GetTimeMillis() - nStartTime)));
nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
- kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
+ kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + static_cast<unsigned int>(kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime)))) / 2;
if (kMasterKey.nDeriveIterations < 25000)
kMasterKey.nDeriveIterations = 25000;
@@ -681,9 +711,9 @@ DBErrors CWallet::ReorderTransactions()
typedef std::multimap<int64_t, TxPair > TxItems;
TxItems txByTime;
- for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ for (auto& entry : mapWallet)
{
- CWalletTx* wtx = &((*it).second);
+ CWalletTx* wtx = &entry.second;
txByTime.insert(std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr)));
}
std::list<CAccountingEntry> acentries;
@@ -914,6 +944,15 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
wtx.fFromMe = wtxIn.fFromMe;
fUpdated = true;
}
+ // If we have a witness-stripped version of this transaction, and we
+ // see a new version with a witness, then we must be upgrading a pre-segwit
+ // wallet. Store the new version of the transaction with the witness,
+ // as the stripped-version must be invalid.
+ // TODO: Store all versions of the transaction, instead of just one.
+ if (wtxIn.tx->HasWitness() && !wtx.tx->HasWitness()) {
+ wtx.SetTx(wtxIn.tx);
+ fUpdated = true;
+ }
}
//// debug print
@@ -933,7 +972,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
// notify an external script when a wallet transaction comes in or is updated
std::string strCmd = gArgs.GetArg("-walletnotify", "");
- if ( !strCmd.empty())
+ if (!strCmd.empty())
{
boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());
boost::thread t(runCommand, strCmd); // thread runs free
@@ -1185,6 +1224,19 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pin
void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) {
LOCK2(cs_main, cs_wallet);
SyncTransaction(ptx);
+
+ auto it = mapWallet.find(ptx->GetHash());
+ if (it != mapWallet.end()) {
+ it->second.fInMempool = true;
+ }
+}
+
+void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
+ LOCK(cs_wallet);
+ auto it = mapWallet.find(ptx->GetHash());
+ if (it != mapWallet.end()) {
+ it->second.fInMempool = false;
+ }
}
void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) {
@@ -1199,10 +1251,14 @@ void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const
for (const CTransactionRef& ptx : vtxConflicted) {
SyncTransaction(ptx);
+ TransactionRemovedFromMempool(ptx);
}
for (size_t i = 0; i < pblock->vtx.size(); i++) {
SyncTransaction(pblock->vtx[i], pindex, i);
+ TransactionRemovedFromMempool(pblock->vtx[i]);
}
+
+ m_last_block_processed = pindex;
}
void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) {
@@ -1215,6 +1271,36 @@ void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) {
+void CWallet::BlockUntilSyncedToCurrentChain() {
+ AssertLockNotHeld(cs_main);
+ AssertLockNotHeld(cs_wallet);
+
+ {
+ // Skip the queue-draining stuff if we know we're caught up with
+ // chainActive.Tip()...
+ // We could also take cs_wallet here, and call m_last_block_processed
+ // protected by cs_wallet instead of cs_main, but as long as we need
+ // cs_main here anyway, its easier to just call it cs_main-protected.
+ LOCK(cs_main);
+ const CBlockIndex* initialChainTip = chainActive.Tip();
+
+ if (m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) {
+ return;
+ }
+ }
+
+ // ...otherwise put a callback in the validation interface queue and wait
+ // for the queue to drain enough to execute it (indicating we are caught up
+ // at least with the time we entered this function).
+
+ std::promise<void> promise;
+ CallFunctionInValidationInterfaceQueue([&promise] {
+ promise.set_value();
+ });
+ promise.get_future().wait();
+}
+
+
isminetype CWallet::IsMine(const CTxIn &txin) const
{
{
@@ -1539,7 +1625,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update)
LogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0);
if (startBlock) {
- const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, update);
+ const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, update);
if (failedBlock) {
return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1;
}
@@ -1555,12 +1641,19 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update)
* Returns null if scan was successful. Otherwise, if a complete rescan was not
* possible (due to pruning or corruption), returns pointer to the most recent
* block that could not be scanned.
+ *
+ * If pindexStop is not a nullptr, the scan will stop at the block-index
+ * defined by pindexStop
*/
-CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
+CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, bool fUpdate)
{
int64_t nNow = GetTime();
const CChainParams& chainParams = Params();
+ if (pindexStop) {
+ assert(pindexStop->nHeight >= pindexStart->nHeight);
+ }
+
CBlockIndex* pindex = pindexStart;
CBlockIndex* ret = nullptr;
{
@@ -1588,6 +1681,9 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f
} else {
ret = pindex;
}
+ if (pindex == pindexStop) {
+ break;
+ }
pindex = chainActive.Next(pindex);
}
if (pindex && fAbortRescan) {
@@ -1623,11 +1719,8 @@ void CWallet::ReacceptWalletTransactions()
}
// Try to add wallet transactions to memory pool
- for (std::pair<const int64_t, CWalletTx*>& item : mapSorted)
- {
+ for (std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second);
-
- LOCK(mempool.cs);
CValidationState state;
wtx.AcceptToMemoryPool(maxTxFee, state);
}
@@ -1679,7 +1772,7 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const
debit += nDebitCached;
else
{
- nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE);
+ nDebitCached = pwallet->GetDebit(*tx, ISMINE_SPENDABLE);
fDebitCached = true;
debit += nDebitCached;
}
@@ -1690,7 +1783,7 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const
debit += nWatchDebitCached;
else
{
- nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY);
+ nWatchDebitCached = pwallet->GetDebit(*tx, ISMINE_WATCH_ONLY);
fWatchDebitCached = true;
debit += nWatchDebitCached;
}
@@ -1712,7 +1805,7 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const
credit += nCreditCached;
else
{
- nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);
+ nCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
fCreditCached = true;
credit += nCreditCached;
}
@@ -1723,7 +1816,7 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const
credit += nWatchCreditCached;
else
{
- nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY);
+ nWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
fWatchCreditCached = true;
credit += nWatchCreditCached;
}
@@ -1737,7 +1830,7 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
{
if (fUseCache && fImmatureCreditCached)
return nImmatureCreditCached;
- nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);
+ nImmatureCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
fImmatureCreditCached = true;
return nImmatureCreditCached;
}
@@ -1775,13 +1868,13 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
return nCredit;
}
-CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const
+CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const
{
if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())
{
if (fUseCache && fImmatureWatchCreditCached)
return nImmatureWatchCreditCached;
- nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY);
+ nImmatureWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
fImmatureWatchCreditCached = true;
return nImmatureWatchCreditCached;
}
@@ -1789,7 +1882,7 @@ CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const
return 0;
}
-CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
+CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool fUseCache) const
{
if (pwallet == nullptr)
return 0;
@@ -1822,21 +1915,20 @@ CAmount CWalletTx::GetChange() const
{
if (fChangeCached)
return nChangeCached;
- nChangeCached = pwallet->GetChange(*this);
+ nChangeCached = pwallet->GetChange(*tx);
fChangeCached = true;
return nChangeCached;
}
bool CWalletTx::InMempool() const
{
- LOCK(mempool.cs);
- return mempool.exists(GetHash());
+ return fInMempool;
}
bool CWalletTx::IsTrusted() const
{
// Quick answer in most cases
- if (!CheckFinalTx(*this))
+ if (!CheckFinalTx(*tx))
return false;
int nDepth = GetDepthInMainChain();
if (nDepth >= 1)
@@ -1937,9 +2029,9 @@ CAmount CWallet::GetBalance() const
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
- for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ for (const auto& entry : mapWallet)
{
- const CWalletTx* pcoin = &(*it).second;
+ const CWalletTx* pcoin = &entry.second;
if (pcoin->IsTrusted())
nTotal += pcoin->GetAvailableCredit();
}
@@ -1953,9 +2045,9 @@ CAmount CWallet::GetUnconfirmedBalance() const
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
- for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ for (const auto& entry : mapWallet)
{
- const CWalletTx* pcoin = &(*it).second;
+ const CWalletTx* pcoin = &entry.second;
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool())
nTotal += pcoin->GetAvailableCredit();
}
@@ -1968,9 +2060,9 @@ CAmount CWallet::GetImmatureBalance() const
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
- for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ for (const auto& entry : mapWallet)
{
- const CWalletTx* pcoin = &(*it).second;
+ const CWalletTx* pcoin = &entry.second;
nTotal += pcoin->GetImmatureCredit();
}
}
@@ -1982,9 +2074,9 @@ CAmount CWallet::GetWatchOnlyBalance() const
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
- for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ for (const auto& entry : mapWallet)
{
- const CWalletTx* pcoin = &(*it).second;
+ const CWalletTx* pcoin = &entry.second;
if (pcoin->IsTrusted())
nTotal += pcoin->GetAvailableWatchOnlyCredit();
}
@@ -1998,9 +2090,9 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
- for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ for (const auto& entry : mapWallet)
{
- const CWalletTx* pcoin = &(*it).second;
+ const CWalletTx* pcoin = &entry.second;
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool())
nTotal += pcoin->GetAvailableWatchOnlyCredit();
}
@@ -2013,9 +2105,9 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
- for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ for (const auto& entry : mapWallet)
{
- const CWalletTx* pcoin = &(*it).second;
+ const CWalletTx* pcoin = &entry.second;
nTotal += pcoin->GetImmatureWatchOnlyCredit();
}
}
@@ -2080,7 +2172,7 @@ CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
return balance;
}
-void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t &nMaximumCount, const int &nMinDepth, const int &nMaxDepth) const
+void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t nMaximumCount, const int nMinDepth, const int nMaxDepth) const
{
vCoins.clear();
@@ -2089,12 +2181,12 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
CAmount nTotal = 0;
- for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ for (const auto& entry : mapWallet)
{
- const uint256& wtxid = it->first;
- const CWalletTx* pcoin = &(*it).second;
+ const uint256& wtxid = entry.first;
+ const CWalletTx* pcoin = &entry.second;
- if (!CheckFinalTx(*pcoin))
+ if (!CheckFinalTx(*pcoin->tx))
continue;
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
@@ -2153,10 +2245,10 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
if (pcoin->tx->vout[i].nValue < nMinimumAmount || pcoin->tx->vout[i].nValue > nMaximumAmount)
continue;
- if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint((*it).first, i)))
+ if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i)))
continue;
- if (IsLockedCoin((*it).first, i))
+ if (IsLockedCoin(entry.first, i))
continue;
if (IsSpent(wtxid, i))
@@ -2503,9 +2595,8 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
{
std::vector<CRecipient> vecSend;
- // Turn the txout set into a CRecipient vector
- for (size_t idx = 0; idx < tx.vout.size(); idx++)
- {
+ // Turn the txout set into a CRecipient vector.
+ for (size_t idx = 0; idx < tx.vout.size(); idx++) {
const CTxOut& txOut = tx.vout[idx];
CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1};
vecSend.push_back(recipient);
@@ -2513,8 +2604,13 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
coinControl.fAllowOtherInputs = true;
- for (const CTxIn& txin : tx.vin)
+ for (const CTxIn& txin : tx.vin) {
coinControl.Select(txin.prevout);
+ }
+
+ // Acquire the locks to prevent races to the new locked unspents between the
+ // CreateTransaction call and LockCoin calls (when lockUnspents is true).
+ LOCK2(cs_main, cs_wallet);
CReserveKey reservekey(this);
CWalletTx wtx;
@@ -2524,31 +2620,28 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
if (nChangePosInOut != -1) {
tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]);
- // we don't have the normal Create/Commit cycle, and don't want to risk reusing change,
- // so just remove the key from the keypool here.
+ // We don't have the normal Create/Commit cycle, and don't want to risk
+ // reusing change, so just remove the key from the keypool here.
reservekey.KeepKey();
}
- // Copy output sizes from new transaction; they may have had the fee subtracted from them
- for (unsigned int idx = 0; idx < tx.vout.size(); idx++)
+ // Copy output sizes from new transaction; they may have had the fee
+ // subtracted from them.
+ for (unsigned int idx = 0; idx < tx.vout.size(); idx++) {
tx.vout[idx].nValue = wtx.tx->vout[idx].nValue;
+ }
- // Add new txins (keeping original txin scriptSig/order)
- for (const CTxIn& txin : wtx.tx->vin)
- {
- if (!coinControl.IsSelected(txin.prevout))
- {
+ // Add new txins while keeping original txin scriptSig/order.
+ for (const CTxIn& txin : wtx.tx->vin) {
+ if (!coinControl.IsSelected(txin.prevout)) {
tx.vin.push_back(txin);
- if (lockUnspents)
- {
- LOCK2(cs_main, cs_wallet);
- LockCoin(txin.prevout);
+ if (lockUnspents) {
+ LockCoin(txin.prevout);
}
}
}
-
return true;
}
@@ -2675,6 +2768,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
if (recipient.fSubtractFeeFromAmount)
{
+ assert(nSubtractFeeFromAmount != 0);
txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient
if (fFirst) // first receiver pays the remainder not divisible by output count
@@ -2875,7 +2969,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));
// Limit size
- if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT)
+ if (GetTransactionWeight(*wtxNew.tx) >= MAX_STANDARD_TX_WEIGHT)
{
strFailReason = _("Transaction too large");
return false;
@@ -2937,14 +3031,18 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
// Track how many getdata requests our transaction gets
mapRequestCount[wtxNew.GetHash()] = 0;
+ // Get the inserted-CWalletTx from mapWallet so that the
+ // fInMempool flag is cached properly
+ CWalletTx& wtx = mapWallet[wtxNew.GetHash()];
+
if (fBroadcastTransactions)
{
// Broadcast
- if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) {
+ if (!wtx.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);
+ wtx.RelayWalletTransaction(connman);
}
}
}
@@ -3072,9 +3170,9 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
}
NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
- if (!strPurpose.empty() && !CWalletDB(*dbw).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose))
+ if (!strPurpose.empty() && !CWalletDB(*dbw).WritePurpose(EncodeDestination(address), strPurpose))
return false;
- return CWalletDB(*dbw).WriteName(CBitcoinAddress(address).ToString(), strName);
+ return CWalletDB(*dbw).WriteName(EncodeDestination(address), strName);
}
bool CWallet::DelAddressBook(const CTxDestination& address)
@@ -3083,7 +3181,7 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
LOCK(cs_wallet); // mapAddressBook
// Delete destdata tuples associated with address
- std::string strAddress = CBitcoinAddress(address).ToString();
+ std::string strAddress = EncodeDestination(address);
for (const std::pair<std::string, std::string> &item : mapAddressBook[address].destdata)
{
CWalletDB(*dbw).EraseDestData(strAddress, item.first);
@@ -3093,8 +3191,8 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED);
- CWalletDB(*dbw).ErasePurpose(CBitcoinAddress(address).ToString());
- return CWalletDB(*dbw).EraseName(CBitcoinAddress(address).ToString());
+ CWalletDB(*dbw).ErasePurpose(EncodeDestination(address));
+ return CWalletDB(*dbw).EraseName(EncodeDestination(address));
}
const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const
@@ -3600,13 +3698,10 @@ void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) c
// map in which we'll infer heights of other keys
CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin
std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock;
- std::set<CKeyID> setKeys;
- GetKeys(setKeys);
- for (const CKeyID &keyid : setKeys) {
+ for (const CKeyID &keyid : GetKeys()) {
if (mapKeyBirth.count(keyid) == 0)
mapKeyFirstBlock[keyid] = pindexMax;
}
- setKeys.clear();
// if there are no such keys, we're done
if (mapKeyFirstBlock.empty())
@@ -3614,9 +3709,9 @@ void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) c
// find first block that affects those keys, if there are any left
std::vector<CKeyID> vAffected;
- for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) {
+ for (const auto& entry : mapWallet) {
// iterate over all wallet transactions...
- const CWalletTx &wtx = (*it).second;
+ const CWalletTx &wtx = entry.second;
BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock);
if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) {
// ... which are already in a block
@@ -3636,8 +3731,8 @@ void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) c
}
// Extract block timestamps for those keys
- for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++)
- mapKeyBirth[it->first] = it->second->GetBlockTime() - TIMESTAMP_WINDOW; // block times can be 2h off
+ for (const auto& entry : mapKeyFirstBlock)
+ mapKeyBirth[entry.first] = entry.second->GetBlockTime() - TIMESTAMP_WINDOW; // block times can be 2h off
}
/**
@@ -3711,14 +3806,14 @@ bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, co
return false;
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
- return CWalletDB(*dbw).WriteDestData(CBitcoinAddress(dest).ToString(), key, value);
+ return CWalletDB(*dbw).WriteDestData(EncodeDestination(dest), key, value);
}
bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key)
{
if (!mapAddressBook[dest].destdata.erase(key))
return false;
- return CWalletDB(*dbw).EraseDestData(CBitcoinAddress(dest).ToString(), key);
+ return CWalletDB(*dbw).EraseDestData(EncodeDestination(dest), key);
}
bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
@@ -3766,7 +3861,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
- std::unique_ptr<CWallet> tempWallet(new CWallet(std::move(dbw)));
+ std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(std::move(dbw));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DB_LOAD_OK) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
@@ -3829,28 +3924,28 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
if (fFirstRun)
{
- // Create new keyUser and set as default key
- if (gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) {
-
- // ensure this wallet.dat can only be opened by clients supporting HD with chain split
- walletInstance->SetMinVersion(FEATURE_HD_SPLIT);
-
- // generate a new master key
- CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
- if (!walletInstance->SetHDMasterKey(masterPubKey))
- throw std::runtime_error(std::string(__func__) + ": Storing master key failed");
+ // ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key
+ if (!gArgs.GetBoolArg("-usehd", true)) {
+ InitError(strprintf(_("Error creating %s: You can't create non-HD wallets with this version."), walletFile));
+ return nullptr;
}
+ walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY);
+
+ // generate a new master key
+ CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
+ if (!walletInstance->SetHDMasterKey(masterPubKey))
+ throw std::runtime_error(std::string(__func__) + ": Storing master key failed");
// Top up the keypool
if (!walletInstance->TopUpKeyPool()) {
InitError(_("Unable to generate initial keys") += "\n");
- return NULL;
+ return nullptr;
}
walletInstance->SetBestChain(chainActive.GetLocator());
}
else if (gArgs.IsArgSet("-usehd")) {
- bool useHD = gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET);
+ bool useHD = gArgs.GetBoolArg("-usehd", true);
if (walletInstance->IsHDEnabled() && !useHD) {
InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletFile));
return nullptr;
@@ -3863,8 +3958,6 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart);
- RegisterValidationInterface(walletInstance);
-
// Try to top up keypool. No-op if the wallet is locked.
walletInstance->TopUpKeyPool();
@@ -3876,6 +3969,10 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
if (walletdb.ReadBestBlock(locator))
pindexRescan = FindForkInGlobalIndex(chainActive, locator);
}
+
+ walletInstance->m_last_block_processed = chainActive.Tip();
+ RegisterValidationInterface(walletInstance);
+
if (chainActive.Tip() && chainActive.Tip() != pindexRescan)
{
//We can't rescan beyond non-pruned blocks, stop and throw an error
@@ -3903,7 +4000,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
}
nStart = GetTimeMillis();
- walletInstance->ScanForWalletTransactions(pindexRescan, true);
+ walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, true);
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart);
walletInstance->SetBestChain(chainActive.GetLocator());
walletInstance->dbw->IncrementUpdateCounter();
@@ -4019,7 +4116,20 @@ int CMerkleTx::GetBlocksToMaturity() const
}
-bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
+bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
{
- return ::AcceptToMemoryPool(mempool, state, tx, true, nullptr, nullptr, false, nAbsurdFee);
+ // Quick check to avoid re-setting fInMempool to false
+ if (mempool.exists(tx->GetHash())) {
+ return false;
+ }
+
+ // We must set fInMempool here - while it will be re-set to true by the
+ // entered-mempool callback, if we did not there would be a race where a
+ // user could call sendmoney in a loop and hit spurious out of funds errors
+ // because we think that the transaction they just generated's change is
+ // unavailable as we're not yet aware its in mempool.
+ bool ret = ::AcceptToMemoryPool(mempool, state, tx, nullptr /* pfMissingInputs */,
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, nAbsurdFee);
+ fInMempool = ret;
+ return ret;
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 73ad3bdeca..97581794a8 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -6,18 +6,18 @@
#ifndef BITCOIN_WALLET_WALLET_H
#define BITCOIN_WALLET_WALLET_H
-#include "amount.h"
-#include "policy/feerate.h"
-#include "streams.h"
-#include "tinyformat.h"
-#include "ui_interface.h"
-#include "utilstrencodings.h"
-#include "validationinterface.h"
-#include "script/ismine.h"
-#include "script/sign.h"
-#include "wallet/crypter.h"
-#include "wallet/walletdb.h"
-#include "wallet/rpcwallet.h"
+#include <amount.h>
+#include <policy/feerate.h>
+#include <streams.h>
+#include <tinyformat.h>
+#include <ui_interface.h>
+#include <utilstrencodings.h>
+#include <validationinterface.h>
+#include <script/ismine.h>
+#include <script/sign.h>
+#include <wallet/crypter.h>
+#include <wallet/walletdb.h>
+#include <wallet/rpcwallet.h>
#include <algorithm>
#include <atomic>
@@ -65,8 +65,6 @@ static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6;
static const bool DEFAULT_WALLET_RBF = false;
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;
extern const char * DEFAULT_WALLET_DAT;
@@ -87,7 +85,7 @@ enum class FeeEstimateMode;
/** (client) version numbers for particular wallet features */
enum WalletFeature
{
- FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getinfo's clientversion output)
+ FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getwalletinfo's clientversion output)
FEATURE_WALLETCRYPT = 40000, // wallet encryption
FEATURE_COMPRPUBKEY = 60000, // compressed public keys
@@ -96,6 +94,8 @@ enum WalletFeature
FEATURE_HD_SPLIT = 139900, // Wallet with HD chain split (change outputs will use m/0'/1'/k)
+ FEATURE_NO_DEFAULT_KEY = 159900, // Wallet without a default key written
+
FEATURE_LATEST = FEATURE_COMPRPUBKEY // HD is optional, use FEATURE_COMPRPUBKEY as latest version
};
@@ -214,10 +214,6 @@ public:
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();
@@ -252,8 +248,6 @@ public:
int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
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(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; }
@@ -330,6 +324,7 @@ public:
mutable bool fImmatureWatchCreditCached;
mutable bool fAvailableWatchCreditCached;
mutable bool fChangeCached;
+ mutable bool fInMempool;
mutable CAmount nDebitCached;
mutable CAmount nCreditCached;
mutable CAmount nImmatureCreditCached;
@@ -369,6 +364,7 @@ public:
fImmatureWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fChangeCached = false;
+ fInMempool = false;
nDebitCached = 0;
nCreditCached = 0;
nImmatureCreditCached = 0;
@@ -449,8 +445,8 @@ public:
CAmount GetCredit(const isminefilter& filter) const;
CAmount GetImmatureCredit(bool fUseCache=true) const;
CAmount GetAvailableCredit(bool fUseCache=true) const;
- CAmount GetImmatureWatchOnlyCredit(const bool& fUseCache=true) const;
- CAmount GetAvailableWatchOnlyCredit(const bool& fUseCache=true) const;
+ CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const;
+ CAmount GetAvailableWatchOnlyCredit(const bool fUseCache=true) const;
CAmount GetChange() const;
void GetAmounts(std::list<COutputEntry>& listReceived,
@@ -473,6 +469,9 @@ public:
// RelayWalletTransaction may only be called if fBroadcastTransactions!
bool RelayWalletTransaction(CConnman* connman);
+ /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
+ bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state);
+
std::set<uint256> GetConflicts() const;
};
@@ -722,6 +721,18 @@ private:
std::unique_ptr<CWalletDBWrapper> dbw;
+ /**
+ * The following is used to keep track of how far behind the wallet is
+ * from the chain sync, and to allow clients to block on us being caught up.
+ *
+ * Note that this is *not* how far we've processed, we may need some rescan
+ * to have seen all transactions in the chain, but is only used to track
+ * live BlockConnected callbacks.
+ *
+ * Protected by cs_main (see BlockUntilSyncedToCurrentChain)
+ */
+ const CBlockIndex* m_last_block_processed;
+
public:
/*
* Main wallet lock.
@@ -750,9 +761,11 @@ public:
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
- // Map from Key ID (for regular keys) or Script ID (for watch-only keys) to
- // key metadata.
- std::map<CTxDestination, CKeyMetadata> mapKeyMetadata;
+ // Map from Key ID to key metadata.
+ std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
+
+ // Map from Script ID to key metadata (for watch-only keys).
+ std::map<CScriptID, CKeyMetadata> m_script_metadata;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
@@ -817,7 +830,7 @@ public:
/**
* populate vCoins with vector of available COutputs.
*/
- void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t& nMaximumCount = 0, const int& nMinDepth = 0, const int& nMaxDepth = 9999999) const;
+ void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const;
/**
* Return list of available coins and locked coins grouped by non-change output address.
@@ -863,7 +876,8 @@ public:
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
//! Load metadata (used by LoadWallet)
- bool LoadKeyMetadata(const CTxDestination& pubKey, const CKeyMetadata &metadata);
+ bool LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata);
+ bool LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata);
bool LoadMinVersion(int nVersion) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; }
void UpdateTimeFirstKey(int64_t nCreateTime);
@@ -919,7 +933,8 @@ public:
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate);
int64_t RescanFromTime(int64_t startTime, bool update);
- CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
+ CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, bool fUpdate = false);
+ void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override;
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
@@ -1106,6 +1121,14 @@ public:
caller must ensure the current wallet version is correct before calling
this function). */
bool SetHDMasterKey(const CPubKey& key);
+
+ /**
+ * Blocks until the wallet state is up-to-date to /at least/ the current
+ * chain at the time this function is entered
+ * Obviously holding cs_main/cs_wallet when going into this call may cause
+ * deadlock
+ */
+ void BlockUntilSyncedToCurrentChain();
};
/** A key allocated from the key pool. */
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 12da3cce64..efc50f72eb 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -3,18 +3,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "wallet/walletdb.h"
-
-#include "base58.h"
-#include "consensus/tx_verify.h"
-#include "consensus/validation.h"
-#include "fs.h"
-#include "protocol.h"
-#include "serialize.h"
-#include "sync.h"
-#include "util.h"
-#include "utiltime.h"
-#include "wallet/wallet.h"
+#include <wallet/walletdb.h>
+
+#include <base58.h>
+#include <consensus/tx_verify.h>
+#include <consensus/validation.h>
+#include <fs.h>
+#include <protocol.h>
+#include <serialize.h>
+#include <sync.h>
+#include <util.h>
+#include <utiltime.h>
+#include <wallet/wallet.h>
#include <atomic>
@@ -41,9 +41,9 @@ bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& s
return WriteIC(std::make_pair(std::string("purpose"), strAddress), strPurpose);
}
-bool CWalletDB::ErasePurpose(const std::string& strPurpose)
+bool CWalletDB::ErasePurpose(const std::string& strAddress)
{
- return EraseIC(std::make_pair(std::string("purpose"), strPurpose));
+ return EraseIC(std::make_pair(std::string("purpose"), strAddress));
}
bool CWalletDB::WriteTx(const CWalletTx& wtx)
@@ -253,13 +253,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
{
std::string strAddress;
ssKey >> strAddress;
- ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name;
+ ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].name;
}
else if (strType == "purpose")
{
std::string strAddress;
ssKey >> strAddress;
- ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose;
+ ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].purpose;
}
else if (strType == "tx")
{
@@ -268,7 +268,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CWalletTx wtx;
ssValue >> wtx;
CValidationState state;
- if (!(CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid()))
+ if (!(CheckTransaction(*wtx.tx, state) && (wtx.GetHash() == hash) && state.IsValid()))
return false;
// Undo serialize changes in 31600
@@ -423,27 +423,23 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
}
wss.fIsEncrypted = true;
}
- else if (strType == "keymeta" || strType == "watchmeta")
+ else if (strType == "keymeta")
{
- CTxDestination keyID;
- if (strType == "keymeta")
- {
- CPubKey vchPubKey;
- ssKey >> vchPubKey;
- keyID = vchPubKey.GetID();
- }
- else if (strType == "watchmeta")
- {
- CScript script;
- ssKey >> script;
- keyID = CScriptID(script);
- }
-
+ CPubKey vchPubKey;
+ ssKey >> vchPubKey;
CKeyMetadata keyMeta;
ssValue >> keyMeta;
wss.nKeyMeta++;
-
- pwallet->LoadKeyMetadata(keyID, keyMeta);
+ pwallet->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
+ }
+ else if (strType == "watchmeta")
+ {
+ CScript script;
+ ssKey >> script;
+ CKeyMetadata keyMeta;
+ ssValue >> keyMeta;
+ wss.nKeyMeta++;
+ pwallet->LoadScriptMetadata(CScriptID(script), keyMeta);
}
else if (strType == "defaultkey")
{
@@ -493,7 +489,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssKey >> strAddress;
ssKey >> strKey;
ssValue >> strValue;
- if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue))
+ if (!pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue))
{
strErr = "Error reading wallet database: LoadDestData failed";
return false;
@@ -814,14 +810,14 @@ bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDa
return true;
}
-bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr)
+bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr)
{
- return CDB::VerifyEnvironment(walletFile, dataDir, errorStr);
+ return CDB::VerifyEnvironment(walletFile, walletDir, errorStr);
}
-bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr)
+bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr)
{
- return CDB::VerifyDatabaseFile(walletFile, dataDir, warningStr, errorStr, CWalletDB::Recover);
+ return CDB::VerifyDatabaseFile(walletFile, walletDir, warningStr, errorStr, CWalletDB::Recover);
}
bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 4f8ea185d5..e815bcfeda 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -6,10 +6,10 @@
#ifndef BITCOIN_WALLET_WALLETDB_H
#define BITCOIN_WALLET_WALLETDB_H
-#include "amount.h"
-#include "primitives/transaction.h"
-#include "wallet/db.h"
-#include "key.h"
+#include <amount.h>
+#include <primitives/transaction.h>
+#include <wallet/db.h>
+#include <key.h>
#include <list>
#include <stdint.h>
@@ -167,6 +167,8 @@ public:
m_dbw(dbw)
{
}
+ CWalletDB(const CWalletDB&) = delete;
+ CWalletDB& operator=(const CWalletDB&) = delete;
bool WriteName(const std::string& strAddress, const std::string& strName);
bool EraseName(const std::string& strAddress);
@@ -224,9 +226,9 @@ public:
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
static bool IsKeyType(const std::string& strType);
/* verifies the database environment */
- static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
+ static bool VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr);
+ static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr);
//! write the hdchain model (external chain child index counter)
bool WriteHDChain(const CHDChain& chain);
@@ -244,9 +246,6 @@ public:
private:
CDB batch;
CWalletDBWrapper& m_dbw;
-
- CWalletDB(const CWalletDB&);
- void operator=(const CWalletDB&);
};
//! Compacts BDB state so that wallet.dat is self-contained (if there are changes)
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
new file mode 100644
index 0000000000..fbb5215a51
--- /dev/null
+++ b/src/wallet/walletutil.cpp
@@ -0,0 +1,27 @@
+// Copyright (c) 2017 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 "wallet/walletutil.h"
+
+fs::path GetWalletDir()
+{
+ fs::path path;
+
+ if (gArgs.IsArgSet("-walletdir")) {
+ path = fs::system_complete(gArgs.GetArg("-walletdir", ""));
+ if (!fs::is_directory(path)) {
+ // If the path specified doesn't exist, we return the deliberately
+ // invalid empty string.
+ path = "";
+ }
+ } else {
+ path = GetDataDir();
+ // If a wallets directory exists, use that, otherwise default to GetDataDir
+ if (fs::is_directory(path / "wallets")) {
+ path /= "wallets";
+ }
+ }
+
+ return path;
+}
diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h
new file mode 100644
index 0000000000..a94f286a44
--- /dev/null
+++ b/src/wallet/walletutil.h
@@ -0,0 +1,13 @@
+// Copyright (c) 2017 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_WALLET_UTIL_H
+#define BITCOIN_WALLET_UTIL_H
+
+#include "util.h"
+
+//! Get the path of the wallet directory.
+fs::path GetWalletDir();
+
+#endif // BITCOIN_WALLET_UTIL_H
diff --git a/src/warnings.cpp b/src/warnings.cpp
index d4e33b701a..c52a1fdb47 100644
--- a/src/warnings.cpp
+++ b/src/warnings.cpp
@@ -3,10 +3,10 @@
// 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"
+#include <sync.h>
+#include <clientversion.h>
+#include <util.h>
+#include <warnings.h>
CCriticalSection cs_warnings;
std::string strMiscWarning;
diff --git a/src/zmq/zmqabstractnotifier.cpp b/src/zmq/zmqabstractnotifier.cpp
index 9f5cb3ba67..ce5c753eb4 100644
--- a/src/zmq/zmqabstractnotifier.cpp
+++ b/src/zmq/zmqabstractnotifier.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "zmqabstractnotifier.h"
-#include "util.h"
+#include <zmq/zmqabstractnotifier.h>
+#include <util.h>
CZMQAbstractNotifier::~CZMQAbstractNotifier()
diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h
index 7828822149..6fbaba2036 100644
--- a/src/zmq/zmqabstractnotifier.h
+++ b/src/zmq/zmqabstractnotifier.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H
#define BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H
-#include "zmqconfig.h"
+#include <zmq/zmqconfig.h>
class CBlockIndex;
class CZMQAbstractNotifier;
diff --git a/src/zmq/zmqconfig.h b/src/zmq/zmqconfig.h
index 610d7fbda4..ce8f3e13db 100644
--- a/src/zmq/zmqconfig.h
+++ b/src/zmq/zmqconfig.h
@@ -6,7 +6,7 @@
#define BITCOIN_ZMQ_ZMQCONFIG_H
#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
+#include <config/bitcoin-config.h>
#endif
#include <stdarg.h>
@@ -16,8 +16,8 @@
#include <zmq.h>
#endif
-#include "primitives/block.h"
-#include "primitives/transaction.h"
+#include <primitives/block.h>
+#include <primitives/transaction.h>
void zmqError(const char *str);
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index 9909395d84..b663615752 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -2,13 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "zmqnotificationinterface.h"
-#include "zmqpublishnotifier.h"
+#include <zmq/zmqnotificationinterface.h>
+#include <zmq/zmqpublishnotifier.h>
-#include "version.h"
-#include "validation.h"
-#include "streams.h"
-#include "util.h"
+#include <version.h>
+#include <validation.h>
+#include <streams.h>
+#include <util.h>
void zmqError(const char *str)
{
@@ -40,15 +40,15 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create()
factories["pubrawblock"] = CZMQAbstractNotifier::Create<CZMQPublishRawBlockNotifier>;
factories["pubrawtx"] = CZMQAbstractNotifier::Create<CZMQPublishRawTransactionNotifier>;
- for (std::map<std::string, CZMQNotifierFactory>::const_iterator i=factories.begin(); i!=factories.end(); ++i)
+ for (const auto& entry : factories)
{
- std::string arg("-zmq" + i->first);
+ std::string arg("-zmq" + entry.first);
if (gArgs.IsArgSet(arg))
{
- CZMQNotifierFactory factory = i->second;
+ CZMQNotifierFactory factory = entry.second;
std::string address = gArgs.GetArg(arg, "");
CZMQAbstractNotifier *notifier = factory();
- notifier->SetType(i->first);
+ notifier->SetType(entry.first);
notifier->SetAddress(address);
notifiers.push_back(notifier);
}
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index cb92216fa4..222d31e2ad 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H
#define BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H
-#include "validationinterface.h"
+#include <validationinterface.h>
#include <string>
#include <map>
#include <list>
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index ab54e2bb8b..acccb896c0 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -2,13 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "chain.h"
-#include "chainparams.h"
-#include "streams.h"
-#include "zmqpublishnotifier.h"
-#include "validation.h"
-#include "util.h"
-#include "rpc/server.h"
+#include <chain.h>
+#include <chainparams.h>
+#include <streams.h>
+#include <zmq/zmqpublishnotifier.h>
+#include <validation.h>
+#include <util.h>
+#include <rpc/server.h>
static std::multimap<std::string, CZMQAbstractPublishNotifier*> mapPublishNotifiers;
diff --git a/src/zmq/zmqpublishnotifier.h b/src/zmq/zmqpublishnotifier.h
index 1790fe5698..0e9bb23e5b 100644
--- a/src/zmq/zmqpublishnotifier.h
+++ b/src/zmq/zmqpublishnotifier.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H
#define BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H
-#include "zmqabstractnotifier.h"
+#include <zmq/zmqabstractnotifier.h>
class CBlockIndex;
diff --git a/test/functional/README.md b/test/functional/README.md
index 2558bd017d..6be4d9cfab 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -63,12 +63,12 @@ wrappers for them, `msg_block`, `msg_tx`, etc).
with the bitcoind(s) being tested (using python's asyncore package); the other
implements the test logic.
-- `NodeConn` is the class used to connect to a bitcoind. If you implement
-a callback class that derives from `NodeConnCB` and pass that to the
-`NodeConn` object, your code will receive the appropriate callbacks when
-events of interest arrive.
+- `P2PConnection` is the class used to connect to a bitcoind. `P2PInterface`
+contains the higher level logic for processing P2P payloads and connecting to
+the Bitcoin Core node application logic. For custom behaviour, subclass the
+P2PInterface object and override the callback methods.
-- Call `NetworkThread.start()` after all `NodeConn` objects are created to
+- Call `network_thread_start()` after all `P2PInterface` objects are created to
start the networking thread. (Continue with the test logic in your existing
thread.)
diff --git a/test/functional/assumevalid.py b/test/functional/assumevalid.py
index beaf8c7055..362b94e0d3 100755
--- a/test/functional/assumevalid.py
+++ b/test/functional/assumevalid.py
@@ -38,16 +38,16 @@ from test_framework.mininode import (CBlockHeader,
CTransaction,
CTxIn,
CTxOut,
- NetworkThread,
- NodeConn,
- NodeConnCB,
+ network_thread_join,
+ network_thread_start,
+ P2PInterface,
msg_block,
msg_headers)
from test_framework.script import (CScript, OP_TRUE)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (p2p_port, assert_equal)
+from test_framework.util import assert_equal
-class BaseNode(NodeConnCB):
+class BaseNode(P2PInterface):
def send_header_for_blocks(self, new_blocks):
headers_message = msg_headers()
headers_message.headers = [CBlockHeader(b) for b in new_blocks]
@@ -65,11 +65,13 @@ class AssumeValidTest(BitcoinTestFramework):
# signature so we can pass in the block hash as assumevalid.
self.start_node(0)
- def send_blocks_until_disconnected(self, node):
+ def send_blocks_until_disconnected(self, p2p_conn):
"""Keep sending blocks to the node until we're disconnected."""
for i in range(len(self.blocks)):
+ if p2p_conn.state != "connected":
+ break
try:
- node.send_message(msg_block(self.blocks[i]))
+ p2p_conn.send_message(msg_block(self.blocks[i]))
except IOError as e:
assert str(e) == 'Not connected, no pushbuf'
break
@@ -95,13 +97,10 @@ class AssumeValidTest(BitcoinTestFramework):
def run_test(self):
# Connect to node0
- node0 = BaseNode()
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
+ p2p0 = self.nodes[0].add_p2p_connection(BaseNode())
- NetworkThread().start() # Start up network handling in another thread
- node0.wait_for_verack()
+ network_thread_start()
+ self.nodes[0].p2p.wait_for_verack()
# Build the blockchain
self.tip = int(self.nodes[0].getbestblockhash(), 16)
@@ -161,39 +160,44 @@ class AssumeValidTest(BitcoinTestFramework):
self.block_time += 1
height += 1
+ # We're adding new connections so terminate the network thread
+ self.nodes[0].disconnect_p2ps()
+ network_thread_join()
+
# Start node1 and node2 with assumevalid so they accept a block with a bad signature.
self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)])
- node1 = BaseNode() # connects to node1
- connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], node1))
- node1.add_connection(connections[1])
- node1.wait_for_verack()
-
self.start_node(2, extra_args=["-assumevalid=" + hex(block102.sha256)])
- node2 = BaseNode() # connects to node2
- connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2))
- node2.add_connection(connections[2])
- node2.wait_for_verack()
+
+ p2p0 = self.nodes[0].add_p2p_connection(BaseNode())
+ p2p1 = self.nodes[1].add_p2p_connection(BaseNode())
+ p2p2 = self.nodes[2].add_p2p_connection(BaseNode())
+
+ network_thread_start()
+
+ p2p0.wait_for_verack()
+ p2p1.wait_for_verack()
+ p2p2.wait_for_verack()
# send header lists to all three nodes
- node0.send_header_for_blocks(self.blocks[0:2000])
- node0.send_header_for_blocks(self.blocks[2000:])
- node1.send_header_for_blocks(self.blocks[0:2000])
- node1.send_header_for_blocks(self.blocks[2000:])
- node2.send_header_for_blocks(self.blocks[0:200])
+ p2p0.send_header_for_blocks(self.blocks[0:2000])
+ p2p0.send_header_for_blocks(self.blocks[2000:])
+ p2p1.send_header_for_blocks(self.blocks[0:2000])
+ p2p1.send_header_for_blocks(self.blocks[2000:])
+ p2p2.send_header_for_blocks(self.blocks[0:200])
# Send blocks to node0. Block 102 will be rejected.
- self.send_blocks_until_disconnected(node0)
+ self.send_blocks_until_disconnected(p2p0)
self.assert_blockchain_height(self.nodes[0], 101)
# Send all blocks to node1. All blocks will be accepted.
for i in range(2202):
- node1.send_message(msg_block(self.blocks[i]))
+ p2p1.send_message(msg_block(self.blocks[i]))
# Syncing 2200 blocks can take a while on slow systems. Give it plenty of time to sync.
- node1.sync_with_ping(120)
+ p2p1.sync_with_ping(120)
assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202)
# Send blocks to node2. Block 102 will be rejected.
- self.send_blocks_until_disconnected(node2)
+ self.send_blocks_until_disconnected(p2p2)
self.assert_blockchain_height(self.nodes[2], 101)
if __name__ == '__main__':
diff --git a/test/functional/bip65-cltv-p2p.py b/test/functional/bip65-cltv-p2p.py
index 2cd6df6e37..f4df879723 100755
--- a/test/functional/bip65-cltv-p2p.py
+++ b/test/functional/bip65-cltv-p2p.py
@@ -66,15 +66,12 @@ class BIP65Test(BitcoinTestFramework):
self.setup_clean_chain = True
def run_test(self):
- node0 = NodeConnCB()
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
+ self.nodes[0].add_p2p_connection(P2PInterface())
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
# wait_for_verack ensures that the P2P connection is fully up.
- node0.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
self.log.info("Mining %d blocks", CLTV_HEIGHT - 2)
self.coinbase_blocks = self.nodes[0].generate(CLTV_HEIGHT - 2)
@@ -95,7 +92,7 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
self.log.info("Test that blocks must now be at least version 4")
@@ -104,15 +101,15 @@ class BIP65Test(BitcoinTestFramework):
block = create_block(tip, create_coinbase(CLTV_HEIGHT), block_time)
block.nVersion = 3
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock)
+ wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
with mininode_lock:
- assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE)
- assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000003)')
- assert_equal(node0.last_message["reject"].data, block.sha256)
- del node0.last_message["reject"]
+ assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_OBSOLETE)
+ assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'bad-version(0x00000003)')
+ assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
+ del self.nodes[0].p2p.last_message["reject"]
self.log.info("Test that invalid-according-to-cltv transactions cannot appear in a block")
block.nVersion = 4
@@ -125,7 +122,7 @@ class BIP65Test(BitcoinTestFramework):
# First we show that this tx is valid except for CLTV by getting it
# accepted to the mempool (which we can achieve with
# -promiscuousmempoolflags).
- node0.send_and_ping(msg_tx(spendtx))
+ self.nodes[0].p2p.send_and_ping(msg_tx(spendtx))
assert spendtx.hash in self.nodes[0].getrawmempool()
# Now we verify that a block with this transaction is invalid.
@@ -133,18 +130,18 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock)
+ wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
with mininode_lock:
- assert node0.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
- assert_equal(node0.last_message["reject"].data, block.sha256)
- if node0.last_message["reject"].code == REJECT_INVALID:
+ assert self.nodes[0].p2p.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
+ assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
+ if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID:
# Generic rejection when a block is invalid
- assert_equal(node0.last_message["reject"].reason, b'block-validation-failed')
+ assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed')
else:
- assert b'Negative locktime' in node0.last_message["reject"].reason
+ assert b'Negative locktime' in self.nodes[0].p2p.last_message["reject"].reason
self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted")
spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1)
@@ -155,7 +152,7 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/bip68-112-113-p2p.py b/test/functional/bip68-112-113-p2p.py
index 7e6a4f4408..d3c7d8fc11 100755
--- a/test/functional/bip68-112-113-p2p.py
+++ b/test/functional/bip68-112-113-p2p.py
@@ -45,7 +45,7 @@ bip112tx_special - test negative argument to OP_CSV
from test_framework.test_framework import ComparisonTestFramework
from test_framework.util import *
-from test_framework.mininode import ToHex, CTransaction, NetworkThread
+from test_framework.mininode import ToHex, CTransaction, network_thread_start
from test_framework.blocktools import create_coinbase, create_block
from test_framework.comptool import TestInstance, TestManager
from test_framework.script import *
@@ -100,7 +100,7 @@ class BIP68_112_113Test(ComparisonTestFramework):
def run_test(self):
test = TestManager(self, self.options.tmpdir)
test.add_all_connections(self.nodes)
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
test.run()
def send_generic_input_tx(self, node, coinbases):
diff --git a/test/functional/bip68-sequence.py b/test/functional/bip68-sequence.py
index 3818287209..5f8f21701f 100755
--- a/test/functional/bip68-sequence.py
+++ b/test/functional/bip68-sequence.py
@@ -83,7 +83,7 @@ class BIP68Test(BitcoinTestFramework):
tx2.vout = [CTxOut(int(value-self.relayfee*COIN), CScript([b'a']))]
tx2.rehash()
- assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2))
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2))
# Setting the version back down to 1 should disable the sequence lock,
# so this should be accepted.
@@ -180,7 +180,7 @@ class BIP68Test(BitcoinTestFramework):
if (using_sequence_locks and not should_pass):
# This transaction should be rejected
- assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx)
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx)
else:
# This raw transaction should be accepted
self.nodes[0].sendrawtransaction(rawtx)
@@ -227,7 +227,7 @@ class BIP68Test(BitcoinTestFramework):
if (orig_tx.hash in node.getrawmempool()):
# sendrawtransaction should fail if the tx is in the mempool
- assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx))
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx))
else:
# sendrawtransaction should succeed if the tx is not in the mempool
node.sendrawtransaction(ToHex(tx))
@@ -280,7 +280,7 @@ class BIP68Test(BitcoinTestFramework):
tx5.vout[0].nValue += int(utxos[0]["amount"]*COIN)
raw_tx5 = self.nodes[0].signrawtransaction(ToHex(tx5))["hex"]
- assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5)
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5)
# Test mempool-BIP68 consistency after reorg
#
@@ -353,7 +353,7 @@ class BIP68Test(BitcoinTestFramework):
tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))]
tx3.rehash()
- assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3))
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3))
# make a block that violates bip68; ensure that the tip updates
tip = int(self.nodes[0].getbestblockhash(), 16)
@@ -369,11 +369,14 @@ class BIP68Test(BitcoinTestFramework):
def activateCSV(self):
# activation should happen at block height 432 (3 periods)
+ # getblockchaininfo will show CSV as active at block 431 (144 * 3 -1) since it's returning whether CSV is active for the next block.
min_activation_height = 432
height = self.nodes[0].getblockcount()
- assert(height < min_activation_height)
- self.nodes[0].generate(min_activation_height-height)
- assert(get_bip9_status(self.nodes[0], 'csv')['status'] == 'active')
+ assert_greater_than(min_activation_height - height, 2)
+ self.nodes[0].generate(min_activation_height - height - 2)
+ assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "locked_in")
+ self.nodes[0].generate(1)
+ assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "active")
sync_blocks(self.nodes)
# Use self.nodes[1] to test that version 2 transactions are standard.
diff --git a/test/functional/bip9-softforks.py b/test/functional/bip9-softforks.py
index 904789301a..4cd6a177aa 100755
--- a/test/functional/bip9-softforks.py
+++ b/test/functional/bip9-softforks.py
@@ -22,7 +22,7 @@ import itertools
from test_framework.test_framework import ComparisonTestFramework
from test_framework.util import *
-from test_framework.mininode import CTransaction, NetworkThread
+from test_framework.mininode import CTransaction, network_thread_start
from test_framework.blocktools import create_coinbase, create_block
from test_framework.comptool import TestInstance, TestManager
from test_framework.script import CScript, OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP
@@ -36,7 +36,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
def run_test(self):
self.test = TestManager(self, self.options.tmpdir)
self.test.add_all_connections(self.nodes)
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
self.test.run()
def create_transaction(self, node, coinbase, to_address, amount):
@@ -245,8 +245,8 @@ class BIP9SoftForksTest(ComparisonTestFramework):
self.setup_chain()
self.setup_network()
self.test.add_all_connections(self.nodes)
- NetworkThread().start()
- self.test.test_nodes[0].wait_for_verack()
+ network_thread_start()
+ self.test.p2p_connections[0].wait_for_verack()
def get_tests(self):
for test in itertools.chain(
diff --git a/test/functional/bipdersig-p2p.py b/test/functional/bipdersig-p2p.py
index c620d3e155..5d7b889e83 100755
--- a/test/functional/bipdersig-p2p.py
+++ b/test/functional/bipdersig-p2p.py
@@ -54,14 +54,12 @@ class BIP66Test(BitcoinTestFramework):
self.setup_clean_chain = True
def run_test(self):
- node0 = NodeConnCB()
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
- NetworkThread().start() # Start up network handling in another thread
+ self.nodes[0].add_p2p_connection(P2PInterface())
+
+ network_thread_start()
# wait_for_verack ensures that the P2P connection is fully up.
- node0.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2)
self.coinbase_blocks = self.nodes[0].generate(DERSIG_HEIGHT - 2)
@@ -83,7 +81,7 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
self.log.info("Test that blocks must now be at least version 3")
@@ -93,15 +91,15 @@ class BIP66Test(BitcoinTestFramework):
block.nVersion = 2
block.rehash()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock)
+ wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
with mininode_lock:
- assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE)
- assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000002)')
- assert_equal(node0.last_message["reject"].data, block.sha256)
- del node0.last_message["reject"]
+ assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_OBSOLETE)
+ assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'bad-version(0x00000002)')
+ assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
+ del self.nodes[0].p2p.last_message["reject"]
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
block.nVersion = 3
@@ -114,7 +112,7 @@ class BIP66Test(BitcoinTestFramework):
# First we show that this tx is valid except for DERSIG by getting it
# accepted to the mempool (which we can achieve with
# -promiscuousmempoolflags).
- node0.send_and_ping(msg_tx(spendtx))
+ self.nodes[0].p2p.send_and_ping(msg_tx(spendtx))
assert spendtx.hash in self.nodes[0].getrawmempool()
# Now we verify that a block with this transaction is invalid.
@@ -123,23 +121,23 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock)
+ wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
with mininode_lock:
# We can receive different reject messages depending on whether
# bitcoind is running with multiple script check threads. If script
# check threads are not in use, then transaction script validation
# happens sequentially, and bitcoind produces more specific reject
# reasons.
- assert node0.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
- assert_equal(node0.last_message["reject"].data, block.sha256)
- if node0.last_message["reject"].code == REJECT_INVALID:
+ assert self.nodes[0].p2p.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
+ assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
+ if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID:
# Generic rejection when a block is invalid
- assert_equal(node0.last_message["reject"].reason, b'block-validation-failed')
+ assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed')
else:
- assert b'Non-canonical DER signature' in node0.last_message["reject"].reason
+ assert b'Non-canonical DER signature' in self.nodes[0].p2p.last_message["reject"].reason
self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
block.vtx[1] = create_transaction(self.nodes[0],
@@ -148,7 +146,7 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
if __name__ == '__main__':
diff --git a/test/functional/bitcoin_cli.py b/test/functional/bitcoin_cli.py
index 7acfede3f7..d1cd3b3620 100755
--- a/test/functional/bitcoin_cli.py
+++ b/test/functional/bitcoin_cli.py
@@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test bitcoin-cli"""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
+from test_framework.util import assert_equal, assert_raises_process_error, get_auth_cookie
class TestBitcoinCli(BitcoinTestFramework):
@@ -15,11 +15,51 @@ class TestBitcoinCli(BitcoinTestFramework):
def run_test(self):
"""Main test logic"""
- self.log.info("Compare responses from getinfo RPC and `bitcoin-cli getinfo`")
- cli_get_info = self.nodes[0].cli.getinfo()
- rpc_get_info = self.nodes[0].getinfo()
+ self.log.info("Compare responses from gewalletinfo RPC and `bitcoin-cli getwalletinfo`")
+ cli_response = self.nodes[0].cli.getwalletinfo()
+ rpc_response = self.nodes[0].getwalletinfo()
+ assert_equal(cli_response, rpc_response)
- assert_equal(cli_get_info, rpc_get_info)
+ self.log.info("Compare responses from getblockchaininfo RPC and `bitcoin-cli getblockchaininfo`")
+ cli_response = self.nodes[0].cli.getblockchaininfo()
+ rpc_response = self.nodes[0].getblockchaininfo()
+ assert_equal(cli_response, rpc_response)
+
+ user, password = get_auth_cookie(self.nodes[0].datadir)
+
+ self.log.info("Test -stdinrpcpass option")
+ assert_equal(0, self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input=password).getblockcount())
+ assert_raises_process_error(1, "incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input="foo").echo)
+
+ self.log.info("Test -stdin and -stdinrpcpass")
+ assert_equal(["foo", "bar"], self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input=password + "\nfoo\nbar").echo())
+ assert_raises_process_error(1, "incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input="foo").echo)
+
+ self.log.info("Make sure that -getinfo with arguments fails")
+ assert_raises_process_error(1, "-getinfo takes no arguments", self.nodes[0].cli('-getinfo').help)
+
+ self.log.info("Compare responses from `bitcoin-cli -getinfo` and the RPCs data is retrieved from.")
+ cli_get_info = self.nodes[0].cli().send_cli('-getinfo')
+ wallet_info = self.nodes[0].getwalletinfo()
+ network_info = self.nodes[0].getnetworkinfo()
+ blockchain_info = self.nodes[0].getblockchaininfo()
+
+ assert_equal(cli_get_info['version'], network_info['version'])
+ assert_equal(cli_get_info['protocolversion'], network_info['protocolversion'])
+ assert_equal(cli_get_info['walletversion'], wallet_info['walletversion'])
+ assert_equal(cli_get_info['balance'], wallet_info['balance'])
+ assert_equal(cli_get_info['blocks'], blockchain_info['blocks'])
+ assert_equal(cli_get_info['timeoffset'], network_info['timeoffset'])
+ assert_equal(cli_get_info['connections'], network_info['connections'])
+ assert_equal(cli_get_info['proxy'], network_info['networks'][0]['proxy'])
+ assert_equal(cli_get_info['difficulty'], blockchain_info['difficulty'])
+ assert_equal(cli_get_info['testnet'], blockchain_info['chain'] == "test")
+ assert_equal(cli_get_info['balance'], wallet_info['balance'])
+ assert_equal(cli_get_info['keypoololdest'], wallet_info['keypoololdest'])
+ assert_equal(cli_get_info['keypoolsize'], wallet_info['keypoolsize'])
+ assert_equal(cli_get_info['paytxfee'], wallet_info['paytxfee'])
+ assert_equal(cli_get_info['relayfee'], network_info['relayfee'])
+ # unlocked_until is not tested because the wallet is not encrypted
if __name__ == '__main__':
TestBitcoinCli().main()
diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py
index 5d04de9940..49fafbc9aa 100755
--- a/test/functional/blockchain.py
+++ b/test/functional/blockchain.py
@@ -5,6 +5,7 @@
"""Test RPCs related to blockchainstate.
Test the following RPCs:
+ - getblockchaininfo
- gettxoutsetinfo
- getdifficulty
- getbestblockhash
@@ -21,11 +22,13 @@ from decimal import Decimal
import http.client
import subprocess
-from test_framework.test_framework import (BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT)
+from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_greater_than,
+ assert_greater_than_or_equal,
assert_raises,
- assert_raises_jsonrpc,
+ assert_raises_rpc_error,
assert_is_hex_string,
assert_is_hash_string,
)
@@ -33,9 +36,10 @@ from test_framework.util import (
class BlockchainTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [['-stopatheight=207']]
+ self.extra_args = [['-stopatheight=207', '-prune=1']]
def run_test(self):
+ self._test_getblockchaininfo()
self._test_getchaintxstats()
self._test_gettxoutsetinfo()
self._test_getblockheader()
@@ -44,6 +48,57 @@ class BlockchainTest(BitcoinTestFramework):
self._test_stopatheight()
assert self.nodes[0].verifychain(4, 0)
+ def _test_getblockchaininfo(self):
+ self.log.info("Test getblockchaininfo")
+
+ keys = [
+ 'bestblockhash',
+ 'bip9_softforks',
+ 'blocks',
+ 'chain',
+ 'chainwork',
+ 'difficulty',
+ 'headers',
+ 'initialblockdownload',
+ 'mediantime',
+ 'pruned',
+ 'size_on_disk',
+ 'softforks',
+ 'verificationprogress',
+ 'warnings',
+ ]
+ res = self.nodes[0].getblockchaininfo()
+
+ # result should have these additional pruning keys if manual pruning is enabled
+ assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning'] + keys))
+
+ # size_on_disk should be > 0
+ assert_greater_than(res['size_on_disk'], 0)
+
+ # pruneheight should be greater or equal to 0
+ assert_greater_than_or_equal(res['pruneheight'], 0)
+
+ # check other pruning fields given that prune=1
+ assert res['pruned']
+ assert not res['automatic_pruning']
+
+ self.restart_node(0, ['-stopatheight=207'])
+ res = self.nodes[0].getblockchaininfo()
+ # should have exact keys
+ assert_equal(sorted(res.keys()), keys)
+
+ self.restart_node(0, ['-stopatheight=207', '-prune=550'])
+ res = self.nodes[0].getblockchaininfo()
+ # result should have these additional pruning keys if prune=550
+ assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning', 'prune_target_size'] + keys))
+
+ # check related fields
+ assert res['pruned']
+ assert_equal(res['pruneheight'], 0)
+ assert res['automatic_pruning']
+ assert_equal(res['prune_target_size'], 576716800)
+ assert_greater_than(res['size_on_disk'], 0)
+
def _test_getchaintxstats(self):
chaintxstats = self.nodes[0].getchaintxstats(1)
# 200 txs plus genesis tx
@@ -52,6 +107,28 @@ class BlockchainTest(BitcoinTestFramework):
# we have to round because of binary math
assert_equal(round(chaintxstats['txrate'] * 600, 10), Decimal(1))
+ b1 = self.nodes[0].getblock(self.nodes[0].getblockhash(1))
+ b200 = self.nodes[0].getblock(self.nodes[0].getblockhash(200))
+ time_diff = b200['mediantime'] - b1['mediantime']
+
+ chaintxstats = self.nodes[0].getchaintxstats()
+ assert_equal(chaintxstats['time'], b200['time'])
+ assert_equal(chaintxstats['txcount'], 201)
+ assert_equal(chaintxstats['window_block_count'], 199)
+ assert_equal(chaintxstats['window_tx_count'], 199)
+ assert_equal(chaintxstats['window_interval'], time_diff)
+ assert_equal(round(chaintxstats['txrate'] * time_diff, 10), Decimal(199))
+
+ chaintxstats = self.nodes[0].getchaintxstats(blockhash=b1['hash'])
+ assert_equal(chaintxstats['time'], b1['time'])
+ assert_equal(chaintxstats['txcount'], 2)
+ assert_equal(chaintxstats['window_block_count'], 0)
+ assert('window_tx_count' not in chaintxstats)
+ assert('window_interval' not in chaintxstats)
+ assert('txrate' not in chaintxstats)
+
+ assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, 201)
+
def _test_gettxoutsetinfo(self):
node = self.nodes[0]
res = node.gettxoutsetinfo()
@@ -96,7 +173,7 @@ class BlockchainTest(BitcoinTestFramework):
def _test_getblockheader(self):
node = self.nodes[0]
- assert_raises_jsonrpc(-5, "Block not found",
+ assert_raises_rpc_error(-5, "Block not found",
node.getblockheader, "nonsense")
besthash = node.getbestblockhash()
@@ -141,7 +218,7 @@ class BlockchainTest(BitcoinTestFramework):
except (ConnectionError, http.client.BadStatusLine):
pass # The node already shut down before response
self.log.debug('Node should stop at this height...')
- self.nodes[0].process.wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT)
+ self.nodes[0].wait_until_stopped()
self.start_node(0)
assert_equal(self.nodes[0].getblockcount(), 207)
diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py
index dde87d5bd1..008e83d5b2 100755
--- a/test/functional/bumpfee.py
+++ b/test/functional/bumpfee.py
@@ -133,7 +133,7 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address):
def test_nonrbf_bumpfee_fails(peer_node, dest_address):
# cannot replace a non RBF transaction (from node which did not enable RBF)
not_rbfid = peer_node.sendtoaddress(dest_address, Decimal("0.00090000"))
- assert_raises_jsonrpc(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid)
+ assert_raises_rpc_error(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid)
def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address):
@@ -153,7 +153,7 @@ def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address):
signedtx = rbf_node.signrawtransaction(rawtx)
signedtx = peer_node.signrawtransaction(signedtx["hex"])
rbfid = rbf_node.sendrawtransaction(signedtx["hex"])
- assert_raises_jsonrpc(-4, "Transaction contains inputs that don't belong to this wallet",
+ assert_raises_rpc_error(-4, "Transaction contains inputs that don't belong to this wallet",
rbf_node.bumpfee, rbfid)
@@ -164,7 +164,7 @@ def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address)
tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000})
tx = rbf_node.signrawtransaction(tx)
rbf_node.sendrawtransaction(tx["hex"])
- assert_raises_jsonrpc(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id)
+ assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id)
def test_small_output_fails(rbf_node, dest_address):
@@ -173,7 +173,7 @@ def test_small_output_fails(rbf_node, dest_address):
rbf_node.bumpfee(rbfid, {"totalFee": 50000})
rbfid = spend_one_input(rbf_node, dest_address)
- assert_raises_jsonrpc(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001})
+ assert_raises_rpc_error(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001})
def test_dust_to_fee(rbf_node, dest_address):
@@ -205,7 +205,7 @@ def test_rebumping(rbf_node, dest_address):
# check that re-bumping the original tx fails, but bumping the bumper succeeds
rbfid = spend_one_input(rbf_node, dest_address)
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 2000})
- assert_raises_jsonrpc(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 3000})
+ assert_raises_rpc_error(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 3000})
rbf_node.bumpfee(bumped["txid"], {"totalFee": 3000})
@@ -213,7 +213,7 @@ def test_rebumping_not_replaceable(rbf_node, dest_address):
# check that re-bumping a non-replaceable bump tx fails
rbfid = spend_one_input(rbf_node, dest_address)
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000, "replaceable": False})
- assert_raises_jsonrpc(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"],
+ assert_raises_rpc_error(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"],
{"totalFee": 20000})
@@ -264,7 +264,7 @@ def test_bumpfee_metadata(rbf_node, dest_address):
def test_locked_wallet_fails(rbf_node, dest_address):
rbfid = spend_one_input(rbf_node, dest_address)
rbf_node.walletlock()
- assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first.",
+ assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first.",
rbf_node.bumpfee, rbfid)
diff --git a/test/functional/dbcrash.py b/test/functional/dbcrash.py
index 71424f641b..24b9765b4e 100755
--- a/test/functional/dbcrash.py
+++ b/test/functional/dbcrash.py
@@ -64,7 +64,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
def setup_network(self):
# Need a bit of extra time for the nodes to start up for this test
- self.add_nodes(self.num_nodes, timewait=90)
+ self.add_nodes(self.num_nodes, extra_args=self.extra_args, timewait=90)
self.start_nodes()
# Leave them unconnected, we'll use submitblock directly in this test
diff --git a/test/functional/deprecated_rpc.py b/test/functional/deprecated_rpc.py
new file mode 100755
index 0000000000..19fd24edb9
--- /dev/null
+++ b/test/functional/deprecated_rpc.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 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 deprecation of RPC calls."""
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_raises_rpc_error
+
+class DeprecatedRpcTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+ self.extra_args = [[], ["-deprecatedrpc=estimatefee"]]
+
+ def run_test(self):
+ self.log.info("estimatefee: Shows deprecated message")
+ assert_raises_rpc_error(-32, 'estimatefee is deprecated', self.nodes[0].estimatefee, 1)
+
+ self.log.info("Using -deprecatedrpc=estimatefee bypasses the error")
+ self.nodes[1].estimatefee(1)
+
+if __name__ == '__main__':
+ DeprecatedRpcTest().main()
diff --git a/test/functional/disablewallet.py b/test/functional/disablewallet.py
index c1d37963bc..c75ef9b9f1 100755
--- a/test/functional/disablewallet.py
+++ b/test/functional/disablewallet.py
@@ -19,7 +19,7 @@ class DisableWalletTest (BitcoinTestFramework):
def run_test (self):
# Make sure wallet is really disabled
- assert_raises_jsonrpc(-32601, 'Method not found', self.nodes[0].getwalletinfo)
+ assert_raises_rpc_error(-32601, 'Method not found', self.nodes[0].getwalletinfo)
x = self.nodes[0].validateaddress('3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
assert(x['isvalid'] == False)
x = self.nodes[0].validateaddress('mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ')
@@ -28,7 +28,7 @@ class DisableWalletTest (BitcoinTestFramework):
# Checking mining to an address without a wallet. Generating to a valid address should succeed
# but generating to an invalid address will fail.
self.nodes[0].generatetoaddress(1, 'mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ')
- assert_raises_jsonrpc(-5, "Invalid address", self.nodes[0].generatetoaddress, 1, '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
+ assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].generatetoaddress, 1, '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
if __name__ == '__main__':
DisableWalletTest ().main ()
diff --git a/test/functional/disconnect_ban.py b/test/functional/disconnect_ban.py
index a6445b9b35..59655d37fb 100755
--- a/test/functional/disconnect_ban.py
+++ b/test/functional/disconnect_ban.py
@@ -8,7 +8,7 @@ import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- assert_raises_jsonrpc,
+ assert_raises_rpc_error,
connect_nodes_bi,
wait_until,
)
@@ -34,14 +34,14 @@ class DisconnectBanTest(BitcoinTestFramework):
self.log.info("setban: fail to ban an already banned subnet")
assert_equal(len(self.nodes[1].listbanned()), 1)
- assert_raises_jsonrpc(-23, "IP/Subnet already banned", self.nodes[1].setban, "127.0.0.1", "add")
+ assert_raises_rpc_error(-23, "IP/Subnet already banned", self.nodes[1].setban, "127.0.0.1", "add")
self.log.info("setban: fail to ban an invalid subnet")
- assert_raises_jsonrpc(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add")
+ assert_raises_rpc_error(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add")
assert_equal(len(self.nodes[1].listbanned()), 1) # still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24
self.log.info("setban remove: fail to unban a non-banned subnet")
- assert_raises_jsonrpc(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove")
+ assert_raises_rpc_error(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove")
assert_equal(len(self.nodes[1].listbanned()), 1)
self.log.info("setban remove: successfully unban subnet")
@@ -81,10 +81,10 @@ class DisconnectBanTest(BitcoinTestFramework):
self.log.info("disconnectnode: fail to disconnect when calling with address and nodeid")
address1 = self.nodes[0].getpeerinfo()[0]['addr']
node1 = self.nodes[0].getpeerinfo()[0]['addr']
- assert_raises_jsonrpc(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1)
+ assert_raises_rpc_error(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1)
self.log.info("disconnectnode: fail to disconnect when calling with junk address")
- assert_raises_jsonrpc(-29, "Node not found in connected nodes", self.nodes[0].disconnectnode, address="221B Baker Street")
+ assert_raises_rpc_error(-29, "Node not found in connected nodes", self.nodes[0].disconnectnode, address="221B Baker Street")
self.log.info("disconnectnode: successfully disconnect node by address")
address1 = self.nodes[0].getpeerinfo()[0]['addr']
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index 87d73ad14a..12be685ecf 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -17,30 +17,29 @@ from collections import defaultdict
from test_framework.blocktools import (create_block, create_coinbase)
from test_framework.mininode import (
CInv,
- NetworkThread,
- NodeConn,
- NodeConnCB,
+ P2PInterface,
mininode_lock,
msg_block,
msg_getdata,
+ network_thread_join,
+ network_thread_start,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes,
- p2p_port,
wait_until,
)
-# NodeConnCB is a class containing callbacks to be executed when a P2P
-# message is received from the node-under-test. Subclass NodeConnCB and
+# P2PInterface is a class containing callbacks to be executed when a P2P
+# message is received from the node-under-test. Subclass P2PInterface and
# override the on_*() methods if you need custom behaviour.
-class BaseNode(NodeConnCB):
+class BaseNode(P2PInterface):
def __init__(self):
- """Initialize the NodeConnCB
+ """Initialize the P2PInterface
Used to inialize custom properties for the Node that aren't
- included by default in the base class. Be aware that the NodeConnCB
+ included by default in the base class. Be aware that the P2PInterface
base class already stores a counter for each P2P message type and the
last received message of each type, which should be sufficient for the
needs of most tests.
@@ -51,14 +50,14 @@ class BaseNode(NodeConnCB):
# Stores a dictionary of all blocks received
self.block_receive_map = defaultdict(int)
- def on_block(self, conn, message):
+ def on_block(self, message):
"""Override the standard on_block callback
Store the hash of a received block in the dictionary."""
message.block.calc_sha256()
self.block_receive_map[message.block.sha256] += 1
- def on_inv(self, conn, message):
+ def on_inv(self, message):
"""Override the standard on_inv callback"""
pass
@@ -133,17 +132,14 @@ class ExampleTest(BitcoinTestFramework):
def run_test(self):
"""Main test logic"""
- # Create a P2P connection to one of the nodes
- node0 = BaseNode()
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
+ # Create P2P connections to two of the nodes
+ self.nodes[0].add_p2p_connection(BaseNode())
# Start up network handling in another thread. This needs to be called
# after the P2P connections have been created.
- NetworkThread().start()
+ network_thread_start()
# wait_for_verack ensures that the P2P connection is fully up.
- node0.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
# Generating a block on one of the nodes will get us out of IBD
blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)]
@@ -179,8 +175,8 @@ class ExampleTest(BitcoinTestFramework):
block = create_block(self.tip, create_coinbase(height), self.block_time)
block.solve()
block_message = msg_block(block)
- # Send message is used to send a P2P message to the node over our NodeConn connection
- node0.send_message(block_message)
+ # Send message is used to send a P2P message to the node over our P2PInterface
+ self.nodes[0].p2p.send_message(block_message)
self.tip = block.sha256
blocks.append(self.tip)
self.block_time += 1
@@ -193,28 +189,33 @@ class ExampleTest(BitcoinTestFramework):
connect_nodes(self.nodes[1], 2)
self.log.info("Add P2P connection to node2")
- node2 = BaseNode()
- connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2))
- node2.add_connection(connections[1])
- node2.wait_for_verack()
+ # We can't add additional P2P connections once the network thread has started. Disconnect the connection
+ # to node0, wait for the network thread to terminate, then connect to node2. This is specific to
+ # the current implementation of the network thread and may be improved in future.
+ self.nodes[0].disconnect_p2ps()
+ network_thread_join()
+
+ self.nodes[2].add_p2p_connection(BaseNode())
+ network_thread_start()
+ self.nodes[2].p2p.wait_for_verack()
self.log.info("Wait for node2 reach current tip. Test that it has propagated all the blocks to us")
getdata_request = msg_getdata()
for block in blocks:
getdata_request.inv.append(CInv(2, block))
- node2.send_message(getdata_request)
+ self.nodes[2].p2p.send_message(getdata_request)
# wait_until() will loop until a predicate condition is met. Use it to test properties of the
- # NodeConnCB objects.
- wait_until(lambda: sorted(blocks) == sorted(list(node2.block_receive_map.keys())), timeout=5, lock=mininode_lock)
+ # P2PInterface objects.
+ wait_until(lambda: sorted(blocks) == sorted(list(self.nodes[2].p2p.block_receive_map.keys())), timeout=5, lock=mininode_lock)
self.log.info("Check that each block was received only once")
- # The network thread uses a global lock on data access to the NodeConn objects when sending and receiving
- # messages. The test thread should acquire the global lock before accessing any NodeConn data to avoid locking
+ # The network thread uses a global lock on data access to the P2PConnection objects when sending and receiving
+ # messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking
# and synchronization issues. Note wait_until() acquires this global lock when testing the predicate.
with mininode_lock:
- for block in node2.block_receive_map.values():
+ for block in self.nodes[2].p2p.block_receive_map.values():
assert_equal(block, 1)
if __name__ == '__main__':
diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py
new file mode 100755
index 0000000000..da4e7b0398
--- /dev/null
+++ b/test/functional/feature_logging.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 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 debug logging."""
+
+import os
+
+from test_framework.test_framework import BitcoinTestFramework
+
+class LoggingTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def run_test(self):
+ # test default log file name
+ assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "debug.log"))
+
+ # test alternative log file name in datadir
+ self.restart_node(0, ["-debuglogfile=foo.log"])
+ assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "foo.log"))
+
+ # test alternative log file name outside datadir
+ tempname = os.path.join(self.options.tmpdir, "foo.log")
+ self.restart_node(0, ["-debuglogfile=%s" % tempname])
+ assert os.path.isfile(tempname)
+
+ # check that invalid log (relative) will cause error
+ invdir = os.path.join(self.nodes[0].datadir, "regtest", "foo")
+ invalidname = os.path.join("foo", "foo.log")
+ self.stop_node(0)
+ self.assert_start_raises_init_error(0, ["-debuglogfile=%s" % (invalidname)],
+ "Error: Could not open debug log file")
+ assert not os.path.isfile(os.path.join(invdir, "foo.log"))
+
+ # check that invalid log (relative) works after path exists
+ self.stop_node(0)
+ os.mkdir(invdir)
+ self.start_node(0, ["-debuglogfile=%s" % (invalidname)])
+ assert os.path.isfile(os.path.join(invdir, "foo.log"))
+
+ # check that invalid log (absolute) will cause error
+ self.stop_node(0)
+ invdir = os.path.join(self.options.tmpdir, "foo")
+ invalidname = os.path.join(invdir, "foo.log")
+ self.assert_start_raises_init_error(0, ["-debuglogfile=%s" % invalidname],
+ "Error: Could not open debug log file")
+ assert not os.path.isfile(os.path.join(invdir, "foo.log"))
+
+ # check that invalid log (absolute) works after path exists
+ self.stop_node(0)
+ os.mkdir(invdir)
+ self.start_node(0, ["-debuglogfile=%s" % (invalidname)])
+ assert os.path.isfile(os.path.join(invdir, "foo.log"))
+
+
+if __name__ == '__main__':
+ LoggingTest().main()
diff --git a/test/functional/forknotify.py b/test/functional/forknotify.py
deleted file mode 100755
index afcad1f9cc..0000000000
--- a/test/functional/forknotify.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/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.
-"""Test the -alertnotify option."""
-import os
-import time
-
-from test_framework.test_framework import BitcoinTestFramework
-
-class ForkNotifyTest(BitcoinTestFramework):
- def set_test_params(self):
- self.num_nodes = 2
-
- def setup_network(self):
- self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
- with open(self.alert_filename, 'w', encoding='utf8'):
- pass # Just open then close to create zero-length file
- self.extra_args = [["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""],
- ["-blockversion=211"]]
- super().setup_network()
-
- def run_test(self):
- # Mine 51 up-version blocks
- self.nodes[1].generate(51)
- self.sync_all()
- # -alertnotify should trigger on the 51'st,
- # but mine and sync another to give
- # -alertnotify time to write
- self.nodes[1].generate(1)
- self.sync_all()
-
- # Give bitcoind 10 seconds to write the alert notification
- timeout = 10.0
- while timeout > 0:
- if os.path.exists(self.alert_filename) and os.path.getsize(self.alert_filename):
- break
- time.sleep(0.1)
- timeout -= 0.1
- else:
- assert False, "-alertnotify did not warn of up-version blocks"
-
- with open(self.alert_filename, 'r', encoding='utf8') as f:
- alert_text = f.read()
-
- # Mine more up-version blocks, should not get more alerts:
- self.nodes[1].generate(1)
- self.sync_all()
- self.nodes[1].generate(1)
- self.sync_all()
-
- with open(self.alert_filename, 'r', encoding='utf8') as f:
- alert_text2 = f.read()
-
- if alert_text != alert_text2:
- raise AssertionError("-alertnotify excessive warning of up-version blocks")
-
-if __name__ == '__main__':
- ForkNotifyTest().main()
diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py
index 75a0dc5f9d..d446f56d0e 100755
--- a/test/functional/fundrawtransaction.py
+++ b/test/functional/fundrawtransaction.py
@@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the fundrawtransaction RPC."""
-from test_framework.test_framework import BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT
+from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
@@ -179,7 +179,7 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
- assert_raises_jsonrpc(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'})
+ assert_raises_rpc_error(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'})
############################################################
# test a fundrawtransaction with an invalid change address #
@@ -192,7 +192,7 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
- assert_raises_jsonrpc(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'})
+ assert_raises_rpc_error(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'})
############################################################
# test a fundrawtransaction with a provided change address #
@@ -206,7 +206,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
change = self.nodes[2].getnewaddress()
- assert_raises_jsonrpc(-8, "changePosition out of bounds", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':change, 'changePosition':2})
+ assert_raises_rpc_error(-8, "changePosition out of bounds", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':change, 'changePosition':2})
rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {'changeAddress': change, 'changePosition': 0})
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
out = dec_tx['vout'][0]
@@ -314,7 +314,7 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
- assert_raises_jsonrpc(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx)
+ assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx)
############################################################
#compare fee of a standard pubkeyhash transaction
@@ -469,14 +469,14 @@ class RawTransactionsTest(BitcoinTestFramework):
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
- assert_raises_jsonrpc(-4, "Keypool ran out, please call keypoolrefill first", self.nodes[1].fundrawtransaction, rawtx)
+ assert_raises_rpc_error(-4, "Keypool ran out, please call keypoolrefill first", self.nodes[1].fundrawtransaction, rawtx)
#refill the keypool
self.nodes[1].walletpassphrase("test", 100)
self.nodes[1].keypoolrefill(8) #need to refill the keypool to get an internal change address
self.nodes[1].walletlock()
- assert_raises_jsonrpc(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2)
+ assert_raises_rpc_error(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2)
oldBalance = self.nodes[0].getbalance()
diff --git a/test/functional/import-rescan.py b/test/functional/import-rescan.py
index cb5b65c682..6807fa6696 100755
--- a/test/functional/import-rescan.py
+++ b/test/functional/import-rescan.py
@@ -19,9 +19,8 @@ importing nodes pick up the new transactions regardless of whether rescans
happened previously.
"""
-from test_framework.authproxy import JSONRPCException
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (connect_nodes, sync_blocks, assert_equal, set_node_times)
+from test_framework.util import (assert_raises_rpc_error, connect_nodes, sync_blocks, assert_equal, set_node_times)
import collections
import enum
@@ -35,21 +34,26 @@ Rescan = enum.Enum("Rescan", "no yes late_timestamp")
class Variant(collections.namedtuple("Variant", "call data rescan prune")):
"""Helper for importing one key and verifying scanned transactions."""
+ def try_rpc(self, func, *args, **kwargs):
+ if self.expect_disabled:
+ assert_raises_rpc_error(-4, "Rescan is disabled in pruned mode", func, *args, **kwargs)
+ else:
+ return func(*args, **kwargs)
+
def do_import(self, timestamp):
"""Call one key import RPC."""
if self.call == Call.single:
if self.data == Data.address:
- response, error = try_rpc(self.node.importaddress, self.address["address"], self.label,
- self.rescan == Rescan.yes)
+ response = self.try_rpc(self.node.importaddress, self.address["address"], self.label,
+ self.rescan == Rescan.yes)
elif self.data == Data.pub:
- response, error = try_rpc(self.node.importpubkey, self.address["pubkey"], self.label,
- self.rescan == Rescan.yes)
+ response = self.try_rpc(self.node.importpubkey, self.address["pubkey"], self.label,
+ self.rescan == Rescan.yes)
elif self.data == Data.priv:
- response, error = try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes)
+ response = self.try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes)
assert_equal(response, None)
- assert_equal(error, {'message': 'Rescan is disabled in pruned mode',
- 'code': -4} if self.expect_disabled else None)
+
elif self.call == Call.multi:
response = self.node.importmulti([{
"scriptPubKey": {
@@ -179,13 +183,5 @@ class ImportRescanTest(BitcoinTestFramework):
else:
variant.check()
-
-def try_rpc(func, *args, **kwargs):
- try:
- return func(*args, **kwargs), None
- except JSONRPCException as e:
- return None, e.error
-
-
if __name__ == "__main__":
ImportRescanTest().main()
diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py
index 32f555c79b..a691595f15 100755
--- a/test/functional/importmulti.py
+++ b/test/functional/importmulti.py
@@ -160,6 +160,18 @@ class ImportMultiTest (BitcoinTestFramework):
assert_equal(address_assert['ismine'], True)
assert_equal(address_assert['timestamp'], timestamp)
+ self.log.info("Should not import an address with private key if is already imported")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "timestamp": "now",
+ "keys": [ self.nodes[0].dumpprivkey(address['address']) ]
+ }])
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -4)
+ assert_equal(result[0]['error']['message'], 'The wallet already contains the private key for this address or script')
+
# Address + Private key + watchonly
self.log.info("Should not import an address with private key and with watchonly")
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
@@ -423,11 +435,11 @@ class ImportMultiTest (BitcoinTestFramework):
# Bad or missing timestamps
self.log.info("Should throw on invalid or missing timestamp values")
- assert_raises_message(JSONRPCException, 'Missing required timestamp field for key',
+ assert_raises_rpc_error(-3, 'Missing required timestamp field for key',
self.nodes[1].importmulti, [{
"scriptPubKey": address['scriptPubKey'],
}])
- assert_raises_message(JSONRPCException, 'Expected number or "now" timestamp value for key. got type string',
+ assert_raises_rpc_error(-3, 'Expected number or "now" timestamp value for key. got type string',
self.nodes[1].importmulti, [{
"scriptPubKey": address['scriptPubKey'],
"timestamp": "",
diff --git a/test/functional/importprunedfunds.py b/test/functional/importprunedfunds.py
index cb70010668..068052409a 100755
--- a/test/functional/importprunedfunds.py
+++ b/test/functional/importprunedfunds.py
@@ -66,7 +66,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
self.sync_all()
#Import with no affiliated address
- assert_raises_jsonrpc(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1)
+ assert_raises_rpc_error(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1)
balance1 = self.nodes[1].getbalance("", 0, True)
assert_equal(balance1, Decimal(0))
@@ -78,7 +78,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
assert_equal(balance2, Decimal('0.05'))
#Import with private key with no rescan
- self.nodes[1].importprivkey(address3_privkey, "add3", False)
+ self.nodes[1].importprivkey(privkey=address3_privkey, label="add3", rescan=False)
self.nodes[1].importprunedfunds(rawtxn3, proof3)
balance3 = self.nodes[1].getbalance("add3", 0, False)
assert_equal(balance3, Decimal('0.025'))
@@ -97,7 +97,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
assert_equal(address_info['ismine'], True)
#Remove transactions
- assert_raises_jsonrpc(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1)
+ assert_raises_rpc_error(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1)
balance1 = self.nodes[1].getbalance("*", 0, True)
assert_equal(balance1, Decimal('0.075'))
diff --git a/test/functional/invalidblockrequest.py b/test/functional/invalidblockrequest.py
index 9f44b44927..a89d1d8ef2 100755
--- a/test/functional/invalidblockrequest.py
+++ b/test/functional/invalidblockrequest.py
@@ -15,6 +15,7 @@ from test_framework.test_framework import ComparisonTestFramework
from test_framework.util import *
from test_framework.comptool import TestManager, TestInstance, RejectResult
from test_framework.blocktools import *
+from test_framework.mininode import network_thread_start
import copy
import time
@@ -32,7 +33,7 @@ class InvalidBlockRequestTest(ComparisonTestFramework):
test.add_all_connections(self.nodes)
self.tip = None
self.block_time = None
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
test.run()
def get_tests(self):
diff --git a/test/functional/invalidtxrequest.py b/test/functional/invalidtxrequest.py
index a22bd8f8cd..c60b0fce16 100755
--- a/test/functional/invalidtxrequest.py
+++ b/test/functional/invalidtxrequest.py
@@ -28,7 +28,7 @@ class InvalidTxRequestTest(ComparisonTestFramework):
test.add_all_connections(self.nodes)
self.tip = None
self.block_time = None
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
test.run()
def get_tests(self):
diff --git a/test/functional/keypool-topup.py b/test/functional/keypool-topup.py
index b87433a9c5..e7af3c3987 100755
--- a/test/functional/keypool-topup.py
+++ b/test/functional/keypool-topup.py
@@ -23,7 +23,7 @@ class KeypoolRestoreTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- self.extra_args = [['-usehd=0'], ['-usehd=1', '-keypool=100', '-keypoolmin=20']]
+ self.extra_args = [[], ['-keypool=100', '-keypoolmin=20']]
def run_test(self):
self.tmpdir = self.options.tmpdir
@@ -33,7 +33,7 @@ class KeypoolRestoreTest(BitcoinTestFramework):
self.stop_node(1)
- shutil.copyfile(self.tmpdir + "/node1/regtest/wallet.dat", self.tmpdir + "/wallet.bak")
+ shutil.copyfile(self.tmpdir + "/node1/regtest/wallets/wallet.dat", self.tmpdir + "/wallet.bak")
self.start_node(1, self.extra_args[1])
connect_nodes_bi(self.nodes, 0, 1)
@@ -56,7 +56,7 @@ class KeypoolRestoreTest(BitcoinTestFramework):
self.stop_node(1)
- shutil.copyfile(self.tmpdir + "/wallet.bak", self.tmpdir + "/node1/regtest/wallet.dat")
+ shutil.copyfile(self.tmpdir + "/wallet.bak", self.tmpdir + "/node1/regtest/wallets/wallet.dat")
self.log.info("Verify keypool is restored and balance is correct")
diff --git a/test/functional/keypool.py b/test/functional/keypool.py
index b823ca63bb..f2701c36bd 100755
--- a/test/functional/keypool.py
+++ b/test/functional/keypool.py
@@ -28,7 +28,7 @@ class KeyPoolTest(BitcoinTestFramework):
wallet_info = nodes[0].getwalletinfo()
assert(addr_before_encrypting_data['hdmasterkeyid'] != wallet_info['hdmasterkeyid'])
assert(addr_data['hdmasterkeyid'] == wallet_info['hdmasterkeyid'])
- assert_raises_jsonrpc(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
+ assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
# put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min)
nodes[0].walletpassphrase('test', 12000)
@@ -47,7 +47,7 @@ class KeyPoolTest(BitcoinTestFramework):
nodes[0].getrawchangeaddress()
addr = set()
# the next one should fail
- assert_raises_jsonrpc(-12, "Keypool ran out", nodes[0].getrawchangeaddress)
+ assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].getrawchangeaddress)
# drain the external keys
addr.add(nodes[0].getnewaddress())
@@ -58,7 +58,7 @@ class KeyPoolTest(BitcoinTestFramework):
addr.add(nodes[0].getnewaddress())
assert(len(addr) == 6)
# the next one should fail
- assert_raises_jsonrpc(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
+ assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
# refill keypool with three new addresses
nodes[0].walletpassphrase('test', 1)
@@ -72,7 +72,7 @@ class KeyPoolTest(BitcoinTestFramework):
nodes[0].generate(1)
nodes[0].generate(1)
nodes[0].generate(1)
- assert_raises_jsonrpc(-12, "Keypool ran out", nodes[0].generate, 1)
+ assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].generate, 1)
nodes[0].walletpassphrase('test', 100)
nodes[0].keypoolrefill(100)
diff --git a/test/functional/listsinceblock.py b/test/functional/listsinceblock.py
index 6f428388ec..67e7744bf8 100755
--- a/test/functional/listsinceblock.py
+++ b/test/functional/listsinceblock.py
@@ -5,7 +5,7 @@
"""Test the listsincelast RPC."""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
+from test_framework.util import assert_equal, assert_array_result, assert_raises_rpc_error
class ListSinceBlockTest (BitcoinTestFramework):
def set_test_params(self):
@@ -16,10 +16,43 @@ class ListSinceBlockTest (BitcoinTestFramework):
self.nodes[2].generate(101)
self.sync_all()
+ self.test_no_blockhash()
+ self.test_invalid_blockhash()
self.test_reorg()
self.test_double_spend()
self.test_double_send()
+ def test_no_blockhash(self):
+ txid = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 1)
+ blockhash, = self.nodes[2].generate(1)
+ self.sync_all()
+
+ txs = self.nodes[0].listtransactions()
+ assert_array_result(txs, {"txid": txid}, {
+ "category": "receive",
+ "amount": 1,
+ "blockhash": blockhash,
+ "confirmations": 1,
+ })
+ assert_equal(
+ self.nodes[0].listsinceblock(),
+ {"lastblock": blockhash,
+ "removed": [],
+ "transactions": txs})
+ assert_equal(
+ self.nodes[0].listsinceblock(""),
+ {"lastblock": blockhash,
+ "removed": [],
+ "transactions": txs})
+
+ def test_invalid_blockhash(self):
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
+ "42759cde25462784395a337460bde75f58e73d3f08bd31fdc3507cbac856a2c4")
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
+ "0000000000000000000000000000000000000000000000000000000000000000")
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
+ "invalid-hex")
+
def test_reorg(self):
'''
`listsinceblock` did not behave correctly when handed a block that was
diff --git a/test/functional/maxuploadtarget.py b/test/functional/maxuploadtarget.py
index 1f402798e7..cf2e484d9f 100755
--- a/test/functional/maxuploadtarget.py
+++ b/test/functional/maxuploadtarget.py
@@ -17,15 +17,15 @@ from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-class TestNode(NodeConnCB):
+class TestNode(P2PInterface):
def __init__(self):
super().__init__()
self.block_receive_map = defaultdict(int)
- def on_inv(self, conn, message):
+ def on_inv(self, message):
pass
- def on_block(self, conn, message):
+ def on_block(self, message):
message.block.calc_sha256()
self.block_receive_map[message.block.sha256] += 1
@@ -49,19 +49,17 @@ class MaxUploadTest(BitcoinTestFramework):
# Generate some old blocks
self.nodes[0].generate(130)
- # test_nodes[0] will only request old blocks
- # test_nodes[1] will only request new blocks
- # test_nodes[2] will test resetting the counters
- test_nodes = []
- connections = []
+ # p2p_conns[0] will only request old blocks
+ # p2p_conns[1] will only request new blocks
+ # p2p_conns[2] will test resetting the counters
+ p2p_conns = []
- for i in range(3):
- test_nodes.append(TestNode())
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i]))
- test_nodes[i].add_connection(connections[i])
+ for _ in range(3):
+ p2p_conns.append(self.nodes[0].add_p2p_connection(TestNode()))
- NetworkThread().start() # Start up network handling in another thread
- [x.wait_for_verack() for x in test_nodes]
+ network_thread_start()
+ for p2pc in p2p_conns:
+ p2pc.wait_for_verack()
# Test logic begins here
@@ -83,7 +81,7 @@ class MaxUploadTest(BitcoinTestFramework):
big_new_block = self.nodes[0].getbestblockhash()
big_new_block = int(big_new_block, 16)
- # test_nodes[0] will test what happens if we just keep requesting the
+ # p2p_conns[0] will test what happens if we just keep requesting the
# the same big old block too many times (expect: disconnect)
getdata_request = msg_getdata()
@@ -97,34 +95,34 @@ class MaxUploadTest(BitcoinTestFramework):
# 576MB will be reserved for relaying new blocks, so expect this to
# succeed for ~235 tries.
for i in range(success_count):
- test_nodes[0].send_message(getdata_request)
- test_nodes[0].sync_with_ping()
- assert_equal(test_nodes[0].block_receive_map[big_old_block], i+1)
+ p2p_conns[0].send_message(getdata_request)
+ p2p_conns[0].sync_with_ping()
+ assert_equal(p2p_conns[0].block_receive_map[big_old_block], i+1)
assert_equal(len(self.nodes[0].getpeerinfo()), 3)
# At most a couple more tries should succeed (depending on how long
# the test has been running so far).
for i in range(3):
- test_nodes[0].send_message(getdata_request)
- test_nodes[0].wait_for_disconnect()
+ p2p_conns[0].send_message(getdata_request)
+ p2p_conns[0].wait_for_disconnect()
assert_equal(len(self.nodes[0].getpeerinfo()), 2)
self.log.info("Peer 0 disconnected after downloading old block too many times")
- # Requesting the current block on test_nodes[1] should succeed indefinitely,
+ # Requesting the current block on p2p_conns[1] should succeed indefinitely,
# even when over the max upload target.
# We'll try 800 times
getdata_request.inv = [CInv(2, big_new_block)]
for i in range(800):
- test_nodes[1].send_message(getdata_request)
- test_nodes[1].sync_with_ping()
- assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1)
+ p2p_conns[1].send_message(getdata_request)
+ p2p_conns[1].sync_with_ping()
+ assert_equal(p2p_conns[1].block_receive_map[big_new_block], i+1)
self.log.info("Peer 1 able to repeatedly download new block")
- # But if test_nodes[1] tries for an old block, it gets disconnected too.
+ # But if p2p_conns[1] tries for an old block, it gets disconnected too.
getdata_request.inv = [CInv(2, big_old_block)]
- test_nodes[1].send_message(getdata_request)
- test_nodes[1].wait_for_disconnect()
+ p2p_conns[1].send_message(getdata_request)
+ p2p_conns[1].wait_for_disconnect()
assert_equal(len(self.nodes[0].getpeerinfo()), 1)
self.log.info("Peer 1 disconnected after trying to download old block")
@@ -132,39 +130,37 @@ class MaxUploadTest(BitcoinTestFramework):
self.log.info("Advancing system time on node to clear counters...")
# If we advance the time by 24 hours, then the counters should reset,
- # and test_nodes[2] should be able to retrieve the old block.
+ # and p2p_conns[2] should be able to retrieve the old block.
self.nodes[0].setmocktime(int(time.time()))
- test_nodes[2].sync_with_ping()
- test_nodes[2].send_message(getdata_request)
- test_nodes[2].sync_with_ping()
- assert_equal(test_nodes[2].block_receive_map[big_old_block], 1)
+ p2p_conns[2].sync_with_ping()
+ p2p_conns[2].send_message(getdata_request)
+ p2p_conns[2].sync_with_ping()
+ assert_equal(p2p_conns[2].block_receive_map[big_old_block], 1)
self.log.info("Peer 2 able to download old block")
- [c.disconnect_node() for c in connections]
+ self.nodes[0].disconnect_p2ps()
#stop and start node 0 with 1MB maxuploadtarget, whitelist 127.0.0.1
self.log.info("Restarting nodes with -whitelist=127.0.0.1")
self.stop_node(0)
self.start_node(0, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000"])
- #recreate/reconnect a test node
- test_nodes = [TestNode()]
- connections = [NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[0])]
- test_nodes[0].add_connection(connections[0])
+ # Reconnect to self.nodes[0]
+ self.nodes[0].add_p2p_connection(TestNode())
- NetworkThread().start() # Start up network handling in another thread
- test_nodes[0].wait_for_verack()
+ network_thread_start()
+ self.nodes[0].p2p.wait_for_verack()
#retrieve 20 blocks which should be enough to break the 1MB limit
getdata_request.inv = [CInv(2, big_new_block)]
for i in range(20):
- test_nodes[0].send_message(getdata_request)
- test_nodes[0].sync_with_ping()
- assert_equal(test_nodes[0].block_receive_map[big_new_block], i+1)
+ self.nodes[0].p2p.send_message(getdata_request)
+ self.nodes[0].p2p.sync_with_ping()
+ assert_equal(self.nodes[0].p2p.block_receive_map[big_new_block], i+1)
getdata_request.inv = [CInv(2, big_old_block)]
- test_nodes[0].send_and_ping(getdata_request)
+ self.nodes[0].p2p.send_and_ping(getdata_request)
assert_equal(len(self.nodes[0].getpeerinfo()), 1) #node is still connected because of the whitelist
self.log.info("Peer still connected after trying to download old block (whitelisted)")
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 2b09889661..b845c75681 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -115,7 +115,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000)
# Adding one more transaction on to the chain should fail.
- assert_raises_jsonrpc(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1)
# Check that prioritising a tx before it's added to the mempool works
# First clear the mempool by mining a block.
@@ -167,7 +167,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
# Sending one more chained transaction will fail
utxo = transaction_package.pop(0)
- assert_raises_jsonrpc(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
# TODO: check that node1's mempool is as expected
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index 01f65b1373..31a96ec60e 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -28,8 +28,14 @@ Test is as follows:
- Restart node0 with -persistmempool. Verify that it has 5
transactions in its mempool. This tests that -persistmempool=0
does not overwrite a previously valid mempool stored on disk.
+ - Remove node0 mempool.dat and verify savemempool RPC recreates it
+ and verify that node1 can load it and has 5 transaction in its
+ mempool.
+ - Verify that savemempool throws when the RPC is called if
+ node1 can't write to disk.
"""
+import os
import time
from test_framework.test_framework import BitcoinTestFramework
@@ -51,21 +57,27 @@ class MempoolPersistTest(BitcoinTestFramework):
self.log.debug("Send 5 transactions from node2 (to its own address)")
for i in range(5):
self.nodes[2].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("10"))
+ node2_balance = self.nodes[2].getbalance()
self.sync_all()
self.log.debug("Verify that node0 and node1 have 5 transactions in their mempools")
assert_equal(len(self.nodes[0].getrawmempool()), 5)
assert_equal(len(self.nodes[1].getrawmempool()), 5)
- self.log.debug("Stop-start node0 and node1. Verify that node0 has the transactions in its mempool and node1 does not.")
+ self.log.debug("Stop-start the nodes. Verify that node0 has the transactions in its mempool and node1 does not. Verify that node2 calculates its balance correctly after loading wallet transactions.")
self.stop_nodes()
self.start_node(0)
self.start_node(1)
+ self.start_node(2)
# Give bitcoind a second to reload the mempool
time.sleep(1)
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5)
+ wait_until(lambda: len(self.nodes[2].getrawmempool()) == 5)
assert_equal(len(self.nodes[1].getrawmempool()), 0)
+ # Verify accounting of mempool transactions after restart is correct
+ assert_equal(node2_balance, self.nodes[2].getbalance())
+
self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.")
self.stop_nodes()
self.start_node(0, extra_args=["-persistmempool=0"])
@@ -78,5 +90,27 @@ class MempoolPersistTest(BitcoinTestFramework):
self.start_node(0)
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5)
+ mempooldat0 = os.path.join(self.options.tmpdir, 'node0', 'regtest', 'mempool.dat')
+ mempooldat1 = os.path.join(self.options.tmpdir, 'node1', 'regtest', 'mempool.dat')
+ self.log.debug("Remove the mempool.dat file. Verify that savemempool to disk via RPC re-creates it")
+ os.remove(mempooldat0)
+ self.nodes[0].savemempool()
+ assert os.path.isfile(mempooldat0)
+
+ self.log.debug("Stop nodes, make node1 use mempool.dat from node0. Verify it has 5 transactions")
+ os.rename(mempooldat0, mempooldat1)
+ self.stop_nodes()
+ self.start_node(1, extra_args=[])
+ wait_until(lambda: len(self.nodes[1].getrawmempool()) == 5)
+
+ self.log.debug("Prevent bitcoind from writing mempool.dat to disk. Verify that `savemempool` fails")
+ # to test the exception we are setting bad permissions on a tmp file called mempool.dat.new
+ # which is an implementation detail that could change and break this test
+ mempooldotnew1 = mempooldat1 + '.new'
+ with os.fdopen(os.open(mempooldotnew1, os.O_CREAT, 0o000), 'w'):
+ pass
+ assert_raises_rpc_error(-1, "Unable to dump mempool to disk", self.nodes[1].savemempool)
+ os.remove(mempooldotnew1)
+
if __name__ == '__main__':
MempoolPersistTest().main()
diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py
index 7dfddd3230..2803371f5b 100755
--- a/test/functional/mempool_reorg.py
+++ b/test/functional/mempool_reorg.py
@@ -50,14 +50,14 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000"
timelock_tx = self.nodes[0].signrawtransaction(timelock_tx)["hex"]
# This will raise an exception because the timelock transaction is too immature to spend
- assert_raises_jsonrpc(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx)
+ assert_raises_rpc_error(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx)
# Broadcast and mine spend_102 and 103:
spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw)
spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw)
self.nodes[0].generate(1)
# Time-locked transaction is still too immature to spend
- assert_raises_jsonrpc(-26,'non-final', self.nodes[0].sendrawtransaction, timelock_tx)
+ assert_raises_rpc_error(-26,'non-final', self.nodes[0].sendrawtransaction, timelock_tx)
# Create 102_1 and 103_1:
spend_102_1_raw = create_tx(self.nodes[0], spend_102_id, node1_address, 49.98)
diff --git a/test/functional/mempool_spendcoinbase.py b/test/functional/mempool_spendcoinbase.py
index 58ccd3e373..6e8a635a76 100755
--- a/test/functional/mempool_spendcoinbase.py
+++ b/test/functional/mempool_spendcoinbase.py
@@ -36,7 +36,7 @@ class MempoolSpendCoinbaseTest(BitcoinTestFramework):
spend_101_id = self.nodes[0].sendrawtransaction(spends_raw[0])
# coinbase at height 102 should be too immature to spend
- assert_raises_jsonrpc(-26,"bad-txns-premature-spend-of-coinbase", self.nodes[0].sendrawtransaction, spends_raw[1])
+ assert_raises_rpc_error(-26,"bad-txns-premature-spend-of-coinbase", self.nodes[0].sendrawtransaction, spends_raw[1])
# mempool should have just spend_101:
assert_equal(self.nodes[0].getrawmempool(), [ spend_101_id ])
diff --git a/test/functional/merkle_blocks.py b/test/functional/merkle_blocks.py
index a58334b2a5..b3989a4c54 100755
--- a/test/functional/merkle_blocks.py
+++ b/test/functional/merkle_blocks.py
@@ -38,7 +38,7 @@ class MerkleBlockTest(BitcoinTestFramework):
tx2 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 49.99})
txid2 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx2)["hex"])
# This will raise an exception because the transaction is not yet in a block
- assert_raises_jsonrpc(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1])
+ assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1])
self.nodes[0].generate(1)
blockhash = self.nodes[0].getblockhash(chain_height + 1)
@@ -63,11 +63,11 @@ class MerkleBlockTest(BitcoinTestFramework):
txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2
# We can't find the block from a fully-spent tx
- assert_raises_jsonrpc(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent])
+ assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent])
# We can get the proof if we specify the block
assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent])
# We can't get the proof if we specify a non-existent block
- assert_raises_jsonrpc(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000")
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000")
# We can get the proof if the transaction is unspent
assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent])
# We can get the proof if we provide a list of transactions and one of them is unspent. The ordering of the list should not matter.
@@ -76,7 +76,7 @@ class MerkleBlockTest(BitcoinTestFramework):
# We can always get a proof if we have a -txindex
assert_equal(self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof([txid_spent])), [txid_spent])
# We can't get a proof if we specify transactions from different blocks
- assert_raises_jsonrpc(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3])
+ assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3])
if __name__ == '__main__':
diff --git a/test/functional/minchainwork.py b/test/functional/minchainwork.py
new file mode 100755
index 0000000000..90a3de0e0d
--- /dev/null
+++ b/test/functional/minchainwork.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 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 logic for setting nMinimumChainWork on command line.
+
+Nodes don't consider themselves out of "initial block download" until
+their active chain has more work than nMinimumChainWork.
+
+Nodes don't download blocks from a peer unless the peer's best known block
+has more work than nMinimumChainWork.
+
+While in initial block download, nodes won't relay blocks to their peers, so
+test that this parameter functions as intended by verifying that block relay
+only succeeds past a given node once its nMinimumChainWork has been exceeded.
+"""
+
+import time
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import connect_nodes, assert_equal
+
+# 2 hashes required per regtest block (with no difficulty adjustment)
+REGTEST_WORK_PER_BLOCK = 2
+
+class MinimumChainWorkTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 3
+
+ self.extra_args = [[], ["-minimumchainwork=0x65"], ["-minimumchainwork=0x65"]]
+ self.node_min_work = [0, 101, 101]
+
+ def setup_network(self):
+ # This test relies on the chain setup being:
+ # node0 <- node1 <- node2
+ # Before leaving IBD, nodes prefer to download blocks from outbound
+ # peers, so ensure that we're mining on an outbound peer and testing
+ # block relay to inbound peers.
+ self.setup_nodes()
+ for i in range(self.num_nodes-1):
+ connect_nodes(self.nodes[i+1], i)
+
+ def run_test(self):
+ # Start building a chain on node0. node2 shouldn't be able to sync until node1's
+ # minchainwork is exceeded
+ starting_chain_work = REGTEST_WORK_PER_BLOCK # Genesis block's work
+ self.log.info("Testing relay across node %d (minChainWork = %d)", 1, self.node_min_work[1])
+
+ starting_blockcount = self.nodes[2].getblockcount()
+
+ num_blocks_to_generate = int((self.node_min_work[1] - starting_chain_work) / REGTEST_WORK_PER_BLOCK)
+ self.log.info("Generating %d blocks on node0", num_blocks_to_generate)
+ hashes = self.nodes[0].generate(num_blocks_to_generate)
+
+ self.log.info("Node0 current chain work: %s", self.nodes[0].getblockheader(hashes[-1])['chainwork'])
+
+ # Sleep a few seconds and verify that node2 didn't get any new blocks
+ # or headers. We sleep, rather than sync_blocks(node0, node1) because
+ # it's reasonable either way for node1 to get the blocks, or not get
+ # them (since they're below node1's minchainwork).
+ time.sleep(3)
+
+ self.log.info("Verifying node 2 has no more blocks than before")
+ self.log.info("Blockcounts: %s", [n.getblockcount() for n in self.nodes])
+ # Node2 shouldn't have any new headers yet, because node1 should not
+ # have relayed anything.
+ assert_equal(len(self.nodes[2].getchaintips()), 1)
+ assert_equal(self.nodes[2].getchaintips()[0]['height'], 0)
+
+ assert self.nodes[1].getbestblockhash() != self.nodes[0].getbestblockhash()
+ assert_equal(self.nodes[2].getblockcount(), starting_blockcount)
+
+ self.log.info("Generating one more block")
+ self.nodes[0].generate(1)
+
+ self.log.info("Verifying nodes are all synced")
+
+ # Because nodes in regtest are all manual connections (eg using
+ # addnode), node1 should not have disconnected node0. If not for that,
+ # we'd expect node1 to have disconnected node0 for serving an
+ # insufficient work chain, in which case we'd need to reconnect them to
+ # continue the test.
+
+ self.sync_all()
+ self.log.info("Blockcounts: %s", [n.getblockcount() for n in self.nodes])
+
+if __name__ == '__main__':
+ MinimumChainWorkTest().main()
diff --git a/test/functional/mining.py b/test/functional/mining.py
index 93f9838896..9aee06864e 100755
--- a/test/functional/mining.py
+++ b/test/functional/mining.py
@@ -15,7 +15,7 @@ from decimal import Decimal
from test_framework.blocktools import create_coinbase
from test_framework.mininode import CBlock
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_jsonrpc
+from test_framework.util import assert_equal, assert_raises_rpc_error
def b2x(b):
return b2a_hex(b).decode('ascii')
@@ -38,7 +38,6 @@ class MiningTest(BitcoinTestFramework):
mining_info = node.getmininginfo()
assert_equal(mining_info['blocks'], 200)
assert_equal(mining_info['chain'], 'regtest')
- assert_equal(mining_info['currentblocksize'], 0)
assert_equal(mining_info['currentblocktx'], 0)
assert_equal(mining_info['currentblockweight'], 0)
assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10'))
@@ -69,7 +68,7 @@ class MiningTest(BitcoinTestFramework):
assert_template(node, block, None)
self.log.info("submitblock: Test block decode failure")
- assert_raises_jsonrpc(-22, "Block decode failed", node.submitblock, b2x(block.serialize()[:-15]))
+ assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, b2x(block.serialize()[:-15]))
self.log.info("getblocktemplate: Test bad input hash for coinbase transaction")
bad_block = copy.deepcopy(block)
@@ -78,10 +77,10 @@ class MiningTest(BitcoinTestFramework):
assert_template(node, bad_block, 'bad-cb-missing')
self.log.info("submitblock: Test invalid coinbase transaction")
- assert_raises_jsonrpc(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize()))
+ assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize()))
self.log.info("getblocktemplate: Test truncated final transaction")
- assert_raises_jsonrpc(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal'})
+ assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal'})
self.log.info("getblocktemplate: Test duplicate transaction")
bad_block = copy.deepcopy(block)
@@ -108,7 +107,7 @@ class MiningTest(BitcoinTestFramework):
bad_block_sn = bytearray(block.serialize())
assert_equal(bad_block_sn[TX_COUNT_OFFSET], 1)
bad_block_sn[TX_COUNT_OFFSET] += 1
- assert_raises_jsonrpc(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal'})
+ assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal'})
self.log.info("getblocktemplate: Test bad bits")
bad_block = copy.deepcopy(block)
diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py
index e5453e9aad..06409b6f31 100755
--- a/test/functional/multiwallet.py
+++ b/test/functional/multiwallet.py
@@ -7,44 +7,75 @@
Verify that a bitcoind node can load multiple wallet files
"""
import os
+import shutil
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_jsonrpc
+from test_framework.util import assert_equal, assert_raises_rpc_error
class MultiWalletTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
- self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3']]
+ self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3', '-wallet=w']]
def run_test(self):
+ assert_equal(set(self.nodes[0].listwallets()), {"w1", "w2", "w3", "w"})
+
self.stop_node(0)
# should not initialize if there are duplicate wallets
self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.')
# should not initialize if wallet file is a directory
- os.mkdir(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w11'))
+ wallet_dir = os.path.join(self.options.tmpdir, 'node0', 'regtest', 'wallets')
+ os.mkdir(os.path.join(wallet_dir, 'w11'))
self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.')
+ # should not initialize if one wallet is a copy of another
+ shutil.copyfile(os.path.join(wallet_dir, 'w2'), os.path.join(wallet_dir, 'w22'))
+ self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid')
+
# should not initialize if wallet file is a symlink
- os.symlink(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w1'), os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w12'))
+ os.symlink(os.path.join(wallet_dir, 'w1'), os.path.join(wallet_dir, 'w12'))
self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.')
+ # should not initialize if the specified walletdir does not exist
+ self.assert_start_raises_init_error(0, ['-walletdir=bad'], 'Error: Specified wallet directory "bad" does not exist.')
+
+ # if wallets/ doesn't exist, datadir should be the default wallet dir
+ wallet_dir2 = os.path.join(self.options.tmpdir, 'node0', 'regtest', 'walletdir')
+ os.rename(wallet_dir, wallet_dir2)
+ self.start_node(0, ['-wallet=w4', '-wallet=w5'])
+ assert_equal(set(self.nodes[0].listwallets()), {"w4", "w5"})
+ w5 = self.nodes[0].get_wallet_rpc("w5")
+ w5.generate(1)
+ self.stop_node(0)
+
+ # now if wallets/ exists again, but the rootdir is specified as the walletdir, w4 and w5 should still be loaded
+ os.rename(wallet_dir2, wallet_dir)
+ self.start_node(0, ['-wallet=w4', '-wallet=w5', '-walletdir=' + os.path.join(self.options.tmpdir, 'node0', 'regtest')])
+ assert_equal(set(self.nodes[0].listwallets()), {"w4", "w5"})
+ w5 = self.nodes[0].get_wallet_rpc("w5")
+ w5_info = w5.getwalletinfo()
+ assert_equal(w5_info['immature_balance'], 50)
+
+ self.stop_node(0)
+
self.start_node(0, self.extra_args[0])
w1 = self.nodes[0].get_wallet_rpc("w1")
w2 = self.nodes[0].get_wallet_rpc("w2")
w3 = self.nodes[0].get_wallet_rpc("w3")
+ w4 = self.nodes[0].get_wallet_rpc("w")
wallet_bad = self.nodes[0].get_wallet_rpc("bad")
w1.generate(1)
# accessing invalid wallet fails
- assert_raises_jsonrpc(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo)
+ assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo)
# accessing wallet RPC without using wallet endpoint fails
- assert_raises_jsonrpc(-19, "Wallet file not specified", self.nodes[0].getwalletinfo)
+ assert_raises_rpc_error(-19, "Wallet file not specified", self.nodes[0].getwalletinfo)
# check w1 wallet balance
w1_info = w1.getwalletinfo()
@@ -61,18 +92,26 @@ class MultiWalletTest(BitcoinTestFramework):
w3_name = w3.getwalletinfo()['walletname']
assert_equal(w3_name, "w3")
- assert_equal({"w1", "w2", "w3"}, {w1_name, w2_name, w3_name})
+ w4_name = w4.getwalletinfo()['walletname']
+ assert_equal(w4_name, "w")
w1.generate(101)
assert_equal(w1.getbalance(), 100)
assert_equal(w2.getbalance(), 0)
assert_equal(w3.getbalance(), 0)
+ assert_equal(w4.getbalance(), 0)
w1.sendtoaddress(w2.getnewaddress(), 1)
w1.sendtoaddress(w3.getnewaddress(), 2)
+ w1.sendtoaddress(w4.getnewaddress(), 3)
w1.generate(1)
assert_equal(w2.getbalance(), 1)
assert_equal(w3.getbalance(), 2)
+ assert_equal(w4.getbalance(), 3)
+
+ batch = w1.batch([w1.getblockchaininfo.get_request(), w1.getwalletinfo.get_request()])
+ assert_equal(batch[0]["result"]["chain"], "regtest")
+ assert_equal(batch[1]["result"]["walletname"], "w1")
if __name__ == '__main__':
MultiWalletTest().main()
diff --git a/test/functional/net.py b/test/functional/net.py
index 830aeb45b4..16e4f6adb4 100755
--- a/test/functional/net.py
+++ b/test/functional/net.py
@@ -12,7 +12,7 @@ import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- assert_raises_jsonrpc,
+ assert_raises_rpc_error,
connect_nodes_bi,
p2p_port,
)
@@ -84,7 +84,7 @@ class NetTest(BitcoinTestFramework):
assert_equal(len(added_nodes), 1)
assert_equal(added_nodes[0]['addednode'], ip_port)
# check that a non-existent node returns an error
- assert_raises_jsonrpc(-24, "Node has not been added",
+ assert_raises_rpc_error(-24, "Node has not been added",
self.nodes[0].getaddednodeinfo, '1.1.1.1')
def _test_getpeerinfo(self):
diff --git a/test/functional/node_network_limited.py b/test/functional/node_network_limited.py
new file mode 100755
index 0000000000..6d1bf7ced2
--- /dev/null
+++ b/test/functional/node_network_limited.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 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 *
+
+class BaseNode(P2PInterface):
+ nServices = 0
+ firstAddrnServices = 0
+ def on_version(self, message):
+ self.nServices = message.nServices
+
+class NodeNetworkLimitedTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+ self.extra_args = [['-prune=550']]
+
+ def getSignaledServiceFlags(self):
+ node = self.nodes[0].add_p2p_connection(BaseNode())
+ NetworkThread().start()
+ node.wait_for_verack()
+ services = node.nServices
+ self.nodes[0].disconnect_p2ps()
+ node.wait_for_disconnect()
+ return services
+
+ def tryGetBlockViaGetData(self, blockhash, must_disconnect):
+ node = self.nodes[0].add_p2p_connection(BaseNode())
+ NetworkThread().start()
+ node.wait_for_verack()
+ node.send_message(msg_verack())
+ getdata_request = msg_getdata()
+ getdata_request.inv.append(CInv(2, int(blockhash, 16)))
+ node.send_message(getdata_request)
+
+ if (must_disconnect):
+ #ensure we get disconnected
+ node.wait_for_disconnect(5)
+ else:
+ # check if the peer sends us the requested block
+ node.wait_for_block(int(blockhash, 16), 3)
+ self.nodes[0].disconnect_p2ps()
+ node.wait_for_disconnect()
+
+ def run_test(self):
+ #NODE_BLOOM & NODE_WITNESS & NODE_NETWORK_LIMITED must now be signaled
+ assert_equal(self.getSignaledServiceFlags(), 1036) #1036 == 0x40C == 0100 0000 1100
+# | ||
+# | |^--- NODE_BLOOM
+# | ^---- NODE_WITNESS
+# ^-- NODE_NETWORK_LIMITED
+
+ #now mine some blocks over the NODE_NETWORK_LIMITED + 2(racy buffer ext.) target
+ firstblock = self.nodes[0].generate(1)[0]
+ blocks = self.nodes[0].generate(292)
+ blockWithinLimitedRange = blocks[-1]
+
+ #make sure we can max retrive block at tip-288
+ #requesting block at height 2 (tip-289) must fail (ignored)
+ self.tryGetBlockViaGetData(firstblock, True) #first block must lead to disconnect
+ self.tryGetBlockViaGetData(blocks[1], False) #last block in valid range
+ self.tryGetBlockViaGetData(blocks[0], True) #first block outside of the 288+2 limit
+
+ #NODE_NETWORK_LIMITED must still be signaled after restart
+ self.restart_node(0)
+ assert_equal(self.getSignaledServiceFlags(), 1036)
+
+ #test the RPC service flags
+ assert_equal(self.nodes[0].getnetworkinfo()['localservices'], "000000000000040c")
+
+ # getdata a block above the NODE_NETWORK_LIMITED threshold must be possible
+ self.tryGetBlockViaGetData(blockWithinLimitedRange, False)
+
+ # getdata a block below the NODE_NETWORK_LIMITED threshold must be ignored
+ self.tryGetBlockViaGetData(firstblock, True)
+
+if __name__ == '__main__':
+ NodeNetworkLimitedTest().main()
diff --git a/test/functional/notifications.py b/test/functional/notifications.py
new file mode 100755
index 0000000000..c88972ab91
--- /dev/null
+++ b/test/functional/notifications.py
@@ -0,0 +1,86 @@
+#!/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.
+"""Test the -alertnotify, -blocknotify and -walletnotify options."""
+import os
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal, wait_until, connect_nodes_bi
+
+class NotificationsTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+
+ def setup_network(self):
+ self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
+ self.block_filename = os.path.join(self.options.tmpdir, "blocks.txt")
+ self.tx_filename = os.path.join(self.options.tmpdir, "transactions.txt")
+
+ # -alertnotify and -blocknotify on node0, walletnotify on node1
+ self.extra_args = [["-blockversion=2",
+ "-alertnotify=echo %%s >> %s" % self.alert_filename,
+ "-blocknotify=echo %%s >> %s" % self.block_filename],
+ ["-blockversion=211",
+ "-rescan",
+ "-walletnotify=echo %%s >> %s" % self.tx_filename]]
+ super().setup_network()
+
+ def run_test(self):
+ self.log.info("test -blocknotify")
+ block_count = 10
+ blocks = self.nodes[1].generate(block_count)
+
+ # wait at most 10 seconds for expected file size before reading the content
+ wait_until(lambda: os.path.isfile(self.block_filename) and os.stat(self.block_filename).st_size >= (block_count * 65), timeout=10)
+
+ # file content should equal the generated blocks hashes
+ with open(self.block_filename, 'r') as f:
+ assert_equal(sorted(blocks), sorted(f.read().splitlines()))
+
+ self.log.info("test -walletnotify")
+ # wait at most 10 seconds for expected file size before reading the content
+ wait_until(lambda: os.path.isfile(self.tx_filename) and os.stat(self.tx_filename).st_size >= (block_count * 65), timeout=10)
+
+ # file content should equal the generated transaction hashes
+ txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
+ with open(self.tx_filename, 'r') as f:
+ assert_equal(sorted(txids_rpc), sorted(f.read().splitlines()))
+ os.remove(self.tx_filename)
+
+ self.log.info("test -walletnotify after rescan")
+ # restart node to rescan to force wallet notifications
+ self.restart_node(1)
+ connect_nodes_bi(self.nodes, 0, 1)
+
+ wait_until(lambda: os.path.isfile(self.tx_filename) and os.stat(self.tx_filename).st_size >= (block_count * 65), timeout=10)
+
+ # file content should equal the generated transaction hashes
+ txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
+ with open(self.tx_filename, 'r') as f:
+ assert_equal(sorted(txids_rpc), sorted(f.read().splitlines()))
+
+ # Mine another 41 up-version blocks. -alertnotify should trigger on the 51st.
+ self.log.info("test -alertnotify")
+ self.nodes[1].generate(41)
+ self.sync_all()
+
+ # Give bitcoind 10 seconds to write the alert notification
+ wait_until(lambda: os.path.isfile(self.alert_filename) and os.path.getsize(self.alert_filename), timeout=10)
+
+ with open(self.alert_filename, 'r', encoding='utf8') as f:
+ alert_text = f.read()
+
+ # Mine more up-version blocks, should not get more alerts:
+ self.nodes[1].generate(2)
+ self.sync_all()
+
+ with open(self.alert_filename, 'r', encoding='utf8') as f:
+ alert_text2 = f.read()
+
+ self.log.info("-alertnotify should not continue notifying for more unknown version blocks")
+ assert_equal(alert_text, alert_text2)
+
+if __name__ == '__main__':
+ NotificationsTest().main()
diff --git a/test/functional/nulldummy.py b/test/functional/nulldummy.py
index 60d0d876df..9f9f2f90c0 100755
--- a/test/functional/nulldummy.py
+++ b/test/functional/nulldummy.py
@@ -15,7 +15,7 @@ Generate 427 more blocks.
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-from test_framework.mininode import CTransaction, NetworkThread
+from test_framework.mininode import CTransaction, network_thread_start
from test_framework.blocktools import create_coinbase, create_block, add_witness_commitment
from test_framework.script import CScript
from io import BytesIO
@@ -40,7 +40,9 @@ class NULLDUMMYTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness']]
+ # This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
+ # normal segwit activation here (and don't use the default always-on behaviour).
+ self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness', '-vbparams=segwit:0:999999999999']]
def run_test(self):
self.address = self.nodes[0].getnewaddress()
@@ -48,7 +50,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
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
+ network_thread_start()
self.coinbase_blocks = self.nodes[0].generate(2) # Block 2
coinbase_txid = []
for i in self.coinbase_blocks:
@@ -71,7 +73,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.log.info("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, 47)
trueDummy(test2tx)
- assert_raises_jsonrpc(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test2tx.serialize_with_witness()), True)
+ assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test2tx.serialize_with_witness()), True)
self.log.info("Test 3: Non-NULLDUMMY base transactions should be accepted in a block before activation [431]")
self.block_submit(self.nodes[0], [test2tx], False, True)
@@ -80,14 +82,14 @@ class NULLDUMMYTest(BitcoinTestFramework):
test4tx = self.create_transaction(self.nodes[0], test2tx.hash, self.address, 46)
test6txs=[CTransaction(test4tx)]
trueDummy(test4tx)
- assert_raises_jsonrpc(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test4tx.serialize_with_witness()), True)
+ assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test4tx.serialize_with_witness()), True)
self.block_submit(self.nodes[0], [test4tx])
self.log.info("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'
- assert_raises_jsonrpc(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test5tx.serialize_with_witness()), True)
+ assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test5tx.serialize_with_witness()), True)
self.block_submit(self.nodes[0], [test5tx], True)
self.log.info("Test 6: NULLDUMMY compliant base/witness transactions should be accepted to mempool and in block after activation [432]")
diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p-acceptblock.py
index 293bc05539..bb204322ed 100755
--- a/test/functional/p2p-acceptblock.py
+++ b/test/functional/p2p-acceptblock.py
@@ -4,37 +4,32 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test processing of unrequested blocks.
-Since behavior differs when receiving unrequested blocks from whitelisted peers
-versus non-whitelisted peers, this tests the behavior of both (effectively two
-separate tests running in parallel).
+Setup: two nodes, node0+node1, not connected to each other. Node1 will have
+nMinimumChainWork set to 0x10, so it won't process low-work unrequested blocks.
-Setup: two nodes, node0 and node1, not connected to each other. Node0 does not
-whitelist localhost, but node1 does. They will each be on their own chain for
-this test.
-
-We have one NodeConn connection to each, test_node and white_node respectively.
+We have one P2PInterface connection to node0 called test_node, and one to node1
+called min_work_node.
The test:
1. Generate one block on each node, to leave IBD.
2. Mine a new block on each tip, and deliver to each node from node's peer.
- The tip should advance.
+ The tip should advance for node0, but node1 should skip processing due to
+ nMinimumChainWork.
+
+Node1 is unused in tests 3-7:
-3. Mine a block that forks the previous block, and deliver to each node from
- corresponding peer.
- Node0 should not process this block (just accept the header), because it is
- unrequested and doesn't have more work than the tip.
- Node1 should process because this is coming from a whitelisted peer.
+3. Mine a block that forks from the genesis block, and deliver to test_node.
+ Node0 should not process this block (just accept the header), because it
+ is unrequested and doesn't have more or equal work to the tip.
-4. Send another block that builds on the forking block.
- Node0 should process this block but be stuck on the shorter chain, because
- it's missing an intermediate block.
- Node1 should reorg to this longer chain.
+4a,b. Send another two blocks that build on the forking block.
+ Node0 should process the second block but be stuck on the shorter chain,
+ because it's missing an intermediate block.
-4b.Send 288 more blocks on the longer chain.
+4c.Send 288 more blocks on the longer chain (the number of blocks ahead
+ we currently store).
Node0 should process all but the last block (too far ahead in height).
- Send all headers to Node1, and then send the last block in that chain.
- Node1 should accept the block because it's coming from a whitelisted peer.
5. Send a duplicate of the block in #3 to Node0.
Node0 should not process the block because it is unrequested, and stay on
@@ -46,13 +41,21 @@ The test:
7. Send Node0 the missing block again.
Node0 should process and the tip should advance.
+
+8. Create a fork which is invalid at a height longer than the current chain
+ (ie to which the node will try to reorg) but which has headers built on top
+ of the invalid block. Check that we get disconnected if we send more headers
+ on the chain the node now knows to be invalid.
+
+9. Test Node1 is able to sync when connected to node0 (which should have sufficient
+ work on its chain).
"""
from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
import time
-from test_framework.blocktools import create_block, create_coinbase
+from test_framework.blocktools import create_block, create_coinbase, create_transaction
class AcceptBlockTest(BitcoinTestFramework):
def add_options(self, parser):
@@ -63,37 +66,35 @@ class AcceptBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- self.extra_args = [[], ["-whitelist=127.0.0.1"]]
+ self.extra_args = [[], ["-minimumchainwork=0x10"]]
def setup_network(self):
# Node0 will be used to test behavior of processing unrequested blocks
# from peers which are not whitelisted, while Node1 will be used for
# the whitelisted case.
+ # Node2 will be used for non-whitelisted peers to test the interaction
+ # with nMinimumChainWork.
self.setup_nodes()
def run_test(self):
# Setup the p2p connections and start up the network thread.
- test_node = NodeConnCB() # connects to node0 (not whitelisted)
- white_node = NodeConnCB() # connects to node1 (whitelisted)
-
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node))
- connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], white_node))
- test_node.add_connection(connections[0])
- white_node.add_connection(connections[1])
+ # test_node connects to node0 (not whitelisted)
+ test_node = self.nodes[0].add_p2p_connection(P2PInterface())
+ # min_work_node connects to node1 (whitelisted)
+ min_work_node = self.nodes[1].add_p2p_connection(P2PInterface())
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
# Test logic begins here
test_node.wait_for_verack()
- white_node.wait_for_verack()
+ min_work_node.wait_for_verack()
- # 1. Have both nodes mine a block (leave IBD)
+ # 1. Have nodes mine a block (leave IBD)
[ n.generate(1) for n in self.nodes ]
tips = [ int("0x" + n.getbestblockhash(), 0) for n in self.nodes ]
# 2. Send one block that builds on each tip.
- # This should be accepted.
+ # This should be accepted by node0
blocks_h2 = [] # the height 2 blocks on each node's chain
block_time = int(time.time()) + 1
for i in range(2):
@@ -101,95 +102,120 @@ class AcceptBlockTest(BitcoinTestFramework):
blocks_h2[i].solve()
block_time += 1
test_node.send_message(msg_block(blocks_h2[0]))
- white_node.send_message(msg_block(blocks_h2[1]))
+ min_work_node.send_message(msg_block(blocks_h2[1]))
- [ x.sync_with_ping() for x in [test_node, white_node] ]
+ for x in [test_node, min_work_node]:
+ x.sync_with_ping()
assert_equal(self.nodes[0].getblockcount(), 2)
- assert_equal(self.nodes[1].getblockcount(), 2)
- self.log.info("First height 2 block accepted by both nodes")
+ assert_equal(self.nodes[1].getblockcount(), 1)
+ self.log.info("First height 2 block accepted by node0; correctly rejected by node1")
- # 3. Send another block that builds on the original tip.
- blocks_h2f = [] # Blocks at height 2 that fork off the main chain
- for i in range(2):
- blocks_h2f.append(create_block(tips[i], create_coinbase(2), blocks_h2[i].nTime+1))
- blocks_h2f[i].solve()
- test_node.send_message(msg_block(blocks_h2f[0]))
- white_node.send_message(msg_block(blocks_h2f[1]))
+ # 3. Send another block that builds on genesis.
+ block_h1f = create_block(int("0x" + self.nodes[0].getblockhash(0), 0), create_coinbase(1), block_time)
+ block_time += 1
+ block_h1f.solve()
+ test_node.send_message(msg_block(block_h1f))
- [ x.sync_with_ping() for x in [test_node, white_node] ]
+ test_node.sync_with_ping()
+ tip_entry_found = False
for x in self.nodes[0].getchaintips():
- if x['hash'] == blocks_h2f[0].hash:
+ if x['hash'] == block_h1f.hash:
assert_equal(x['status'], "headers-only")
+ tip_entry_found = True
+ assert(tip_entry_found)
+ assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_h1f.hash)
- for x in self.nodes[1].getchaintips():
- if x['hash'] == blocks_h2f[1].hash:
- assert_equal(x['status'], "valid-headers")
+ # 4. Send another two block that build on the fork.
+ block_h2f = create_block(block_h1f.sha256, create_coinbase(2), block_time)
+ block_time += 1
+ block_h2f.solve()
+ test_node.send_message(msg_block(block_h2f))
- self.log.info("Second height 2 block accepted only from whitelisted peer")
+ test_node.sync_with_ping()
+ # Since the earlier block was not processed by node, the new block
+ # can't be fully validated.
+ tip_entry_found = False
+ for x in self.nodes[0].getchaintips():
+ if x['hash'] == block_h2f.hash:
+ assert_equal(x['status'], "headers-only")
+ tip_entry_found = True
+ assert(tip_entry_found)
- # 4. Now send another block that builds on the forking chain.
- blocks_h3 = []
- for i in range(2):
- blocks_h3.append(create_block(blocks_h2f[i].sha256, create_coinbase(3), blocks_h2f[i].nTime+1))
- blocks_h3[i].solve()
- test_node.send_message(msg_block(blocks_h3[0]))
- white_node.send_message(msg_block(blocks_h3[1]))
+ # But this block should be accepted by node since it has equal work.
+ self.nodes[0].getblock(block_h2f.hash)
+ self.log.info("Second height 2 block accepted, but not reorg'ed to")
+
+ # 4b. Now send another block that builds on the forking chain.
+ block_h3 = create_block(block_h2f.sha256, create_coinbase(3), block_h2f.nTime+1)
+ block_h3.solve()
+ test_node.send_message(msg_block(block_h3))
- [ x.sync_with_ping() for x in [test_node, white_node] ]
- # Since the earlier block was not processed by node0, the new block
+ test_node.sync_with_ping()
+ # Since the earlier block was not processed by node, the new block
# can't be fully validated.
+ tip_entry_found = False
for x in self.nodes[0].getchaintips():
- if x['hash'] == blocks_h3[0].hash:
+ if x['hash'] == block_h3.hash:
assert_equal(x['status'], "headers-only")
+ tip_entry_found = True
+ assert(tip_entry_found)
+ self.nodes[0].getblock(block_h3.hash)
+
+ # But this block should be accepted by node since it has more work.
+ self.nodes[0].getblock(block_h3.hash)
+ self.log.info("Unrequested more-work block accepted")
+
+ # 4c. Now mine 288 more blocks and deliver; all should be processed but
+ # the last (height-too-high) on node (as long as its not missing any headers)
+ tip = block_h3
+ all_blocks = []
+ for i in range(288):
+ next_block = create_block(tip.sha256, create_coinbase(i + 4), tip.nTime+1)
+ next_block.solve()
+ all_blocks.append(next_block)
+ tip = next_block
+
+ # Now send the block at height 5 and check that it wasn't accepted (missing header)
+ test_node.send_message(msg_block(all_blocks[1]))
+ test_node.sync_with_ping()
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblock, all_blocks[1].hash)
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblockheader, all_blocks[1].hash)
- # But this block should be accepted by node0 since it has more work.
- self.nodes[0].getblock(blocks_h3[0].hash)
- self.log.info("Unrequested more-work block accepted from non-whitelisted peer")
+ # The block at height 5 should be accepted if we provide the missing header, though
+ headers_message = msg_headers()
+ headers_message.headers.append(CBlockHeader(all_blocks[0]))
+ test_node.send_message(headers_message)
+ test_node.send_message(msg_block(all_blocks[1]))
+ test_node.sync_with_ping()
+ self.nodes[0].getblock(all_blocks[1].hash)
- # Node1 should have accepted and reorged.
- assert_equal(self.nodes[1].getblockcount(), 3)
- self.log.info("Successfully reorged to length 3 chain from whitelisted peer")
+ # Now send the blocks in all_blocks
+ for i in range(288):
+ test_node.send_message(msg_block(all_blocks[i]))
+ test_node.sync_with_ping()
- # 4b. Now mine 288 more blocks and deliver; all should be processed but
- # the last (height-too-high) on node0. Node1 should process the tip if
- # we give it the headers chain leading to the tip.
- tips = blocks_h3
- headers_message = msg_headers()
- all_blocks = [] # node0's blocks
- for j in range(2):
- for i in range(288):
- next_block = create_block(tips[j].sha256, create_coinbase(i + 4), tips[j].nTime+1)
- next_block.solve()
- if j==0:
- test_node.send_message(msg_block(next_block))
- all_blocks.append(next_block)
- else:
- headers_message.headers.append(CBlockHeader(next_block))
- tips[j] = next_block
-
- time.sleep(2)
# Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead
for x in all_blocks[:-1]:
self.nodes[0].getblock(x.hash)
- assert_raises_jsonrpc(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash)
-
- headers_message.headers.pop() # Ensure the last block is unrequested
- white_node.send_message(headers_message) # Send headers leading to tip
- white_node.send_message(msg_block(tips[1])) # Now deliver the tip
- white_node.sync_with_ping()
- self.nodes[1].getblock(tips[1].hash)
- self.log.info("Unrequested block far ahead of tip accepted from whitelisted peer")
+ assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash)
# 5. Test handling of unrequested block on the node that didn't process
# Should still not be processed (even though it has a child that has more
# work).
- test_node.send_message(msg_block(blocks_h2f[0]))
- # Here, if the sleep is too short, the test could falsely succeed (if the
- # node hasn't processed the block by the time the sleep returns, and then
- # the node processes it and incorrectly advances the tip).
- # But this would be caught later on, when we verify that an inv triggers
- # a getdata request for this block.
+ # The node should have requested the blocks at some point, so
+ # disconnect/reconnect first
+
+ self.nodes[0].disconnect_p2ps()
+ self.nodes[1].disconnect_p2ps()
+ network_thread_join()
+
+ test_node = self.nodes[0].add_p2p_connection(P2PInterface())
+ network_thread_start()
+ test_node.wait_for_verack()
+
+ test_node.send_message(msg_block(block_h1f))
+
test_node.sync_with_ping()
assert_equal(self.nodes[0].getblockcount(), 2)
self.log.info("Unrequested block that would complete more-work chain was ignored")
@@ -200,24 +226,98 @@ class AcceptBlockTest(BitcoinTestFramework):
with mininode_lock:
# Clear state so we can check the getdata request
test_node.last_message.pop("getdata", None)
- test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)]))
+ test_node.send_message(msg_inv([CInv(2, block_h3.sha256)]))
test_node.sync_with_ping()
with mininode_lock:
getdata = test_node.last_message["getdata"]
# Check that the getdata includes the right block
- assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256)
+ assert_equal(getdata.inv[0].hash, block_h1f.sha256)
self.log.info("Inv at tip triggered getdata for unprocessed block")
# 7. Send the missing block for the third time (now it is requested)
- test_node.send_message(msg_block(blocks_h2f[0]))
+ test_node.send_message(msg_block(block_h1f))
test_node.sync_with_ping()
assert_equal(self.nodes[0].getblockcount(), 290)
+ self.nodes[0].getblock(all_blocks[286].hash)
+ assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash)
+ assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[287].hash)
self.log.info("Successfully reorged to longer chain from non-whitelisted peer")
- [ c.disconnect_node() for c in connections ]
+ # 8. Create a chain which is invalid at a height longer than the
+ # current chain, but which has more blocks on top of that
+ block_289f = create_block(all_blocks[284].sha256, create_coinbase(289), all_blocks[284].nTime+1)
+ block_289f.solve()
+ block_290f = create_block(block_289f.sha256, create_coinbase(290), block_289f.nTime+1)
+ block_290f.solve()
+ block_291 = create_block(block_290f.sha256, create_coinbase(291), block_290f.nTime+1)
+ # block_291 spends a coinbase below maturity!
+ block_291.vtx.append(create_transaction(block_290f.vtx[0], 0, b"42", 1))
+ block_291.hashMerkleRoot = block_291.calc_merkle_root()
+ block_291.solve()
+ block_292 = create_block(block_291.sha256, create_coinbase(292), block_291.nTime+1)
+ block_292.solve()
+
+ # Now send all the headers on the chain and enough blocks to trigger reorg
+ headers_message = msg_headers()
+ headers_message.headers.append(CBlockHeader(block_289f))
+ headers_message.headers.append(CBlockHeader(block_290f))
+ headers_message.headers.append(CBlockHeader(block_291))
+ headers_message.headers.append(CBlockHeader(block_292))
+ test_node.send_message(headers_message)
+
+ test_node.sync_with_ping()
+ tip_entry_found = False
+ for x in self.nodes[0].getchaintips():
+ if x['hash'] == block_292.hash:
+ assert_equal(x['status'], "headers-only")
+ tip_entry_found = True
+ assert(tip_entry_found)
+ assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_292.hash)
+
+ test_node.send_message(msg_block(block_289f))
+ test_node.send_message(msg_block(block_290f))
+
+ test_node.sync_with_ping()
+ self.nodes[0].getblock(block_289f.hash)
+ self.nodes[0].getblock(block_290f.hash)
+
+ test_node.send_message(msg_block(block_291))
+
+ # At this point we've sent an obviously-bogus block, wait for full processing
+ # without assuming whether we will be disconnected or not
+ try:
+ # Only wait a short while so the test doesn't take forever if we do get
+ # disconnected
+ test_node.sync_with_ping(timeout=1)
+ except AssertionError:
+ test_node.wait_for_disconnect()
+
+ self.nodes[0].disconnect_p2ps()
+ test_node = self.nodes[0].add_p2p_connection(P2PInterface())
+
+ network_thread_start()
+ test_node.wait_for_verack()
+
+ # We should have failed reorg and switched back to 290 (but have block 291)
+ assert_equal(self.nodes[0].getblockcount(), 290)
+ assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash)
+ assert_equal(self.nodes[0].getblock(block_291.hash)["confirmations"], -1)
+
+ # Now send a new header on the invalid chain, indicating we're forked off, and expect to get disconnected
+ block_293 = create_block(block_292.sha256, create_coinbase(293), block_292.nTime+1)
+ block_293.solve()
+ headers_message = msg_headers()
+ headers_message.headers.append(CBlockHeader(block_293))
+ test_node.send_message(headers_message)
+ test_node.wait_for_disconnect()
+
+ # 9. Connect node1 to node0 and ensure it is able to sync
+ connect_nodes(self.nodes[0], 1)
+ sync_blocks([self.nodes[0], self.nodes[1]])
+ self.log.info("Successfully synced nodes 1 and 0")
if __name__ == '__main__':
AcceptBlockTest().main()
diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p-compactblocks.py
index 94513d3f43..1e763df2a4 100755
--- a/test/functional/p2p-compactblocks.py
+++ b/test/functional/p2p-compactblocks.py
@@ -15,7 +15,7 @@ from test_framework.blocktools import create_block, create_coinbase, add_witness
from test_framework.script import CScript, OP_TRUE
# TestNode: A peer we use to send messages to bitcoind, and store responses.
-class TestNode(NodeConnCB):
+class TestNode(P2PInterface):
def __init__(self):
super().__init__()
self.last_sendcmpct = []
@@ -25,21 +25,21 @@ class TestNode(NodeConnCB):
# so we can eg wait until a particular block is announced.
self.announced_blockhashes = set()
- def on_sendcmpct(self, conn, message):
+ def on_sendcmpct(self, message):
self.last_sendcmpct.append(message)
- def on_cmpctblock(self, conn, message):
+ def on_cmpctblock(self, message):
self.block_announced = True
self.last_message["cmpctblock"].header_and_shortids.header.calc_sha256()
self.announced_blockhashes.add(self.last_message["cmpctblock"].header_and_shortids.header.sha256)
- def on_headers(self, conn, message):
+ def on_headers(self, message):
self.block_announced = True
for x in self.last_message["headers"].headers:
x.calc_sha256()
self.announced_blockhashes.add(x.sha256)
- def on_inv(self, conn, message):
+ def on_inv(self, message):
for x in self.last_message["inv"].inv:
if x.type == 2:
self.block_announced = True
@@ -60,7 +60,7 @@ class TestNode(NodeConnCB):
msg = msg_getheaders()
msg.locator.vHave = locator
msg.hashstop = hashstop
- self.connection.send_message(msg)
+ self.send_message(msg)
def send_header_for_blocks(self, new_blocks):
headers_message = msg_headers()
@@ -86,14 +86,16 @@ class TestNode(NodeConnCB):
This is used when we want to send a message into the node that we expect
will get us disconnected, eg an invalid block."""
self.send_message(message)
- wait_until(lambda: not self.connected, timeout=timeout, lock=mininode_lock)
+ wait_until(lambda: self.state != "connected", timeout=timeout, lock=mininode_lock)
class CompactBlocksTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
# Node0 = pre-segwit, node1 = segwit-aware
self.num_nodes = 2
- self.extra_args = [["-vbparams=segwit:0:0"], ["-txindex"]]
+ # This test was written assuming SegWit is activated using BIP9 at height 432 (3x confirmation window).
+ # TODO: Rewrite this test to support SegWit being always active.
+ self.extra_args = [["-vbparams=segwit:0:0"], ["-vbparams=segwit:0:999999999999", "-txindex"]]
self.utxos = []
def build_block_on_tip(self, node, segwit=False):
@@ -786,23 +788,12 @@ class CompactBlocksTest(BitcoinTestFramework):
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
-
- # Test logic begins here
+ self.test_node = self.nodes[0].add_p2p_connection(TestNode())
+ self.segwit_node = self.nodes[1].add_p2p_connection(TestNode(), services=NODE_NETWORK|NODE_WITNESS)
+ self.old_node = self.nodes[1].add_p2p_connection(TestNode(), services=NODE_NETWORK)
+
+ network_thread_start()
+
self.test_node.wait_for_verack()
# We will need UTXOs to construct transactions in later tests.
diff --git a/test/functional/p2p-feefilter.py b/test/functional/p2p-feefilter.py
index 8c92365ced..ff4bed0efd 100755
--- a/test/functional/p2p-feefilter.py
+++ b/test/functional/p2p-feefilter.py
@@ -22,12 +22,12 @@ def allInvsMatch(invsExpected, testnode):
time.sleep(1)
return False
-class TestNode(NodeConnCB):
+class TestNode(P2PInterface):
def __init__(self):
super().__init__()
self.txinvs = []
- def on_inv(self, conn, message):
+ def on_inv(self, message):
for i in message.inv:
if (i.type == 1):
self.txinvs.append(hashToHex(i.hash))
@@ -48,25 +48,23 @@ class FeeFilterTest(BitcoinTestFramework):
sync_blocks(self.nodes)
# Setup the p2p connections and start up the network thread.
- test_node = TestNode()
- connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)
- test_node.add_connection(connection)
- NetworkThread().start()
- test_node.wait_for_verack()
+ self.nodes[0].add_p2p_connection(TestNode())
+ network_thread_start()
+ self.nodes[0].p2p.wait_for_verack()
# Test that invs are received for all txs at feerate of 20 sat/byte
node1.settxfee(Decimal("0.00020000"))
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
- assert(allInvsMatch(txids, test_node))
- test_node.clear_invs()
+ assert(allInvsMatch(txids, self.nodes[0].p2p))
+ self.nodes[0].p2p.clear_invs()
# Set a filter of 15 sat/byte
- test_node.send_and_ping(msg_feefilter(15000))
+ self.nodes[0].p2p.send_and_ping(msg_feefilter(15000))
# Test that txs are still being received (paying 20 sat/byte)
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
- assert(allInvsMatch(txids, test_node))
- test_node.clear_invs()
+ assert(allInvsMatch(txids, self.nodes[0].p2p))
+ self.nodes[0].p2p.clear_invs()
# Change tx fee rate to 10 sat/byte and test they are no longer received
node1.settxfee(Decimal("0.00010000"))
@@ -82,14 +80,14 @@ class FeeFilterTest(BitcoinTestFramework):
# as well.
node0.settxfee(Decimal("0.00020000"))
txids = [node0.sendtoaddress(node0.getnewaddress(), 1)]
- assert(allInvsMatch(txids, test_node))
- test_node.clear_invs()
+ assert(allInvsMatch(txids, self.nodes[0].p2p))
+ self.nodes[0].p2p.clear_invs()
# Remove fee filter and check that txs are received again
- test_node.send_and_ping(msg_feefilter(0))
+ self.nodes[0].p2p.send_and_ping(msg_feefilter(0))
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
- assert(allInvsMatch(txids, test_node))
- test_node.clear_invs()
+ assert(allInvsMatch(txids, self.nodes[0].p2p))
+ self.nodes[0].p2p.clear_invs()
if __name__ == '__main__':
FeeFilterTest().main()
diff --git a/test/functional/p2p-fingerprint.py b/test/functional/p2p-fingerprint.py
new file mode 100755
index 0000000000..93ef73e25e
--- /dev/null
+++ b/test/functional/p2p-fingerprint.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 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 various fingerprinting protections.
+
+If an stale block more than a month old or its header are requested by a peer,
+the node should pretend that it does not have it to avoid fingerprinting.
+"""
+
+import time
+
+from test_framework.blocktools import (create_block, create_coinbase)
+from test_framework.mininode import (
+ CInv,
+ P2PInterface,
+ msg_headers,
+ msg_block,
+ msg_getdata,
+ msg_getheaders,
+ network_thread_start,
+ wait_until,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+)
+
+class P2PFingerprintTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+
+ # Build a chain of blocks on top of given one
+ def build_chain(self, nblocks, prev_hash, prev_height, prev_median_time):
+ blocks = []
+ for _ in range(nblocks):
+ coinbase = create_coinbase(prev_height + 1)
+ block_time = prev_median_time + 1
+ block = create_block(int(prev_hash, 16), coinbase, block_time)
+ block.solve()
+
+ blocks.append(block)
+ prev_hash = block.hash
+ prev_height += 1
+ prev_median_time = block_time
+ return blocks
+
+ # Send a getdata request for a given block hash
+ def send_block_request(self, block_hash, node):
+ msg = msg_getdata()
+ msg.inv.append(CInv(2, block_hash)) # 2 == "Block"
+ node.send_message(msg)
+
+ # Send a getheaders request for a given single block hash
+ def send_header_request(self, block_hash, node):
+ msg = msg_getheaders()
+ msg.hashstop = block_hash
+ node.send_message(msg)
+
+ # Check whether last block received from node has a given hash
+ def last_block_equals(self, expected_hash, node):
+ block_msg = node.last_message.get("block")
+ return block_msg and block_msg.block.rehash() == expected_hash
+
+ # Check whether last block header received from node has a given hash
+ def last_header_equals(self, expected_hash, node):
+ headers_msg = node.last_message.get("headers")
+ return (headers_msg and
+ headers_msg.headers and
+ headers_msg.headers[0].rehash() == expected_hash)
+
+ # Checks that stale blocks timestamped more than a month ago are not served
+ # by the node while recent stale blocks and old active chain blocks are.
+ # This does not currently test that stale blocks timestamped within the
+ # last month but that have over a month's worth of work are also withheld.
+ def run_test(self):
+ node0 = self.nodes[0].add_p2p_connection(P2PInterface())
+
+ network_thread_start()
+ node0.wait_for_verack()
+
+ # Set node time to 60 days ago
+ self.nodes[0].setmocktime(int(time.time()) - 60 * 24 * 60 * 60)
+
+ # Generating a chain of 10 blocks
+ block_hashes = self.nodes[0].generate(nblocks=10)
+
+ # Create longer chain starting 2 blocks before current tip
+ height = len(block_hashes) - 2
+ block_hash = block_hashes[height - 1]
+ block_time = self.nodes[0].getblockheader(block_hash)["mediantime"] + 1
+ new_blocks = self.build_chain(5, block_hash, height, block_time)
+
+ # Force reorg to a longer chain
+ node0.send_message(msg_headers(new_blocks))
+ node0.wait_for_getdata()
+ for block in new_blocks:
+ node0.send_and_ping(msg_block(block))
+
+ # Check that reorg succeeded
+ assert_equal(self.nodes[0].getblockcount(), 13)
+
+ stale_hash = int(block_hashes[-1], 16)
+
+ # Check that getdata request for stale block succeeds
+ self.send_block_request(stale_hash, node0)
+ test_function = lambda: self.last_block_equals(stale_hash, node0)
+ wait_until(test_function, timeout=3)
+
+ # Check that getheader request for stale block header succeeds
+ self.send_header_request(stale_hash, node0)
+ test_function = lambda: self.last_header_equals(stale_hash, node0)
+ wait_until(test_function, timeout=3)
+
+ # Longest chain is extended so stale is much older than chain tip
+ self.nodes[0].setmocktime(0)
+ tip = self.nodes[0].generate(nblocks=1)[0]
+ assert_equal(self.nodes[0].getblockcount(), 14)
+
+ # Send getdata & getheaders to refresh last received getheader message
+ block_hash = int(tip, 16)
+ self.send_block_request(block_hash, node0)
+ self.send_header_request(block_hash, node0)
+ node0.sync_with_ping()
+
+ # Request for very old stale block should now fail
+ self.send_block_request(stale_hash, node0)
+ time.sleep(3)
+ assert not self.last_block_equals(stale_hash, node0)
+
+ # Request for very old stale block header should now fail
+ self.send_header_request(stale_hash, node0)
+ time.sleep(3)
+ assert not self.last_header_equals(stale_hash, node0)
+
+ # Verify we can fetch very old blocks and headers on the active chain
+ block_hash = int(block_hashes[2], 16)
+ self.send_block_request(block_hash, node0)
+ self.send_header_request(block_hash, node0)
+ node0.sync_with_ping()
+
+ self.send_block_request(block_hash, node0)
+ test_function = lambda: self.last_block_equals(block_hash, node0)
+ wait_until(test_function, timeout=3)
+
+ self.send_header_request(block_hash, node0)
+ test_function = lambda: self.last_header_equals(block_hash, node0)
+ wait_until(test_function, timeout=3)
+
+if __name__ == '__main__':
+ P2PFingerprintTest().main()
diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py
index 1d969fc7c1..010dbdccad 100755
--- a/test/functional/p2p-fullblocktest.py
+++ b/test/functional/p2p-fullblocktest.py
@@ -18,9 +18,10 @@ from test_framework.blocktools import *
import time
from test_framework.key import CECKey
from test_framework.script import *
+from test_framework.mininode import network_thread_start
import struct
-class PreviousSpendableOutput(object):
+class PreviousSpendableOutput():
def __init__(self, tx = CTransaction(), n = -1):
self.tx = tx
self.n = n # the output we're spending
@@ -68,7 +69,7 @@ class FullBlockTest(ComparisonTestFramework):
def run_test(self):
self.test = TestManager(self, self.options.tmpdir)
self.test.add_all_connections(self.nodes)
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
self.test.run()
def add_transactions_to_block(self, block, tx_list):
diff --git a/test/functional/p2p-leaktests.py b/test/functional/p2p-leaktests.py
index f27086c97e..ce4e6e9144 100755
--- a/test/functional/p2p-leaktests.py
+++ b/test/functional/p2p-leaktests.py
@@ -20,7 +20,7 @@ from test_framework.util import *
banscore = 10
-class CLazyNode(NodeConnCB):
+class CLazyNode(P2PInterface):
def __init__(self):
super().__init__()
self.unexpected_msg = False
@@ -30,44 +30,42 @@ class CLazyNode(NodeConnCB):
self.unexpected_msg = True
self.log.info("should not have received message: %s" % message.command)
- def on_open(self, conn):
- self.connected = True
+ def on_open(self):
self.ever_connected = True
- def on_version(self, conn, message): self.bad_message(message)
- def on_verack(self, conn, message): self.bad_message(message)
- def on_reject(self, conn, message): self.bad_message(message)
- def on_inv(self, conn, message): self.bad_message(message)
- def on_addr(self, conn, message): self.bad_message(message)
- def on_alert(self, conn, message): self.bad_message(message)
- def on_getdata(self, conn, message): self.bad_message(message)
- def on_getblocks(self, conn, message): self.bad_message(message)
- def on_tx(self, conn, message): self.bad_message(message)
- def on_block(self, conn, message): self.bad_message(message)
- def on_getaddr(self, conn, message): self.bad_message(message)
- def on_headers(self, conn, message): self.bad_message(message)
- def on_getheaders(self, conn, message): self.bad_message(message)
- def on_ping(self, conn, message): self.bad_message(message)
- def on_mempool(self, conn): self.bad_message(message)
- def on_pong(self, conn, message): self.bad_message(message)
- def on_feefilter(self, conn, message): self.bad_message(message)
- def on_sendheaders(self, conn, message): self.bad_message(message)
- def on_sendcmpct(self, conn, message): self.bad_message(message)
- def on_cmpctblock(self, conn, message): self.bad_message(message)
- def on_getblocktxn(self, conn, message): self.bad_message(message)
- def on_blocktxn(self, conn, message): self.bad_message(message)
+ def on_version(self, message): self.bad_message(message)
+ def on_verack(self, message): self.bad_message(message)
+ def on_reject(self, message): self.bad_message(message)
+ def on_inv(self, message): self.bad_message(message)
+ def on_addr(self, message): self.bad_message(message)
+ def on_getdata(self, message): self.bad_message(message)
+ def on_getblocks(self, message): self.bad_message(message)
+ def on_tx(self, message): self.bad_message(message)
+ def on_block(self, message): self.bad_message(message)
+ def on_getaddr(self, message): self.bad_message(message)
+ def on_headers(self, message): self.bad_message(message)
+ def on_getheaders(self, message): self.bad_message(message)
+ def on_ping(self, message): self.bad_message(message)
+ def on_mempool(self, message): self.bad_message(message)
+ def on_pong(self, message): self.bad_message(message)
+ def on_feefilter(self, message): self.bad_message(message)
+ def on_sendheaders(self, message): self.bad_message(message)
+ def on_sendcmpct(self, message): self.bad_message(message)
+ def on_cmpctblock(self, message): self.bad_message(message)
+ def on_getblocktxn(self, message): self.bad_message(message)
+ def on_blocktxn(self, message): self.bad_message(message)
# Node that never sends a version. We'll use this to send a bunch of messages
# anyway, and eventually get disconnected.
class CNodeNoVersionBan(CLazyNode):
# send a bunch of veracks without sending a message. This should get us disconnected.
# NOTE: implementation-specific check here. Remove if bitcoind ban behavior changes
- def on_open(self, conn):
- super().on_open(conn)
+ def on_open(self):
+ super().on_open()
for i in range(banscore):
self.send_message(msg_verack())
- def on_reject(self, conn, message): pass
+ def on_reject(self, message): pass
# Node that never sends a version. This one just sits idle and hopes to receive
# any message (it shouldn't!)
@@ -81,15 +79,15 @@ class CNodeNoVerackIdle(CLazyNode):
self.version_received = False
super().__init__()
- def on_reject(self, conn, message): pass
- def on_verack(self, conn, message): pass
+ def on_reject(self, message): pass
+ def on_verack(self, message): pass
# When version is received, don't reply with a verack. Instead, see if the
# node will give us a message that it shouldn't. This is not an exhaustive
# list!
- def on_version(self, conn, message):
+ def on_version(self, message):
self.version_received = True
- conn.send_message(msg_ping())
- conn.send_message(msg_getaddr())
+ self.send_message(msg_ping())
+ self.send_message(msg_getaddr())
class P2PLeakTest(BitcoinTestFramework):
def set_test_params(self):
@@ -97,26 +95,15 @@ class P2PLeakTest(BitcoinTestFramework):
self.extra_args = [['-banscore='+str(banscore)]]
def run_test(self):
- no_version_bannode = CNodeNoVersionBan()
- no_version_idlenode = CNodeNoVersionIdle()
- no_verack_idlenode = CNodeNoVerackIdle()
- unsupported_service_bit5_node = CLazyNode()
- unsupported_service_bit7_node = CLazyNode()
-
self.nodes[0].setmocktime(1501545600) # August 1st 2017
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_bannode, send_version=False))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_idlenode, send_version=False))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_verack_idlenode))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], unsupported_service_bit5_node, services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], unsupported_service_bit7_node, services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7))
- no_version_bannode.add_connection(connections[0])
- no_version_idlenode.add_connection(connections[1])
- no_verack_idlenode.add_connection(connections[2])
- unsupported_service_bit5_node.add_connection(connections[3])
- unsupported_service_bit7_node.add_connection(connections[4])
-
- NetworkThread().start() # Start up network handling in another thread
+
+ no_version_bannode = self.nodes[0].add_p2p_connection(CNodeNoVersionBan(), send_version=False)
+ no_version_idlenode = self.nodes[0].add_p2p_connection(CNodeNoVersionIdle(), send_version=False)
+ no_verack_idlenode = self.nodes[0].add_p2p_connection(CNodeNoVerackIdle())
+ unsupported_service_bit5_node = self.nodes[0].add_p2p_connection(CLazyNode(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5)
+ unsupported_service_bit7_node = self.nodes[0].add_p2p_connection(CLazyNode(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7)
+
+ network_thread_start()
wait_until(lambda: no_version_bannode.ever_connected, timeout=10, lock=mininode_lock)
wait_until(lambda: no_version_idlenode.ever_connected, timeout=10, lock=mininode_lock)
@@ -131,13 +118,17 @@ class P2PLeakTest(BitcoinTestFramework):
time.sleep(5)
#This node should have been banned
- assert not no_version_bannode.connected
+ assert no_version_bannode.state != "connected"
# These nodes should have been disconnected
- assert not unsupported_service_bit5_node.connected
- assert not unsupported_service_bit7_node.connected
+ assert unsupported_service_bit5_node.state != "connected"
+ assert unsupported_service_bit7_node.state != "connected"
+
+ self.nodes[0].disconnect_p2ps()
- [conn.disconnect_node() for conn in connections]
+ # Wait until all connections are closed and the network thread has terminated
+ wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 0)
+ network_thread_join()
# Make sure no unexpected messages came in
assert(no_version_bannode.unexpected_msg == False)
@@ -149,16 +140,14 @@ class P2PLeakTest(BitcoinTestFramework):
self.log.info("Service bits 5 and 7 are allowed after August 1st 2018")
self.nodes[0].setmocktime(1533168000) # August 2nd 2018
- allowed_service_bit5_node = NodeConnCB()
- allowed_service_bit7_node = NodeConnCB()
+ allowed_service_bit5_node = self.nodes[0].add_p2p_connection(P2PInterface(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5)
+ allowed_service_bit7_node = self.nodes[0].add_p2p_connection(P2PInterface(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7)
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], allowed_service_bit5_node, services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], allowed_service_bit7_node, services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7))
- allowed_service_bit5_node.add_connection(connections[5])
- allowed_service_bit7_node.add_connection(connections[6])
+ # Network thread stopped when all previous P2PInterfaces disconnected. Restart it
+ network_thread_start()
- wait_until(lambda: allowed_service_bit5_node.message_count["verack"], timeout=10, lock=mininode_lock)
- wait_until(lambda: allowed_service_bit7_node.message_count["verack"], timeout=10, lock=mininode_lock)
+ wait_until(lambda: allowed_service_bit5_node.message_count["verack"], lock=mininode_lock)
+ wait_until(lambda: allowed_service_bit7_node.message_count["verack"], lock=mininode_lock)
if __name__ == '__main__':
P2PLeakTest().main()
diff --git a/test/functional/p2p-mempool.py b/test/functional/p2p-mempool.py
index 40fcde2605..168f9f685a 100755
--- a/test/functional/p2p-mempool.py
+++ b/test/functional/p2p-mempool.py
@@ -19,16 +19,14 @@ class P2PMempoolTests(BitcoinTestFramework):
self.extra_args = [["-peerbloomfilters=0"]]
def run_test(self):
- #connect a mininode
- aTestNode = NodeConnCB()
- node = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], aTestNode)
- aTestNode.add_connection(node)
- NetworkThread().start()
- aTestNode.wait_for_verack()
+ # Add a p2p connection
+ self.nodes[0].add_p2p_connection(P2PInterface())
+ network_thread_start()
+ self.nodes[0].p2p.wait_for_verack()
#request mempool
- aTestNode.send_message(msg_mempool())
- aTestNode.wait_for_disconnect()
+ self.nodes[0].p2p.send_message(msg_mempool())
+ self.nodes[0].p2p.wait_for_disconnect()
#mininode must be disconnected at this point
assert_equal(len(self.nodes[0].getpeerinfo()), 0)
diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py
index 943bc2c6d2..a06601c38e 100755
--- a/test/functional/p2p-segwit.py
+++ b/test/functional/p2p-segwit.py
@@ -31,12 +31,40 @@ def get_virtual_size(witness_block):
vsize = int((3*base_size + total_size + 3)/4)
return vsize
-class TestNode(NodeConnCB):
- def set_test_params(self):
- self.num_nodes = 3
+def test_transaction_acceptance(rpc, p2p, tx, with_witness, accepted, reason=None):
+ """Send a transaction to the node and check that it's accepted to the mempool
+
+ - Submit the transaction over the p2p interface
+ - use the getrawmempool rpc to check for acceptance."""
+ tx_message = msg_tx(tx)
+ if with_witness:
+ tx_message = msg_witness_tx(tx)
+ p2p.send_message(tx_message)
+ p2p.sync_with_ping()
+ assert_equal(tx.hash in rpc.getrawmempool(), accepted)
+ if (reason != None and not accepted):
+ # Check the rejection reason as well.
+ with mininode_lock:
+ assert_equal(p2p.last_message["reject"].reason, reason)
+
+def test_witness_block(rpc, p2p, block, accepted, with_witness=True):
+ """Send a block to the node and check that it's accepted
+
+ - Submit the block over the p2p interface
+ - use the getbestblockhash rpc to check for acceptance."""
+ if with_witness:
+ p2p.send_message(msg_witness_block(block))
+ else:
+ p2p.send_message(msg_block(block))
+ p2p.sync_with_ping()
+ assert_equal(rpc.getbestblockhash() == block.hash, accepted)
+
+class TestNode(P2PInterface):
+ def __init__(self):
+ super().__init__()
self.getdataset = set()
- def on_getdata(self, conn, message):
+ def on_getdata(self, message):
for inv in message.inv:
self.getdataset.add(inv.hash)
@@ -67,29 +95,8 @@ class TestNode(NodeConnCB):
self.wait_for_block(blockhash, timeout)
return self.last_message["block"].block
- def test_transaction_acceptance(self, tx, with_witness, accepted, reason=None):
- tx_message = msg_tx(tx)
- if with_witness:
- tx_message = msg_witness_tx(tx)
- self.send_message(tx_message)
- self.sync_with_ping()
- assert_equal(tx.hash in self.connection.rpc.getrawmempool(), accepted)
- if (reason != None and not accepted):
- # Check the rejection reason as well.
- with mininode_lock:
- assert_equal(self.last_message["reject"].reason, reason)
-
- # Test whether a witness block had the correct effect on the tip
- def test_witness_block(self, block, accepted, with_witness=True):
- if with_witness:
- self.send_message(msg_witness_block(block))
- else:
- self.send_message(msg_block(block))
- self.sync_with_ping()
- assert_equal(self.connection.rpc.getbestblockhash() == block.hash, accepted)
-
# Used to keep track of anyone-can-spend outputs that we can use in the tests
-class UTXO(object):
+class UTXO():
def __init__(self, sha256, n, nValue):
self.sha256 = sha256
self.n = n
@@ -111,7 +118,8 @@ class SegWitTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
- self.extra_args = [["-whitelist=127.0.0.1"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]]
+ # This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
+ self.extra_args = [["-whitelist=127.0.0.1", "-vbparams=segwit:0:999999999999"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-vbparams=segwit:0:999999999999"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]]
def setup_network(self):
self.setup_nodes()
@@ -140,7 +148,7 @@ class SegWitTest(BitcoinTestFramework):
''' Individual tests '''
def test_witness_services(self):
self.log.info("Verifying NODE_WITNESS service bit")
- assert((self.test_node.connection.nServices & NODE_WITNESS) != 0)
+ assert((self.test_node.nServices & NODE_WITNESS) != 0)
# See if sending a regular transaction works, and create a utxo
@@ -199,7 +207,7 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx])
# Sending witness data before activation is not allowed (anti-spam
# rule).
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# TODO: fix synchronization so we can test reject reason
# Right now, bitcoind delays sending reject messages for blocks
# until the future, making synchronization here difficult.
@@ -226,7 +234,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, scriptPubKey))
tx2.rehash()
- self.test_node.test_transaction_acceptance(tx2, False, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, False, True)
self.nodes[0].generate(1)
sync_blocks(self.nodes)
@@ -243,18 +251,18 @@ class SegWitTest(BitcoinTestFramework):
tx3.rehash()
# Note that this should be rejected for the premature witness reason,
# rather than a policy check, since segwit hasn't activated yet.
- self.std_node.test_transaction_acceptance(tx3, True, False, b'no-witness-yet')
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'no-witness-yet')
# If we send without witness, it should be accepted.
- self.std_node.test_transaction_acceptance(tx3, False, True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, False, True)
# Now create a new anyone-can-spend utxo for the next test.
tx4 = CTransaction()
tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), CScript([p2sh_program])))
tx4.vout.append(CTxOut(tx3.vout[0].nValue-1000, CScript([OP_TRUE])))
tx4.rehash()
- self.test_node.test_transaction_acceptance(tx3, False, True)
- self.test_node.test_transaction_acceptance(tx4, False, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, False, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx4, False, True)
self.nodes[0].generate(1)
sync_blocks(self.nodes)
@@ -315,7 +323,7 @@ class SegWitTest(BitcoinTestFramework):
assert(msg_witness_block(block).serialize() != msg_block(block).serialize())
# This empty block should be valid.
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Try to tweak the nonce
block_2 = self.build_next_block()
@@ -326,7 +334,7 @@ class SegWitTest(BitcoinTestFramework):
assert(block_2.vtx[0].vout[-1] != block.vtx[0].vout[-1])
# This should also be valid.
- self.test_node.test_witness_block(block_2, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_2, accepted=True)
# Now test commitments with actual transactions
assert (len(self.utxo) > 0)
@@ -359,7 +367,7 @@ class SegWitTest(BitcoinTestFramework):
block_3.rehash()
block_3.solve()
- self.test_node.test_witness_block(block_3, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=False)
# Add a different commitment with different nonce, but in the
# right location, and with some funds burned(!).
@@ -373,7 +381,7 @@ class SegWitTest(BitcoinTestFramework):
block_3.rehash()
assert(len(block_3.vtx[0].vout) == 4) # 3 OP_returns
block_3.solve()
- self.test_node.test_witness_block(block_3, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=True)
# Finally test that a block with no witness transactions can
# omit the commitment.
@@ -385,7 +393,7 @@ class SegWitTest(BitcoinTestFramework):
block_4.vtx.append(tx3)
block_4.hashMerkleRoot = block_4.calc_merkle_root()
block_4.solve()
- self.test_node.test_witness_block(block_4, with_witness=False, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_4, with_witness=False, accepted=True)
# Update available utxo's for use in later test.
self.utxo.pop(0)
@@ -426,11 +434,11 @@ class SegWitTest(BitcoinTestFramework):
# Change the nonce -- should not cause the block to be permanently
# failed
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(1) ]
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Changing the witness nonce doesn't change the block hash
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(0) ]
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
def test_witness_block_size(self):
@@ -495,7 +503,7 @@ class SegWitTest(BitcoinTestFramework):
# limit
assert(len(block.serialize(True)) > 2*1024*1024)
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now resize the second transaction to make the block fit.
cur_length = len(block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0])
@@ -505,7 +513,7 @@ class SegWitTest(BitcoinTestFramework):
block.solve()
assert(get_virtual_size(block) == MAX_BLOCK_BASE_SIZE)
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Update available utxo's
self.utxo.pop(0)
@@ -572,7 +580,7 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx])
# Extra witness data should not be allowed.
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Try extra signature data. Ok if we're not spending a witness output.
block.vtx[1].wit.vtxinwit = []
@@ -581,7 +589,7 @@ class SegWitTest(BitcoinTestFramework):
add_witness_commitment(block)
block.solve()
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Now try extra witness/signature data on an input that DOES require a
# witness
@@ -597,7 +605,7 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx2])
# This has extra witness data, so it should fail.
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now get rid of the extra witness, but add extra scriptSig data
tx2.vin[0].scriptSig = CScript([OP_TRUE])
@@ -609,7 +617,7 @@ class SegWitTest(BitcoinTestFramework):
block.solve()
# This has extra signature data for a witness input, so it should fail.
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now get rid of the extra scriptsig on the witness input, and verify
# success (even with extra scriptsig data in the non-witness input)
@@ -618,7 +626,7 @@ class SegWitTest(BitcoinTestFramework):
add_witness_commitment(block)
block.solve()
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Update utxo for later tests
self.utxo.pop(0)
@@ -651,14 +659,14 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
self.update_witness_block_with_transactions(block, [tx, tx2])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now reduce the length of the stack element
tx2.wit.vtxinwit[0].scriptWitness.stack[0] = b'a'*(MAX_SCRIPT_ELEMENT_SIZE)
add_witness_commitment(block)
block.solve()
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Update the utxo for later tests
self.utxo.pop()
@@ -693,7 +701,7 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx, tx2])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Try again with one less byte in the witness program
witness_program = CScript([b'a'*520]*19 + [OP_DROP]*62 + [OP_TRUE])
@@ -708,7 +716,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx, tx2])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
self.utxo.pop()
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
@@ -734,7 +742,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Try various ways to spend tx that should all break.
# This "broken" transaction serializer will not normalize
@@ -769,7 +777,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx2])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now try using a too short vtxinwit
tx2.wit.vtxinwit.pop()
@@ -777,7 +785,7 @@ class SegWitTest(BitcoinTestFramework):
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx2])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now make one of the intermediate witnesses be incorrect
tx2.wit.vtxinwit.append(CTxInWitness())
@@ -786,13 +794,13 @@ class SegWitTest(BitcoinTestFramework):
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx2])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Fix the broken witness and the block should be accepted.
tx2.wit.vtxinwit[5].scriptWitness.stack = [b'a', witness_program]
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx2])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
self.utxo.pop()
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
@@ -832,11 +840,11 @@ class SegWitTest(BitcoinTestFramework):
# its from)
assert_equal(len(self.nodes[0].getrawmempool()), 0)
assert_equal(len(self.nodes[1].getrawmempool()), 0)
- self.old_node.test_transaction_acceptance(tx, with_witness=True, accepted=False)
- self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.old_node, tx, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=False)
# But eliminating the witness should fix it
- self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True)
# Cleanup: mine the first transaction and update utxo
self.nodes[0].generate(1)
@@ -868,11 +876,11 @@ class SegWitTest(BitcoinTestFramework):
# Verify that unnecessary witnesses are rejected.
self.test_node.announce_tx_and_wait_for_getdata(tx)
assert_equal(len(self.nodes[0].getrawmempool()), 0)
- self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=False)
# Verify that removing the witness succeeds.
self.test_node.announce_tx_and_wait_for_getdata(tx)
- self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True)
# Now try to add extra witness data to a valid witness tx.
witness_program = CScript([OP_TRUE])
@@ -897,24 +905,24 @@ class SegWitTest(BitcoinTestFramework):
# Node will not be blinded to the transaction
self.std_node.announce_tx_and_wait_for_getdata(tx3)
- self.std_node.test_transaction_acceptance(tx3, True, False, b'tx-size')
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'tx-size')
self.std_node.announce_tx_and_wait_for_getdata(tx3)
- self.std_node.test_transaction_acceptance(tx3, True, False, b'tx-size')
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'tx-size')
# Remove witness stuffing, instead add extra witness push on stack
tx3.vout[0] = CTxOut(tx2.vout[0].nValue-1000, CScript([OP_TRUE]))
tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_program ]
tx3.rehash()
- self.test_node.test_transaction_acceptance(tx2, with_witness=True, accepted=True)
- self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=False)
# Get rid of the extra witness, and verify acceptance.
tx3.wit.vtxinwit[0].scriptWitness.stack = [ witness_program ]
# Also check that old_node gets a tx announcement, even though this is
# a witness transaction.
self.old_node.wait_for_inv([CInv(1, tx2.sha256)]) # wait until tx2 was inv'ed
- self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True)
self.old_node.wait_for_inv([CInv(1, tx3.sha256)])
# Test that getrawtransaction returns correct witness information
@@ -953,20 +961,20 @@ class SegWitTest(BitcoinTestFramework):
self.test_node.announce_block_and_wait_for_getdata(block1, use_header=False)
assert(self.test_node.last_message["getdata"].inv[0].type == blocktype)
- self.test_node.test_witness_block(block1, True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block1, True)
block2 = self.build_next_block(nVersion=4)
block2.solve()
self.test_node.announce_block_and_wait_for_getdata(block2, use_header=True)
assert(self.test_node.last_message["getdata"].inv[0].type == blocktype)
- self.test_node.test_witness_block(block2, True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block2, True)
block3 = self.build_next_block(nVersion=(VB_TOP_BITS | (1<<15)))
block3.solve()
self.test_node.announce_block_and_wait_for_getdata(block3, use_header=True)
assert(self.test_node.last_message["getdata"].inv[0].type == blocktype)
- self.test_node.test_witness_block(block3, True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block3, True)
# Check that we can getdata for witness blocks or regular blocks,
# and the right thing happens.
@@ -996,7 +1004,7 @@ class SegWitTest(BitcoinTestFramework):
# This gives us a witness commitment.
assert(len(block.vtx[0].wit.vtxinwit) == 1)
assert(len(block.vtx[0].wit.vtxinwit[0].scriptWitness.stack) == 1)
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Now try to retrieve it...
rpc_block = self.nodes[0].getblock(block.hash, False)
non_wit_block = self.test_node.request_block(block.sha256, 2)
@@ -1050,7 +1058,7 @@ class SegWitTest(BitcoinTestFramework):
p2sh_tx.rehash()
# Mine it on test_node to create the confirmed output.
- self.test_node.test_transaction_acceptance(p2sh_tx, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_tx, with_witness=True, accepted=True)
self.nodes[0].generate(1)
sync_blocks(self.nodes)
@@ -1062,7 +1070,7 @@ class SegWitTest(BitcoinTestFramework):
tx.vout.append(CTxOut(8000, scriptPubKey)) # Might burn this later
tx.rehash()
- self.std_node.test_transaction_acceptance(tx, with_witness=True, accepted=segwit_activated)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=segwit_activated)
# Now create something that looks like a P2PKH output. This won't be spendable.
scriptPubKey = CScript([OP_0, hash160(witness_hash)])
@@ -1079,7 +1087,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vout = [CTxOut(p2sh_tx.vout[0].nValue-1000, scriptPubKey)]
tx2.rehash()
- self.std_node.test_transaction_acceptance(tx2, with_witness=True, accepted=segwit_activated)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=segwit_activated)
# Now update self.utxo for later tests.
tx3 = CTransaction()
@@ -1092,13 +1100,13 @@ class SegWitTest(BitcoinTestFramework):
tx3.wit.vtxinwit.append(CTxInWitness())
tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
tx3.rehash()
- self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True)
else:
# tx and tx2 didn't go anywhere; just clean up the p2sh_tx output.
tx3.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))]
tx3.vout = [CTxOut(p2sh_tx.vout[0].nValue-1000, witness_program)]
tx3.rehash()
- self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True)
self.nodes[0].generate(1)
sync_blocks(self.nodes)
@@ -1122,7 +1130,7 @@ class SegWitTest(BitcoinTestFramework):
tx.rehash()
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
self.utxo.pop(0)
for i in range(NUM_TESTS):
self.utxo.append(UTXO(tx.sha256, i, split_value))
@@ -1141,8 +1149,8 @@ class SegWitTest(BitcoinTestFramework):
tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")]
tx.vout = [CTxOut(self.utxo[0].nValue-1000, scriptPubKey)]
tx.rehash()
- self.std_node.test_transaction_acceptance(tx, with_witness=True, accepted=False)
- self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=True)
self.utxo.pop(0)
temp_utxo.append(UTXO(tx.sha256, 0, tx.vout[0].nValue))
@@ -1161,8 +1169,8 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
# Gets accepted to test_node, because standardness of outputs isn't
# checked with fRequireStandard
- self.test_node.test_transaction_acceptance(tx2, with_witness=True, accepted=True)
- self.std_node.test_transaction_acceptance(tx2, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=False)
temp_utxo.pop() # last entry in temp_utxo was the output we just spent
temp_utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
@@ -1178,7 +1186,7 @@ class SegWitTest(BitcoinTestFramework):
tx3.rehash()
# Spending a higher version witness output is not allowed by policy,
# even with fRequireStandard=false.
- self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=False)
self.test_node.sync_with_ping()
with mininode_lock:
assert(b"reserved for soft-fork upgrades" in self.test_node.last_message["reject"].reason)
@@ -1186,7 +1194,7 @@ class SegWitTest(BitcoinTestFramework):
# Building a block with the transaction must be valid, however.
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx2, tx3])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
sync_blocks(self.nodes)
# Add utxo to our list
@@ -1204,7 +1212,7 @@ class SegWitTest(BitcoinTestFramework):
# This next line will rehash the coinbase and update the merkle
# root, and solve.
self.update_witness_block_with_transactions(block, [])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
spend_tx = CTransaction()
spend_tx.vin = [CTxIn(COutPoint(block.vtx[0].sha256, 0), b"")]
@@ -1218,13 +1226,13 @@ class SegWitTest(BitcoinTestFramework):
sync_blocks(self.nodes)
block2 = self.build_next_block()
self.update_witness_block_with_transactions(block2, [spend_tx])
- self.test_node.test_witness_block(block2, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block2, accepted=False)
# Advancing one more block should allow the spend.
self.nodes[0].generate(1)
block2 = self.build_next_block()
self.update_witness_block_with_transactions(block2, [spend_tx])
- self.test_node.test_witness_block(block2, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block2, accepted=True)
sync_blocks(self.nodes)
@@ -1245,11 +1253,11 @@ class SegWitTest(BitcoinTestFramework):
tx.vout.append(CTxOut(self.utxo[0].nValue-1000, scriptPubKey))
tx.rehash()
- self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=True)
# Mine this transaction in preparation for following tests.
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
sync_blocks(self.nodes)
self.utxo.pop(0)
@@ -1266,19 +1274,19 @@ class SegWitTest(BitcoinTestFramework):
# Too-large input value
sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue+1, key)
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Too-small input value
sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue-1, key)
block.vtx.pop() # remove last tx
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now try correct value
sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue, key)
block.vtx.pop()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
prev_utxo = UTXO(tx.sha256, 0, tx.vout[0].nValue)
@@ -1302,7 +1310,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
block = self.build_next_block()
used_sighash_single_out_of_bounds = False
@@ -1344,7 +1352,7 @@ class SegWitTest(BitcoinTestFramework):
# Test the block periodically, if we're close to maxblocksize
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)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
block = self.build_next_block()
if (not used_sighash_single_out_of_bounds):
@@ -1352,7 +1360,7 @@ class SegWitTest(BitcoinTestFramework):
# Test the transactions we've added to the block
if (len(block.vtx) > 1):
self.update_witness_block_with_transactions(block, [])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Now test witness version 0 P2PKH transactions
pubkeyhash = hash160(pubkey)
@@ -1374,7 +1382,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin[0].scriptSig = CScript([signature, pubkey])
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx, tx2])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Move the signature to the witness.
block.vtx.pop()
@@ -1384,7 +1392,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
self.update_witness_block_with_transactions(block, [tx2])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
temp_utxos.pop(0)
@@ -1403,7 +1411,7 @@ class SegWitTest(BitcoinTestFramework):
index += 1
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
for i in range(len(tx.vout)):
self.utxo.append(UTXO(tx.sha256, i, tx.vout[i].nValue))
@@ -1430,10 +1438,10 @@ class SegWitTest(BitcoinTestFramework):
tx.rehash()
# Verify mempool acceptance and block validity
- self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True)
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True, with_witness=segwit_activated)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True, with_witness=segwit_activated)
sync_blocks(self.nodes)
# Now test attempts to spend the output.
@@ -1447,12 +1455,12 @@ class SegWitTest(BitcoinTestFramework):
# will require a witness to spend a witness program regardless of
# segwit activation. Note that older bitcoind's that are not
# segwit-aware would also reject this for failing CLEANSTACK.
- self.test_node.test_transaction_acceptance(spend_tx, with_witness=False, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=False, accepted=False)
# Try to put the witness script in the scriptSig, should also fail.
spend_tx.vin[0].scriptSig = CScript([p2wsh_pubkey, b'a'])
spend_tx.rehash()
- self.test_node.test_transaction_acceptance(spend_tx, with_witness=False, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=False, accepted=False)
# Now put the witness script in the witness, should succeed after
# segwit activates.
@@ -1462,7 +1470,7 @@ class SegWitTest(BitcoinTestFramework):
spend_tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a', witness_program ]
# Verify mempool acceptance
- self.test_node.test_transaction_acceptance(spend_tx, with_witness=True, accepted=segwit_activated)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=True, accepted=segwit_activated)
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [spend_tx])
@@ -1470,9 +1478,9 @@ class SegWitTest(BitcoinTestFramework):
# should be valid. If we're after activation, then sending this with
# witnesses should be valid.
if segwit_activated:
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
else:
- self.test_node.test_witness_block(block, accepted=True, with_witness=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True, with_witness=False)
# Update self.utxo
self.utxo.pop(0)
@@ -1493,7 +1501,7 @@ class SegWitTest(BitcoinTestFramework):
# Restart with the new binary
self.stop_node(node_id)
- self.start_node(node_id, extra_args=[])
+ self.start_node(node_id, extra_args=["-vbparams=segwit:0:999999999999"])
connect_nodes(self.nodes[0], node_id)
sync_blocks(self.nodes)
@@ -1556,7 +1564,7 @@ class SegWitTest(BitcoinTestFramework):
block_1 = self.build_next_block()
self.update_witness_block_with_transactions(block_1, [tx])
- self.test_node.test_witness_block(block_1, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_1, accepted=True)
tx2 = CTransaction()
# If we try to spend the first n-1 outputs from tx, that should be
@@ -1573,7 +1581,7 @@ class SegWitTest(BitcoinTestFramework):
block_2 = self.build_next_block()
self.update_witness_block_with_transactions(block_2, [tx2])
- self.test_node.test_witness_block(block_2, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_2, accepted=False)
# Try dropping the last input in tx2, and add an output that has
# too many sigops (contributing to legacy sigop count).
@@ -1586,14 +1594,14 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
block_3 = self.build_next_block()
self.update_witness_block_with_transactions(block_3, [tx2])
- self.test_node.test_witness_block(block_3, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=False)
# If we drop the last checksig in this output, the tx should succeed.
block_4 = self.build_next_block()
tx2.vout[-1].scriptPubKey = CScript([OP_CHECKSIG]*(checksig_count-1))
tx2.rehash()
self.update_witness_block_with_transactions(block_4, [tx2])
- self.test_node.test_witness_block(block_4, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_4, accepted=True)
# Reset the tip back down for the next test
sync_blocks(self.nodes)
@@ -1609,7 +1617,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_justright ]
tx2.rehash()
self.update_witness_block_with_transactions(block_5, [tx2])
- self.test_node.test_witness_block(block_5, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_5, accepted=True)
# TODO: test p2sh sigop counting
@@ -1687,7 +1695,7 @@ class SegWitTest(BitcoinTestFramework):
# 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)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Now try to spend it. Send it to a P2WSH output, which we'll
# use in the next test.
@@ -1706,11 +1714,11 @@ class SegWitTest(BitcoinTestFramework):
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)')
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, 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_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Test 2: P2WSH
# Try to spend the P2WSH output created in last test.
@@ -1726,11 +1734,11 @@ class SegWitTest(BitcoinTestFramework):
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)')
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, 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_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Test 3: P2SH(P2WSH)
# Try to spend the P2SH output created in the last test.
@@ -1743,10 +1751,10 @@ class SegWitTest(BitcoinTestFramework):
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)')
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, 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_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Test 4: Uncompressed pubkeys should still be valid in non-segwit
# transactions.
@@ -1758,10 +1766,10 @@ class SegWitTest(BitcoinTestFramework):
tx5.vin[0].scriptSig = CScript([signature, pubkey])
tx5.rehash()
# Should pass policy and consensus.
- self.test_node.test_transaction_acceptance(tx5, True, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, 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)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
self.utxo.append(UTXO(tx5.sha256, 0, tx5.vout[0].nValue))
def test_non_standard_witness(self):
@@ -1791,7 +1799,7 @@ class SegWitTest(BitcoinTestFramework):
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)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True)
self.nodes[0].generate(1)
sync_blocks(self.nodes)
@@ -1816,45 +1824,45 @@ class SegWitTest(BitcoinTestFramework):
# 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')
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, 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)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, 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')
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, 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)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, 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)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, 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)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[2], True, True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, 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')
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, 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)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, 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)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[0], True, False, b'bad-witness-nonstandard')
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, 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)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[1], True, False, b'bad-witness-nonstandard')
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, 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)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, 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)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[2], True, True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, 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)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[3], True, False, b'bad-witness-nonstandard')
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, 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
@@ -1867,21 +1875,14 @@ class SegWitTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- self.test_node = TestNode() # sets NODE_WITNESS|NODE_NETWORK
- self.old_node = TestNode() # only NODE_NETWORK
- self.std_node = TestNode() # for testing node1 (fRequireStandard=true)
-
- self.p2p_connections = [self.test_node, self.old_node]
-
- self.connections = []
- self.connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.test_node, services=NODE_NETWORK|NODE_WITNESS))
- self.connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.old_node, services=NODE_NETWORK))
- self.connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], self.std_node, services=NODE_NETWORK|NODE_WITNESS))
- self.test_node.add_connection(self.connections[0])
- self.old_node.add_connection(self.connections[1])
- self.std_node.add_connection(self.connections[2])
-
- NetworkThread().start() # Start up network handling in another thread
+ # self.test_node sets NODE_WITNESS|NODE_NETWORK
+ self.test_node = self.nodes[0].add_p2p_connection(TestNode(), services=NODE_NETWORK|NODE_WITNESS)
+ # self.old_node sets only NODE_NETWORK
+ self.old_node = self.nodes[0].add_p2p_connection(TestNode(), services=NODE_NETWORK)
+ # self.std_node is for testing node1 (fRequireStandard=true)
+ self.std_node = self.nodes[1].add_p2p_connection(TestNode(), services=NODE_NETWORK|NODE_WITNESS)
+
+ network_thread_start()
# Keep a place to store utxo's that can be used in later tests
self.utxo = []
diff --git a/test/functional/p2p-timeouts.py b/test/functional/p2p-timeouts.py
index 51d4769efc..984a3c8b90 100755
--- a/test/functional/p2p-timeouts.py
+++ b/test/functional/p2p-timeouts.py
@@ -27,8 +27,8 @@ from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-class TestNode(NodeConnCB):
- def on_version(self, conn, message):
+class TestNode(P2PInterface):
+ def on_version(self, message):
# Don't send a verack in response
pass
@@ -39,46 +39,37 @@ class TimeoutsTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- self.no_verack_node = TestNode() # never send verack
- self.no_version_node = TestNode() # never send version (just ping)
- self.no_send_node = TestNode() # never send anything
+ no_verack_node = self.nodes[0].add_p2p_connection(TestNode())
+ no_version_node = self.nodes[0].add_p2p_connection(TestNode(), send_version=False)
+ no_send_node = self.nodes[0].add_p2p_connection(TestNode(), send_version=False)
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_verack_node))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_version_node, send_version=False))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_send_node, send_version=False))
- self.no_verack_node.add_connection(connections[0])
- self.no_version_node.add_connection(connections[1])
- self.no_send_node.add_connection(connections[2])
-
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
sleep(1)
- assert(self.no_verack_node.connected)
- assert(self.no_version_node.connected)
- assert(self.no_send_node.connected)
+ assert no_verack_node.connected
+ assert no_version_node.connected
+ assert no_send_node.connected
- ping_msg = msg_ping()
- connections[0].send_message(ping_msg)
- connections[1].send_message(ping_msg)
+ no_verack_node.send_message(msg_ping())
+ no_version_node.send_message(msg_ping())
sleep(30)
- assert "version" in self.no_verack_node.last_message
+ assert "version" in no_verack_node.last_message
- assert(self.no_verack_node.connected)
- assert(self.no_version_node.connected)
- assert(self.no_send_node.connected)
+ assert no_verack_node.connected
+ assert no_version_node.connected
+ assert no_send_node.connected
- connections[0].send_message(ping_msg)
- connections[1].send_message(ping_msg)
+ no_verack_node.send_message(msg_ping())
+ no_version_node.send_message(msg_ping())
sleep(31)
- assert(not self.no_verack_node.connected)
- assert(not self.no_version_node.connected)
- assert(not self.no_send_node.connected)
+ assert not no_verack_node.connected
+ assert not no_version_node.connected
+ assert not no_send_node.connected
if __name__ == '__main__':
TimeoutsTest().main()
diff --git a/test/functional/p2p-versionbits-warning.py b/test/functional/p2p-versionbits-warning.py
index 5dfac6dd10..d29d43ebed 100755
--- a/test/functional/p2p-versionbits-warning.py
+++ b/test/functional/p2p-versionbits-warning.py
@@ -23,8 +23,8 @@ WARN_UNKNOWN_RULES_MINED = "Unknown block versions being mined! It's possible un
WARN_UNKNOWN_RULES_ACTIVE = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT)
VB_PATTERN = re.compile("^Warning.*versionbit")
-class TestNode(NodeConnCB):
- def on_inv(self, conn, message):
+class TestNode(P2PInterface):
+ def on_inv(self, message):
pass
class VersionBitsWarningTest(BitcoinTestFramework):
@@ -64,16 +64,12 @@ class VersionBitsWarningTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connection and start up the network thread.
- test_node = TestNode()
+ self.nodes[0].add_p2p_connection(TestNode())
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node))
- test_node.add_connection(connections[0])
-
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
# Test logic begins here
- test_node.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
# 1. Have the node mine one period worth of blocks
self.nodes[0].generate(VB_PERIOD)
@@ -81,26 +77,24 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# 2. Now build one period of blocks on the tip, with < VB_THRESHOLD
# blocks signaling some unknown bit.
nVersion = VB_TOP_BITS | (1<<VB_UNKNOWN_BIT)
- self.send_blocks_with_version(test_node, VB_THRESHOLD-1, nVersion)
+ self.send_blocks_with_version(self.nodes[0].p2p, VB_THRESHOLD-1, nVersion)
# 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
# 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].getmininginfo()["warnings"]))
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
- self.send_blocks_with_version(test_node, VB_THRESHOLD, nVersion)
+ self.send_blocks_with_version(self.nodes[0].p2p, VB_THRESHOLD, nVersion)
self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD)
# 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 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].getmininginfo()["warnings"])
assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getnetworkinfo()["warnings"])
# Mine a period worth of expected blocks so the generic block-version warning
@@ -115,8 +109,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# Connecting one block should be enough to generate an error.
self.nodes[0].generate(1)
- 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].getmininginfo()["warnings"])
assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getnetworkinfo()["warnings"])
self.stop_nodes()
self.test_versionbits_in_alert_file()
diff --git a/test/functional/prioritise_transaction.py b/test/functional/prioritise_transaction.py
index 7ad368acd4..bb56db9b40 100755
--- a/test/functional/prioritise_transaction.py
+++ b/test/functional/prioritise_transaction.py
@@ -101,7 +101,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
tx_id = self.nodes[0].decoderawtransaction(tx_hex)["txid"]
# This will raise an exception due to min relay fee not being met
- assert_raises_jsonrpc(-26, "66: min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex)
+ assert_raises_rpc_error(-26, "66: min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex)
assert(tx_id not in self.nodes[0].getrawmempool())
# This is a less than 1000-byte transaction, so just set the fee
diff --git a/test/functional/pruning.py b/test/functional/pruning.py
index f53fe82881..0101f61185 100755
--- a/test/functional/pruning.py
+++ b/test/functional/pruning.py
@@ -185,7 +185,7 @@ class PruneTest(BitcoinTestFramework):
def reorg_back(self):
# Verify that a block on the old main chain fork has been pruned away
- assert_raises_jsonrpc(-1, "Block not available (pruned data)", self.nodes[2].getblock, self.forkhash)
+ assert_raises_rpc_error(-1, "Block not available (pruned data)", self.nodes[2].getblock, self.forkhash)
self.log.info("Will need to redownload block %d" % self.forkheight)
# Verify that we have enough history to reorg back to the fork point
@@ -232,7 +232,7 @@ class PruneTest(BitcoinTestFramework):
self.start_node(node_number)
node = self.nodes[node_number]
assert_equal(node.getblockcount(), 995)
- assert_raises_jsonrpc(-1, "not in prune mode", node.pruneblockchain, 500)
+ assert_raises_rpc_error(-1, "not in prune mode", node.pruneblockchain, 500)
# now re-start in manual pruning mode
self.stop_node(node_number)
@@ -265,14 +265,14 @@ class PruneTest(BitcoinTestFramework):
return os.path.isfile(self.options.tmpdir + "/node{}/regtest/blocks/blk{:05}.dat".format(node_number, index))
# should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000)
- assert_raises_jsonrpc(-1, "Blockchain is too short for pruning", node.pruneblockchain, height(500))
+ assert_raises_rpc_error(-1, "Blockchain is too short for pruning", node.pruneblockchain, height(500))
# mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight)
node.generate(6)
assert_equal(node.getblockchaininfo()["blocks"], 1001)
# negative heights should raise an exception
- assert_raises_jsonrpc(-8, "Negative", node.pruneblockchain, -10)
+ assert_raises_rpc_error(-8, "Negative", node.pruneblockchain, -10)
# height=100 too low to prune first block file so this is a no-op
prune(100)
diff --git a/test/functional/rawtransactions.py b/test/functional/rawtransactions.py
index 70620059ed..d6213a4ba5 100755
--- a/test/functional/rawtransactions.py
+++ b/test/functional/rawtransactions.py
@@ -15,6 +15,25 @@ Test the following RPCs:
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
+
+class multidict(dict):
+ """Dictionary that allows duplicate keys.
+
+ Constructed with a list of (key, value) tuples. When dumped by the json module,
+ will output invalid json with repeated keys, eg:
+ >>> json.dumps(multidict([(1,2),(1,2)])
+ '{"1": 2, "1": 2}'
+
+ Used to test calls to rpc methods with repeated keys in the json object."""
+
+ def __init__(self, x):
+ dict.__init__(self, x)
+ self.x = x
+
+ def items(self):
+ return self.x
+
+
# Create one-input, one-output, no-fee transaction:
class RawTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
@@ -39,6 +58,41 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(5)
self.sync_all()
+ # Test `createrawtransaction` required parameters
+ assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction)
+ assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, [])
+
+ # Test `createrawtransaction` invalid extra parameters
+ assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, [], {}, 0, False, 'foo')
+
+ # Test `createrawtransaction` invalid `inputs`
+ txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000'
+ assert_raises_rpc_error(-3, "Expected type array", self.nodes[0].createrawtransaction, 'foo', {})
+ assert_raises_rpc_error(-1, "JSON value is not an object as expected", self.nodes[0].createrawtransaction, ['foo'], {})
+ assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{}], {})
+ assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{'txid': 'foo'}], {})
+ assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid}], {})
+ assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 'foo'}], {})
+ assert_raises_rpc_error(-8, "Invalid parameter, vout must be positive", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': -1}], {})
+ assert_raises_rpc_error(-8, "Invalid parameter, sequence number is out of range", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 0, 'sequence': -1}], {})
+
+ # Test `createrawtransaction` invalid `outputs`
+ address = self.nodes[0].getnewaddress()
+ assert_raises_rpc_error(-3, "Expected type object", self.nodes[0].createrawtransaction, [], 'foo')
+ assert_raises_rpc_error(-8, "Data must be hexadecimal string", self.nodes[0].createrawtransaction, [], {'data': 'foo'})
+ assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].createrawtransaction, [], {'foo': 0})
+ assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].createrawtransaction, [], {address: 'foo'})
+ assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].createrawtransaction, [], {address: -1})
+ assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)]))
+
+ # Test `createrawtransaction` invalid `locktime`
+ assert_raises_rpc_error(-3, "Expected type number", self.nodes[0].createrawtransaction, [], {}, 'foo')
+ assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, -1)
+ assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, 4294967296)
+
+ # Test `createrawtransaction` invalid `replaceable`
+ assert_raises_rpc_error(-3, "Expected type bool", self.nodes[0].createrawtransaction, [], {}, 0, 'foo')
+
#########################################
# sendrawtransaction with missing input #
#########################################
@@ -48,7 +102,37 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = self.nodes[2].signrawtransaction(rawtx)
# This will raise an exception since there are missing inputs
- assert_raises_jsonrpc(-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex'])
+ assert_raises_rpc_error(-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex'])
+
+ #####################################
+ # getrawtransaction with block hash #
+ #####################################
+
+ # make a tx by sending then generate 2 blocks; block1 has the tx in it
+ tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1)
+ block1, block2 = self.nodes[2].generate(2)
+ self.sync_all()
+ # We should be able to get the raw transaction by providing the correct block
+ gottx = self.nodes[0].getrawtransaction(tx, True, block1)
+ assert_equal(gottx['txid'], tx)
+ assert_equal(gottx['in_active_chain'], True)
+ # We should not have the 'in_active_chain' flag when we don't provide a block
+ gottx = self.nodes[0].getrawtransaction(tx, True)
+ assert_equal(gottx['txid'], tx)
+ assert 'in_active_chain' not in gottx
+ # We should not get the tx if we provide an unrelated block
+ assert_raises_rpc_error(-5, "No such transaction found", self.nodes[0].getrawtransaction, tx, True, block2)
+ # An invalid block hash should raise the correct errors
+ assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, True)
+ assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, "foobar")
+ assert_raises_rpc_error(-8, "parameter 3 must be of length 64", self.nodes[0].getrawtransaction, tx, True, "abcd1234")
+ assert_raises_rpc_error(-5, "Block hash not found", self.nodes[0].getrawtransaction, tx, True, "0000000000000000000000000000000000000000000000000000000000000000")
+ # Undo the blocks and check in_active_chain
+ self.nodes[0].invalidateblock(block1)
+ gottx = self.nodes[0].getrawtransaction(txid=tx, verbose=True, blockhash=block1)
+ assert_equal(gottx['in_active_chain'], False)
+ self.nodes[0].reconsiderblock(block1)
+ assert_equal(self.nodes[0].getbestblockhash(), block2)
#########################
# RAW TX MULTISIG TESTS #
@@ -199,13 +283,13 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex'])
# 6. invalid parameters - supply txid and string "Flase"
- assert_raises_jsonrpc(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, "Flase")
+ assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, "Flase")
# 7. invalid parameters - supply txid and empty array
- assert_raises_jsonrpc(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, [])
+ assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, [])
# 8. invalid parameters - supply txid and empty dict
- assert_raises_jsonrpc(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, {})
+ assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, {})
inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 1000}]
outputs = { self.nodes[0].getnewaddress() : 1 }
@@ -216,12 +300,12 @@ class RawTransactionsTest(BitcoinTestFramework):
# 9. invalid parameters - sequence number out of range
inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : -1}]
outputs = { self.nodes[0].getnewaddress() : 1 }
- assert_raises_jsonrpc(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs)
+ assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs)
# 10. invalid parameters - sequence number out of range
inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967296}]
outputs = { self.nodes[0].getnewaddress() : 1 }
- assert_raises_jsonrpc(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs)
+ assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs)
inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967294}]
outputs = { self.nodes[0].getnewaddress() : 1 }
diff --git a/test/functional/receivedby.py b/test/functional/receivedby.py
index db6fc86b82..97da19546f 100755
--- a/test/functional/receivedby.py
+++ b/test/functional/receivedby.py
@@ -3,97 +3,83 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the listreceivedbyaddress RPC."""
+from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
-
-def get_sub_array_from_array(object_array, to_match):
- '''
- Finds and returns a sub array from an array of arrays.
- to_match should be a unique idetifier of a sub array
- '''
- for item in object_array:
- all_match = True
- for key,value in to_match.items():
- if item[key] != value:
- all_match = False
- if not all_match:
- continue
- return item
- return []
+from test_framework.util import (assert_array_result,
+ assert_equal,
+ assert_raises_rpc_error,
+ )
class ReceivedByTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- self.enable_mocktime()
def run_test(self):
- '''
- listreceivedbyaddress Test
- '''
+ # Generate block to get out of IBD
+ self.nodes[0].generate(1)
+
+ self.log.info("listreceivedbyaddress Test")
+
# Send from node 0 to 1
addr = self.nodes[1].getnewaddress()
txid = self.nodes[0].sendtoaddress(addr, 0.1)
self.sync_all()
- #Check not listed in listreceivedbyaddress because has 0 confirmations
+ # Check not listed in listreceivedbyaddress because has 0 confirmations
assert_array_result(self.nodes[1].listreceivedbyaddress(),
- {"address":addr},
- { },
- True)
- #Bury Tx under 10 block so it will be returned by listreceivedbyaddress
+ {"address": addr},
+ {},
+ True)
+ # Bury Tx under 10 block so it will be returned by listreceivedbyaddress
self.nodes[1].generate(10)
self.sync_all()
assert_array_result(self.nodes[1].listreceivedbyaddress(),
- {"address":addr},
- {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]})
- #With min confidence < 10
+ {"address": addr},
+ {"address": addr, "account": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]})
+ # With min confidence < 10
assert_array_result(self.nodes[1].listreceivedbyaddress(5),
- {"address":addr},
- {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]})
- #With min confidence > 10, should not find Tx
- assert_array_result(self.nodes[1].listreceivedbyaddress(11),{"address":addr},{ },True)
+ {"address": addr},
+ {"address": addr, "account": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]})
+ # With min confidence > 10, should not find Tx
+ assert_array_result(self.nodes[1].listreceivedbyaddress(11), {"address": addr}, {}, True)
- #Empty Tx
+ # Empty Tx
addr = self.nodes[1].getnewaddress()
- assert_array_result(self.nodes[1].listreceivedbyaddress(0,True),
- {"address":addr},
- {"address":addr, "account":"", "amount":0, "confirmations":0, "txids":[]})
+ assert_array_result(self.nodes[1].listreceivedbyaddress(0, True),
+ {"address": addr},
+ {"address": addr, "account": "", "amount": 0, "confirmations": 0, "txids": []})
+
+ self.log.info("getreceivedbyaddress Test")
- '''
- getreceivedbyaddress Test
- '''
# Send from node 0 to 1
addr = self.nodes[1].getnewaddress()
txid = self.nodes[0].sendtoaddress(addr, 0.1)
self.sync_all()
- #Check balance is 0 because of 0 confirmations
+ # Check balance is 0 because of 0 confirmations
balance = self.nodes[1].getreceivedbyaddress(addr)
- if balance != Decimal("0.0"):
- raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
+ assert_equal(balance, Decimal("0.0"))
- #Check balance is 0.1
- balance = self.nodes[1].getreceivedbyaddress(addr,0)
- if balance != Decimal("0.1"):
- raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
+ # Check balance is 0.1
+ balance = self.nodes[1].getreceivedbyaddress(addr, 0)
+ assert_equal(balance, Decimal("0.1"))
- #Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress
+ # Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress
self.nodes[1].generate(10)
self.sync_all()
balance = self.nodes[1].getreceivedbyaddress(addr)
- if balance != Decimal("0.1"):
- raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
+ assert_equal(balance, Decimal("0.1"))
+
+ # Trying to getreceivedby for an address the wallet doesn't own should return an error
+ assert_raises_rpc_error(-4, "Address not found in wallet", self.nodes[0].getreceivedbyaddress, addr)
+
+ self.log.info("listreceivedbyaccount + getreceivedbyaccount Test")
- '''
- listreceivedbyaccount + getreceivedbyaccount Test
- '''
- #set pre-state
+ # set pre-state
addrArr = self.nodes[1].getnewaddress()
account = self.nodes[1].getaccount(addrArr)
- received_by_account_json = get_sub_array_from_array(self.nodes[1].listreceivedbyaccount(),{"account":account})
- if len(received_by_account_json) == 0:
- raise AssertionError("No accounts found in node")
+ received_by_account_json = [r for r in self.nodes[1].listreceivedbyaccount() if r["account"] == account][0]
balance_by_account = self.nodes[1].getreceivedbyaccount(account)
txid = self.nodes[0].sendtoaddress(addr, 0.1)
@@ -101,40 +87,34 @@ class ReceivedByTest(BitcoinTestFramework):
# listreceivedbyaccount should return received_by_account_json because of 0 confirmations
assert_array_result(self.nodes[1].listreceivedbyaccount(),
- {"account":account},
- received_by_account_json)
+ {"account": account},
+ received_by_account_json)
# getreceivedbyaddress should return same balance because of 0 confirmations
balance = self.nodes[1].getreceivedbyaccount(account)
- if balance != balance_by_account:
- raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
+ assert_equal(balance, balance_by_account)
self.nodes[1].generate(10)
self.sync_all()
# listreceivedbyaccount should return updated account balance
assert_array_result(self.nodes[1].listreceivedbyaccount(),
- {"account":account},
- {"account":received_by_account_json["account"], "amount":(received_by_account_json["amount"] + Decimal("0.1"))})
+ {"account": account},
+ {"account": received_by_account_json["account"], "amount": (received_by_account_json["amount"] + Decimal("0.1"))})
# getreceivedbyaddress should return updates balance
balance = self.nodes[1].getreceivedbyaccount(account)
- if balance != balance_by_account + Decimal("0.1"):
- raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
+ assert_equal(balance, balance_by_account + Decimal("0.1"))
- #Create a new account named "mynewaccount" that has a 0 balance
+ # Create a new account named "mynewaccount" that has a 0 balance
self.nodes[1].getaccountaddress("mynewaccount")
- received_by_account_json = get_sub_array_from_array(self.nodes[1].listreceivedbyaccount(0,True),{"account":"mynewaccount"})
- if len(received_by_account_json) == 0:
- raise AssertionError("No accounts found in node")
+ received_by_account_json = [r for r in self.nodes[1].listreceivedbyaccount(0, True) if r["account"] == "mynewaccount"][0]
# Test includeempty of listreceivedbyaccount
- if received_by_account_json["amount"] != Decimal("0.0"):
- raise AssertionError("Wrong balance returned by listreceivedbyaccount, %0.2f"%(received_by_account_json["amount"]))
+ assert_equal(received_by_account_json["amount"], Decimal("0.0"))
# Test getreceivedbyaccount for 0 amount accounts
balance = self.nodes[1].getreceivedbyaccount("mynewaccount")
- if balance != Decimal("0.0"):
- raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
+ assert_equal(balance, Decimal("0.0"))
if __name__ == '__main__':
ReceivedByTest().main()
diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py
index 1d6494fe41..815e964848 100755
--- a/test/functional/replace-by-fee.py
+++ b/test/functional/replace-by-fee.py
@@ -62,17 +62,24 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])):
class ReplaceByFeeTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 1
+ self.num_nodes = 2
self.extra_args= [["-maxorphantx=1000",
"-whitelist=127.0.0.1",
"-limitancestorcount=50",
"-limitancestorsize=101",
"-limitdescendantcount=200",
- "-limitdescendantsize=101"]]
+ "-limitdescendantsize=101"],
+ ["-mempoolreplacement=0"]]
def run_test(self):
+ # Leave IBD
+ self.nodes[0].generate(1)
+
make_utxo(self.nodes[0], 1*COIN)
+ # Ensure nodes are synced
+ self.sync_all()
+
self.log.info("Running test simple doublespend...")
self.test_simple_doublespend()
@@ -109,12 +116,19 @@ class ReplaceByFeeTest(BitcoinTestFramework):
"""Simple doublespend"""
tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ # make_utxo may have generated a bunch of blocks, so we need to sync
+ # before we can spend the coins generated, or else the resulting
+ # transactions might not be accepted by our peers.
+ self.sync_all()
+
tx1a = CTransaction()
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))]
tx1a_hex = txToHex(tx1a)
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
+ self.sync_all()
+
# Should fail because we haven't changed the fee
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
@@ -122,13 +136,18 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1b_hex = txToHex(tx1b)
# This will raise an exception due to insufficient fee
- assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
+ assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
+ # This will raise an exception due to transaction replacement being disabled
+ assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True)
# Extra 0.1 BTC fee
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))]
tx1b_hex = txToHex(tx1b)
+ # Replacement still disabled even with "enough fee"
+ assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True)
+ # Works when enabled
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
mempool = self.nodes[0].getrawmempool()
@@ -138,6 +157,11 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert_equal(tx1b_hex, self.nodes[0].getrawtransaction(tx1b_txid))
+ # Second node is running mempoolreplacement=0, will not replace originally-seen txn
+ mempool = self.nodes[1].getrawmempool()
+ assert tx1a_txid in mempool
+ assert tx1b_txid not in mempool
+
def test_doublespend_chain(self):
"""Doublespend of a long chain"""
@@ -165,7 +189,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
dbl_tx_hex = txToHex(dbl_tx)
# This will raise an exception due to insufficient fee
- assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True)
+ assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True)
# Accepted with sufficient fee
dbl_tx = CTransaction()
@@ -226,7 +250,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
dbl_tx.vout = [CTxOut(initial_nValue - fee*n, CScript([1]))]
dbl_tx_hex = txToHex(dbl_tx)
# This will raise an exception due to insufficient fee
- assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True)
+ assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True)
# 1 BTC fee is enough
dbl_tx = CTransaction()
@@ -254,7 +278,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
dbl_tx.vout = [CTxOut(initial_nValue - 2*fee*n, CScript([1]))]
dbl_tx_hex = txToHex(dbl_tx)
# This will raise an exception
- assert_raises_jsonrpc(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, True)
+ assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, True)
for tx in tree_txs:
tx.rehash()
@@ -278,7 +302,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1b_hex = txToHex(tx1b)
# This will raise an exception due to insufficient fee
- assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
+ assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
def test_spends_of_conflicting_outputs(self):
"""Replacements that spend conflicting tx outputs are rejected"""
@@ -301,7 +325,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2_hex = txToHex(tx2)
# This will raise an exception
- assert_raises_jsonrpc(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True)
+ assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True)
# Spend tx1a's output to test the indirect case.
tx1b = CTransaction()
@@ -318,7 +342,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2_hex = txToHex(tx2)
# This will raise an exception
- assert_raises_jsonrpc(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True)
+ assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True)
def test_new_unconfirmed_inputs(self):
"""Replacements that add new unconfirmed inputs are rejected"""
@@ -337,7 +361,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2_hex = txToHex(tx2)
# This will raise an exception
- assert_raises_jsonrpc(-26, "replacement-adds-unconfirmed", self.nodes[0].sendrawtransaction, tx2_hex, True)
+ assert_raises_rpc_error(-26, "replacement-adds-unconfirmed", self.nodes[0].sendrawtransaction, tx2_hex, True)
def test_too_many_replacements(self):
"""Replacements that evict too many transactions are rejected"""
@@ -383,7 +407,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
double_tx_hex = txToHex(double_tx)
# This will raise an exception
- assert_raises_jsonrpc(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, double_tx_hex, True)
+ assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, double_tx_hex, True)
# If we remove an input, it should pass
double_tx = CTransaction()
@@ -410,7 +434,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1b_hex = txToHex(tx1b)
# This will raise an exception
- assert_raises_jsonrpc(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, True)
+ assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, True)
tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
@@ -428,7 +452,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2b_hex = txToHex(tx2b)
# This will raise an exception
- assert_raises_jsonrpc(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, True)
+ assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, True)
# Now create a new transaction that spends from tx1a and tx2a
# opt-in on one of the inputs
@@ -480,7 +504,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1b_hex = txToHex(tx1b)
# Verify tx1b cannot replace tx1a.
- assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
+ assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
# Use prioritisetransaction to set tx1a's fee to 0.
self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1*COIN))
@@ -507,7 +531,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2b_hex = txToHex(tx2b)
# Verify tx2b cannot replace tx2a.
- assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, True)
+ assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, True)
# Now prioritise tx2b to have a higher modified fee
self.nodes[0].prioritisetransaction(txid=tx2b.hash, fee_delta=int(0.1*COIN))
diff --git a/test/functional/resendwallettransactions.py b/test/functional/resendwallettransactions.py
index d6ba591391..d959bb4c38 100755
--- a/test/functional/resendwallettransactions.py
+++ b/test/functional/resendwallettransactions.py
@@ -5,7 +5,7 @@
"""Test resendwallettransactions RPC."""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_jsonrpc
+from test_framework.util import assert_equal, assert_raises_rpc_error
class ResendWalletTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
@@ -14,7 +14,7 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
def run_test(self):
# Should raise RPC_WALLET_ERROR (-4) if walletbroadcast is disabled.
- assert_raises_jsonrpc(-4, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast", self.nodes[0].resendwallettransactions)
+ assert_raises_rpc_error(-4, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast", self.nodes[0].resendwallettransactions)
# Should return an empty array if there aren't unconfirmed wallet transactions.
self.stop_node(0)
diff --git a/test/functional/rpcbind_test.py b/test/functional/rpcbind_test.py
index 0cf64beebd..0e8c3fa209 100755
--- a/test/functional/rpcbind_test.py
+++ b/test/functional/rpcbind_test.py
@@ -101,7 +101,7 @@ class RPCBindTest(BitcoinTestFramework):
# Check that with invalid rpcallowip, we are denied
self.run_allowip_test([non_loopback_ip], non_loopback_ip, defaultport)
- assert_raises_jsonrpc(-342, "non-JSON HTTP response with '403 Forbidden' from server", self.run_allowip_test, ['1.1.1.1'], non_loopback_ip, defaultport)
+ assert_raises_rpc_error(-342, "non-JSON HTTP response with '403 Forbidden' from server", self.run_allowip_test, ['1.1.1.1'], non_loopback_ip, defaultport)
if __name__ == '__main__':
RPCBindTest().main()
diff --git a/test/functional/rpcnamedargs.py b/test/functional/rpcnamedargs.py
index da61cc66e6..c47212bddb 100755
--- a/test/functional/rpcnamedargs.py
+++ b/test/functional/rpcnamedargs.py
@@ -7,7 +7,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- assert_raises_jsonrpc,
+ assert_raises_rpc_error,
)
class NamedArgumentTest(BitcoinTestFramework):
@@ -16,10 +16,10 @@ class NamedArgumentTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0]
- h = node.help(command='getinfo')
- assert(h.startswith('getinfo\n'))
+ h = node.help(command='getblockchaininfo')
+ assert(h.startswith('getblockchaininfo\n'))
- assert_raises_jsonrpc(-8, 'Unknown named parameter', node.help, random='getinfo')
+ assert_raises_rpc_error(-8, 'Unknown named parameter', node.help, random='getblockchaininfo')
h = node.getblockhash(height=0)
node.getblock(blockhash=h)
diff --git a/test/functional/segwit.py b/test/functional/segwit.py
index 609c592ed3..338fa1bc52 100755
--- a/test/functional/segwit.py
+++ b/test/functional/segwit.py
@@ -7,7 +7,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex
-from test_framework.address import script_to_p2sh, key_to_p2pkh
+from test_framework.address import script_to_p2sh, key_to_p2pkh, key_to_p2sh_p2wpkh, key_to_p2wpkh, script_to_p2sh_p2wsh, script_to_p2wsh, program_to_witness
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, OP_TRUE
from io import BytesIO
@@ -33,15 +33,15 @@ def witness_script(use_p2wsh, pubkey):
# Return a transaction (in hex) that spends the given utxo to a segwit output,
# optionally wrapping the segwit output using P2SH.
-def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount):
- pkscript = hex_str_to_bytes(witness_script(use_p2wsh, pubkey))
- if (encode_p2sh):
- p2sh_hash = hash160(pkscript)
- pkscript = CScript([OP_HASH160, p2sh_hash, OP_EQUAL])
- tx = CTransaction()
- tx.vin.append(CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), b""))
- tx.vout.append(CTxOut(int(amount*COIN), pkscript))
- return ToHex(tx)
+def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
+ if use_p2wsh:
+ program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
+ addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
+ else:
+ addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey)
+ if not encode_p2sh:
+ assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey))
+ return node.createrawtransaction([utxo], {addr: amount})
# Create a transaction spending a given utxo to a segwit output corresponding
# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH;
@@ -49,7 +49,7 @@ def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount):
# sign=True will have the given node sign the transaction.
# insert_redeem_script will be added to the scriptSig, if given.
def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""):
- tx_to_witness = create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount)
+ tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount)
if (sign):
signed = node.signrawtransaction(tx_to_witness)
assert("errors" not in signed or len(["errors"]) == 0)
@@ -77,9 +77,10 @@ class SegWitTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
- self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0"],
- ["-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1"],
- ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness"]]
+ # This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
+ self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0", "-vbparams=segwit:0:999999999999"],
+ ["-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1", "-vbparams=segwit:0:999999999999"],
+ ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-vbparams=segwit:0:999999999999"]]
def setup_network(self):
super().setup_network()
@@ -99,11 +100,11 @@ class SegWitTest(BitcoinTestFramework):
sync_blocks(self.nodes)
def fail_accept(self, node, error_msg, txid, sign, redeem_script=""):
- assert_raises_jsonrpc(-26, error_msg, send_to_witness, 1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script)
+ assert_raises_rpc_error(-26, error_msg, send_to_witness, 1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script)
def fail_mine(self, node, txid, sign, redeem_script=""):
send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script)
- assert_raises_jsonrpc(-1, "CreateNewBlock: TestBlockValidity failed", node.generate, 1)
+ assert_raises_rpc_error(-1, "CreateNewBlock: TestBlockValidity failed", node.generate, 1)
sync_blocks(self.nodes)
def run_test(self):
@@ -133,8 +134,15 @@ class SegWitTest(BitcoinTestFramework):
newaddress = self.nodes[i].getnewaddress()
self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"])
multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]])
- self.nodes[i].addwitnessaddress(newaddress)
- self.nodes[i].addwitnessaddress(multiaddress)
+ multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG])
+ p2sh_addr = self.nodes[i].addwitnessaddress(newaddress, True)
+ bip173_addr = self.nodes[i].addwitnessaddress(newaddress, False)
+ p2sh_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, True)
+ bip173_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, False)
+ assert_equal(p2sh_addr, key_to_p2sh_p2wpkh(self.pubkey[-1]))
+ assert_equal(bip173_addr, key_to_p2wpkh(self.pubkey[-1]))
+ assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript))
+ assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript))
p2sh_ids.append([])
wit_ids.append([])
for v in range(2):
@@ -279,6 +287,9 @@ class SegWitTest(BitcoinTestFramework):
assert(txid2 in template_txids)
assert(txid3 in template_txids)
+ # Check that wtxid is properly reported in mempool entry
+ assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True))
+
# Mine a block to clear the gbt cache again.
self.nodes[0].generate(1)
@@ -442,11 +453,7 @@ class SegWitTest(BitcoinTestFramework):
for i in importlist:
# import all generated addresses. The wallet already has the private keys for some of these, so catch JSON RPC
# exceptions and continue.
- 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")
- assert_equal(exp.error["code"], -4)
+ try_rpc(-4, "The wallet already contains the private key for this address or script", self.nodes[0].importaddress, i, "", False, True)
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
@@ -459,7 +466,7 @@ class SegWitTest(BitcoinTestFramework):
# addwitnessaddress should refuse to return a witness address if an uncompressed key is used
# note that no witness address should be returned by unsolvable addresses
for i in uncompressed_spendable_address + uncompressed_solvable_address + unknown_address + unsolvable_address:
- assert_raises_jsonrpc(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i)
+ assert_raises_rpc_error(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i)
# addwitnessaddress should return a witness addresses even if keys are not in the wallet
self.nodes[0].addwitnessaddress(multisig_without_privkey_address)
@@ -542,7 +549,7 @@ class SegWitTest(BitcoinTestFramework):
# premature_witaddress are not accepted until the script is added with addwitnessaddress first
for i in uncompressed_spendable_address + uncompressed_solvable_address + premature_witaddress:
# This will raise an exception
- assert_raises_jsonrpc(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i)
+ assert_raises_rpc_error(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i)
# after importaddress it should pass addwitnessaddress
v = self.nodes[0].validateaddress(compressed_solvable_address[1])
@@ -555,6 +562,13 @@ class SegWitTest(BitcoinTestFramework):
solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1))
self.mine_and_test_listunspent(unseen_anytime, 0)
+ # Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works
+ v1_addr = program_to_witness(1, [3,5])
+ v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])],{v1_addr: 1})
+ v1_decoded = self.nodes[1].decoderawtransaction(v1_tx)
+ assert_equal(v1_decoded['vout'][0]['scriptPubKey']['addresses'][0], v1_addr)
+ assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305")
+
# Check that spendable outputs are really spendable
self.create_and_mine_tx_from_txids(spendable_txid)
@@ -567,6 +581,29 @@ class SegWitTest(BitcoinTestFramework):
self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K")
self.create_and_mine_tx_from_txids(solvable_txid)
+ # Test that importing native P2WPKH/P2WSH scripts works
+ for use_p2wsh in [False, True]:
+ if use_p2wsh:
+ scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a"
+ transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000"
+ else:
+ scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87"
+ transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000"
+
+ self.nodes[1].importaddress(scriptPubKey, "", False)
+ rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex']
+ rawtxfund = self.nodes[1].signrawtransaction(rawtxfund)["hex"]
+ txid = self.nodes[1].sendrawtransaction(rawtxfund)
+
+ assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
+ assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
+
+ # Assert it is properly saved
+ self.stop_node(1)
+ self.start_node(1)
+ assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
+ assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
+
def mine_and_test_listunspent(self, script_list, ismine):
utxo = find_unspent(self.nodes[0], 50)
tx = CTransaction()
diff --git a/test/functional/sendheaders.py b/test/functional/sendheaders.py
index fe577dc20a..256227f721 100755
--- a/test/functional/sendheaders.py
+++ b/test/functional/sendheaders.py
@@ -4,11 +4,24 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test behavior of headers messages to announce blocks.
-Setup:
+Setup:
-- Two nodes, two p2p connections to node0. One p2p connection should only ever
- receive inv's (omitted from testing description below, this is our control).
- Second node is used for creating reorgs.
+- Two nodes:
+ - node0 is the node-under-test. We create two p2p connections to it. The
+ first p2p connection is a control and should only ever receive inv's. The
+ second p2p connection tests the headers sending logic.
+ - node1 is used to create reorgs.
+
+test_null_locators
+==================
+
+Sends two getheaders requests with null locator values. First request's hashstop
+value refers to validated block, while second request's hashstop value refers to
+a block which hasn't been validated. Verifies only the first request returns
+headers.
+
+test_nonnull_locators
+=====================
Part 1: No headers announcements before "sendheaders"
a. node mines a block [expect: inv]
@@ -72,159 +85,197 @@ d. Announce 49 headers that don't connect.
e. Announce one more that doesn't connect.
Expect: disconnect.
"""
-
-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.mininode import (
+ CBlockHeader,
+ CInv,
+ NODE_WITNESS,
+ network_thread_start,
+ P2PInterface,
+ mininode_lock,
+ msg_block,
+ msg_getblocks,
+ msg_getdata,
+ msg_getheaders,
+ msg_headers,
+ msg_inv,
+ msg_sendheaders,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ sync_blocks,
+ wait_until,
+)
+DIRECT_FETCH_RESPONSE_TIME = 0.05
-direct_fetch_response_time = 0.05
-
-class TestNode(NodeConnCB):
+class BaseNode(P2PInterface):
def __init__(self):
super().__init__()
+
self.block_announced = False
self.last_blockhash_announced = None
- def clear_last_announcement(self):
- with mininode_lock:
- self.block_announced = False
- self.last_message.pop("inv", None)
- self.last_message.pop("headers", None)
-
- # Request data for a list of block hashes
- def get_data(self, block_hashes):
+ def send_get_data(self, block_hashes):
+ """Request data for a list of block hashes."""
msg = msg_getdata()
for x in block_hashes:
msg.inv.append(CInv(2, x))
- self.connection.send_message(msg)
+ self.send_message(msg)
- def get_headers(self, locator, hashstop):
+ def send_get_headers(self, locator, hashstop):
msg = msg_getheaders()
msg.locator.vHave = locator
msg.hashstop = hashstop
- self.connection.send_message(msg)
+ self.send_message(msg)
def send_block_inv(self, blockhash):
msg = msg_inv()
msg.inv = [CInv(2, blockhash)]
- self.connection.send_message(msg)
+ self.send_message(msg)
+
+ def send_header_for_blocks(self, new_blocks):
+ headers_message = msg_headers()
+ headers_message.headers = [CBlockHeader(b) for b in new_blocks]
+ self.send_message(headers_message)
- def on_inv(self, conn, message):
+ def send_getblocks(self, locator):
+ getblocks_message = msg_getblocks()
+ getblocks_message.locator.vHave = locator
+ self.send_message(getblocks_message)
+
+ def wait_for_getdata(self, hash_list, timeout=60):
+ if hash_list == []:
+ return
+
+ test_function = lambda: "getdata" in self.last_message and [x.hash for x in self.last_message["getdata"].inv] == hash_list
+ wait_until(test_function, timeout=timeout, lock=mininode_lock)
+
+ def wait_for_block_announcement(self, block_hash, timeout=60):
+ test_function = lambda: self.last_blockhash_announced == block_hash
+ wait_until(test_function, timeout=timeout, lock=mininode_lock)
+
+ def on_inv(self, message):
self.block_announced = True
self.last_blockhash_announced = message.inv[-1].hash
- def on_headers(self, conn, message):
+ def on_headers(self, message):
if len(message.headers):
self.block_announced = True
message.headers[-1].calc_sha256()
self.last_blockhash_announced = message.headers[-1].sha256
- # Test whether the last announcement we received had the
- # right header or the right inv
- # inv and headers should be lists of block hashes
+ def clear_last_announcement(self):
+ with mininode_lock:
+ self.block_announced = False
+ self.last_message.pop("inv", None)
+ self.last_message.pop("headers", None)
+
def check_last_announcement(self, headers=None, inv=None):
- expect_headers = headers if headers != None else []
- expect_inv = inv if inv != None else []
+ """Test whether the last announcement received had the right header or the right inv.
+
+ inv and headers should be lists of block hashes."""
+
test_function = lambda: self.block_announced
wait_until(test_function, timeout=60, lock=mininode_lock)
+
with mininode_lock:
self.block_announced = False
- success = True
compare_inv = []
if "inv" in self.last_message:
compare_inv = [x.hash for x in self.last_message["inv"].inv]
- if compare_inv != expect_inv:
- success = False
+ if inv is not None:
+ assert_equal(compare_inv, inv)
- hash_headers = []
+ compare_headers = []
if "headers" in self.last_message:
- # treat headers as a list of block hashes
- hash_headers = [ x.sha256 for x in self.last_message["headers"].headers ]
- if hash_headers != expect_headers:
- success = False
+ compare_headers = [x.sha256 for x in self.last_message["headers"].headers]
+ if headers is not None:
+ assert_equal(compare_headers, headers)
self.last_message.pop("inv", None)
self.last_message.pop("headers", None)
- return success
-
- def wait_for_getdata(self, hash_list, timeout=60):
- if hash_list == []:
- return
-
- test_function = lambda: "getdata" in self.last_message and [x.hash for x in self.last_message["getdata"].inv] == hash_list
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
- return
-
- def wait_for_block_announcement(self, block_hash, timeout=60):
- test_function = lambda: self.last_blockhash_announced == block_hash
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
- return
-
- def send_header_for_blocks(self, new_blocks):
- headers_message = msg_headers()
- headers_message.headers = [ CBlockHeader(b) for b in new_blocks ]
- self.send_message(headers_message)
-
- def send_getblocks(self, locator):
- getblocks_message = msg_getblocks()
- getblocks_message.locator.vHave = locator
- self.send_message(getblocks_message)
class SendHeadersTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- # mine count blocks and return the new tip
def mine_blocks(self, count):
+ """Mine count blocks and return the new tip."""
+
# Clear out last block announcement from each p2p listener
- [ x.clear_last_announcement() for x in self.p2p_connections ]
+ [x.clear_last_announcement() for x in self.nodes[0].p2ps]
self.nodes[0].generate(count)
return int(self.nodes[0].getbestblockhash(), 16)
- # mine a reorg that invalidates length blocks (replacing them with
- # length+1 blocks).
- # Note: we clear the state of our p2p connections after the
- # to-be-reorged-out blocks are mined, so that we don't break later tests.
- # return the list of block hashes newly mined
def mine_reorg(self, length):
- self.nodes[0].generate(length) # make sure all invalidated blocks are node0's
+ """Mine a reorg that invalidates length blocks (replacing them with # length+1 blocks).
+
+ Note: we clear the state of our p2p connections after the
+ to-be-reorged-out blocks are mined, so that we don't break later tests.
+ return the list of block hashes newly mined."""
+
+ self.nodes[0].generate(length) # make sure all invalidated blocks are node0's
sync_blocks(self.nodes, wait=0.1)
- for x in self.p2p_connections:
+ for x in self.nodes[0].p2ps:
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))
+ hash_to_invalidate = self.nodes[1].getblockhash(tip_height - (length - 1))
self.nodes[1].invalidateblock(hash_to_invalidate)
- all_hashes = self.nodes[1].generate(length+1) # Must be longer than the orig chain
+ all_hashes = self.nodes[1].generate(length + 1) # Must be longer than the orig chain
sync_blocks(self.nodes, wait=0.1)
return [int(x, 16) for x in all_hashes]
def run_test(self):
# Setup the p2p connections and start up the network thread.
- inv_node = TestNode()
- test_node = TestNode()
-
- self.p2p_connections = [inv_node, test_node]
-
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], inv_node))
- # Set nServices to 0 for test_node, so no block download will occur outside of
- # direct fetching
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node, services=0))
- inv_node.add_connection(connections[0])
- test_node.add_connection(connections[1])
+ inv_node = self.nodes[0].add_p2p_connection(BaseNode())
+ # Make sure NODE_NETWORK is not set for test_node, so no block download
+ # will occur outside of direct fetching
+ test_node = self.nodes[0].add_p2p_connection(BaseNode(), services=NODE_WITNESS)
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
# Test logic begins here
inv_node.wait_for_verack()
test_node.wait_for_verack()
+ # Ensure verack's have been processed by our peer
+ inv_node.sync_with_ping()
+ test_node.sync_with_ping()
+
+ self.test_null_locators(test_node, inv_node)
+ self.test_nonnull_locators(test_node, inv_node)
+
+ def test_null_locators(self, test_node, inv_node):
+ tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0])
+ tip_hash = int(tip["hash"], 16)
+
+ inv_node.check_last_announcement(inv=[tip_hash], headers=[])
+ test_node.check_last_announcement(inv=[tip_hash], headers=[])
+
+ self.log.info("Verify getheaders with null locator and valid hashstop returns headers.")
+ test_node.clear_last_announcement()
+ test_node.send_get_headers(locator=[], hashstop=tip_hash)
+ test_node.check_last_announcement(headers=[tip_hash])
+
+ self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.")
+ block = create_block(int(tip["hash"], 16), create_coinbase(tip["height"] + 1), tip["mediantime"] + 1)
+ block.solve()
+ test_node.send_header_for_blocks([block])
+ test_node.clear_last_announcement()
+ test_node.send_get_headers(locator=[], hashstop=int(block.hash, 16))
+ test_node.sync_with_ping()
+ assert_equal(test_node.block_announced, False)
+ inv_node.clear_last_announcement()
+ test_node.send_message(msg_block(block))
+ inv_node.check_last_announcement(inv=[int(block.hash, 16)], headers=[])
+
+ def test_nonnull_locators(self, test_node, inv_node):
tip = int(self.nodes[0].getbestblockhash(), 16)
# PART 1
@@ -233,30 +284,30 @@ class SendHeadersTest(BitcoinTestFramework):
for i in range(4):
old_tip = tip
tip = self.mine_blocks(1)
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(inv=[tip]), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(inv=[tip], headers=[])
# Try a few different responses; none should affect next announcement
if i == 0:
# first request the block
- test_node.get_data([tip])
+ test_node.send_get_data([tip])
test_node.wait_for_block(tip)
elif i == 1:
# next try requesting header and block
- test_node.get_headers(locator=[old_tip], hashstop=tip)
- test_node.get_data([tip])
+ test_node.send_get_headers(locator=[old_tip], hashstop=tip)
+ test_node.send_get_data([tip])
test_node.wait_for_block(tip)
- test_node.clear_last_announcement() # since we requested headers...
+ test_node.clear_last_announcement() # since we requested headers...
elif i == 2:
# this time announce own block via headers
height = self.nodes[0].getblockcount()
last_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time']
block_time = last_time + 1
- new_block = create_block(tip, create_coinbase(height+1), block_time)
+ new_block = create_block(tip, create_coinbase(height + 1), block_time)
new_block.solve()
test_node.send_header_for_blocks([new_block])
test_node.wait_for_getdata([new_block.sha256])
test_node.send_message(msg_block(new_block))
- test_node.sync_with_ping() # make sure this block is processed
+ test_node.sync_with_ping() # make sure this block is processed
inv_node.clear_last_announcement()
test_node.clear_last_announcement()
@@ -267,15 +318,15 @@ class SendHeadersTest(BitcoinTestFramework):
# commence and keep working.
test_node.send_message(msg_sendheaders())
prev_tip = int(self.nodes[0].getbestblockhash(), 16)
- test_node.get_headers(locator=[prev_tip], hashstop=0)
+ test_node.send_get_headers(locator=[prev_tip], hashstop=0)
test_node.sync_with_ping()
# Now that we've synced headers, headers announcements should work
tip = self.mine_blocks(1)
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(headers=[tip]), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(headers=[tip])
- height = self.nodes[0].getblockcount()+1
+ height = self.nodes[0].getblockcount() + 1
block_time += 10 # Advance far enough ahead
for i in range(10):
# Mine i blocks, and alternate announcing either via
@@ -284,7 +335,7 @@ class SendHeadersTest(BitcoinTestFramework):
# with block header, even though the blocks are never requested
for j in range(2):
blocks = []
- for b in range(i+1):
+ for b in range(i + 1):
blocks.append(create_block(tip, create_coinbase(height), block_time))
blocks[-1].solve()
tip = blocks[-1].sha256
@@ -298,7 +349,7 @@ class SendHeadersTest(BitcoinTestFramework):
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(x.sha256) for x in blocks ]
+ [inv_node.send_block_inv(x.sha256) for x in blocks]
test_node.wait_for_getdata([x.sha256 for x in blocks])
inv_node.sync_with_ping()
else:
@@ -309,7 +360,7 @@ class SendHeadersTest(BitcoinTestFramework):
# getdata requests (the check is further down)
inv_node.send_header_for_blocks(blocks)
inv_node.sync_with_ping()
- [ test_node.send_message(msg_block(x)) for x in blocks ]
+ [test_node.send_message(msg_block(x)) for x in blocks]
test_node.sync_with_ping()
inv_node.sync_with_ping()
# This block should not be announced to the inv node (since it also
@@ -317,8 +368,8 @@ class SendHeadersTest(BitcoinTestFramework):
assert "inv" not in inv_node.last_message
assert "headers" not in inv_node.last_message
tip = self.mine_blocks(1)
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(headers=[tip]), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(headers=[tip])
height += 1
block_time += 1
@@ -332,16 +383,16 @@ class SendHeadersTest(BitcoinTestFramework):
# First try mining a reorg that can propagate with header announcement
new_block_hashes = self.mine_reorg(length=7)
tip = new_block_hashes[-1]
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(headers=new_block_hashes), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(headers=new_block_hashes)
- block_time += 8
+ block_time += 8
# Mine a too-large reorg, which should be announced with a single inv
new_block_hashes = self.mine_reorg(length=8)
tip = new_block_hashes[-1]
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(inv=[tip]), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(inv=[tip], headers=[])
block_time += 9
@@ -349,42 +400,42 @@ class SendHeadersTest(BitcoinTestFramework):
fork_point = int(fork_point, 16)
# Use getblocks/getdata
- test_node.send_getblocks(locator = [fork_point])
- assert_equal(test_node.check_last_announcement(inv=new_block_hashes), True)
- test_node.get_data(new_block_hashes)
+ test_node.send_getblocks(locator=[fork_point])
+ test_node.check_last_announcement(inv=new_block_hashes, headers=[])
+ test_node.send_get_data(new_block_hashes)
test_node.wait_for_block(new_block_hashes[-1])
for i in range(3):
# Mine another block, still should get only an inv
tip = self.mine_blocks(1)
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(inv=[tip]), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(inv=[tip], headers=[])
if i == 0:
# Just get the data -- shouldn't cause headers announcements to resume
- test_node.get_data([tip])
+ test_node.send_get_data([tip])
test_node.wait_for_block(tip)
elif i == 1:
# Send a getheaders message that shouldn't trigger headers announcements
# to resume (best header sent will be too old)
- test_node.get_headers(locator=[fork_point], hashstop=new_block_hashes[1])
- test_node.get_data([tip])
+ test_node.send_get_headers(locator=[fork_point], hashstop=new_block_hashes[1])
+ test_node.send_get_data([tip])
test_node.wait_for_block(tip)
elif i == 2:
- test_node.get_data([tip])
+ test_node.send_get_data([tip])
test_node.wait_for_block(tip)
# This time, try sending either a getheaders to trigger resumption
- # of headers announcements, or mine a new block and inv it, also
+ # of headers announcements, or mine a new block and inv it, also
# triggering resumption of headers announcements.
if j == 0:
- test_node.get_headers(locator=[tip], hashstop=0)
+ test_node.send_get_headers(locator=[tip], hashstop=0)
test_node.sync_with_ping()
else:
test_node.send_block_inv(tip)
test_node.sync_with_ping()
# New blocks should now be announced with header
tip = self.mine_blocks(1)
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(headers=[tip]), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(headers=[tip])
self.log.info("Part 3: success!")
@@ -404,7 +455,7 @@ class SendHeadersTest(BitcoinTestFramework):
height += 1
inv_node.send_message(msg_block(blocks[-1]))
- inv_node.sync_with_ping() # Make sure blocks are processed
+ inv_node.sync_with_ping() # Make sure blocks are processed
test_node.last_message.pop("getdata", None)
test_node.send_header_for_blocks(blocks)
test_node.sync_with_ping()
@@ -423,9 +474,9 @@ 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=direct_fetch_response_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 ]
+ [test_node.send_message(msg_block(x)) for x in blocks]
test_node.sync_with_ping()
@@ -454,13 +505,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=direct_fetch_response_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=direct_fetch_response_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_message.pop("getdata", None)
@@ -472,7 +523,7 @@ class SendHeadersTest(BitcoinTestFramework):
self.log.info("Part 4: success!")
# Now deliver all those blocks we announced.
- [ test_node.send_message(msg_block(x)) for x in blocks ]
+ [test_node.send_message(msg_block(x)) for x in blocks]
self.log.info("Part 5: Testing handling of unconnecting headers")
# First we test that receipt of an unconnecting header doesn't prevent
@@ -494,7 +545,7 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.wait_for_getheaders()
test_node.send_header_for_blocks(blocks)
test_node.wait_for_getdata([x.sha256 for x in blocks])
- [ test_node.send_message(msg_block(x)) for x in blocks ]
+ [test_node.send_message(msg_block(x)) for x in blocks]
test_node.sync_with_ping()
assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256)
@@ -502,7 +553,7 @@ class SendHeadersTest(BitcoinTestFramework):
# Now we test that if we repeatedly don't send connecting headers, we
# don't go into an infinite loop trying to get them to connect.
MAX_UNCONNECTING_HEADERS = 10
- for j in range(MAX_UNCONNECTING_HEADERS+1):
+ for j in range(MAX_UNCONNECTING_HEADERS + 1):
blocks.append(create_block(tip, create_coinbase(height), block_time))
blocks[-1].solve()
tip = blocks[-1].sha256
@@ -524,11 +575,11 @@ class SendHeadersTest(BitcoinTestFramework):
# Now try to see how many unconnecting headers we can send
# before we get disconnected. Should be 5*MAX_UNCONNECTING_HEADERS
- for i in range(5*MAX_UNCONNECTING_HEADERS - 1):
+ for i in range(5 * MAX_UNCONNECTING_HEADERS - 1):
# Send a header that doesn't connect, check that we get a getheaders.
with mininode_lock:
test_node.last_message.pop("getheaders", None)
- test_node.send_header_for_blocks([blocks[i%len(blocks)]])
+ test_node.send_header_for_blocks([blocks[i % len(blocks)]])
test_node.wait_for_getheaders()
# Eventually this stops working.
diff --git a/test/functional/signrawtransactions.py b/test/functional/signrawtransactions.py
index b47ef93955..9a45d53cb8 100755
--- a/test/functional/signrawtransactions.py
+++ b/test/functional/signrawtransactions.py
@@ -82,7 +82,7 @@ class SignRawTransactionsTest(BitcoinTestFramework):
assert_equal(decodedRawTx["vin"][i]["vout"], inp["vout"])
# Make sure decoderawtransaction throws if there is extra data
- assert_raises(JSONRPCException, self.nodes[0].decoderawtransaction, rawTx + "00")
+ assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, rawTx + "00")
rawTxSigned = self.nodes[0].signrawtransaction(rawTx, scripts, privKeys)
diff --git a/test/functional/smartfees.py b/test/functional/smartfees.py
index 76632fc578..986f4546a8 100755
--- a/test/functional/smartfees.py
+++ b/test/functional/smartfees.py
@@ -151,7 +151,7 @@ class EstimateFeeTest(BitcoinTestFramework):
which we will use to generate our transactions.
"""
self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"],
- ["-blockmaxsize=17000", "-maxorphantx=1000"],
+ ["-blockmaxsize=17000", "-maxorphantx=1000", "-deprecatedrpc=estimatefee"],
["-blockmaxsize=8000", "-maxorphantx=1000"]])
# Use node0 to mine blocks for input splitting
# Node1 mines small blocks but that are bigger than the expected transaction rate.
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index 180dac197e..2e2db5ffb2 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -7,6 +7,8 @@
from .script import hash256, hash160, sha256, CScript, OP_0
from .util import bytes_to_hex_str, hex_str_to_bytes
+from . import segwit_addr
+
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def byte_to_base58(b, version):
@@ -44,6 +46,32 @@ 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 program_to_witness(version, program, main = False):
+ if (type(program) is str):
+ program = hex_str_to_bytes(program)
+ assert 0 <= version <= 16
+ assert 2 <= len(program) <= 40
+ assert version > 0 or len(program) in [20, 32]
+ return segwit_addr.encode("bc" if main else "bcrt", version, program)
+
+def script_to_p2wsh(script, main = False):
+ script = check_script(script)
+ return program_to_witness(0, sha256(script), main)
+
+def key_to_p2wpkh(key, main = False):
+ key = check_key(key)
+ return program_to_witness(0, hash160(key), 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
diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py
index b3671cbdc5..bd3a3b3fab 100644
--- a/test/functional/test_framework/authproxy.py
+++ b/test/functional/test_framework/authproxy.py
@@ -33,24 +33,17 @@ ServiceProxy class:
- uses standard Python json lib
"""
-try:
- import http.client as httplib
-except ImportError:
- import httplib
import base64
import decimal
+import http.client
import json
import logging
import socket
import time
-try:
- import urllib.parse as urlparse
-except ImportError:
- import urlparse
-
-USER_AGENT = "AuthServiceProxy/0.1"
+import urllib.parse
HTTP_TIMEOUT = 30
+USER_AGENT = "AuthServiceProxy/0.1"
log = logging.getLogger("BitcoinRPC")
@@ -60,7 +53,7 @@ class JSONRPCException(Exception):
errmsg = '%(message)s (%(code)i)' % rpc_error
except (KeyError, TypeError):
errmsg = ''
- Exception.__init__(self, errmsg)
+ super().__init__(errmsg)
self.error = rpc_error
@@ -69,28 +62,18 @@ def EncodeDecimal(o):
return str(o)
raise TypeError(repr(o) + " is not JSON serializable")
-class AuthServiceProxy(object):
+class AuthServiceProxy():
__id_count = 0
# ensure_ascii: escape unicode as \uXXXX, passed to json.dumps
def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connection=None, ensure_ascii=True):
self.__service_url = service_url
self._service_name = service_name
- self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests
- self.__url = urlparse.urlparse(service_url)
- if self.__url.port is None:
- port = 80
- else:
- port = self.__url.port
- (user, passwd) = (self.__url.username, self.__url.password)
- try:
- user = user.encode('utf8')
- except AttributeError:
- pass
- try:
- passwd = passwd.encode('utf8')
- except AttributeError:
- pass
+ self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests
+ self.__url = urllib.parse.urlparse(service_url)
+ port = 80 if self.__url.port is None else self.__url.port
+ user = None if self.__url.username is None else self.__url.username.encode('utf8')
+ passwd = None if self.__url.password is None else self.__url.password.encode('utf8')
authpair = user + b':' + passwd
self.__auth_header = b'Basic ' + base64.b64encode(authpair)
@@ -98,11 +81,9 @@ class AuthServiceProxy(object):
# Callables re-use the connection of the original proxy
self.__conn = connection
elif self.__url.scheme == 'https':
- self.__conn = httplib.HTTPSConnection(self.__url.hostname, port,
- timeout=timeout)
+ self.__conn = http.client.HTTPSConnection(self.__url.hostname, port, timeout=timeout)
else:
- self.__conn = httplib.HTTPConnection(self.__url.hostname, port,
- timeout=timeout)
+ self.__conn = http.client.HTTPConnection(self.__url.hostname, port, timeout=timeout)
def __getattr__(self, name):
if name.startswith('__') and name.endswith('__'):
@@ -124,31 +105,34 @@ class AuthServiceProxy(object):
try:
self.__conn.request(method, path, postdata, headers)
return self._get_response()
- except httplib.BadStatusLine as e:
- if e.line == "''": # if connection was closed, try again
+ except http.client.BadStatusLine as e:
+ if e.line == "''": # if connection was closed, try again
self.__conn.close()
self.__conn.request(method, path, postdata, headers)
return self._get_response()
else:
raise
- except (BrokenPipeError,ConnectionResetError):
+ 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()
- def __call__(self, *args, **argsn):
+ def get_request(self, *args, **argsn):
AuthServiceProxy.__id_count += 1
- log.debug("-%s-> %s %s"%(AuthServiceProxy.__id_count, self._service_name,
- json.dumps(args, default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
+ log.debug("-%s-> %s %s" % (AuthServiceProxy.__id_count, self._service_name,
+ json.dumps(args, default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
if args and argsn:
raise ValueError('Cannot handle both named and positional arguments')
- postdata = json.dumps({'version': '1.1',
- 'method': self._service_name,
- 'params': args or argsn,
- 'id': AuthServiceProxy.__id_count}, default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
+ return {'version': '1.1',
+ 'method': self._service_name,
+ 'params': args or argsn,
+ 'id': AuthServiceProxy.__id_count}
+
+ def __call__(self, *args, **argsn):
+ postdata = json.dumps(self.get_request(*args, **argsn), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
response = self._request('POST', self.__url.path, postdata.encode('utf-8'))
if response['error'] is not None:
raise JSONRPCException(response['error'])
@@ -158,9 +142,9 @@ class AuthServiceProxy(object):
else:
return response['result']
- def _batch(self, rpc_call_list):
+ def batch(self, rpc_call_list):
postdata = json.dumps(list(rpc_call_list), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
- log.debug("--> "+postdata)
+ log.debug("--> " + postdata)
return self._request('POST', self.__url.path, postdata.encode('utf-8'))
def _get_response(self):
@@ -187,9 +171,9 @@ class AuthServiceProxy(object):
response = json.loads(responsedata, parse_float=decimal.Decimal)
elapsed = time.time() - req_start_time
if "error" in response and response["error"] is None:
- log.debug("<-%s- [%.6f] %s"%(response["id"], elapsed, json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
+ log.debug("<-%s- [%.6f] %s" % (response["id"], elapsed, json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
else:
- log.debug("<-- [%.6f] %s"%(elapsed,responsedata))
+ log.debug("<-- [%.6f] %s" % (elapsed, responsedata))
return response
def __truediv__(self, relative_uri):
diff --git a/test/functional/test_framework/blockstore.py b/test/functional/test_framework/blockstore.py
index 4b2170a03f..051c57a6c7 100644
--- a/test/functional/test_framework/blockstore.py
+++ b/test/functional/test_framework/blockstore.py
@@ -10,7 +10,7 @@ import dbm.dumb as dbmd
logger = logging.getLogger("TestFramework.blockstore")
-class BlockStore(object):
+class BlockStore():
"""BlockStore helper class.
BlockStore keeps a map of blocks and implements helper functions for
@@ -100,7 +100,7 @@ class BlockStore(object):
def get_blocks(self, inv):
responses = []
for i in inv:
- if (i.type == 2): # MSG_BLOCK
+ if (i.type == 2 or i.type == (2 | (1 << 30))): # MSG_BLOCK or MSG_WITNESS_BLOCK
data = self.get(i.hash)
if data is not None:
# Use msg_generic to avoid re-serialization
@@ -127,7 +127,7 @@ class BlockStore(object):
locator.vHave = r
return locator
-class TxStore(object):
+class TxStore():
def __init__(self, datadir):
self.txDB = dbmd.open(datadir + "/transactions", 'c')
@@ -153,7 +153,7 @@ class TxStore(object):
def get_transactions(self, inv):
responses = []
for i in inv:
- if (i.type == 1): # MSG_TX
+ if (i.type == 1 or i.type == (1 | (1 << 30))): # MSG_TX or MSG_WITNESS_TX
tx = self.get(i.hash)
if tx is not None:
responses.append(msg_generic(b"tx", tx))
diff --git a/test/functional/test_framework/comptool.py b/test/functional/test_framework/comptool.py
index bfbc0c3b03..f0f5c847ca 100755
--- a/test/functional/test_framework/comptool.py
+++ b/test/functional/test_framework/comptool.py
@@ -27,7 +27,7 @@ logger=logging.getLogger("TestFramework.comptool")
global mininode_lock
-class RejectResult(object):
+class RejectResult():
"""Outcome that expects rejection of a transaction or block."""
def __init__(self, code, reason=b''):
self.code = code
@@ -39,11 +39,10 @@ class RejectResult(object):
def __repr__(self):
return '%i:%s' % (self.code,self.reason or '*')
-class TestNode(NodeConnCB):
+class TestNode(P2PInterface):
def __init__(self, block_store, tx_store):
super().__init__()
- self.conn = None
self.bestblockhash = None
self.block_store = block_store
self.block_request_map = {}
@@ -58,43 +57,40 @@ class TestNode(NodeConnCB):
self.lastInv = []
self.closed = False
- def on_close(self, conn):
+ def on_close(self):
self.closed = True
- def add_connection(self, conn):
- self.conn = conn
-
- def on_headers(self, conn, message):
+ def on_headers(self, message):
if len(message.headers) > 0:
best_header = message.headers[-1]
best_header.calc_sha256()
self.bestblockhash = best_header.sha256
- def on_getheaders(self, conn, message):
+ def on_getheaders(self, message):
response = self.block_store.headers_for(message.locator, message.hashstop)
if response is not None:
- conn.send_message(response)
+ self.send_message(response)
- def on_getdata(self, conn, message):
- [conn.send_message(r) for r in self.block_store.get_blocks(message.inv)]
- [conn.send_message(r) for r in self.tx_store.get_transactions(message.inv)]
+ def on_getdata(self, message):
+ [self.send_message(r) for r in self.block_store.get_blocks(message.inv)]
+ [self.send_message(r) for r in self.tx_store.get_transactions(message.inv)]
for i in message.inv:
- if i.type == 1:
+ if i.type == 1 or i.type == 1 | (1 << 30): # MSG_TX or MSG_WITNESS_TX
self.tx_request_map[i.hash] = True
- elif i.type == 2:
+ elif i.type == 2 or i.type == 2 | (1 << 30): # MSG_BLOCK or MSG_WITNESS_BLOCK
self.block_request_map[i.hash] = True
- def on_inv(self, conn, message):
+ def on_inv(self, message):
self.lastInv = [x.hash for x in message.inv]
- def on_pong(self, conn, message):
+ def on_pong(self, message):
try:
del self.pingMap[message.nonce]
except KeyError:
raise AssertionError("Got pong for unknown ping [%s]" % repr(message))
- def on_reject(self, conn, message):
+ def on_reject(self, message):
if message.message == b'tx':
self.tx_reject_map[message.data] = RejectResult(message.code, message.reason)
if message.message == b'block':
@@ -102,30 +98,30 @@ class TestNode(NodeConnCB):
def send_inv(self, obj):
mtype = 2 if isinstance(obj, CBlock) else 1
- self.conn.send_message(msg_inv([CInv(mtype, obj.sha256)]))
+ self.send_message(msg_inv([CInv(mtype, obj.sha256)]))
def send_getheaders(self):
# We ask for headers from their last tip.
m = msg_getheaders()
m.locator = self.block_store.get_locator(self.bestblockhash)
- self.conn.send_message(m)
+ self.send_message(m)
def send_header(self, header):
m = msg_headers()
m.headers.append(header)
- self.conn.send_message(m)
+ self.send_message(m)
# This assumes BIP31
def send_ping(self, nonce):
self.pingMap[nonce] = True
- self.conn.send_message(msg_ping(nonce))
+ self.send_message(msg_ping(nonce))
def received_ping_response(self, nonce):
return nonce not in self.pingMap
def send_mempool(self):
self.lastInv = []
- self.conn.send_message(msg_mempool())
+ self.send_message(msg_mempool())
# TestInstance:
#
@@ -156,18 +152,17 @@ class TestNode(NodeConnCB):
# across all connections. (If outcome of final tx is specified as true
# or false, then only the last tx is tested against outcome.)
-class TestInstance(object):
+class TestInstance():
def __init__(self, objects=None, sync_every_block=True, sync_every_tx=False):
self.blocks_and_transactions = objects if objects else []
self.sync_every_block = sync_every_block
self.sync_every_tx = sync_every_tx
-class TestManager(object):
+class TestManager():
def __init__(self, testgen, datadir):
self.test_generator = testgen
- self.connections = []
- self.test_nodes = []
+ self.p2p_connections= []
self.block_store = BlockStore(datadir)
self.tx_store = TxStore(datadir)
self.ping_counter = 1
@@ -175,28 +170,24 @@ class TestManager(object):
def add_all_connections(self, nodes):
for i in range(len(nodes)):
# Create a p2p connection to each node
- test_node = TestNode(self.block_store, self.tx_store)
- self.test_nodes.append(test_node)
- self.connections.append(NodeConn('127.0.0.1', p2p_port(i), nodes[i], test_node))
- # Make sure the TestNode (callback class) has a reference to its
- # associated NodeConn
- test_node.add_connection(self.connections[-1])
+ node = TestNode(self.block_store, self.tx_store)
+ node.peer_connect('127.0.0.1', p2p_port(i))
+ self.p2p_connections.append(node)
def clear_all_connections(self):
- self.connections = []
- self.test_nodes = []
+ self.p2p_connections = []
def wait_for_disconnections(self):
def disconnected():
- return all(node.closed for node in self.test_nodes)
+ return all(node.closed for node in self.p2p_connections)
wait_until(disconnected, timeout=10, lock=mininode_lock)
def wait_for_verack(self):
- return all(node.wait_for_verack() for node in self.test_nodes)
+ return all(node.wait_for_verack() for node in self.p2p_connections)
def wait_for_pings(self, counter):
def received_pongs():
- return all(node.received_ping_response(counter) for node in self.test_nodes)
+ return all(node.received_ping_response(counter) for node in self.p2p_connections)
wait_until(received_pongs, lock=mininode_lock)
# sync_blocks: Wait for all connections to request the blockhash given
@@ -206,17 +197,17 @@ class TestManager(object):
def blocks_requested():
return all(
blockhash in node.block_request_map and node.block_request_map[blockhash]
- for node in self.test_nodes
+ for node in self.p2p_connections
)
# --> error if not requested
wait_until(blocks_requested, attempts=20*num_blocks, lock=mininode_lock)
# Send getheaders message
- [ c.cb.send_getheaders() for c in self.connections ]
+ [ c.send_getheaders() for c in self.p2p_connections ]
# Send ping and wait for response -- synchronization hack
- [ c.cb.send_ping(self.ping_counter) for c in self.connections ]
+ [ c.send_ping(self.ping_counter) for c in self.p2p_connections ]
self.wait_for_pings(self.ping_counter)
self.ping_counter += 1
@@ -226,42 +217,42 @@ class TestManager(object):
def transaction_requested():
return all(
txhash in node.tx_request_map and node.tx_request_map[txhash]
- for node in self.test_nodes
+ for node in self.p2p_connections
)
# --> error if not requested
wait_until(transaction_requested, attempts=20*num_events, lock=mininode_lock)
# Get the mempool
- [ c.cb.send_mempool() for c in self.connections ]
+ [ c.send_mempool() for c in self.p2p_connections ]
# Send ping and wait for response -- synchronization hack
- [ c.cb.send_ping(self.ping_counter) for c in self.connections ]
+ [ c.send_ping(self.ping_counter) for c in self.p2p_connections ]
self.wait_for_pings(self.ping_counter)
self.ping_counter += 1
# Sort inv responses from each node
with mininode_lock:
- [ c.cb.lastInv.sort() for c in self.connections ]
+ [ c.lastInv.sort() for c in self.p2p_connections ]
# Verify that the tip of each connection all agree with each other, and
# with the expected outcome (if given)
def check_results(self, blockhash, outcome):
with mininode_lock:
- for c in self.connections:
+ for c in self.p2p_connections:
if outcome is None:
- if c.cb.bestblockhash != self.connections[0].cb.bestblockhash:
+ if c.bestblockhash != self.p2p_connections[0].bestblockhash:
return False
elif isinstance(outcome, RejectResult): # Check that block was rejected w/ code
- if c.cb.bestblockhash == blockhash:
+ if c.bestblockhash == blockhash:
return False
- if blockhash not in c.cb.block_reject_map:
+ if blockhash not in c.block_reject_map:
logger.error('Block not in reject map: %064x' % (blockhash))
return False
- if not outcome.match(c.cb.block_reject_map[blockhash]):
- logger.error('Block rejected with %s instead of expected %s: %064x' % (c.cb.block_reject_map[blockhash], outcome, blockhash))
+ if not outcome.match(c.block_reject_map[blockhash]):
+ logger.error('Block rejected with %s instead of expected %s: %064x' % (c.block_reject_map[blockhash], outcome, blockhash))
return False
- elif ((c.cb.bestblockhash == blockhash) != outcome):
+ elif ((c.bestblockhash == blockhash) != outcome):
return False
return True
@@ -273,21 +264,21 @@ class TestManager(object):
# a particular tx's existence in the mempool is the same across all nodes.
def check_mempool(self, txhash, outcome):
with mininode_lock:
- for c in self.connections:
+ for c in self.p2p_connections:
if outcome is None:
# Make sure the mempools agree with each other
- if c.cb.lastInv != self.connections[0].cb.lastInv:
+ if c.lastInv != self.p2p_connections[0].lastInv:
return False
elif isinstance(outcome, RejectResult): # Check that tx was rejected w/ code
- if txhash in c.cb.lastInv:
+ if txhash in c.lastInv:
return False
- if txhash not in c.cb.tx_reject_map:
+ if txhash not in c.tx_reject_map:
logger.error('Tx not in reject map: %064x' % (txhash))
return False
- if not outcome.match(c.cb.tx_reject_map[txhash]):
- logger.error('Tx rejected with %s instead of expected %s: %064x' % (c.cb.tx_reject_map[txhash], outcome, txhash))
+ if not outcome.match(c.tx_reject_map[txhash]):
+ logger.error('Tx rejected with %s instead of expected %s: %064x' % (c.tx_reject_map[txhash], outcome, txhash))
return False
- elif ((txhash in c.cb.lastInv) != outcome):
+ elif ((txhash in c.lastInv) != outcome):
return False
return True
@@ -295,8 +286,11 @@ class TestManager(object):
# Wait until verack is received
self.wait_for_verack()
- test_number = 1
- for test_instance in self.test_generator.get_tests():
+ test_number = 0
+ tests = self.test_generator.get_tests()
+ for test_instance in tests:
+ test_number += 1
+ logger.info("Running test %d: %s line %s" % (test_number, tests.gi_code.co_filename, tests.gi_frame.f_lineno))
# We use these variables to keep track of the last block
# and last transaction in the tests, which are used
# if we're not syncing on every block or every tx.
@@ -329,25 +323,25 @@ class TestManager(object):
first_block_with_hash = False
with mininode_lock:
self.block_store.add_block(block)
- for c in self.connections:
- if first_block_with_hash and block.sha256 in c.cb.block_request_map and c.cb.block_request_map[block.sha256] == True:
+ for c in self.p2p_connections:
+ if first_block_with_hash and block.sha256 in c.block_request_map and c.block_request_map[block.sha256] == True:
# There was a previous request for this block hash
# Most likely, we delivered a header for this block
# but never had the block to respond to the getdata
c.send_message(msg_block(block))
else:
- c.cb.block_request_map[block.sha256] = False
+ c.block_request_map[block.sha256] = False
# Either send inv's to each node and sync, or add
# to invqueue for later inv'ing.
if (test_instance.sync_every_block):
# 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 ]
+ [ c.send_inv(block) for c in self.p2p_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 ]
+ [ c.send_message(msg_block(block)) for c in self.p2p_connections ]
+ [ c.send_ping(self.ping_counter) for c in self.p2p_connections ]
self.wait_for_pings(self.ping_counter)
self.ping_counter += 1
if (not self.check_results(tip, outcome)):
@@ -357,7 +351,7 @@ 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 ]
+ [ c.send_header(block_header) for c in self.p2p_connections ]
else: # Tx test runner
assert(isinstance(b_or_t, CTransaction))
@@ -366,11 +360,11 @@ class TestManager(object):
# Add to shared tx store and clear map entry
with mininode_lock:
self.tx_store.add_transaction(tx)
- for c in self.connections:
- c.cb.tx_request_map[tx.sha256] = False
+ for c in self.p2p_connections:
+ c.tx_request_map[tx.sha256] = False
# Again, either inv to all nodes or save for later
if (test_instance.sync_every_tx):
- [ c.cb.send_inv(tx) for c in self.connections ]
+ [ c.send_inv(tx) for c in self.p2p_connections ]
self.sync_transaction(tx.sha256, 1)
if (not self.check_mempool(tx.sha256, outcome)):
raise AssertionError("Test failed at test %d" % test_number)
@@ -378,29 +372,26 @@ class TestManager(object):
invqueue.append(CInv(1, tx.sha256))
# Ensure we're not overflowing the inv queue
if len(invqueue) == MAX_INV_SZ:
- [ c.send_message(msg_inv(invqueue)) for c in self.connections ]
+ [ c.send_message(msg_inv(invqueue)) for c in self.p2p_connections ]
invqueue = []
# Do final sync if we weren't syncing on every block or every tx.
if (not test_instance.sync_every_block and block is not None):
if len(invqueue) > 0:
- [ c.send_message(msg_inv(invqueue)) for c in self.connections ]
+ [ c.send_message(msg_inv(invqueue)) for c in self.p2p_connections ]
invqueue = []
self.sync_blocks(block.sha256, len(test_instance.blocks_and_transactions))
if (not self.check_results(tip, block_outcome)):
raise AssertionError("Block test failed at test %d" % test_number)
if (not test_instance.sync_every_tx and tx is not None):
if len(invqueue) > 0:
- [ c.send_message(msg_inv(invqueue)) for c in self.connections ]
+ [ c.send_message(msg_inv(invqueue)) for c in self.p2p_connections ]
invqueue = []
self.sync_transaction(tx.sha256, len(test_instance.blocks_and_transactions))
if (not self.check_mempool(tx.sha256, tx_outcome)):
raise AssertionError("Mempool test failed at test %d" % test_number)
- logger.info("Test %d: PASS" % test_number)
- test_number += 1
-
- [ c.disconnect_node() for c in self.connections ]
+ [ c.disconnect_node() for c in self.p2p_connections ]
self.wait_for_disconnections()
self.block_store.close()
self.tx_store.close()
diff --git a/test/functional/test_framework/coverage.py b/test/functional/test_framework/coverage.py
index 227b1a17af..ddc3c515b2 100644
--- a/test/functional/test_framework/coverage.py
+++ b/test/functional/test_framework/coverage.py
@@ -14,7 +14,7 @@ import os
REFERENCE_FILENAME = 'rpc_interface.txt'
-class AuthServiceProxyWrapper(object):
+class AuthServiceProxyWrapper():
"""
An object that wraps AuthServiceProxy to record specific RPC calls.
@@ -31,10 +31,11 @@ class AuthServiceProxyWrapper(object):
self.auth_service_proxy_instance = auth_service_proxy_instance
self.coverage_logfile = coverage_logfile
- def __getattr__(self, *args, **kwargs):
- return_val = self.auth_service_proxy_instance.__getattr__(
- *args, **kwargs)
-
+ def __getattr__(self, name):
+ return_val = getattr(self.auth_service_proxy_instance, name)
+ if not isinstance(return_val, type(self.auth_service_proxy_instance)):
+ # If proxy getattr returned an unwrapped value, do the same here.
+ return return_val
return AuthServiceProxyWrapper(return_val, self.coverage_logfile)
def __call__(self, *args, **kwargs):
@@ -44,20 +45,23 @@ class AuthServiceProxyWrapper(object):
"""
return_val = self.auth_service_proxy_instance.__call__(*args, **kwargs)
+ self._log_call()
+ return return_val
+
+ def _log_call(self):
rpc_method = self.auth_service_proxy_instance._service_name
if self.coverage_logfile:
with open(self.coverage_logfile, 'a+', encoding='utf8') as f:
f.write("%s\n" % rpc_method)
- return return_val
-
- @property
- def url(self):
- return self.auth_service_proxy_instance.url
-
def __truediv__(self, relative_uri):
- return AuthServiceProxyWrapper(self.auth_service_proxy_instance / relative_uri)
+ return AuthServiceProxyWrapper(self.auth_service_proxy_instance / relative_uri,
+ self.coverage_logfile)
+
+ def get_request(self, *args, **kwargs):
+ self._log_call()
+ return self.auth_service_proxy_instance.get_request(*args, **kwargs)
def get_filename(dirname, n_node):
"""
diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py
index 85a6158a2f..aa91fb5b0d 100644
--- a/test/functional/test_framework/key.py
+++ b/test/functional/test_framework/key.py
@@ -84,7 +84,7 @@ def _check_result(val, func, args):
ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
ssl.EC_KEY_new_by_curve_name.errcheck = _check_result
-class CECKey(object):
+class CECKey():
"""Wrapper around OpenSSL's EC_KEY"""
POINT_CONVERSION_COMPRESSED = 2
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
new file mode 100644
index 0000000000..2ab1bdac0f
--- /dev/null
+++ b/test/functional/test_framework/messages.py
@@ -0,0 +1,1304 @@
+#!/usr/bin/env python3
+# Copyright (c) 2010 ArtForz -- public domain half-a-node
+# Copyright (c) 2012 Jeff Garzik
+# Copyright (c) 2010-2017 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 test framework primitive and message strcutures
+
+CBlock, CTransaction, CBlockHeader, CTxIn, CTxOut, etc....:
+ data structures that should map to corresponding structures in
+ bitcoin/primitives
+
+msg_block, msg_tx, msg_headers, etc.:
+ data structures that represent network messages
+
+ser_*, deser_*: functions that handle serialization/deserialization."""
+from codecs import encode
+import copy
+import hashlib
+from io import BytesIO
+import random
+import socket
+import struct
+import time
+
+from test_framework.siphash import siphash256
+from test_framework.util import hex_str_to_bytes, bytes_to_hex_str
+
+MIN_VERSION_SUPPORTED = 60001
+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_BASE_SIZE = 1000000
+
+COIN = 100000000 # 1 btc in satoshis
+
+NODE_NETWORK = (1 << 0)
+# NODE_GETUTXO = (1 << 1)
+# NODE_BLOOM = (1 << 2)
+NODE_WITNESS = (1 << 3)
+NODE_UNSUPPORTED_SERVICE_BIT_5 = (1 << 5)
+NODE_UNSUPPORTED_SERVICE_BIT_7 = (1 << 7)
+
+# Serialization/deserialization tools
+def sha256(s):
+ return hashlib.new('sha256', s).digest()
+
+def ripemd160(s):
+ return hashlib.new('ripemd160', s).digest()
+
+def hash256(s):
+ return sha256(sha256(s))
+
+def ser_compact_size(l):
+ r = b""
+ if l < 253:
+ r = struct.pack("B", l)
+ elif l < 0x10000:
+ r = struct.pack("<BH", 253, l)
+ elif l < 0x100000000:
+ r = struct.pack("<BI", 254, l)
+ else:
+ r = struct.pack("<BQ", 255, l)
+ return r
+
+def deser_compact_size(f):
+ nit = struct.unpack("<B", f.read(1))[0]
+ if nit == 253:
+ nit = struct.unpack("<H", f.read(2))[0]
+ elif nit == 254:
+ nit = struct.unpack("<I", f.read(4))[0]
+ elif nit == 255:
+ nit = struct.unpack("<Q", f.read(8))[0]
+ return nit
+
+def deser_string(f):
+ nit = deser_compact_size(f)
+ return f.read(nit)
+
+def ser_string(s):
+ return ser_compact_size(len(s)) + s
+
+def deser_uint256(f):
+ r = 0
+ for i in range(8):
+ t = struct.unpack("<I", f.read(4))[0]
+ r += t << (i * 32)
+ return r
+
+
+def ser_uint256(u):
+ rs = b""
+ for i in range(8):
+ rs += struct.pack("<I", u & 0xFFFFFFFF)
+ u >>= 32
+ return rs
+
+
+def uint256_from_str(s):
+ r = 0
+ t = struct.unpack("<IIIIIIII", s[:32])
+ for i in range(8):
+ r += t[i] << (i * 32)
+ return r
+
+
+def uint256_from_compact(c):
+ nbytes = (c >> 24) & 0xFF
+ v = (c & 0xFFFFFF) << (8 * (nbytes - 3))
+ return v
+
+
+def deser_vector(f, c):
+ nit = deser_compact_size(f)
+ r = []
+ for i in range(nit):
+ t = c()
+ t.deserialize(f)
+ r.append(t)
+ return r
+
+
+# ser_function_name: Allow for an alternate serialization function on the
+# entries in the vector (we use this for serializing the vector of transactions
+# for a witness block).
+def ser_vector(l, ser_function_name=None):
+ r = ser_compact_size(len(l))
+ for i in l:
+ if ser_function_name:
+ r += getattr(i, ser_function_name)()
+ else:
+ r += i.serialize()
+ return r
+
+
+def deser_uint256_vector(f):
+ nit = deser_compact_size(f)
+ r = []
+ for i in range(nit):
+ t = deser_uint256(f)
+ r.append(t)
+ return r
+
+
+def ser_uint256_vector(l):
+ r = ser_compact_size(len(l))
+ for i in l:
+ r += ser_uint256(i)
+ return r
+
+
+def deser_string_vector(f):
+ nit = deser_compact_size(f)
+ r = []
+ for i in range(nit):
+ t = deser_string(f)
+ r.append(t)
+ return r
+
+
+def ser_string_vector(l):
+ r = ser_compact_size(len(l))
+ for sv in l:
+ r += ser_string(sv)
+ return r
+
+
+# Deserialize from a hex string representation (eg from RPC)
+def FromHex(obj, hex_string):
+ obj.deserialize(BytesIO(hex_str_to_bytes(hex_string)))
+ return obj
+
+# Convert a binary-serializable object to hex (eg for submission via RPC)
+def ToHex(obj):
+ return bytes_to_hex_str(obj.serialize())
+
+# Objects that map to bitcoind objects, which can be serialized/deserialized
+
+class CAddress():
+ def __init__(self):
+ self.nServices = 1
+ self.pchReserved = b"\x00" * 10 + b"\xff" * 2
+ self.ip = "0.0.0.0"
+ self.port = 0
+
+ def deserialize(self, f):
+ self.nServices = struct.unpack("<Q", f.read(8))[0]
+ self.pchReserved = f.read(12)
+ self.ip = socket.inet_ntoa(f.read(4))
+ self.port = struct.unpack(">H", f.read(2))[0]
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<Q", self.nServices)
+ r += self.pchReserved
+ r += socket.inet_aton(self.ip)
+ r += struct.pack(">H", self.port)
+ return r
+
+ def __repr__(self):
+ return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices,
+ self.ip, self.port)
+
+MSG_WITNESS_FLAG = 1<<30
+
+class CInv():
+ typemap = {
+ 0: "Error",
+ 1: "TX",
+ 2: "Block",
+ 1|MSG_WITNESS_FLAG: "WitnessTx",
+ 2|MSG_WITNESS_FLAG : "WitnessBlock",
+ 4: "CompactBlock"
+ }
+
+ def __init__(self, t=0, h=0):
+ self.type = t
+ self.hash = h
+
+ def deserialize(self, f):
+ self.type = struct.unpack("<i", f.read(4))[0]
+ self.hash = deser_uint256(f)
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<i", self.type)
+ r += ser_uint256(self.hash)
+ return r
+
+ def __repr__(self):
+ return "CInv(type=%s hash=%064x)" \
+ % (self.typemap[self.type], self.hash)
+
+
+class CBlockLocator():
+ def __init__(self):
+ self.nVersion = MY_VERSION
+ self.vHave = []
+
+ def deserialize(self, f):
+ self.nVersion = struct.unpack("<i", f.read(4))[0]
+ self.vHave = deser_uint256_vector(f)
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<i", self.nVersion)
+ r += ser_uint256_vector(self.vHave)
+ return r
+
+ def __repr__(self):
+ return "CBlockLocator(nVersion=%i vHave=%s)" \
+ % (self.nVersion, repr(self.vHave))
+
+
+class COutPoint():
+ def __init__(self, hash=0, n=0):
+ self.hash = hash
+ self.n = n
+
+ def deserialize(self, f):
+ self.hash = deser_uint256(f)
+ self.n = struct.unpack("<I", f.read(4))[0]
+
+ def serialize(self):
+ r = b""
+ r += ser_uint256(self.hash)
+ r += struct.pack("<I", self.n)
+ return r
+
+ def __repr__(self):
+ return "COutPoint(hash=%064x n=%i)" % (self.hash, self.n)
+
+
+class CTxIn():
+ def __init__(self, outpoint=None, scriptSig=b"", nSequence=0):
+ if outpoint is None:
+ self.prevout = COutPoint()
+ else:
+ self.prevout = outpoint
+ self.scriptSig = scriptSig
+ self.nSequence = nSequence
+
+ def deserialize(self, f):
+ self.prevout = COutPoint()
+ self.prevout.deserialize(f)
+ self.scriptSig = deser_string(f)
+ self.nSequence = struct.unpack("<I", f.read(4))[0]
+
+ def serialize(self):
+ r = b""
+ r += self.prevout.serialize()
+ r += ser_string(self.scriptSig)
+ r += struct.pack("<I", self.nSequence)
+ return r
+
+ def __repr__(self):
+ return "CTxIn(prevout=%s scriptSig=%s nSequence=%i)" \
+ % (repr(self.prevout), bytes_to_hex_str(self.scriptSig),
+ self.nSequence)
+
+
+class CTxOut():
+ def __init__(self, nValue=0, scriptPubKey=b""):
+ self.nValue = nValue
+ self.scriptPubKey = scriptPubKey
+
+ def deserialize(self, f):
+ self.nValue = struct.unpack("<q", f.read(8))[0]
+ self.scriptPubKey = deser_string(f)
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<q", self.nValue)
+ r += ser_string(self.scriptPubKey)
+ return r
+
+ def __repr__(self):
+ return "CTxOut(nValue=%i.%08i scriptPubKey=%s)" \
+ % (self.nValue // COIN, self.nValue % COIN,
+ bytes_to_hex_str(self.scriptPubKey))
+
+
+class CScriptWitness():
+ def __init__(self):
+ # stack is a vector of strings
+ self.stack = []
+
+ def __repr__(self):
+ return "CScriptWitness(%s)" % \
+ (",".join([bytes_to_hex_str(x) for x in self.stack]))
+
+ def is_null(self):
+ if self.stack:
+ return False
+ return True
+
+
+class CTxInWitness():
+ def __init__(self):
+ self.scriptWitness = CScriptWitness()
+
+ def deserialize(self, f):
+ self.scriptWitness.stack = deser_string_vector(f)
+
+ def serialize(self):
+ return ser_string_vector(self.scriptWitness.stack)
+
+ def __repr__(self):
+ return repr(self.scriptWitness)
+
+ def is_null(self):
+ return self.scriptWitness.is_null()
+
+
+class CTxWitness():
+ def __init__(self):
+ self.vtxinwit = []
+
+ def deserialize(self, f):
+ for i in range(len(self.vtxinwit)):
+ self.vtxinwit[i].deserialize(f)
+
+ def serialize(self):
+ r = b""
+ # This is different than the usual vector serialization --
+ # we omit the length of the vector, which is required to be
+ # the same length as the transaction's vin vector.
+ for x in self.vtxinwit:
+ r += x.serialize()
+ return r
+
+ def __repr__(self):
+ return "CTxWitness(%s)" % \
+ (';'.join([repr(x) for x in self.vtxinwit]))
+
+ def is_null(self):
+ for x in self.vtxinwit:
+ if not x.is_null():
+ return False
+ return True
+
+
+class CTransaction():
+ def __init__(self, tx=None):
+ if tx is None:
+ self.nVersion = 1
+ self.vin = []
+ self.vout = []
+ self.wit = CTxWitness()
+ self.nLockTime = 0
+ self.sha256 = None
+ self.hash = None
+ else:
+ self.nVersion = tx.nVersion
+ self.vin = copy.deepcopy(tx.vin)
+ self.vout = copy.deepcopy(tx.vout)
+ self.nLockTime = tx.nLockTime
+ self.sha256 = tx.sha256
+ self.hash = tx.hash
+ self.wit = copy.deepcopy(tx.wit)
+
+ def deserialize(self, f):
+ self.nVersion = struct.unpack("<i", f.read(4))[0]
+ self.vin = deser_vector(f, CTxIn)
+ flags = 0
+ if len(self.vin) == 0:
+ flags = struct.unpack("<B", f.read(1))[0]
+ # Not sure why flags can't be zero, but this
+ # matches the implementation in bitcoind
+ if (flags != 0):
+ self.vin = deser_vector(f, CTxIn)
+ self.vout = deser_vector(f, CTxOut)
+ else:
+ self.vout = deser_vector(f, CTxOut)
+ if flags != 0:
+ 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
+ self.hash = None
+
+ def serialize_without_witness(self):
+ r = b""
+ r += struct.pack("<i", self.nVersion)
+ r += ser_vector(self.vin)
+ r += ser_vector(self.vout)
+ r += struct.pack("<I", self.nLockTime)
+ return r
+
+ # Only serialize with witness when explicitly called for
+ def serialize_with_witness(self):
+ flags = 0
+ if not self.wit.is_null():
+ flags |= 1
+ r = b""
+ r += struct.pack("<i", self.nVersion)
+ if flags:
+ dummy = []
+ r += ser_vector(dummy)
+ r += struct.pack("<B", flags)
+ r += ser_vector(self.vin)
+ r += ser_vector(self.vout)
+ if flags & 1:
+ if (len(self.wit.vtxinwit) != len(self.vin)):
+ # vtxinwit must have the same length as vin
+ self.wit.vtxinwit = self.wit.vtxinwit[:len(self.vin)]
+ for i in range(len(self.wit.vtxinwit), len(self.vin)):
+ self.wit.vtxinwit.append(CTxInWitness())
+ r += self.wit.serialize()
+ r += struct.pack("<I", self.nLockTime)
+ return r
+
+ # Regular serialization is without witness -- must explicitly
+ # call serialize_with_witness to include witness data.
+ def serialize(self):
+ return self.serialize_without_witness()
+
+ # Recalculate the txid (transaction hash without witness)
+ def rehash(self):
+ self.sha256 = None
+ self.calc_sha256()
+
+ # We will only cache the serialization without witness in
+ # self.sha256 and self.hash -- those are expected to be the txid.
+ def calc_sha256(self, with_witness=False):
+ if with_witness:
+ # Don't cache the result, just return it
+ return uint256_from_str(hash256(self.serialize_with_witness()))
+
+ if self.sha256 is None:
+ self.sha256 = uint256_from_str(hash256(self.serialize_without_witness()))
+ self.hash = encode(hash256(self.serialize())[::-1], 'hex_codec').decode('ascii')
+
+ def is_valid(self):
+ self.calc_sha256()
+ for tout in self.vout:
+ if tout.nValue < 0 or tout.nValue > 21000000 * COIN:
+ return False
+ return True
+
+ def __repr__(self):
+ 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():
+ def __init__(self, header=None):
+ if header is None:
+ self.set_null()
+ else:
+ self.nVersion = header.nVersion
+ self.hashPrevBlock = header.hashPrevBlock
+ self.hashMerkleRoot = header.hashMerkleRoot
+ self.nTime = header.nTime
+ self.nBits = header.nBits
+ self.nNonce = header.nNonce
+ self.sha256 = header.sha256
+ self.hash = header.hash
+ self.calc_sha256()
+
+ def set_null(self):
+ self.nVersion = 1
+ self.hashPrevBlock = 0
+ self.hashMerkleRoot = 0
+ self.nTime = 0
+ self.nBits = 0
+ self.nNonce = 0
+ self.sha256 = None
+ self.hash = None
+
+ def deserialize(self, f):
+ self.nVersion = struct.unpack("<i", f.read(4))[0]
+ self.hashPrevBlock = deser_uint256(f)
+ self.hashMerkleRoot = deser_uint256(f)
+ self.nTime = struct.unpack("<I", f.read(4))[0]
+ self.nBits = struct.unpack("<I", f.read(4))[0]
+ self.nNonce = struct.unpack("<I", f.read(4))[0]
+ self.sha256 = None
+ self.hash = None
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<i", self.nVersion)
+ r += ser_uint256(self.hashPrevBlock)
+ r += ser_uint256(self.hashMerkleRoot)
+ r += struct.pack("<I", self.nTime)
+ r += struct.pack("<I", self.nBits)
+ r += struct.pack("<I", self.nNonce)
+ return r
+
+ def calc_sha256(self):
+ if self.sha256 is None:
+ r = b""
+ r += struct.pack("<i", self.nVersion)
+ r += ser_uint256(self.hashPrevBlock)
+ r += ser_uint256(self.hashMerkleRoot)
+ r += struct.pack("<I", self.nTime)
+ r += struct.pack("<I", self.nBits)
+ r += struct.pack("<I", self.nNonce)
+ self.sha256 = uint256_from_str(hash256(r))
+ self.hash = encode(hash256(r)[::-1], 'hex_codec').decode('ascii')
+
+ def rehash(self):
+ self.sha256 = None
+ self.calc_sha256()
+ return self.sha256
+
+ def __repr__(self):
+ return "CBlockHeader(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x)" \
+ % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
+ time.ctime(self.nTime), self.nBits, self.nNonce)
+
+
+class CBlock(CBlockHeader):
+ def __init__(self, header=None):
+ super(CBlock, self).__init__(header)
+ self.vtx = []
+
+ def deserialize(self, f):
+ super(CBlock, self).deserialize(f)
+ self.vtx = deser_vector(f, CTransaction)
+
+ def serialize(self, with_witness=False):
+ r = b""
+ r += super(CBlock, self).serialize()
+ if with_witness:
+ r += ser_vector(self.vtx, "serialize_with_witness")
+ else:
+ r += ser_vector(self.vtx)
+ return r
+
+ # Calculate the merkle root given a vector of transaction hashes
+ @classmethod
+ def get_merkle_root(cls, hashes):
+ while len(hashes) > 1:
+ newhashes = []
+ for i in range(0, len(hashes), 2):
+ i2 = min(i+1, len(hashes)-1)
+ newhashes.append(hash256(hashes[i] + hashes[i2]))
+ hashes = newhashes
+ return uint256_from_str(hashes[0])
+
+ def calc_merkle_root(self):
+ hashes = []
+ for tx in self.vtx:
+ tx.calc_sha256()
+ hashes.append(ser_uint256(tx.sha256))
+ return self.get_merkle_root(hashes)
+
+ def calc_witness_merkle_root(self):
+ # For witness root purposes, the hash of the
+ # coinbase, with witness, is defined to be 0...0
+ hashes = [ser_uint256(0)]
+
+ for tx in self.vtx[1:]:
+ # Calculate the hashes with witness data
+ hashes.append(ser_uint256(tx.calc_sha256(True)))
+
+ return self.get_merkle_root(hashes)
+
+ def is_valid(self):
+ self.calc_sha256()
+ target = uint256_from_compact(self.nBits)
+ if self.sha256 > target:
+ return False
+ for tx in self.vtx:
+ if not tx.is_valid():
+ return False
+ if self.calc_merkle_root() != self.hashMerkleRoot:
+ return False
+ return True
+
+ def solve(self):
+ self.rehash()
+ target = uint256_from_compact(self.nBits)
+ while self.sha256 > target:
+ self.nNonce += 1
+ self.rehash()
+
+ def __repr__(self):
+ return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" \
+ % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
+ time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx))
+
+
+class PrefilledTransaction():
+ def __init__(self, index=0, tx = None):
+ self.index = index
+ self.tx = tx
+
+ def deserialize(self, f):
+ self.index = deser_compact_size(f)
+ self.tx = CTransaction()
+ self.tx.deserialize(f)
+
+ def serialize(self, with_witness=False):
+ r = b""
+ r += ser_compact_size(self.index)
+ if with_witness:
+ r += self.tx.serialize_with_witness()
+ else:
+ 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))
+
+# This is what we send on the wire, in a cmpctblock message.
+class P2PHeaderAndShortIDs():
+ def __init__(self):
+ self.header = CBlockHeader()
+ self.nonce = 0
+ self.shortids_length = 0
+ self.shortids = []
+ self.prefilled_txn_length = 0
+ self.prefilled_txn = []
+
+ def deserialize(self, f):
+ self.header.deserialize(f)
+ self.nonce = struct.unpack("<Q", f.read(8))[0]
+ self.shortids_length = deser_compact_size(f)
+ for i in range(self.shortids_length):
+ # shortids are defined to be 6 bytes in the spec, so append
+ # two zero bytes and read it in as an 8-byte number
+ self.shortids.append(struct.unpack("<Q", f.read(6) + b'\x00\x00')[0])
+ 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()
+ r += struct.pack("<Q", self.nonce)
+ r += ser_compact_size(self.shortids_length)
+ for x in self.shortids:
+ # We only want the first 6 bytes
+ r += struct.pack("<Q", x)[0:6]
+ 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):
+ expected_shortid = siphash256(k0, k1, tx_hash)
+ expected_shortid &= 0x0000ffffffffffff
+ return expected_shortid
+
+# This version gets rid of the array lengths, and reinterprets the differential
+# encoding into indices that can be used for lookup.
+class HeaderAndShortIDs():
+ def __init__(self, p2pheaders_and_shortids = None):
+ self.header = CBlockHeader()
+ self.nonce = 0
+ self.shortids = []
+ self.prefilled_txn = []
+ self.use_witness = False
+
+ if p2pheaders_and_shortids != None:
+ self.header = p2pheaders_and_shortids.header
+ self.nonce = p2pheaders_and_shortids.nonce
+ self.shortids = p2pheaders_and_shortids.shortids
+ last_index = -1
+ for x in p2pheaders_and_shortids.prefilled_txn:
+ self.prefilled_txn.append(PrefilledTransaction(x.index + last_index + 1, x.tx))
+ last_index = self.prefilled_txn[-1].index
+
+ def to_p2p(self):
+ if self.use_witness:
+ ret = P2PHeaderAndShortWitnessIDs()
+ else:
+ ret = P2PHeaderAndShortIDs()
+ ret.header = self.header
+ ret.nonce = self.nonce
+ ret.shortids_length = len(self.shortids)
+ ret.shortids = self.shortids
+ ret.prefilled_txn_length = len(self.prefilled_txn)
+ ret.prefilled_txn = []
+ last_index = -1
+ for x in self.prefilled_txn:
+ ret.prefilled_txn.append(PrefilledTransaction(x.index - last_index - 1, x.tx))
+ last_index = x.index
+ return ret
+
+ def get_siphash_keys(self):
+ header_nonce = self.header.serialize()
+ header_nonce += struct.pack("<Q", self.nonce)
+ hash_header_nonce_as_str = sha256(header_nonce)
+ key0 = struct.unpack("<Q", hash_header_nonce_as_str[0:8])[0]
+ key1 = struct.unpack("<Q", hash_header_nonce_as_str[8:16])[0]
+ return [ key0, key1 ]
+
+ # 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:
+ 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))
+
+
+class BlockTransactionsRequest():
+
+ def __init__(self, blockhash=0, indexes = None):
+ self.blockhash = blockhash
+ self.indexes = indexes if indexes != None else []
+
+ def deserialize(self, f):
+ self.blockhash = deser_uint256(f)
+ indexes_length = deser_compact_size(f)
+ for i in range(indexes_length):
+ self.indexes.append(deser_compact_size(f))
+
+ def serialize(self):
+ r = b""
+ r += ser_uint256(self.blockhash)
+ r += ser_compact_size(len(self.indexes))
+ for x in self.indexes:
+ r += ser_compact_size(x)
+ return r
+
+ # helper to set the differentially encoded indexes from absolute ones
+ def from_absolute(self, absolute_indexes):
+ self.indexes = []
+ last_index = -1
+ for x in absolute_indexes:
+ self.indexes.append(x-last_index-1)
+ last_index = x
+
+ def to_absolute(self):
+ absolute_indexes = []
+ last_index = -1
+ for x in self.indexes:
+ absolute_indexes.append(x+last_index+1)
+ last_index = absolute_indexes[-1]
+ return absolute_indexes
+
+ def __repr__(self):
+ return "BlockTransactionsRequest(hash=%064x indexes=%s)" % (self.blockhash, repr(self.indexes))
+
+
+class BlockTransactions():
+
+ def __init__(self, blockhash=0, transactions = None):
+ self.blockhash = blockhash
+ self.transactions = transactions if transactions != None else []
+
+ def deserialize(self, f):
+ self.blockhash = deser_uint256(f)
+ self.transactions = deser_vector(f, CTransaction)
+
+ def serialize(self, with_witness=False):
+ r = b""
+ r += ser_uint256(self.blockhash)
+ if with_witness:
+ r += ser_vector(self.transactions, "serialize_with_witness")
+ else:
+ r += ser_vector(self.transactions)
+ return r
+
+ def __repr__(self):
+ return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions))
+
+
+# Objects that correspond to messages on the wire
+class msg_version():
+ command = b"version"
+
+ def __init__(self):
+ self.nVersion = MY_VERSION
+ self.nServices = NODE_NETWORK | NODE_WITNESS
+ self.nTime = int(time.time())
+ self.addrTo = CAddress()
+ self.addrFrom = CAddress()
+ 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]
+ if self.nVersion == 10300:
+ self.nVersion = 300
+ self.nServices = struct.unpack("<Q", f.read(8))[0]
+ 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)
+ 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)
+ r += struct.pack("<Q", self.nServices)
+ r += struct.pack("<q", self.nTime)
+ r += self.addrTo.serialize()
+ r += self.addrFrom.serialize()
+ 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 nRelay=%i)' \
+ % (self.nVersion, self.nServices, time.ctime(self.nTime),
+ repr(self.addrTo), repr(self.addrFrom), self.nNonce,
+ self.strSubVer, self.nStartingHeight, self.nRelay)
+
+
+class msg_verack():
+ command = b"verack"
+
+ def __init__(self):
+ pass
+
+ def deserialize(self, f):
+ pass
+
+ def serialize(self):
+ return b""
+
+ def __repr__(self):
+ return "msg_verack()"
+
+
+class msg_addr():
+ command = b"addr"
+
+ def __init__(self):
+ self.addrs = []
+
+ def deserialize(self, f):
+ self.addrs = deser_vector(f, CAddress)
+
+ def serialize(self):
+ return ser_vector(self.addrs)
+
+ def __repr__(self):
+ return "msg_addr(addrs=%s)" % (repr(self.addrs))
+
+
+class msg_inv():
+ command = b"inv"
+
+ def __init__(self, inv=None):
+ if inv is None:
+ self.inv = []
+ else:
+ self.inv = inv
+
+ def deserialize(self, f):
+ self.inv = deser_vector(f, CInv)
+
+ def serialize(self):
+ return ser_vector(self.inv)
+
+ def __repr__(self):
+ return "msg_inv(inv=%s)" % (repr(self.inv))
+
+
+class msg_getdata():
+ command = b"getdata"
+
+ def __init__(self, inv=None):
+ self.inv = inv if inv != None else []
+
+ def deserialize(self, f):
+ self.inv = deser_vector(f, CInv)
+
+ def serialize(self):
+ return ser_vector(self.inv)
+
+ def __repr__(self):
+ return "msg_getdata(inv=%s)" % (repr(self.inv))
+
+
+class msg_getblocks():
+ command = b"getblocks"
+
+ def __init__(self):
+ self.locator = CBlockLocator()
+ self.hashstop = 0
+
+ def deserialize(self, f):
+ self.locator = CBlockLocator()
+ self.locator.deserialize(f)
+ self.hashstop = deser_uint256(f)
+
+ def serialize(self):
+ r = b""
+ r += self.locator.serialize()
+ r += ser_uint256(self.hashstop)
+ return r
+
+ def __repr__(self):
+ return "msg_getblocks(locator=%s hashstop=%064x)" \
+ % (repr(self.locator), self.hashstop)
+
+
+class msg_tx():
+ command = b"tx"
+
+ def __init__(self, tx=CTransaction()):
+ self.tx = tx
+
+ def deserialize(self, f):
+ self.tx.deserialize(f)
+
+ def serialize(self):
+ return self.tx.serialize_without_witness()
+
+ def __repr__(self):
+ return "msg_tx(tx=%s)" % (repr(self.tx))
+
+class msg_witness_tx(msg_tx):
+
+ def serialize(self):
+ return self.tx.serialize_with_witness()
+
+
+class msg_block():
+ command = b"block"
+
+ def __init__(self, block=None):
+ if block is None:
+ self.block = CBlock()
+ else:
+ self.block = block
+
+ def deserialize(self, f):
+ self.block.deserialize(f)
+
+ def serialize(self):
+ return self.block.serialize()
+
+ def __repr__(self):
+ return "msg_block(block=%s)" % (repr(self.block))
+
+# for cases where a user needs tighter control over what is sent over the wire
+# note that the user must supply the name of the command, and the data
+class msg_generic():
+ def __init__(self, command, data=None):
+ self.command = command
+ self.data = data
+
+ def serialize(self):
+ return self.data
+
+ def __repr__(self):
+ return "msg_generic()"
+
+class msg_witness_block(msg_block):
+
+ def serialize(self):
+ r = self.block.serialize(with_witness=True)
+ return r
+
+class msg_getaddr():
+ command = b"getaddr"
+
+ def __init__(self):
+ pass
+
+ def deserialize(self, f):
+ pass
+
+ def serialize(self):
+ return b""
+
+ def __repr__(self):
+ return "msg_getaddr()"
+
+
+class msg_ping():
+ command = b"ping"
+
+ def __init__(self, nonce=0):
+ self.nonce = nonce
+
+ def deserialize(self, f):
+ self.nonce = struct.unpack("<Q", f.read(8))[0]
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<Q", self.nonce)
+ return r
+
+ def __repr__(self):
+ return "msg_ping(nonce=%08x)" % self.nonce
+
+
+class msg_pong():
+ command = b"pong"
+
+ def __init__(self, nonce=0):
+ self.nonce = nonce
+
+ def deserialize(self, f):
+ self.nonce = struct.unpack("<Q", f.read(8))[0]
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<Q", self.nonce)
+ return r
+
+ def __repr__(self):
+ return "msg_pong(nonce=%08x)" % self.nonce
+
+
+class msg_mempool():
+ command = b"mempool"
+
+ def __init__(self):
+ pass
+
+ def deserialize(self, f):
+ pass
+
+ def serialize(self):
+ return b""
+
+ def __repr__(self):
+ return "msg_mempool()"
+
+class msg_sendheaders():
+ command = b"sendheaders"
+
+ def __init__(self):
+ pass
+
+ def deserialize(self, f):
+ pass
+
+ def serialize(self):
+ return b""
+
+ def __repr__(self):
+ return "msg_sendheaders()"
+
+
+# getheaders message has
+# number of entries
+# vector of hashes
+# hash_stop (hash of last desired block header, 0 to get as many as possible)
+class msg_getheaders():
+ command = b"getheaders"
+
+ def __init__(self):
+ self.locator = CBlockLocator()
+ self.hashstop = 0
+
+ def deserialize(self, f):
+ self.locator = CBlockLocator()
+ self.locator.deserialize(f)
+ self.hashstop = deser_uint256(f)
+
+ def serialize(self):
+ r = b""
+ r += self.locator.serialize()
+ r += ser_uint256(self.hashstop)
+ return r
+
+ def __repr__(self):
+ return "msg_getheaders(locator=%s, stop=%064x)" \
+ % (repr(self.locator), self.hashstop)
+
+
+# headers message has
+# <count> <vector of block headers>
+class msg_headers():
+ command = b"headers"
+
+ def __init__(self, headers=None):
+ self.headers = headers if headers is not None else []
+
+ def deserialize(self, f):
+ # comment in bitcoind indicates these should be deserialized as blocks
+ blocks = deser_vector(f, CBlock)
+ for x in blocks:
+ self.headers.append(CBlockHeader(x))
+
+ def serialize(self):
+ blocks = [CBlock(x) for x in self.headers]
+ return ser_vector(blocks)
+
+ def __repr__(self):
+ return "msg_headers(headers=%s)" % repr(self.headers)
+
+
+class msg_reject():
+ command = b"reject"
+ REJECT_MALFORMED = 1
+
+ def __init__(self):
+ self.message = b""
+ self.code = 0
+ self.reason = b""
+ self.data = 0
+
+ def deserialize(self, f):
+ self.message = deser_string(f)
+ self.code = struct.unpack("<B", f.read(1))[0]
+ self.reason = deser_string(f)
+ if (self.code != self.REJECT_MALFORMED and
+ (self.message == b"block" or self.message == b"tx")):
+ self.data = deser_uint256(f)
+
+ def serialize(self):
+ r = ser_string(self.message)
+ r += struct.pack("<B", self.code)
+ r += ser_string(self.reason)
+ if (self.code != self.REJECT_MALFORMED and
+ (self.message == b"block" or self.message == b"tx")):
+ r += ser_uint256(self.data)
+ return r
+
+ def __repr__(self):
+ return "msg_reject: %s %d %s [%064x]" \
+ % (self.message, self.code, self.reason, self.data)
+
+class msg_feefilter():
+ command = b"feefilter"
+
+ def __init__(self, feerate=0):
+ self.feerate = feerate
+
+ def deserialize(self, f):
+ self.feerate = struct.unpack("<Q", f.read(8))[0]
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<Q", self.feerate)
+ return r
+
+ def __repr__(self):
+ return "msg_feefilter(feerate=%08x)" % self.feerate
+
+class msg_sendcmpct():
+ command = b"sendcmpct"
+
+ def __init__(self):
+ self.announce = False
+ self.version = 1
+
+ def deserialize(self, f):
+ self.announce = struct.unpack("<?", f.read(1))[0]
+ self.version = struct.unpack("<Q", f.read(8))[0]
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<?", self.announce)
+ r += struct.pack("<Q", self.version)
+ return r
+
+ def __repr__(self):
+ return "msg_sendcmpct(announce=%s, version=%lu)" % (self.announce, self.version)
+
+class msg_cmpctblock():
+ command = b"cmpctblock"
+
+ def __init__(self, header_and_shortids = None):
+ self.header_and_shortids = header_and_shortids
+
+ def deserialize(self, f):
+ self.header_and_shortids = P2PHeaderAndShortIDs()
+ self.header_and_shortids.deserialize(f)
+
+ def serialize(self):
+ r = b""
+ r += self.header_and_shortids.serialize()
+ return r
+
+ def __repr__(self):
+ return "msg_cmpctblock(HeaderAndShortIDs=%s)" % repr(self.header_and_shortids)
+
+class msg_getblocktxn():
+ command = b"getblocktxn"
+
+ def __init__(self):
+ self.block_txn_request = None
+
+ def deserialize(self, f):
+ self.block_txn_request = BlockTransactionsRequest()
+ self.block_txn_request.deserialize(f)
+
+ def serialize(self):
+ r = b""
+ r += self.block_txn_request.serialize()
+ return r
+
+ def __repr__(self):
+ return "msg_getblocktxn(block_txn_request=%s)" % (repr(self.block_txn_request))
+
+class msg_blocktxn():
+ command = b"blocktxn"
+
+ def __init__(self):
+ self.block_transactions = BlockTransactions()
+
+ def deserialize(self, f):
+ self.block_transactions.deserialize(f)
+
+ def serialize(self):
+ r = b""
+ r += self.block_transactions.serialize()
+ return r
+
+ 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
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index 2607b9b07c..724d418099 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -9,1462 +9,252 @@
This python code was modified from ArtForz' public domain half-a-node, as
found in the mini-node branch of http://github.com/jgarzik/pynode.
-NodeConn: an object which manages p2p connectivity to a bitcoin node
-NodeConnCB: a base class that describes the interface for receiving
- callbacks with network messages from a NodeConn
-CBlock, CTransaction, CBlockHeader, CTxIn, CTxOut, etc....:
- data structures that should map to corresponding structures in
- bitcoin/primitives
-msg_block, msg_tx, msg_headers, etc.:
- data structures that represent network messages
-ser_*, deser_*: functions that handle serialization/deserialization
-"""
-
+P2PConnection: A low-level connection object to a node's P2P interface
+P2PInterface: A high-level interface object for communicating to a node over P2P"""
import asyncore
-from codecs import encode
from collections import defaultdict
-import copy
-import hashlib
from io import BytesIO
import logging
-import random
import socket
import struct
import sys
-import time
-from threading import RLock, Thread
-
-from test_framework.siphash import siphash256
-from test_framework.util import hex_str_to_bytes, bytes_to_hex_str, wait_until
-
-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)
+import threading
-MAX_INV_SZ = 50000
-MAX_BLOCK_BASE_SIZE = 1000000
-
-COIN = 100000000 # 1 btc in satoshis
-
-NODE_NETWORK = (1 << 0)
-# NODE_GETUTXO = (1 << 1)
-# NODE_BLOOM = (1 << 2)
-NODE_WITNESS = (1 << 3)
-NODE_UNSUPPORTED_SERVICE_BIT_5 = (1 << 5)
-NODE_UNSUPPORTED_SERVICE_BIT_7 = (1 << 7)
+from test_framework.messages import *
+from test_framework.util import wait_until
logger = logging.getLogger("TestFramework.mininode")
-# Keep our own socket map for asyncore, so that we can track disconnects
-# ourselves (to workaround an issue with closing an asyncore socket when
-# using select)
-mininode_socket_map = dict()
-
-# One lock for synchronizing all data access between the networking thread (see
-# NetworkThread below) and the thread running the test logic. For simplicity,
-# NodeConn acquires this lock whenever delivering a message to a NodeConnCB,
-# and whenever adding anything to the send buffer (in send_message()). This
-# lock should be acquired in the thread running the test logic to synchronize
-# access to any data shared with the NodeConnCB or NodeConn.
-mininode_lock = RLock()
-
-# Serialization/deserialization tools
-def sha256(s):
- return hashlib.new('sha256', s).digest()
-
-def ripemd160(s):
- return hashlib.new('ripemd160', s).digest()
-
-def hash256(s):
- return sha256(sha256(s))
-
-def ser_compact_size(l):
- r = b""
- if l < 253:
- r = struct.pack("B", l)
- elif l < 0x10000:
- r = struct.pack("<BH", 253, l)
- elif l < 0x100000000:
- r = struct.pack("<BI", 254, l)
- else:
- r = struct.pack("<BQ", 255, l)
- return r
-
-def deser_compact_size(f):
- nit = struct.unpack("<B", f.read(1))[0]
- if nit == 253:
- nit = struct.unpack("<H", f.read(2))[0]
- elif nit == 254:
- nit = struct.unpack("<I", f.read(4))[0]
- elif nit == 255:
- nit = struct.unpack("<Q", f.read(8))[0]
- return nit
-
-def deser_string(f):
- nit = deser_compact_size(f)
- return f.read(nit)
-
-def ser_string(s):
- return ser_compact_size(len(s)) + s
-
-def deser_uint256(f):
- r = 0
- for i in range(8):
- t = struct.unpack("<I", f.read(4))[0]
- r += t << (i * 32)
- return r
-
-
-def ser_uint256(u):
- rs = b""
- for i in range(8):
- rs += struct.pack("<I", u & 0xFFFFFFFF)
- u >>= 32
- return rs
-
-
-def uint256_from_str(s):
- r = 0
- t = struct.unpack("<IIIIIIII", s[:32])
- for i in range(8):
- r += t[i] << (i * 32)
- return r
-
-
-def uint256_from_compact(c):
- nbytes = (c >> 24) & 0xFF
- v = (c & 0xFFFFFF) << (8 * (nbytes - 3))
- return v
-
-
-def deser_vector(f, c):
- nit = deser_compact_size(f)
- r = []
- for i in range(nit):
- t = c()
- t.deserialize(f)
- r.append(t)
- return r
-
-
-# ser_function_name: Allow for an alternate serialization function on the
-# entries in the vector (we use this for serializing the vector of transactions
-# for a witness block).
-def ser_vector(l, ser_function_name=None):
- r = ser_compact_size(len(l))
- for i in l:
- if ser_function_name:
- r += getattr(i, ser_function_name)()
- else:
- r += i.serialize()
- return r
-
-
-def deser_uint256_vector(f):
- nit = deser_compact_size(f)
- r = []
- for i in range(nit):
- t = deser_uint256(f)
- r.append(t)
- return r
-
-
-def ser_uint256_vector(l):
- r = ser_compact_size(len(l))
- for i in l:
- r += ser_uint256(i)
- return r
-
-
-def deser_string_vector(f):
- nit = deser_compact_size(f)
- r = []
- for i in range(nit):
- t = deser_string(f)
- r.append(t)
- return r
-
-
-def ser_string_vector(l):
- r = ser_compact_size(len(l))
- for sv in l:
- r += ser_string(sv)
- return r
-
-
-def deser_int_vector(f):
- nit = deser_compact_size(f)
- r = []
- for i in range(nit):
- t = struct.unpack("<i", f.read(4))[0]
- r.append(t)
- return r
-
-
-def ser_int_vector(l):
- r = ser_compact_size(len(l))
- for i in l:
- r += struct.pack("<i", i)
- return r
-
-# Deserialize from a hex string representation (eg from RPC)
-def FromHex(obj, hex_string):
- obj.deserialize(BytesIO(hex_str_to_bytes(hex_string)))
- return obj
-
-# Convert a binary-serializable object to hex (eg for submission via RPC)
-def ToHex(obj):
- return bytes_to_hex_str(obj.serialize())
-
-# Objects that map to bitcoind objects, which can be serialized/deserialized
-
-class CAddress(object):
- def __init__(self):
- self.nServices = 1
- self.pchReserved = b"\x00" * 10 + b"\xff" * 2
- self.ip = "0.0.0.0"
- self.port = 0
-
- def deserialize(self, f):
- self.nServices = struct.unpack("<Q", f.read(8))[0]
- self.pchReserved = f.read(12)
- self.ip = socket.inet_ntoa(f.read(4))
- self.port = struct.unpack(">H", f.read(2))[0]
-
- def serialize(self):
- r = b""
- r += struct.pack("<Q", self.nServices)
- r += self.pchReserved
- r += socket.inet_aton(self.ip)
- r += struct.pack(">H", self.port)
- return r
-
- def __repr__(self):
- return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices,
- self.ip, self.port)
-
-MSG_WITNESS_FLAG = 1<<30
-
-class CInv(object):
- typemap = {
- 0: "Error",
- 1: "TX",
- 2: "Block",
- 1|MSG_WITNESS_FLAG: "WitnessTx",
- 2|MSG_WITNESS_FLAG : "WitnessBlock",
- 4: "CompactBlock"
- }
-
- def __init__(self, t=0, h=0):
- self.type = t
- self.hash = h
-
- def deserialize(self, f):
- self.type = struct.unpack("<i", f.read(4))[0]
- self.hash = deser_uint256(f)
-
- def serialize(self):
- r = b""
- r += struct.pack("<i", self.type)
- r += ser_uint256(self.hash)
- return r
-
- def __repr__(self):
- return "CInv(type=%s hash=%064x)" \
- % (self.typemap[self.type], self.hash)
-
-
-class CBlockLocator(object):
- def __init__(self):
- self.nVersion = MY_VERSION
- self.vHave = []
-
- def deserialize(self, f):
- self.nVersion = struct.unpack("<i", f.read(4))[0]
- self.vHave = deser_uint256_vector(f)
-
- def serialize(self):
- r = b""
- r += struct.pack("<i", self.nVersion)
- r += ser_uint256_vector(self.vHave)
- return r
-
- def __repr__(self):
- return "CBlockLocator(nVersion=%i vHave=%s)" \
- % (self.nVersion, repr(self.vHave))
-
-
-class COutPoint(object):
- def __init__(self, hash=0, n=0):
- self.hash = hash
- self.n = n
-
- def deserialize(self, f):
- self.hash = deser_uint256(f)
- self.n = struct.unpack("<I", f.read(4))[0]
-
- def serialize(self):
- r = b""
- r += ser_uint256(self.hash)
- r += struct.pack("<I", self.n)
- return r
-
- def __repr__(self):
- return "COutPoint(hash=%064x n=%i)" % (self.hash, self.n)
-
-
-class CTxIn(object):
- def __init__(self, outpoint=None, scriptSig=b"", nSequence=0):
- if outpoint is None:
- self.prevout = COutPoint()
- else:
- self.prevout = outpoint
- self.scriptSig = scriptSig
- self.nSequence = nSequence
-
- def deserialize(self, f):
- self.prevout = COutPoint()
- self.prevout.deserialize(f)
- self.scriptSig = deser_string(f)
- self.nSequence = struct.unpack("<I", f.read(4))[0]
-
- def serialize(self):
- r = b""
- r += self.prevout.serialize()
- r += ser_string(self.scriptSig)
- r += struct.pack("<I", self.nSequence)
- return r
-
- def __repr__(self):
- return "CTxIn(prevout=%s scriptSig=%s nSequence=%i)" \
- % (repr(self.prevout), bytes_to_hex_str(self.scriptSig),
- self.nSequence)
-
-
-class CTxOut(object):
- def __init__(self, nValue=0, scriptPubKey=b""):
- self.nValue = nValue
- self.scriptPubKey = scriptPubKey
-
- def deserialize(self, f):
- self.nValue = struct.unpack("<q", f.read(8))[0]
- self.scriptPubKey = deser_string(f)
-
- def serialize(self):
- r = b""
- r += struct.pack("<q", self.nValue)
- r += ser_string(self.scriptPubKey)
- return r
-
- def __repr__(self):
- return "CTxOut(nValue=%i.%08i scriptPubKey=%s)" \
- % (self.nValue // COIN, self.nValue % COIN,
- bytes_to_hex_str(self.scriptPubKey))
-
-
-class CScriptWitness(object):
- def __init__(self):
- # stack is a vector of strings
- self.stack = []
-
- def __repr__(self):
- return "CScriptWitness(%s)" % \
- (",".join([bytes_to_hex_str(x) for x in self.stack]))
-
- def is_null(self):
- if self.stack:
- return False
- return True
-
-
-class CTxInWitness(object):
- def __init__(self):
- self.scriptWitness = CScriptWitness()
-
- def deserialize(self, f):
- self.scriptWitness.stack = deser_string_vector(f)
-
- def serialize(self):
- return ser_string_vector(self.scriptWitness.stack)
-
- def __repr__(self):
- return repr(self.scriptWitness)
-
- def is_null(self):
- return self.scriptWitness.is_null()
-
-
-class CTxWitness(object):
- def __init__(self):
- self.vtxinwit = []
-
- def deserialize(self, f):
- for i in range(len(self.vtxinwit)):
- self.vtxinwit[i].deserialize(f)
-
- def serialize(self):
- r = b""
- # This is different than the usual vector serialization --
- # we omit the length of the vector, which is required to be
- # the same length as the transaction's vin vector.
- for x in self.vtxinwit:
- r += x.serialize()
- return r
-
- def __repr__(self):
- return "CTxWitness(%s)" % \
- (';'.join([repr(x) for x in self.vtxinwit]))
-
- def is_null(self):
- for x in self.vtxinwit:
- if not x.is_null():
- return False
- return True
-
-
-class CTransaction(object):
- def __init__(self, tx=None):
- if tx is None:
- self.nVersion = 1
- self.vin = []
- self.vout = []
- self.wit = CTxWitness()
- self.nLockTime = 0
- self.sha256 = None
- self.hash = None
- else:
- self.nVersion = tx.nVersion
- self.vin = copy.deepcopy(tx.vin)
- self.vout = copy.deepcopy(tx.vout)
- self.nLockTime = tx.nLockTime
- self.sha256 = tx.sha256
- self.hash = tx.hash
- self.wit = copy.deepcopy(tx.wit)
-
- def deserialize(self, f):
- self.nVersion = struct.unpack("<i", f.read(4))[0]
- self.vin = deser_vector(f, CTxIn)
- flags = 0
- if len(self.vin) == 0:
- flags = struct.unpack("<B", f.read(1))[0]
- # Not sure why flags can't be zero, but this
- # matches the implementation in bitcoind
- if (flags != 0):
- self.vin = deser_vector(f, CTxIn)
- self.vout = deser_vector(f, CTxOut)
- else:
- self.vout = deser_vector(f, CTxOut)
- if flags != 0:
- 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
- self.hash = None
-
- def serialize_without_witness(self):
- r = b""
- r += struct.pack("<i", self.nVersion)
- r += ser_vector(self.vin)
- r += ser_vector(self.vout)
- r += struct.pack("<I", self.nLockTime)
- return r
-
- # Only serialize with witness when explicitly called for
- def serialize_with_witness(self):
- flags = 0
- if not self.wit.is_null():
- flags |= 1
- r = b""
- r += struct.pack("<i", self.nVersion)
- if flags:
- dummy = []
- r += ser_vector(dummy)
- r += struct.pack("<B", flags)
- r += ser_vector(self.vin)
- r += ser_vector(self.vout)
- if flags & 1:
- if (len(self.wit.vtxinwit) != len(self.vin)):
- # vtxinwit must have the same length as vin
- self.wit.vtxinwit = self.wit.vtxinwit[:len(self.vin)]
- for i in range(len(self.wit.vtxinwit), len(self.vin)):
- self.wit.vtxinwit.append(CTxInWitness())
- r += self.wit.serialize()
- r += struct.pack("<I", self.nLockTime)
- return r
-
- # Regular serialization is without witness -- must explicitly
- # call serialize_with_witness to include witness data.
- def serialize(self):
- return self.serialize_without_witness()
-
- # Recalculate the txid (transaction hash without witness)
- def rehash(self):
- self.sha256 = None
- self.calc_sha256()
-
- # We will only cache the serialization without witness in
- # self.sha256 and self.hash -- those are expected to be the txid.
- def calc_sha256(self, with_witness=False):
- if with_witness:
- # Don't cache the result, just return it
- return uint256_from_str(hash256(self.serialize_with_witness()))
-
- if self.sha256 is None:
- self.sha256 = uint256_from_str(hash256(self.serialize_without_witness()))
- self.hash = encode(hash256(self.serialize())[::-1], 'hex_codec').decode('ascii')
-
- def is_valid(self):
- self.calc_sha256()
- for tout in self.vout:
- if tout.nValue < 0 or tout.nValue > 21000000 * COIN:
- return False
- return True
-
- def __repr__(self):
- 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):
- def __init__(self, header=None):
- if header is None:
- self.set_null()
- else:
- self.nVersion = header.nVersion
- self.hashPrevBlock = header.hashPrevBlock
- self.hashMerkleRoot = header.hashMerkleRoot
- self.nTime = header.nTime
- self.nBits = header.nBits
- self.nNonce = header.nNonce
- self.sha256 = header.sha256
- self.hash = header.hash
- self.calc_sha256()
-
- def set_null(self):
- self.nVersion = 1
- self.hashPrevBlock = 0
- self.hashMerkleRoot = 0
- self.nTime = 0
- self.nBits = 0
- self.nNonce = 0
- self.sha256 = None
- self.hash = None
-
- def deserialize(self, f):
- self.nVersion = struct.unpack("<i", f.read(4))[0]
- self.hashPrevBlock = deser_uint256(f)
- self.hashMerkleRoot = deser_uint256(f)
- self.nTime = struct.unpack("<I", f.read(4))[0]
- self.nBits = struct.unpack("<I", f.read(4))[0]
- self.nNonce = struct.unpack("<I", f.read(4))[0]
- self.sha256 = None
- self.hash = None
-
- def serialize(self):
- r = b""
- r += struct.pack("<i", self.nVersion)
- r += ser_uint256(self.hashPrevBlock)
- r += ser_uint256(self.hashMerkleRoot)
- r += struct.pack("<I", self.nTime)
- r += struct.pack("<I", self.nBits)
- r += struct.pack("<I", self.nNonce)
- return r
-
- def calc_sha256(self):
- if self.sha256 is None:
- r = b""
- r += struct.pack("<i", self.nVersion)
- r += ser_uint256(self.hashPrevBlock)
- r += ser_uint256(self.hashMerkleRoot)
- r += struct.pack("<I", self.nTime)
- r += struct.pack("<I", self.nBits)
- r += struct.pack("<I", self.nNonce)
- self.sha256 = uint256_from_str(hash256(r))
- self.hash = encode(hash256(r)[::-1], 'hex_codec').decode('ascii')
-
- def rehash(self):
- self.sha256 = None
- self.calc_sha256()
- return self.sha256
-
- def __repr__(self):
- return "CBlockHeader(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x)" \
- % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
- time.ctime(self.nTime), self.nBits, self.nNonce)
-
-
-class CBlock(CBlockHeader):
- def __init__(self, header=None):
- super(CBlock, self).__init__(header)
- self.vtx = []
-
- def deserialize(self, f):
- super(CBlock, self).deserialize(f)
- self.vtx = deser_vector(f, CTransaction)
-
- def serialize(self, with_witness=False):
- r = b""
- r += super(CBlock, self).serialize()
- if with_witness:
- r += ser_vector(self.vtx, "serialize_with_witness")
- else:
- r += ser_vector(self.vtx)
- return r
-
- # Calculate the merkle root given a vector of transaction hashes
- @classmethod
- def get_merkle_root(cls, hashes):
- while len(hashes) > 1:
- newhashes = []
- for i in range(0, len(hashes), 2):
- i2 = min(i+1, len(hashes)-1)
- newhashes.append(hash256(hashes[i] + hashes[i2]))
- hashes = newhashes
- return uint256_from_str(hashes[0])
-
- def calc_merkle_root(self):
- hashes = []
- for tx in self.vtx:
- tx.calc_sha256()
- hashes.append(ser_uint256(tx.sha256))
- return self.get_merkle_root(hashes)
-
- def calc_witness_merkle_root(self):
- # For witness root purposes, the hash of the
- # coinbase, with witness, is defined to be 0...0
- hashes = [ser_uint256(0)]
-
- for tx in self.vtx[1:]:
- # Calculate the hashes with witness data
- hashes.append(ser_uint256(tx.calc_sha256(True)))
-
- return self.get_merkle_root(hashes)
-
- def is_valid(self):
- self.calc_sha256()
- target = uint256_from_compact(self.nBits)
- if self.sha256 > target:
- return False
- for tx in self.vtx:
- if not tx.is_valid():
- return False
- if self.calc_merkle_root() != self.hashMerkleRoot:
- return False
- return True
-
- def solve(self):
- self.rehash()
- target = uint256_from_compact(self.nBits)
- while self.sha256 > target:
- self.nNonce += 1
- self.rehash()
-
- def __repr__(self):
- return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" \
- % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
- time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx))
-
-
-class CUnsignedAlert(object):
- def __init__(self):
- self.nVersion = 1
- self.nRelayUntil = 0
- self.nExpiration = 0
- self.nID = 0
- self.nCancel = 0
- self.setCancel = []
- self.nMinVer = 0
- self.nMaxVer = 0
- self.setSubVer = []
- self.nPriority = 0
- self.strComment = b""
- self.strStatusBar = b""
- self.strReserved = b""
-
- def deserialize(self, f):
- self.nVersion = struct.unpack("<i", f.read(4))[0]
- self.nRelayUntil = struct.unpack("<q", f.read(8))[0]
- self.nExpiration = struct.unpack("<q", f.read(8))[0]
- self.nID = struct.unpack("<i", f.read(4))[0]
- self.nCancel = struct.unpack("<i", f.read(4))[0]
- self.setCancel = deser_int_vector(f)
- self.nMinVer = struct.unpack("<i", f.read(4))[0]
- self.nMaxVer = struct.unpack("<i", f.read(4))[0]
- self.setSubVer = deser_string_vector(f)
- self.nPriority = struct.unpack("<i", f.read(4))[0]
- self.strComment = deser_string(f)
- self.strStatusBar = deser_string(f)
- self.strReserved = deser_string(f)
-
- def serialize(self):
- r = b""
- r += struct.pack("<i", self.nVersion)
- r += struct.pack("<q", self.nRelayUntil)
- r += struct.pack("<q", self.nExpiration)
- r += struct.pack("<i", self.nID)
- r += struct.pack("<i", self.nCancel)
- r += ser_int_vector(self.setCancel)
- r += struct.pack("<i", self.nMinVer)
- r += struct.pack("<i", self.nMaxVer)
- r += ser_string_vector(self.setSubVer)
- r += struct.pack("<i", self.nPriority)
- r += ser_string(self.strComment)
- r += ser_string(self.strStatusBar)
- r += ser_string(self.strReserved)
- return r
-
- def __repr__(self):
- return "CUnsignedAlert(nVersion %d, nRelayUntil %d, nExpiration %d, nID %d, nCancel %d, nMinVer %d, nMaxVer %d, nPriority %d, strComment %s, strStatusBar %s, strReserved %s)" \
- % (self.nVersion, self.nRelayUntil, self.nExpiration, self.nID,
- self.nCancel, self.nMinVer, self.nMaxVer, self.nPriority,
- self.strComment, self.strStatusBar, self.strReserved)
-
-
-class CAlert(object):
- def __init__(self):
- self.vchMsg = b""
- self.vchSig = b""
-
- def deserialize(self, f):
- self.vchMsg = deser_string(f)
- self.vchSig = deser_string(f)
-
- def serialize(self):
- r = b""
- r += ser_string(self.vchMsg)
- r += ser_string(self.vchSig)
- return r
-
- def __repr__(self):
- return "CAlert(vchMsg.sz %d, vchSig.sz %d)" \
- % (len(self.vchMsg), len(self.vchSig))
-
-
-class PrefilledTransaction(object):
- def __init__(self, index=0, tx = None):
- self.index = index
- self.tx = tx
-
- def deserialize(self, f):
- self.index = deser_compact_size(f)
- self.tx = CTransaction()
- self.tx.deserialize(f)
-
- def serialize(self, with_witness=False):
- r = b""
- r += ser_compact_size(self.index)
- if with_witness:
- r += self.tx.serialize_with_witness()
- else:
- 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))
-
-# This is what we send on the wire, in a cmpctblock message.
-class P2PHeaderAndShortIDs(object):
- def __init__(self):
- self.header = CBlockHeader()
- self.nonce = 0
- self.shortids_length = 0
- self.shortids = []
- self.prefilled_txn_length = 0
- self.prefilled_txn = []
-
- def deserialize(self, f):
- self.header.deserialize(f)
- self.nonce = struct.unpack("<Q", f.read(8))[0]
- self.shortids_length = deser_compact_size(f)
- for i in range(self.shortids_length):
- # shortids are defined to be 6 bytes in the spec, so append
- # two zero bytes and read it in as an 8-byte number
- self.shortids.append(struct.unpack("<Q", f.read(6) + b'\x00\x00')[0])
- 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()
- r += struct.pack("<Q", self.nonce)
- r += ser_compact_size(self.shortids_length)
- for x in self.shortids:
- # We only want the first 6 bytes
- r += struct.pack("<Q", x)[0:6]
- 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):
- expected_shortid = siphash256(k0, k1, tx_hash)
- expected_shortid &= 0x0000ffffffffffff
- return expected_shortid
-
-# This version gets rid of the array lengths, and reinterprets the differential
-# encoding into indices that can be used for lookup.
-class HeaderAndShortIDs(object):
- def __init__(self, p2pheaders_and_shortids = None):
- self.header = CBlockHeader()
- self.nonce = 0
- self.shortids = []
- self.prefilled_txn = []
- self.use_witness = False
-
- if p2pheaders_and_shortids != None:
- self.header = p2pheaders_and_shortids.header
- self.nonce = p2pheaders_and_shortids.nonce
- self.shortids = p2pheaders_and_shortids.shortids
- last_index = -1
- for x in p2pheaders_and_shortids.prefilled_txn:
- self.prefilled_txn.append(PrefilledTransaction(x.index + last_index + 1, x.tx))
- last_index = self.prefilled_txn[-1].index
-
- def to_p2p(self):
- if self.use_witness:
- ret = P2PHeaderAndShortWitnessIDs()
- else:
- ret = P2PHeaderAndShortIDs()
- ret.header = self.header
- ret.nonce = self.nonce
- ret.shortids_length = len(self.shortids)
- ret.shortids = self.shortids
- ret.prefilled_txn_length = len(self.prefilled_txn)
- ret.prefilled_txn = []
- last_index = -1
- for x in self.prefilled_txn:
- ret.prefilled_txn.append(PrefilledTransaction(x.index - last_index - 1, x.tx))
- last_index = x.index
- return ret
-
- def get_siphash_keys(self):
- header_nonce = self.header.serialize()
- header_nonce += struct.pack("<Q", self.nonce)
- hash_header_nonce_as_str = sha256(header_nonce)
- key0 = struct.unpack("<Q", hash_header_nonce_as_str[0:8])[0]
- key1 = struct.unpack("<Q", hash_header_nonce_as_str[8:16])[0]
- return [ key0, key1 ]
-
- # 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:
- 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))
-
-
-class BlockTransactionsRequest(object):
-
- def __init__(self, blockhash=0, indexes = None):
- self.blockhash = blockhash
- self.indexes = indexes if indexes != None else []
-
- def deserialize(self, f):
- self.blockhash = deser_uint256(f)
- indexes_length = deser_compact_size(f)
- for i in range(indexes_length):
- self.indexes.append(deser_compact_size(f))
-
- def serialize(self):
- r = b""
- r += ser_uint256(self.blockhash)
- r += ser_compact_size(len(self.indexes))
- for x in self.indexes:
- r += ser_compact_size(x)
- return r
-
- # helper to set the differentially encoded indexes from absolute ones
- def from_absolute(self, absolute_indexes):
- self.indexes = []
- last_index = -1
- for x in absolute_indexes:
- self.indexes.append(x-last_index-1)
- last_index = x
-
- def to_absolute(self):
- absolute_indexes = []
- last_index = -1
- for x in self.indexes:
- absolute_indexes.append(x+last_index+1)
- last_index = absolute_indexes[-1]
- return absolute_indexes
-
- def __repr__(self):
- return "BlockTransactionsRequest(hash=%064x indexes=%s)" % (self.blockhash, repr(self.indexes))
-
-
-class BlockTransactions(object):
-
- def __init__(self, blockhash=0, transactions = None):
- self.blockhash = blockhash
- self.transactions = transactions if transactions != None else []
-
- def deserialize(self, f):
- self.blockhash = deser_uint256(f)
- self.transactions = deser_vector(f, CTransaction)
-
- def serialize(self, with_witness=False):
- r = b""
- r += ser_uint256(self.blockhash)
- if with_witness:
- r += ser_vector(self.transactions, "serialize_with_witness")
- else:
- r += ser_vector(self.transactions)
- return r
-
- def __repr__(self):
- return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions))
-
-
-# Objects that correspond to messages on the wire
-class msg_version(object):
- command = b"version"
-
- def __init__(self):
- self.nVersion = MY_VERSION
- self.nServices = 1
- self.nTime = int(time.time())
- self.addrTo = CAddress()
- self.addrFrom = CAddress()
- 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]
- if self.nVersion == 10300:
- self.nVersion = 300
- self.nServices = struct.unpack("<Q", f.read(8))[0]
- 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)
- 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)
- r += struct.pack("<Q", self.nServices)
- r += struct.pack("<q", self.nTime)
- r += self.addrTo.serialize()
- r += self.addrFrom.serialize()
- 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 nRelay=%i)' \
- % (self.nVersion, self.nServices, time.ctime(self.nTime),
- repr(self.addrTo), repr(self.addrFrom), self.nNonce,
- self.strSubVer, self.nStartingHeight, self.nRelay)
-
-
-class msg_verack(object):
- command = b"verack"
-
- def __init__(self):
- pass
-
- def deserialize(self, f):
- pass
-
- def serialize(self):
- return b""
-
- def __repr__(self):
- return "msg_verack()"
-
-
-class msg_addr(object):
- command = b"addr"
-
- def __init__(self):
- self.addrs = []
-
- def deserialize(self, f):
- self.addrs = deser_vector(f, CAddress)
-
- def serialize(self):
- return ser_vector(self.addrs)
-
- def __repr__(self):
- return "msg_addr(addrs=%s)" % (repr(self.addrs))
-
-
-class msg_alert(object):
- command = b"alert"
-
- def __init__(self):
- self.alert = CAlert()
-
- def deserialize(self, f):
- self.alert = CAlert()
- self.alert.deserialize(f)
-
- def serialize(self):
- r = b""
- r += self.alert.serialize()
- return r
-
- def __repr__(self):
- return "msg_alert(alert=%s)" % (repr(self.alert), )
-
-
-class msg_inv(object):
- command = b"inv"
-
- def __init__(self, inv=None):
- if inv is None:
- self.inv = []
- else:
- self.inv = inv
-
- def deserialize(self, f):
- self.inv = deser_vector(f, CInv)
-
- def serialize(self):
- return ser_vector(self.inv)
-
- def __repr__(self):
- return "msg_inv(inv=%s)" % (repr(self.inv))
-
-
-class msg_getdata(object):
- command = b"getdata"
-
- def __init__(self, inv=None):
- self.inv = inv if inv != None else []
-
- def deserialize(self, f):
- self.inv = deser_vector(f, CInv)
-
- def serialize(self):
- return ser_vector(self.inv)
-
- def __repr__(self):
- return "msg_getdata(inv=%s)" % (repr(self.inv))
-
-
-class msg_getblocks(object):
- command = b"getblocks"
+MESSAGEMAP = {
+ b"addr": msg_addr,
+ b"block": msg_block,
+ b"blocktxn": msg_blocktxn,
+ b"cmpctblock": msg_cmpctblock,
+ b"feefilter": msg_feefilter,
+ b"getaddr": msg_getaddr,
+ b"getblocks": msg_getblocks,
+ b"getblocktxn": msg_getblocktxn,
+ b"getdata": msg_getdata,
+ b"getheaders": msg_getheaders,
+ b"headers": msg_headers,
+ b"inv": msg_inv,
+ b"mempool": msg_mempool,
+ b"ping": msg_ping,
+ b"pong": msg_pong,
+ b"reject": msg_reject,
+ b"sendcmpct": msg_sendcmpct,
+ b"sendheaders": msg_sendheaders,
+ b"tx": msg_tx,
+ b"verack": msg_verack,
+ b"version": msg_version,
+}
+
+MAGIC_BYTES = {
+ "mainnet": b"\xf9\xbe\xb4\xd9", # mainnet
+ "testnet3": b"\x0b\x11\x09\x07", # testnet3
+ "regtest": b"\xfa\xbf\xb5\xda", # regtest
+}
+
+class P2PConnection(asyncore.dispatcher):
+ """A low-level connection object to a node's P2P interface.
+
+ This class is responsible for:
+
+ - opening and closing the TCP connection to the node
+ - reading bytes from and writing bytes to the socket
+ - deserializing and serializing the P2P message header
+ - logging messages as they are sent and received
+
+ This class contains no logic for handing the P2P message payloads. It must be
+ sub-classed and the on_message() callback overridden."""
def __init__(self):
- self.locator = CBlockLocator()
- self.hashstop = 0
-
- def deserialize(self, f):
- self.locator = CBlockLocator()
- self.locator.deserialize(f)
- self.hashstop = deser_uint256(f)
-
- def serialize(self):
- r = b""
- r += self.locator.serialize()
- r += ser_uint256(self.hashstop)
- return r
-
- def __repr__(self):
- return "msg_getblocks(locator=%s hashstop=%064x)" \
- % (repr(self.locator), self.hashstop)
-
-
-class msg_tx(object):
- command = b"tx"
-
- def __init__(self, tx=CTransaction()):
- self.tx = tx
-
- def deserialize(self, f):
- self.tx.deserialize(f)
-
- def serialize(self):
- return self.tx.serialize_without_witness()
-
- def __repr__(self):
- return "msg_tx(tx=%s)" % (repr(self.tx))
-
-class msg_witness_tx(msg_tx):
-
- def serialize(self):
- return self.tx.serialize_with_witness()
-
-
-class msg_block(object):
- command = b"block"
-
- def __init__(self, block=None):
- if block is None:
- self.block = CBlock()
- else:
- self.block = block
-
- def deserialize(self, f):
- self.block.deserialize(f)
-
- def serialize(self):
- return self.block.serialize()
-
- def __repr__(self):
- return "msg_block(block=%s)" % (repr(self.block))
-
-# for cases where a user needs tighter control over what is sent over the wire
-# note that the user must supply the name of the command, and the data
-class msg_generic(object):
- def __init__(self, command, data=None):
- self.command = command
- self.data = data
-
- def serialize(self):
- return self.data
-
- def __repr__(self):
- return "msg_generic()"
-
-class msg_witness_block(msg_block):
+ # All P2PConnections must be created before starting the NetworkThread.
+ # assert that the network thread is not running.
+ assert not network_thread_running()
- def serialize(self):
- r = self.block.serialize(with_witness=True)
- return r
+ super().__init__(map=mininode_socket_map)
-class msg_getaddr(object):
- command = b"getaddr"
-
- def __init__(self):
- pass
-
- def deserialize(self, f):
- pass
-
- def serialize(self):
- return b""
-
- def __repr__(self):
- return "msg_getaddr()"
-
-
-class msg_ping_prebip31(object):
- command = b"ping"
-
- def __init__(self):
- pass
-
- def deserialize(self, f):
- pass
-
- def serialize(self):
- return b""
-
- def __repr__(self):
- return "msg_ping() (pre-bip31)"
-
-
-class msg_ping(object):
- command = b"ping"
-
- def __init__(self, nonce=0):
- self.nonce = nonce
-
- def deserialize(self, f):
- self.nonce = struct.unpack("<Q", f.read(8))[0]
-
- def serialize(self):
- r = b""
- r += struct.pack("<Q", self.nonce)
- return r
-
- def __repr__(self):
- return "msg_ping(nonce=%08x)" % self.nonce
-
-
-class msg_pong(object):
- command = b"pong"
-
- def __init__(self, nonce=0):
- self.nonce = nonce
-
- def deserialize(self, f):
- self.nonce = struct.unpack("<Q", f.read(8))[0]
-
- def serialize(self):
- r = b""
- r += struct.pack("<Q", self.nonce)
- return r
-
- def __repr__(self):
- return "msg_pong(nonce=%08x)" % self.nonce
-
-
-class msg_mempool(object):
- command = b"mempool"
-
- def __init__(self):
- pass
-
- def deserialize(self, f):
- pass
-
- def serialize(self):
- return b""
-
- def __repr__(self):
- return "msg_mempool()"
-
-class msg_sendheaders(object):
- command = b"sendheaders"
-
- def __init__(self):
- pass
-
- def deserialize(self, f):
- pass
-
- def serialize(self):
- return b""
-
- def __repr__(self):
- return "msg_sendheaders()"
-
-
-# getheaders message has
-# number of entries
-# vector of hashes
-# hash_stop (hash of last desired block header, 0 to get as many as possible)
-class msg_getheaders(object):
- command = b"getheaders"
-
- def __init__(self):
- self.locator = CBlockLocator()
- self.hashstop = 0
-
- def deserialize(self, f):
- self.locator = CBlockLocator()
- self.locator.deserialize(f)
- self.hashstop = deser_uint256(f)
-
- def serialize(self):
- r = b""
- r += self.locator.serialize()
- r += ser_uint256(self.hashstop)
- return r
-
- def __repr__(self):
- return "msg_getheaders(locator=%s, stop=%064x)" \
- % (repr(self.locator), self.hashstop)
-
-
-# headers message has
-# <count> <vector of block headers>
-class msg_headers(object):
- command = b"headers"
-
- def __init__(self):
- self.headers = []
-
- def deserialize(self, f):
- # comment in bitcoind indicates these should be deserialized as blocks
- blocks = deser_vector(f, CBlock)
- for x in blocks:
- self.headers.append(CBlockHeader(x))
-
- def serialize(self):
- blocks = [CBlock(x) for x in self.headers]
- return ser_vector(blocks)
-
- def __repr__(self):
- return "msg_headers(headers=%s)" % repr(self.headers)
-
-
-class msg_reject(object):
- command = b"reject"
- REJECT_MALFORMED = 1
+ def peer_connect(self, dstaddr, dstport, net="regtest"):
+ self.dstaddr = dstaddr
+ self.dstport = dstport
+ self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+ self.sendbuf = b""
+ self.recvbuf = b""
+ self.state = "connecting"
+ self.network = net
+ self.disconnect = False
- def __init__(self):
- self.message = b""
- self.code = 0
- self.reason = b""
- self.data = 0
-
- def deserialize(self, f):
- self.message = deser_string(f)
- self.code = struct.unpack("<B", f.read(1))[0]
- self.reason = deser_string(f)
- if (self.code != self.REJECT_MALFORMED and
- (self.message == b"block" or self.message == b"tx")):
- self.data = deser_uint256(f)
-
- def serialize(self):
- r = ser_string(self.message)
- r += struct.pack("<B", self.code)
- r += ser_string(self.reason)
- if (self.code != self.REJECT_MALFORMED and
- (self.message == b"block" or self.message == b"tx")):
- r += ser_uint256(self.data)
- return r
-
- def __repr__(self):
- return "msg_reject: %s %d %s [%064x]" \
- % (self.message, self.code, self.reason, self.data)
-
-class msg_feefilter(object):
- command = b"feefilter"
-
- def __init__(self, feerate=0):
- self.feerate = feerate
-
- def deserialize(self, f):
- self.feerate = struct.unpack("<Q", f.read(8))[0]
-
- def serialize(self):
- r = b""
- r += struct.pack("<Q", self.feerate)
- return r
-
- def __repr__(self):
- return "msg_feefilter(feerate=%08x)" % self.feerate
-
-class msg_sendcmpct(object):
- command = b"sendcmpct"
+ logger.info('Connecting to Bitcoin Node: %s:%d' % (self.dstaddr, self.dstport))
- def __init__(self):
- self.announce = False
- self.version = 1
+ try:
+ self.connect((dstaddr, dstport))
+ except:
+ self.handle_close()
- def deserialize(self, f):
- self.announce = struct.unpack("<?", f.read(1))[0]
- self.version = struct.unpack("<Q", f.read(8))[0]
+ def peer_disconnect(self):
+ # Connection could have already been closed by other end.
+ if self.state == "connected":
+ self.disconnect_node()
- def serialize(self):
- r = b""
- r += struct.pack("<?", self.announce)
- r += struct.pack("<Q", self.version)
- return r
+ # Connection and disconnection methods
- def __repr__(self):
- return "msg_sendcmpct(announce=%s, version=%lu)" % (self.announce, self.version)
+ def handle_connect(self):
+ """asyncore callback when a connection is opened."""
+ if self.state != "connected":
+ logger.debug("Connected & Listening: %s:%d" % (self.dstaddr, self.dstport))
+ self.state = "connected"
+ self.on_open()
-class msg_cmpctblock(object):
- command = b"cmpctblock"
+ def handle_close(self):
+ """asyncore callback when a connection is closed."""
+ logger.debug("Closing connection to: %s:%d" % (self.dstaddr, self.dstport))
+ self.state = "closed"
+ self.recvbuf = b""
+ self.sendbuf = b""
+ try:
+ self.close()
+ except:
+ pass
+ self.on_close()
- def __init__(self, header_and_shortids = None):
- self.header_and_shortids = header_and_shortids
+ def disconnect_node(self):
+ """Disconnect the p2p connection.
- def deserialize(self, f):
- self.header_and_shortids = P2PHeaderAndShortIDs()
- self.header_and_shortids.deserialize(f)
+ Called by the test logic thread. Causes the p2p connection
+ to be disconnected on the next iteration of the asyncore loop."""
+ self.disconnect = True
- def serialize(self):
- r = b""
- r += self.header_and_shortids.serialize()
- return r
+ # Socket read methods
- def __repr__(self):
- return "msg_cmpctblock(HeaderAndShortIDs=%s)" % repr(self.header_and_shortids)
+ def handle_read(self):
+ """asyncore callback when data is read from the socket."""
+ t = self.recv(8192)
+ if len(t) > 0:
+ self.recvbuf += t
+ self._on_data()
+
+ def _on_data(self):
+ """Try to read P2P messages from the recv buffer.
+
+ This method reads data from the buffer in a loop. It deserializes,
+ parses and verifies the P2P header, then passes the P2P payload to
+ the on_message callback for processing."""
+ try:
+ while True:
+ if len(self.recvbuf) < 4:
+ return
+ if self.recvbuf[:4] != MAGIC_BYTES[self.network]:
+ raise ValueError("got garbage %s" % repr(self.recvbuf))
+ if len(self.recvbuf) < 4 + 12 + 4 + 4:
+ return
+ command = self.recvbuf[4:4+12].split(b"\x00", 1)[0]
+ msglen = struct.unpack("<i", self.recvbuf[4+12:4+12+4])[0]
+ checksum = self.recvbuf[4+12+4:4+12+4+4]
+ if len(self.recvbuf) < 4 + 12 + 4 + 4 + msglen:
+ return
+ msg = self.recvbuf[4+12+4+4:4+12+4+4+msglen]
+ th = sha256(msg)
+ h = sha256(th)
+ if checksum != h[:4]:
+ raise ValueError("got bad checksum " + repr(self.recvbuf))
+ self.recvbuf = self.recvbuf[4+12+4+4+msglen:]
+ if command not in MESSAGEMAP:
+ raise ValueError("Received unknown command from %s:%d: '%s' %s" % (self.dstaddr, self.dstport, command, repr(msg)))
+ f = BytesIO(msg)
+ t = MESSAGEMAP[command]()
+ t.deserialize(f)
+ self._log_message("receive", t)
+ self.on_message(t)
+ except Exception as e:
+ logger.exception('Error reading message:', repr(e))
+ raise
-class msg_getblocktxn(object):
- command = b"getblocktxn"
+ def on_message(self, message):
+ """Callback for processing a P2P payload. Must be overridden by derived class."""
+ raise NotImplementedError
- def __init__(self):
- self.block_txn_request = None
+ # Socket write methods
- def deserialize(self, f):
- self.block_txn_request = BlockTransactionsRequest()
- self.block_txn_request.deserialize(f)
+ def writable(self):
+ """asyncore method to determine whether the handle_write() callback should be called on the next loop."""
+ with mininode_lock:
+ pre_connection = self.state == "connecting"
+ length = len(self.sendbuf)
+ return (length > 0 or pre_connection)
- def serialize(self):
- r = b""
- r += self.block_txn_request.serialize()
- return r
+ def handle_write(self):
+ """asyncore callback when data should be written to the socket."""
+ with mininode_lock:
+ # asyncore does not expose socket connection, only the first read/write
+ # event, thus we must check connection manually here to know when we
+ # actually connect
+ if self.state == "connecting":
+ self.handle_connect()
+ if not self.writable():
+ return
- def __repr__(self):
- return "msg_getblocktxn(block_txn_request=%s)" % (repr(self.block_txn_request))
+ try:
+ sent = self.send(self.sendbuf)
+ except:
+ self.handle_close()
+ return
+ self.sendbuf = self.sendbuf[sent:]
-class msg_blocktxn(object):
- command = b"blocktxn"
+ def send_message(self, message, pushbuf=False):
+ """Send a P2P message over the socket.
- def __init__(self):
- self.block_transactions = BlockTransactions()
+ This method takes a P2P payload, builds the P2P header and adds
+ the message to the send buffer to be sent over the socket."""
+ if self.state != "connected" and not pushbuf:
+ raise IOError('Not connected, no pushbuf')
+ self._log_message("send", message)
+ command = message.command
+ data = message.serialize()
+ tmsg = MAGIC_BYTES[self.network]
+ tmsg += command
+ tmsg += b"\x00" * (12 - len(command))
+ tmsg += struct.pack("<I", len(data))
+ th = sha256(data)
+ h = sha256(th)
+ tmsg += h[:4]
+ tmsg += data
+ with mininode_lock:
+ if (len(self.sendbuf) == 0 and not pushbuf):
+ try:
+ sent = self.send(tmsg)
+ self.sendbuf = tmsg[sent:]
+ except BlockingIOError:
+ self.sendbuf = tmsg
+ else:
+ self.sendbuf += tmsg
- def deserialize(self, f):
- self.block_transactions.deserialize(f)
+ # Class utility methods
- def serialize(self):
- r = b""
- r += self.block_transactions.serialize()
- return r
+ def _log_message(self, direction, msg):
+ """Logs a message being sent or received over the connection."""
+ if direction == "send":
+ log_message = "Send message to "
+ elif direction == "receive":
+ log_message = "Received message from "
+ log_message += "%s:%d: %s" % (self.dstaddr, self.dstport, repr(msg)[:500])
+ if len(log_message) > 500:
+ log_message += "... (msg truncated)"
+ logger.debug(log_message)
- 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
+class P2PInterface(P2PConnection):
+ """A high-level P2P interface class for communicating with a Bitcoin node.
-class NodeConnCB(object):
- """Callback and helper functions for P2P connection to a bitcoind node.
+ This class provides high-level callbacks for processing P2P message
+ payloads, as well as convenience methods for interacting with the
+ node over P2P.
Individual testcases should subclass this and override the on_* methods
- if they want to alter message handling behaviour.
- """
-
+ if they want to alter message handling behaviour."""
def __init__(self):
- # Track whether we have a P2P connection open to the node
- self.connected = False
- self.connection = None
+ super().__init__()
# Track number of messages of each type received and the most recent
# message of each type
@@ -1474,99 +264,89 @@ class NodeConnCB(object):
# A count of the number of ping messages we've sent to the node
self.ping_counter = 1
- # deliver_sleep_time is helpful for debugging race conditions in p2p
- # tests; it causes message delivery to sleep for the specified time
- # before acquiring the global lock and delivering the next message.
- self.deliver_sleep_time = None
+ # The network services received from the peer
+ self.nServices = 0
+
+ def peer_connect(self, *args, services=NODE_NETWORK|NODE_WITNESS, send_version=True, **kwargs):
+ super().peer_connect(*args, **kwargs)
+
+ if send_version:
+ # Send a version msg
+ vt = msg_version()
+ vt.nServices = services
+ vt.addrTo.ip = self.dstaddr
+ vt.addrTo.port = self.dstport
+ vt.addrFrom.ip = "0.0.0.0"
+ vt.addrFrom.port = 0
+ self.send_message(vt, True)
# Message receiving methods
- def deliver(self, conn, message):
+ def on_message(self, message):
"""Receive message and dispatch message to appropriate callback.
We keep a count of how many of each message type has been received
- and the most recent message of each type.
-
- Optionally waits for deliver_sleep_time before dispatching message.
- """
-
- deliver_sleep = self.get_deliver_sleep_time()
- if deliver_sleep is not None:
- time.sleep(deliver_sleep)
+ and the most recent message of each type."""
with mininode_lock:
try:
command = message.command.decode('ascii')
self.message_count[command] += 1
self.last_message[command] = message
- getattr(self, 'on_' + command)(conn, message)
+ getattr(self, 'on_' + command)(message)
except:
- print("ERROR delivering %s (%s)" % (repr(message),
- sys.exc_info()[0]))
-
- def get_deliver_sleep_time(self):
- with mininode_lock:
- return self.deliver_sleep_time
+ print("ERROR delivering %s (%s)" % (repr(message), sys.exc_info()[0]))
+ raise
# Callback methods. Can be overridden by subclasses in individual test
# cases to provide custom message handling behaviour.
- def on_open(self, conn):
- self.connected = True
-
- def on_close(self, conn):
- self.connected = False
- self.connection = None
-
- def on_addr(self, conn, message): pass
- def on_alert(self, conn, message): pass
- def on_block(self, conn, message): pass
- def on_blocktxn(self, conn, message): pass
- def on_cmpctblock(self, conn, message): pass
- def on_feefilter(self, conn, message): pass
- def on_getaddr(self, conn, message): pass
- def on_getblocks(self, conn, message): pass
- def on_getblocktxn(self, conn, message): pass
- def on_getdata(self, conn, message): pass
- def on_getheaders(self, conn, message): pass
- def on_headers(self, conn, message): pass
- def on_mempool(self, conn): pass
- def on_pong(self, conn, message): pass
- def on_reject(self, conn, message): pass
- def on_sendcmpct(self, conn, message): pass
- def on_sendheaders(self, conn, message): pass
- def on_tx(self, conn, message): pass
-
- def on_inv(self, conn, message):
+ def on_open(self):
+ pass
+
+ def on_close(self):
+ pass
+
+ def on_addr(self, message): pass
+ def on_block(self, message): pass
+ def on_blocktxn(self, message): pass
+ def on_cmpctblock(self, message): pass
+ def on_feefilter(self, message): pass
+ def on_getaddr(self, message): pass
+ def on_getblocks(self, message): pass
+ def on_getblocktxn(self, message): pass
+ def on_getdata(self, message): pass
+ def on_getheaders(self, message): pass
+ def on_headers(self, message): pass
+ def on_mempool(self, message): pass
+ def on_pong(self, message): pass
+ def on_reject(self, message): pass
+ def on_sendcmpct(self, message): pass
+ def on_sendheaders(self, message): pass
+ def on_tx(self, message): pass
+
+ def on_inv(self, message):
want = msg_getdata()
for i in message.inv:
if i.type != 0:
want.inv.append(i)
if len(want.inv):
- conn.send_message(want)
+ self.send_message(want)
- def on_ping(self, conn, message):
- if conn.ver_send > BIP0031_VERSION:
- conn.send_message(msg_pong(message.nonce))
+ def on_ping(self, message):
+ self.send_message(msg_pong(message.nonce))
- def on_verack(self, conn, message):
- conn.ver_recv = conn.ver_send
+ def on_verack(self, message):
self.verack_received = True
- def on_version(self, conn, message):
- if message.nVersion >= 209:
- conn.send_message(msg_verack())
- conn.ver_send = min(MY_VERSION, message.nVersion)
- if message.nVersion < 209:
- conn.ver_recv = conn.ver_send
- conn.nServices = message.nServices
+ def on_version(self, message):
+ assert message.nVersion >= MIN_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format(message.nVersion, MIN_VERSION_SUPPORTED)
+ self.send_message(msg_verack())
+ self.nServices = message.nServices
# Connection helper methods
- def add_connection(self, conn):
- self.connection = conn
-
def wait_for_disconnect(self, timeout=60):
- test_function = lambda: not self.connected
+ test_function = lambda: self.state != "connected"
wait_until(test_function, timeout=timeout, lock=mininode_lock)
# Message receiving helper methods
@@ -1598,12 +378,6 @@ class NodeConnCB(object):
# Message sending helper functions
- def send_message(self, message):
- if self.connection:
- self.connection.send_message(message)
- else:
- logger.error("Cannot send message. No connection to node!")
-
def send_and_ping(self, message):
self.send_message(message)
self.sync_with_ping()
@@ -1614,211 +388,25 @@ class NodeConnCB(object):
test_function = lambda: self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter
wait_until(test_function, timeout=timeout, lock=mininode_lock)
self.ping_counter += 1
- return True
-
-# The actual NodeConn class
-# This class provides an interface for a p2p connection to a specified node
-class NodeConn(asyncore.dispatcher):
- messagemap = {
- b"version": msg_version,
- b"verack": msg_verack,
- b"addr": msg_addr,
- b"alert": msg_alert,
- b"inv": msg_inv,
- b"getdata": msg_getdata,
- b"getblocks": msg_getblocks,
- b"tx": msg_tx,
- b"block": msg_block,
- b"getaddr": msg_getaddr,
- b"ping": msg_ping,
- b"pong": msg_pong,
- b"headers": msg_headers,
- b"getheaders": msg_getheaders,
- b"reject": msg_reject,
- b"mempool": msg_mempool,
- b"feefilter": msg_feefilter,
- b"sendheaders": msg_sendheaders,
- b"sendcmpct": msg_sendcmpct,
- b"cmpctblock": msg_cmpctblock,
- b"getblocktxn": msg_getblocktxn,
- b"blocktxn": msg_blocktxn
- }
- MAGIC_BYTES = {
- "mainnet": b"\xf9\xbe\xb4\xd9", # mainnet
- "testnet3": b"\x0b\x11\x09\x07", # testnet3
- "regtest": b"\xfa\xbf\xb5\xda", # regtest
- }
-
- def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=NODE_NETWORK, send_version=True):
- asyncore.dispatcher.__init__(self, map=mininode_socket_map)
- self.dstaddr = dstaddr
- self.dstport = dstport
- self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
- self.sendbuf = b""
- self.recvbuf = b""
- self.ver_send = 209
- self.ver_recv = 209
- self.last_sent = 0
- self.state = "connecting"
- self.network = net
- self.cb = callback
- self.disconnect = False
- self.nServices = 0
-
- if send_version:
- # stuff version msg into sendbuf
- vt = msg_version()
- vt.nServices = services
- vt.addrTo.ip = self.dstaddr
- vt.addrTo.port = self.dstport
- vt.addrFrom.ip = "0.0.0.0"
- vt.addrFrom.port = 0
- self.send_message(vt, True)
- logger.info('Connecting to Bitcoin Node: %s:%d' % (self.dstaddr, self.dstport))
-
- try:
- self.connect((dstaddr, dstport))
- except:
- self.handle_close()
- self.rpc = rpc
-
- def handle_connect(self):
- if self.state != "connected":
- logger.debug("Connected & Listening: %s:%d" % (self.dstaddr, self.dstport))
- self.state = "connected"
- self.cb.on_open(self)
-
- def handle_close(self):
- logger.debug("Closing connection to: %s:%d" % (self.dstaddr, self.dstport))
- self.state = "closed"
- self.recvbuf = b""
- self.sendbuf = b""
- try:
- self.close()
- except:
- pass
- self.cb.on_close(self)
-
- def handle_read(self):
- try:
- t = self.recv(8192)
- if len(t) > 0:
- self.recvbuf += t
- self.got_data()
- except:
- pass
-
- def readable(self):
- return True
-
- def writable(self):
- with mininode_lock:
- pre_connection = self.state == "connecting"
- length = len(self.sendbuf)
- return (length > 0 or pre_connection)
- def handle_write(self):
- with mininode_lock:
- # asyncore does not expose socket connection, only the first read/write
- # event, thus we must check connection manually here to know when we
- # actually connect
- if self.state == "connecting":
- self.handle_connect()
- if not self.writable():
- return
-
- try:
- sent = self.send(self.sendbuf)
- except:
- self.handle_close()
- return
- self.sendbuf = self.sendbuf[sent:]
-
- def got_data(self):
- try:
- while True:
- if len(self.recvbuf) < 4:
- return
- if self.recvbuf[:4] != self.MAGIC_BYTES[self.network]:
- raise ValueError("got garbage %s" % repr(self.recvbuf))
- if self.ver_recv < 209:
- if len(self.recvbuf) < 4 + 12 + 4:
- return
- command = self.recvbuf[4:4+12].split(b"\x00", 1)[0]
- msglen = struct.unpack("<i", self.recvbuf[4+12:4+12+4])[0]
- checksum = None
- if len(self.recvbuf) < 4 + 12 + 4 + msglen:
- return
- msg = self.recvbuf[4+12+4:4+12+4+msglen]
- self.recvbuf = self.recvbuf[4+12+4+msglen:]
- else:
- if len(self.recvbuf) < 4 + 12 + 4 + 4:
- return
- command = self.recvbuf[4:4+12].split(b"\x00", 1)[0]
- msglen = struct.unpack("<i", self.recvbuf[4+12:4+12+4])[0]
- checksum = self.recvbuf[4+12+4:4+12+4+4]
- if len(self.recvbuf) < 4 + 12 + 4 + 4 + msglen:
- return
- msg = self.recvbuf[4+12+4+4:4+12+4+4+msglen]
- th = sha256(msg)
- h = sha256(th)
- if checksum != h[:4]:
- raise ValueError("got bad checksum " + repr(self.recvbuf))
- self.recvbuf = self.recvbuf[4+12+4+4+msglen:]
- if command in self.messagemap:
- f = BytesIO(msg)
- t = self.messagemap[command]()
- t.deserialize(f)
- self.got_message(t)
- else:
- logger.warning("Received unknown command from %s:%d: '%s' %s" % (self.dstaddr, self.dstport, command, repr(msg)))
- except Exception as e:
- logger.exception('got_data:', repr(e))
-
- def send_message(self, message, pushbuf=False):
- if self.state != "connected" and not pushbuf:
- raise IOError('Not connected, no pushbuf')
- self._log_message("send", message)
- command = message.command
- data = message.serialize()
- tmsg = self.MAGIC_BYTES[self.network]
- tmsg += command
- tmsg += b"\x00" * (12 - len(command))
- tmsg += struct.pack("<I", len(data))
- if self.ver_send >= 209:
- th = sha256(data)
- h = sha256(th)
- tmsg += h[:4]
- tmsg += data
- with mininode_lock:
- self.sendbuf += tmsg
- self.last_sent = time.time()
-
- def got_message(self, message):
- if message.command == b"version":
- if message.nVersion <= BIP0031_VERSION:
- self.messagemap[b'ping'] = msg_ping_prebip31
- if self.last_sent + 30 * 60 < time.time():
- self.send_message(self.messagemap[b'ping']())
- self._log_message("receive", message)
- self.cb.deliver(self, message)
-
- def _log_message(self, direction, msg):
- if direction == "send":
- log_message = "Send message to "
- elif direction == "receive":
- log_message = "Received message from "
- log_message += "%s:%d: %s" % (self.dstaddr, self.dstport, repr(msg)[:500])
- if len(log_message) > 500:
- log_message += "... (msg truncated)"
- logger.debug(log_message)
+# Keep our own socket map for asyncore, so that we can track disconnects
+# ourselves (to workaround an issue with closing an asyncore socket when
+# using select)
+mininode_socket_map = dict()
- def disconnect_node(self):
- self.disconnect = True
+# One lock for synchronizing all data access between the networking thread (see
+# NetworkThread below) and the thread running the test logic. For simplicity,
+# P2PConnection acquires this lock whenever delivering a message to a P2PInterface,
+# and whenever adding anything to the send buffer (in send_message()). This
+# lock should be acquired in the thread running the test logic to synchronize
+# access to any data shared with the P2PInterface or P2PConnection.
+mininode_lock = threading.RLock()
+class NetworkThread(threading.Thread):
+ def __init__(self):
+ super().__init__(name="NetworkThread")
-class NetworkThread(Thread):
def run(self):
while mininode_socket_map:
# We check for whether to disconnect outside of the asyncore
@@ -1828,15 +416,27 @@ class NetworkThread(Thread):
for fd, obj in mininode_socket_map.items():
if obj.disconnect:
disconnected.append(obj)
- [ obj.handle_close() for obj in disconnected ]
+ [obj.handle_close() for obj in disconnected]
asyncore.loop(0.1, use_poll=True, map=mininode_socket_map, count=1)
+ logger.debug("Network thread closing")
+
+def network_thread_start():
+ """Start the network thread."""
+ # Only one network thread may run at a time
+ assert not network_thread_running()
+
+ NetworkThread().start()
+def network_thread_running():
+ """Return whether the network thread is running."""
+ return any([thread.name == "NetworkThread" for thread in threading.enumerate()])
-# An exception we can raise if we detect a potential disconnect
-# (p2p or rpc) before the test is complete
-class EarlyDisconnectError(Exception):
- def __init__(self, value):
- self.value = value
+def network_thread_join(timeout=10):
+ """Wait timeout seconds for the network thread to terminate.
- def __str__(self):
- return repr(self.value)
+ Throw if the network thread doesn't terminate in timeout seconds."""
+ network_threads = [thread for thread in threading.enumerate() if thread.name == "NetworkThread"]
+ assert len(network_threads) <= 1
+ for thread in network_threads:
+ thread.join(timeout)
+ assert not thread.is_alive()
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index 8f5339a02a..a4c046bd3d 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -370,7 +370,7 @@ class CScriptTruncatedPushDataError(CScriptInvalidError):
super(CScriptTruncatedPushDataError, self).__init__(msg)
# This is used, eg, for blockchain heights in coinbase scripts (bip34)
-class CScriptNum(object):
+class CScriptNum():
def __init__(self, d=0):
self.value = d
diff --git a/test/functional/test_framework/segwit_addr.py b/test/functional/test_framework/segwit_addr.py
new file mode 100644
index 0000000000..02368e938f
--- /dev/null
+++ b/test/functional/test_framework/segwit_addr.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 Pieter Wuille
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Reference implementation for Bech32 and segwit addresses."""
+
+
+CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
+
+
+def bech32_polymod(values):
+ """Internal function that computes the Bech32 checksum."""
+ generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
+ chk = 1
+ for value in values:
+ top = chk >> 25
+ chk = (chk & 0x1ffffff) << 5 ^ value
+ for i in range(5):
+ chk ^= generator[i] if ((top >> i) & 1) else 0
+ return chk
+
+
+def bech32_hrp_expand(hrp):
+ """Expand the HRP into values for checksum computation."""
+ return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
+
+
+def bech32_verify_checksum(hrp, data):
+ """Verify a checksum given HRP and converted data characters."""
+ return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1
+
+
+def bech32_create_checksum(hrp, data):
+ """Compute the checksum values given HRP and data."""
+ values = bech32_hrp_expand(hrp) + data
+ polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
+ return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
+
+
+def bech32_encode(hrp, data):
+ """Compute a Bech32 string given HRP and data values."""
+ combined = data + bech32_create_checksum(hrp, data)
+ return hrp + '1' + ''.join([CHARSET[d] for d in combined])
+
+
+def bech32_decode(bech):
+ """Validate a Bech32 string, and determine HRP and data."""
+ if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
+ (bech.lower() != bech and bech.upper() != bech)):
+ return (None, None)
+ bech = bech.lower()
+ pos = bech.rfind('1')
+ if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
+ return (None, None)
+ if not all(x in CHARSET for x in bech[pos+1:]):
+ return (None, None)
+ hrp = bech[:pos]
+ data = [CHARSET.find(x) for x in bech[pos+1:]]
+ if not bech32_verify_checksum(hrp, data):
+ return (None, None)
+ return (hrp, data[:-6])
+
+
+def convertbits(data, frombits, tobits, pad=True):
+ """General power-of-2 base conversion."""
+ acc = 0
+ bits = 0
+ ret = []
+ maxv = (1 << tobits) - 1
+ max_acc = (1 << (frombits + tobits - 1)) - 1
+ for value in data:
+ if value < 0 or (value >> frombits):
+ return None
+ acc = ((acc << frombits) | value) & max_acc
+ bits += frombits
+ while bits >= tobits:
+ bits -= tobits
+ ret.append((acc >> bits) & maxv)
+ if pad:
+ if bits:
+ ret.append((acc << (tobits - bits)) & maxv)
+ elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
+ return None
+ return ret
+
+
+def decode(hrp, addr):
+ """Decode a segwit address."""
+ hrpgot, data = bech32_decode(addr)
+ if hrpgot != hrp:
+ return (None, None)
+ decoded = convertbits(data[1:], 5, 8, False)
+ if decoded is None or len(decoded) < 2 or len(decoded) > 40:
+ return (None, None)
+ if data[0] > 16:
+ return (None, None)
+ if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
+ return (None, None)
+ return (data[0], decoded)
+
+
+def encode(hrp, witver, witprog):
+ """Encode a segwit address."""
+ ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
+ if decode(hrp, ret) == (None, None):
+ return None
+ return ret
diff --git a/test/functional/test_framework/socks5.py b/test/functional/test_framework/socks5.py
index 0070844168..7b40c47fbf 100644
--- a/test/functional/test_framework/socks5.py
+++ b/test/functional/test_framework/socks5.py
@@ -31,7 +31,7 @@ def recvall(s, n):
return rv
### Implementation classes
-class Socks5Configuration(object):
+class Socks5Configuration():
"""Proxy configuration."""
def __init__(self):
self.addr = None # Bind address (must be set)
@@ -39,7 +39,7 @@ class Socks5Configuration(object):
self.unauth = False # Support unauthenticated
self.auth = False # Support authentication
-class Socks5Command(object):
+class Socks5Command():
"""Information about an incoming socks5 command."""
def __init__(self, cmd, atyp, addr, port, username, password):
self.cmd = cmd # Command (one of Command.*)
@@ -51,7 +51,7 @@ class Socks5Command(object):
def __repr__(self):
return 'Socks5Command(%s,%s,%s,%s,%s,%s)' % (self.cmd, self.atyp, self.addr, self.port, self.username, self.password)
-class Socks5Connection(object):
+class Socks5Connection():
def __init__(self, serv, conn, peer):
self.serv = serv
self.conn = conn
@@ -122,7 +122,7 @@ class Socks5Connection(object):
finally:
self.conn.close()
-class Socks5Server(object):
+class Socks5Server():
def __init__(self, conf):
self.conf = conf
self.s = socket.socket(conf.af)
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 103651f175..a46312d62c 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Base class for RPC testing."""
-from collections import deque
from enum import Enum
import logging
import optparse
@@ -14,7 +13,6 @@ import shutil
import sys
import tempfile
import time
-import traceback
from .authproxy import JSONRPCException
from . import coverage
@@ -43,9 +41,7 @@ TEST_EXIT_PASSED = 0
TEST_EXIT_FAILED = 1
TEST_EXIT_SKIPPED = 77
-BITCOIND_PROC_WAIT_TIMEOUT = 60
-
-class BitcoinTestFramework(object):
+class BitcoinTestFramework():
"""Base class for a bitcoin test script.
Individual bitcoin test scripts should subclass this class and override the set_test_params() and run_test() methods.
@@ -104,8 +100,11 @@ class BitcoinTestFramework(object):
check_json_precision()
+ self.options.cachedir = os.path.abspath(self.options.cachedir)
+
# Set up temp directory and start logging
if self.options.tmpdir:
+ self.options.tmpdir = os.path.abspath(self.options.tmpdir)
os.makedirs(self.options.tmpdir, exist_ok=False)
else:
self.options.tmpdir = tempfile.mkdtemp(prefix="test")
@@ -148,32 +147,19 @@ class BitcoinTestFramework(object):
shutil.rmtree(self.options.tmpdir)
else:
self.log.warning("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 = [self.options.tmpdir + "/test_framework.log"]
- filenames += glob.glob(self.options.tmpdir + "/node*/regtest/debug.log")
- MAX_LINES_TO_PRINT = 1000
- for fn in filenames:
- try:
- with open(fn, 'r') as f:
- print("From", fn, ":")
- print("".join(deque(f, MAX_LINES_TO_PRINT)))
- except OSError:
- print("Opening file %s failed." % fn)
- traceback.print_exc()
if success == TestStatus.PASSED:
self.log.info("Tests successful")
- sys.exit(TEST_EXIT_PASSED)
+ exit_code = TEST_EXIT_PASSED
elif success == TestStatus.SKIPPED:
self.log.info("Test skipped")
- sys.exit(TEST_EXIT_SKIPPED)
+ exit_code = TEST_EXIT_SKIPPED
else:
self.log.error("Test failed. Test logging available at %s/test_framework.log", self.options.tmpdir)
- logging.shutdown()
- sys.exit(TEST_EXIT_FAILED)
+ self.log.error("Hint: Call {} '{}' to consolidate all logs".format(os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../combine_logs.py"), self.options.tmpdir))
+ exit_code = TEST_EXIT_FAILED
+ logging.shutdown()
+ sys.exit(exit_code)
# Methods to override in subclass test scripts.
def set_test_params(self):
@@ -263,8 +249,7 @@ class BitcoinTestFramework(object):
def stop_node(self, i):
"""Stop a bitcoind test node"""
self.nodes[i].stop_node()
- while not self.nodes[i].is_node_stopped():
- time.sleep(0.1)
+ self.nodes[i].wait_until_stopped()
def stop_nodes(self):
"""Stop multiple bitcoind test nodes"""
@@ -274,8 +259,12 @@ class BitcoinTestFramework(object):
for node in self.nodes:
# Wait for nodes to stop
- while not node.is_node_stopped():
- time.sleep(0.1)
+ node.wait_until_stopped()
+
+ def restart_node(self, i, extra_args=None):
+ """Stop and start a test node"""
+ self.stop_node(i)
+ self.start_node(i, extra_args)
def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None):
with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr:
@@ -428,7 +417,7 @@ class BitcoinTestFramework(object):
self.disable_mocktime()
for i in range(MAX_NODES):
os.remove(log_filename(self.options.cachedir, i, "debug.log"))
- os.remove(log_filename(self.options.cachedir, i, "db.log"))
+ os.remove(log_filename(self.options.cachedir, i, "wallets/db.log"))
os.remove(log_filename(self.options.cachedir, i, "peers.dat"))
os.remove(log_filename(self.options.cachedir, i, "fee_estimates.dat"))
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index efb3ac9d16..a9248c764e 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -13,12 +13,16 @@ import os
import subprocess
import time
+from .authproxy import JSONRPCException
from .util import (
assert_equal,
get_rpc_proxy,
rpc_url,
+ wait_until,
+ p2p_port,
)
-from .authproxy import JSONRPCException
+
+BITCOIND_PROC_WAIT_TIMEOUT = 60
class TestNode():
"""A class for representing a bitcoind node under test.
@@ -28,9 +32,11 @@ class TestNode():
- state about the node (whether it's running, etc)
- a Python subprocess.Popen object representing the running process
- an RPC connection to the node
+ - one or more P2P connections to the node
- To make things easier for the test writer, a bit of magic is happening under the covers.
- Any unrecognised messages will be dispatched to the RPC connection."""
+
+ To make things easier for the test writer, any unrecognised messages will
+ be dispatched to the RPC connection."""
def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir):
self.index = i
@@ -60,10 +66,12 @@ class TestNode():
self.url = None
self.log = logging.getLogger('TestFramework.node%d' % i)
- def __getattr__(self, *args, **kwargs):
+ self.p2ps = []
+
+ def __getattr__(self, name):
"""Dispatches any unrecognised messages to the RPC connection."""
assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection"
- return self.rpc.__getattr__(*args, **kwargs)
+ return getattr(self.rpc, name)
def start(self, extra_args=None, stderr=None):
"""Start the node."""
@@ -116,6 +124,7 @@ class TestNode():
self.stop()
except http.client.CannotSendRequest:
self.log.exception("Unable to stop node.")
+ del self.p2ps[:]
def is_node_stopped(self):
"""Checks whether the node has stopped.
@@ -125,14 +134,20 @@ class TestNode():
if not self.running:
return True
return_code = self.process.poll()
- if return_code is not None:
- # process has stopped. Assert that it didn't return an error code.
- assert_equal(return_code, 0)
- self.running = False
- self.process = None
- self.log.debug("Node stopped")
- return True
- return False
+ if return_code is None:
+ return False
+
+ # process has stopped. Assert that it didn't return an error code.
+ assert_equal(return_code, 0)
+ self.running = False
+ self.process = None
+ self.rpc_connected = False
+ self.rpc = None
+ self.log.debug("Node stopped")
+ return True
+
+ def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT):
+ wait_until(self.is_node_stopped, timeout=timeout)
def node_encrypt_wallet(self, passphrase):
""""Encrypts the wallet.
@@ -140,17 +155,53 @@ class TestNode():
This causes bitcoind to shutdown, so this method takes
care of cleaning up resources."""
self.encryptwallet(passphrase)
- while not self.is_node_stopped():
- time.sleep(0.1)
- self.rpc = None
- self.rpc_connected = False
+ self.wait_until_stopped()
+
+ def add_p2p_connection(self, p2p_conn, *args, **kwargs):
+ """Add a p2p connection to the node.
+
+ This method adds the p2p connection to the self.p2ps list and also
+ returns the connection to the caller."""
+ if 'dstport' not in kwargs:
+ kwargs['dstport'] = p2p_port(self.index)
+ if 'dstaddr' not in kwargs:
+ kwargs['dstaddr'] = '127.0.0.1'
+
+ p2p_conn.peer_connect(*args, **kwargs)
+ self.p2ps.append(p2p_conn)
+
+ return p2p_conn
+
+ @property
+ def p2p(self):
+ """Return the first p2p connection
+
+ Convenience property - most tests only use a single p2p connection to each
+ node, so this saves having to write node.p2ps[0] many times."""
+ assert self.p2ps, "No p2p connection"
+ return self.p2ps[0]
+
+ def disconnect_p2ps(self):
+ """Close all p2p connections to the node."""
+ for p in self.p2ps:
+ p.peer_disconnect()
+ del self.p2ps[:]
+
class TestNodeCLI():
"""Interface to bitcoin-cli for an individual node"""
def __init__(self, binary, datadir):
+ self.args = []
self.binary = binary
self.datadir = datadir
+ self.input = None
+
+ def __call__(self, *args, input=None):
+ # TestNodeCLI is callable with bitcoin-cli command-line args
+ self.args = [str(arg) for arg in args]
+ self.input = input
+ return self
def __getattr__(self, command):
def dispatcher(*args, **kwargs):
@@ -163,9 +214,14 @@ class TestNodeCLI():
pos_args = [str(arg) for arg in args]
named_args = [str(key) + "=" + str(value) for (key, value) in kwargs.items()]
assert not (pos_args and named_args), "Cannot use positional arguments and named arguments in the same bitcoin-cli call"
- p_args = [self.binary, "-datadir=" + self.datadir]
+ p_args = [self.binary, "-datadir=" + self.datadir] + self.args
if named_args:
p_args += ["-named"]
p_args += [command] + pos_args + named_args
- cli_output = subprocess.check_output(p_args, universal_newlines=True)
- return json.loads(cli_output, parse_float=decimal.Decimal)
+ process = subprocess.Popen(p_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
+ cli_stdout, cli_stderr = process.communicate(input=self.input)
+ returncode = process.poll()
+ if returncode:
+ # Ignore cli_stdout, raise with cli_stderr
+ raise subprocess.CalledProcessError(returncode, self.binary, output=cli_stderr)
+ return json.loads(cli_stdout, parse_float=decimal.Decimal)
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index a14cda07d0..102c903018 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -7,11 +7,13 @@
from base64 import b64encode
from binascii import hexlify, unhexlify
from decimal import Decimal, ROUND_DOWN
+import hashlib
import json
import logging
import os
import random
import re
+from subprocess import CalledProcessError
import time
from . import coverage
@@ -49,6 +51,8 @@ def assert_raises(exc, fun, *args, **kwds):
def assert_raises_message(exc, message, fun, *args, **kwds):
try:
fun(*args, **kwds)
+ except JSONRPCException:
+ raise AssertionError("Use assert_raises_rpc_error() to test RPC failures")
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'])
@@ -57,22 +61,53 @@ def assert_raises_message(exc, message, fun, *args, **kwds):
else:
raise AssertionError("No exception raised")
-def assert_raises_jsonrpc(code, message, fun, *args, **kwds):
+def assert_raises_process_error(returncode, output, fun, *args, **kwds):
+ """Execute a process and asserts the process return code and output.
+
+ Calls function `fun` with arguments `args` and `kwds`. Catches a CalledProcessError
+ and verifies that the return code and output are as expected. Throws AssertionError if
+ no CalledProcessError was raised or if the return code and output are not as expected.
+
+ Args:
+ returncode (int): the process return code.
+ output (string): [a substring of] the process output.
+ fun (function): the function to call. This should execute a process.
+ args*: positional arguments for the function.
+ kwds**: named arguments for the function.
+ """
+ try:
+ fun(*args, **kwds)
+ except CalledProcessError as e:
+ if returncode != e.returncode:
+ raise AssertionError("Unexpected returncode %i" % e.returncode)
+ if output not in e.output:
+ raise AssertionError("Expected substring not found:" + e.output)
+ else:
+ raise AssertionError("No exception raised")
+
+def assert_raises_rpc_error(code, message, fun, *args, **kwds):
"""Run an RPC and verify that a specific JSONRPC exception code and message is raised.
Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException
and verifies that the error code and message are as expected. Throws AssertionError if
- no JSONRPCException was returned or if the error code/message are not as expected.
+ no JSONRPCException was raised or if the error code/message are not as expected.
Args:
code (int), optional: the error code returned by the RPC call (defined
in src/rpc/protocol.h). Set to None if checking the error code is not required.
message (string), optional: [a substring of] the error string returned by the
- RPC call. Set to None if checking the error string is not required
+ RPC call. Set to None if checking the error string is not required.
fun (function): the function to call. This should be the name of an RPC.
args*: positional arguments for the function.
kwds**: named arguments for the function.
"""
+ assert try_rpc(code, message, fun, *args, **kwds), "No exception raised"
+
+def try_rpc(code, message, fun, *args, **kwds):
+ """Tries to run an rpc command.
+
+ Test against error code and message if the rpc fails.
+ Returns whether a JSONRPCException was raised."""
try:
fun(*args, **kwds)
except JSONRPCException as e:
@@ -81,10 +116,11 @@ def assert_raises_jsonrpc(code, message, fun, *args, **kwds):
raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"])
if (message is not None) and (message not in e.error['message']):
raise AssertionError("Expected substring not found:" + e.error['message'])
+ return True
except Exception as e:
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
else:
- raise AssertionError("No exception raised")
+ return False
def assert_is_hex_string(string):
try:
@@ -148,6 +184,13 @@ def count_bytes(hex_string):
def bytes_to_hex_str(byte_str):
return hexlify(byte_str).decode('ascii')
+def hash256(byte_str):
+ sha256 = hashlib.sha256()
+ sha256.update(byte_str)
+ sha256d = hashlib.sha256()
+ sha256d.update(sha256.digest())
+ return sha256d.digest()[::-1]
+
def hex_str_to_bytes(hex_str):
return unhexlify(hex_str.encode('ascii'))
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 59c236ce15..58faec521d 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -15,6 +15,7 @@ For a description of arguments recognized by test scripts, see
"""
import argparse
+from collections import deque
import configparser
import datetime
import os
@@ -98,6 +99,7 @@ BASE_SCRIPTS= [
'disconnect_ban.py',
'decodescript.py',
'blockchain.py',
+ 'deprecated_rpc.py',
'disablewallet.py',
'net.py',
'keypool.py',
@@ -121,6 +123,12 @@ BASE_SCRIPTS= [
'bip65-cltv-p2p.py',
'uptime.py',
'resendwallettransactions.py',
+ 'minchainwork.py',
+ 'p2p-fingerprint.py',
+ 'uacomment.py',
+ 'p2p-acceptblock.py',
+ 'feature_logging.py',
+ 'node_network_limited.py',
]
EXTENDED_SCRIPTS = [
@@ -146,9 +154,8 @@ EXTENDED_SCRIPTS = [
'example_test.py',
'txn_doublespend.py',
'txn_clone.py --mineblock',
- 'forknotify.py',
+ 'notifications.py',
'invalidateblock.py',
- 'p2p-acceptblock.py',
'replace-by-fee.py',
]
@@ -170,6 +177,7 @@ def main():
epilog='''
Help text and arguments for individual test script:''',
formatter_class=argparse.RawTextHelpFormatter)
+ parser.add_argument('--combinedlogslen', '-c', type=int, default=0, help='print a combined log (of length n lines) from all test nodes and test framework to the console on failure.')
parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface')
parser.add_argument('--exclude', '-x', help='specify a comma-separated-list of scripts to exclude.')
parser.add_argument('--extended', action='store_true', help='run the extended test suite in addition to the basic tests')
@@ -262,9 +270,9 @@ def main():
if not args.keepcache:
shutil.rmtree("%s/test/cache" % config["environment"]["BUILDDIR"], ignore_errors=True)
- run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], tmpdir, args.jobs, args.coverage, passon_args)
+ run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], tmpdir, args.jobs, args.coverage, passon_args, args.combinedlogslen)
-def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_coverage=False, args=[]):
+def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_coverage=False, args=[], combined_logs_len=0):
# Warn if bitcoind is already running (unix only)
try:
if subprocess.check_output(["pidof", "bitcoind"]) is not None:
@@ -296,7 +304,11 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
if len(test_list) > 1 and jobs > 1:
# Populate cache
- subprocess.check_output([tests_dir + 'create_cache.py'] + flags + ["--tmpdir=%s/cache" % tmpdir])
+ try:
+ subprocess.check_output([tests_dir + 'create_cache.py'] + flags + ["--tmpdir=%s/cache" % tmpdir])
+ except Exception as e:
+ print(e.output)
+ raise e
#Run Tests
job_queue = TestHandler(jobs, tests_dir, tmpdir, test_list, flags)
@@ -306,7 +318,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
max_len_name = len(max(test_list, key=len))
for _ in range(len(test_list)):
- test_result, stdout, stderr = job_queue.get_next()
+ test_result, testdir, stdout, stderr = job_queue.get_next()
test_results.append(test_result)
if test_result.status == "Passed":
@@ -317,6 +329,14 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
print("\n%s%s%s failed, Duration: %s s\n" % (BOLD[1], test_result.name, BOLD[0], test_result.time))
print(BOLD[1] + 'stdout:\n' + BOLD[0] + stdout + '\n')
print(BOLD[1] + 'stderr:\n' + BOLD[0] + stderr + '\n')
+ if combined_logs_len and os.path.isdir(testdir):
+ # Print the final `combinedlogslen` lines of the combined logs
+ print('{}Combine the logs and print the last {} lines ...{}'.format(BOLD[1], combined_logs_len, BOLD[0]))
+ print('\n============')
+ print('{}Combined log for {}:{}'.format(BOLD[1], testdir, BOLD[0]))
+ print('============\n')
+ combined_logs, _ = subprocess.Popen([os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate()
+ print("\n".join(deque(combined_logs.splitlines(), combined_logs_len)))
print_results(test_results, max_len_name, (int(time.time() - time0)))
@@ -381,13 +401,15 @@ class TestHandler:
log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16)
log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16)
test_argv = t.split()
- tmpdir = ["--tmpdir=%s/%s_%s" % (self.tmpdir, re.sub(".py$", "", test_argv[0]), portseed)]
+ testdir = "{}/{}_{}".format(self.tmpdir, re.sub(".py$", "", test_argv[0]), portseed)
+ tmpdir_arg = ["--tmpdir={}".format(testdir)]
self.jobs.append((t,
time.time(),
- subprocess.Popen([self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir,
+ subprocess.Popen([self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg,
universal_newlines=True,
stdout=log_stdout,
stderr=log_stderr),
+ testdir,
log_stdout,
log_stderr))
if not self.jobs:
@@ -396,7 +418,7 @@ class TestHandler:
# Return first proc that finishes
time.sleep(.5)
for j in self.jobs:
- (name, time0, proc, log_out, log_err) = j
+ (name, time0, proc, testdir, log_out, log_err) = j
if os.getenv('TRAVIS') == 'true' and int(time.time() - time0) > 20 * 60:
# In travis, timeout individual tests after 20 minutes (to stop tests hanging and not
# providing useful output.
@@ -414,7 +436,7 @@ class TestHandler:
self.num_running -= 1
self.jobs.remove(j)
- return TestResult(name, status, int(time.time() - time0)), stdout, stderr
+ return TestResult(name, status, int(time.time() - time0)), testdir, stdout, stderr
print('.', end='', flush=True)
class TestResult():
@@ -456,7 +478,7 @@ def check_script_list(src_dir):
# On travis this warning is an error to prevent merging incomplete commits into master
sys.exit(1)
-class RPCCoverage(object):
+class RPCCoverage():
"""
Coverage reporting utilities for test_runner.
diff --git a/test/functional/uacomment.py b/test/functional/uacomment.py
new file mode 100755
index 0000000000..0b2c64ab69
--- /dev/null
+++ b/test/functional/uacomment.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 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 the -uacomment option."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+class UacommentTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def run_test(self):
+ self.log.info("test multiple -uacomment")
+ test_uacomment = self.nodes[0].getnetworkinfo()["subversion"][-12:-1]
+ assert_equal(test_uacomment, "(testnode0)")
+
+ self.restart_node(0, ["-uacomment=foo"])
+ foo_uacomment = self.nodes[0].getnetworkinfo()["subversion"][-17:-1]
+ assert_equal(foo_uacomment, "(testnode0; foo)")
+
+ self.log.info("test -uacomment max length")
+ self.stop_node(0)
+ expected = "Total length of network version string (286) exceeds maximum length (256). Reduce the number or size of uacomments."
+ self.assert_start_raises_init_error(0, ["-uacomment=" + 'a' * 256], expected)
+
+ self.log.info("test -uacomment unsafe characters")
+ for unsafe_char in ['/', ':', '(', ')']:
+ expected = "User Agent comment (" + unsafe_char + ") contains unsafe characters"
+ self.assert_start_raises_init_error(0, ["-uacomment=" + unsafe_char], expected)
+
+if __name__ == '__main__':
+ UacommentTest().main()
diff --git a/test/functional/wallet-accounts.py b/test/functional/wallet-accounts.py
index 40726d2a76..bc1efaee15 100755
--- a/test/functional/wallet-accounts.py
+++ b/test/functional/wallet-accounts.py
@@ -72,62 +72,135 @@ class WalletAccountsTest(BitcoinTestFramework):
# otherwise we're off by exactly the fee amount as that's mined
# and matures in the next 100 blocks
node.sendfrom("", common_address, fee)
- accounts = ["a", "b", "c", "d", "e"]
amount_to_send = 1.0
- account_addresses = dict()
+
+ # Create accounts and make sure subsequent account API calls
+ # recognize the account/address associations.
+ accounts = [Account(name) for name in ("a", "b", "c", "d", "e")]
for account in accounts:
- address = node.getaccountaddress(account)
- account_addresses[account] = address
-
- node.getnewaddress(account)
- assert_equal(node.getaccount(address), account)
- assert(address in node.getaddressesbyaccount(account))
-
- node.sendfrom("", address, amount_to_send)
-
+ account.add_receive_address(node.getaccountaddress(account.name))
+ account.verify(node)
+
+ # Send a transaction to each account, and make sure this forces
+ # getaccountaddress to generate a new receiving address.
+ for account in accounts:
+ node.sendtoaddress(account.receive_address, amount_to_send)
+ account.add_receive_address(node.getaccountaddress(account.name))
+ account.verify(node)
+
+ # Check the amounts received.
node.generate(1)
+ for account in accounts:
+ assert_equal(
+ node.getreceivedbyaddress(account.addresses[0]), amount_to_send)
+ assert_equal(node.getreceivedbyaccount(account.name), amount_to_send)
- for i in range(len(accounts)):
- from_account = accounts[i]
+ # Check that sendfrom account reduces listaccounts balances.
+ for i, account in enumerate(accounts):
to_account = accounts[(i+1) % len(accounts)]
- to_address = account_addresses[to_account]
- node.sendfrom(from_account, to_address, amount_to_send)
-
+ node.sendfrom(account.name, to_account.receive_address, amount_to_send)
node.generate(1)
-
for account in accounts:
- address = node.getaccountaddress(account)
- assert(address != account_addresses[account])
- assert_equal(node.getreceivedbyaccount(account), 2)
- node.move(account, "", node.getbalance(account))
-
+ account.add_receive_address(node.getaccountaddress(account.name))
+ account.verify(node)
+ assert_equal(node.getreceivedbyaccount(account.name), 2)
+ node.move(account.name, "", node.getbalance(account.name))
+ account.verify(node)
node.generate(101)
-
expected_account_balances = {"": 5200}
for account in accounts:
- expected_account_balances[account] = 0
-
+ expected_account_balances[account.name] = 0
assert_equal(node.listaccounts(), expected_account_balances)
-
assert_equal(node.getbalance(""), 5200)
+ # Check that setaccount can assign an account to a new unused address.
for account in accounts:
address = node.getaccountaddress("")
- node.setaccount(address, account)
- assert(address in node.getaddressesbyaccount(account))
+ node.setaccount(address, account.name)
+ account.add_address(address)
+ account.verify(node)
assert(address not in node.getaddressesbyaccount(""))
+ # Check that addmultisigaddress can assign accounts.
for account in accounts:
addresses = []
for x in range(10):
addresses.append(node.getnewaddress())
- multisig_address = node.addmultisigaddress(5, addresses, account)
+ multisig_address = node.addmultisigaddress(5, addresses, account.name)
+ account.add_address(multisig_address)
+ account.verify(node)
node.sendfrom("", multisig_address, 50)
-
node.generate(101)
-
for account in accounts:
- assert_equal(node.getbalance(account), 50)
+ assert_equal(node.getbalance(account.name), 50)
+
+ # Check that setaccount can change the account of an address from a
+ # different account.
+ change_account(node, accounts[0].addresses[0], accounts[0], accounts[1])
+
+ # Check that setaccount can change the account of an address which
+ # is the receiving address of a different account.
+ change_account(node, accounts[0].receive_address, accounts[0], accounts[1])
+
+ # Check that setaccount can set the account of an address already
+ # in the account. This is a no-op.
+ change_account(node, accounts[2].addresses[0], accounts[2], accounts[2])
+
+ # Check that setaccount can set the account of an address which is
+ # already the receiving address of the account. It would probably make
+ # sense for this to be a no-op, but right now it resets the receiving
+ # address, causing getaccountaddress to return a brand new address.
+ change_account(node, accounts[2].receive_address, accounts[2], accounts[2])
+
+class Account:
+ def __init__(self, name):
+ # Account name
+ self.name = name
+ # Current receiving address associated with this account.
+ self.receive_address = None
+ # List of all addresses assigned with this account
+ self.addresses = []
+
+ def add_address(self, address):
+ assert_equal(address not in self.addresses, True)
+ self.addresses.append(address)
+
+ def add_receive_address(self, address):
+ self.add_address(address)
+ self.receive_address = address
+
+ def verify(self, node):
+ if self.receive_address is not None:
+ assert self.receive_address in self.addresses
+ assert_equal(node.getaccountaddress(self.name), self.receive_address)
+
+ for address in self.addresses:
+ assert_equal(node.getaccount(address), self.name)
+
+ assert_equal(
+ set(node.getaddressesbyaccount(self.name)), set(self.addresses))
+
+
+def change_account(node, address, old_account, new_account):
+ assert_equal(address in old_account.addresses, True)
+ node.setaccount(address, new_account.name)
+
+ old_account.addresses.remove(address)
+ new_account.add_address(address)
+
+ # Calling setaccount on an address which was previously the receiving
+ # address of a different account should reset the receiving address of
+ # the old account, causing getaccountaddress to return a brand new
+ # address.
+ if address == old_account.receive_address:
+ new_address = node.getaccountaddress(old_account.name)
+ assert_equal(new_address not in old_account.addresses, True)
+ assert_equal(new_address not in new_account.addresses, True)
+ old_account.add_receive_address(new_address)
+
+ old_account.verify(node)
+ new_account.verify(node)
+
if __name__ == '__main__':
WalletAccountsTest().main()
diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py
index e4757c8c03..47de8777a6 100755
--- a/test/functional/wallet-dump.py
+++ b/test/functional/wallet-dump.py
@@ -7,7 +7,7 @@
import os
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
+from test_framework.util import (assert_equal, assert_raises_rpc_error)
def read_dump(file_name, addrs, hd_master_addr_old):
@@ -105,5 +105,8 @@ class WalletDumpTest(BitcoinTestFramework):
assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now
assert_equal(found_addr_rsv, 90*2)
+ # Overwriting should fail
+ assert_raises_rpc_error(-8, "already exists", self.nodes[0].dumpwallet, tmpdir + "/node0/wallet.unencrypted.dump")
+
if __name__ == '__main__':
WalletDumpTest().main ()
diff --git a/test/functional/wallet-encryption.py b/test/functional/wallet-encryption.py
index f63bb2ea5e..db62e1e30f 100755
--- a/test/functional/wallet-encryption.py
+++ b/test/functional/wallet-encryption.py
@@ -6,10 +6,10 @@
import time
-from test_framework.test_framework import BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT
+from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- assert_raises_jsonrpc,
+ assert_raises_rpc_error,
)
class WalletEncryptionTest(BitcoinTestFramework):
@@ -32,7 +32,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
self.start_node(0)
# Test that the wallet is encrypted
- assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
+ assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
# Check that walletpassphrase works
self.nodes[0].walletpassphrase(passphrase, 2)
@@ -40,20 +40,20 @@ class WalletEncryptionTest(BitcoinTestFramework):
# Check that the timeout is right
time.sleep(2)
- assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
+ assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
# Test wrong passphrase
- assert_raises_jsonrpc(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase + "wrong", 10)
+ assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase + "wrong", 10)
# Test walletlock
self.nodes[0].walletpassphrase(passphrase, 84600)
assert_equal(privkey, self.nodes[0].dumpprivkey(address))
self.nodes[0].walletlock()
- assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
+ assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
# Test passphrase changes
self.nodes[0].walletpassphrasechange(passphrase, passphrase2)
- assert_raises_jsonrpc(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase, 10)
+ assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase, 10)
self.nodes[0].walletpassphrase(passphrase2, 10)
assert_equal(privkey, self.nodes[0].dumpprivkey(address))
diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py
index a6b96b7455..d21656a971 100755
--- a/test/functional/wallet-hd.py
+++ b/test/functional/wallet-hd.py
@@ -10,12 +10,13 @@ from test_framework.util import (
connect_nodes_bi,
)
import shutil
+import os
class WalletHDTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- self.extra_args = [['-usehd=0'], ['-usehd=1', '-keypool=0']]
+ self.extra_args = [[], ['-keypool=0']]
def run_test (self):
tmpdir = self.options.tmpdir
@@ -70,9 +71,9 @@ class WalletHDTest(BitcoinTestFramework):
self.stop_node(1)
# we need to delete the complete regtest directory
# otherwise node1 would auto-recover all funds in flag the keypool keys as used
- shutil.rmtree(tmpdir + "/node1/regtest/blocks")
- shutil.rmtree(tmpdir + "/node1/regtest/chainstate")
- shutil.copyfile(tmpdir + "/hd.bak", tmpdir + "/node1/regtest/wallet.dat")
+ shutil.rmtree(os.path.join(tmpdir, "node1/regtest/blocks"))
+ shutil.rmtree(os.path.join(tmpdir, "node1/regtest/chainstate"))
+ shutil.copyfile(os.path.join(tmpdir, "hd.bak"), os.path.join(tmpdir, "node1/regtest/wallets/wallet.dat"))
self.start_node(1)
# Assert that derivation is deterministic
@@ -91,6 +92,22 @@ class WalletHDTest(BitcoinTestFramework):
self.start_node(1, extra_args=self.extra_args[1] + ['-rescan'])
assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1)
+ # Try a RPC based rescan
+ self.stop_node(1)
+ shutil.rmtree(os.path.join(tmpdir, "node1/regtest/blocks"))
+ shutil.rmtree(os.path.join(tmpdir, "node1/regtest/chainstate"))
+ shutil.copyfile(os.path.join(tmpdir, "hd.bak"), os.path.join(tmpdir, "node1/regtest/wallet.dat"))
+ self.start_node(1, extra_args=self.extra_args[1])
+ connect_nodes_bi(self.nodes, 0, 1)
+ self.sync_all()
+ out = self.nodes[1].rescanblockchain(0, 1)
+ assert_equal(out['start_height'], 0)
+ assert_equal(out['stop_height'], 1)
+ out = self.nodes[1].rescanblockchain()
+ assert_equal(out['start_height'], 0)
+ assert_equal(out['stop_height'], self.nodes[1].getblockcount())
+ assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1)
+
# send a tx and make sure its using the internal chain for the changeoutput
txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
outs = self.nodes[1].decoderawtransaction(self.nodes[1].gettransaction(txid)['hex'])['vout']
diff --git a/test/functional/wallet.py b/test/functional/wallet.py
index 27089e8845..db60df18ed 100755
--- a/test/functional/wallet.py
+++ b/test/functional/wallet.py
@@ -10,10 +10,9 @@ class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.setup_clean_chain = True
- self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)]
def setup_network(self):
- self.add_nodes(4, self.extra_args)
+ self.add_nodes(4)
self.start_node(0)
self.start_node(1)
self.start_node(2)
@@ -101,11 +100,19 @@ class WalletTest(BitcoinTestFramework):
# Exercise locking of unspent outputs
unspent_0 = self.nodes[2].listunspent()[0]
unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]}
+ assert_raises_rpc_error(-8, "Invalid parameter, expected locked output", self.nodes[2].lockunspent, True, [unspent_0])
self.nodes[2].lockunspent(False, [unspent_0])
- assert_raises_jsonrpc(-4, "Insufficient funds", self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20)
+ assert_raises_rpc_error(-8, "Invalid parameter, output already locked", self.nodes[2].lockunspent, False, [unspent_0])
+ assert_raises_rpc_error(-4, "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)
+ assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction",
+ self.nodes[2].lockunspent, False,
+ [{"txid": "0000000000000000000000000000000000", "vout": 0}])
+ assert_raises_rpc_error(-8, "Invalid parameter, vout index out of bounds",
+ self.nodes[2].lockunspent, False,
+ [{"txid": unspent_0["txid"], "vout": 999}])
# Have node1 generate 100 blocks (so node0 can recover the fee)
self.nodes[1].generate(100)
@@ -144,6 +151,10 @@ class WalletTest(BitcoinTestFramework):
assert_equal(self.nodes[2].getbalance(), 94)
assert_equal(self.nodes[2].getbalance("from1"), 94-21)
+ # Verify that a spent output cannot be locked anymore
+ spent_0 = {"txid": node0utxos[0]["txid"], "vout": node0utxos[0]["vout"]}
+ assert_raises_rpc_error(-8, "Invalid parameter, expected unspent output", self.nodes[0].lockunspent, False, [spent_0])
+
# Send 10 BTC normal
address = self.nodes[0].getnewaddress("test")
fee_per_byte = Decimal('0.001') / 1000
@@ -287,10 +298,10 @@ class WalletTest(BitcoinTestFramework):
assert_equal(txObj['amount'], Decimal('-0.0001'))
# This will raise an exception because the amount type is wrong
- assert_raises_jsonrpc(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4")
+ assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4")
# This will raise an exception since generate does not accept a string
- assert_raises_jsonrpc(-1, "not an integer", self.nodes[0].generate, "2")
+ assert_raises_rpc_error(-1, "not an integer", self.nodes[0].generate, "2")
# Import address and private key to check correct behavior of spendable unspents
# 1. Send some coins to generate new UTXO
@@ -423,7 +434,7 @@ class WalletTest(BitcoinTestFramework):
node0_balance = self.nodes[0].getbalance()
# With walletrejectlongchains we will not create the tx and store it in our wallet.
- assert_raises_jsonrpc(-4, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01'))
+ assert_raises_rpc_error(-4, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01'))
# Verify nothing new in wallet
assert_equal(total_txs, len(self.nodes[0].listtransactions("*",99999)))
diff --git a/test/functional/walletbackup.py b/test/functional/walletbackup.py
index 15ea26afa1..8ef5620cd8 100755
--- a/test/functional/walletbackup.py
+++ b/test/functional/walletbackup.py
@@ -90,9 +90,9 @@ class WalletBackupTest(BitcoinTestFramework):
self.stop_node(2)
def erase_three(self):
- os.remove(self.options.tmpdir + "/node0/regtest/wallet.dat")
- os.remove(self.options.tmpdir + "/node1/regtest/wallet.dat")
- os.remove(self.options.tmpdir + "/node2/regtest/wallet.dat")
+ os.remove(self.options.tmpdir + "/node0/regtest/wallets/wallet.dat")
+ os.remove(self.options.tmpdir + "/node1/regtest/wallets/wallet.dat")
+ os.remove(self.options.tmpdir + "/node2/regtest/wallets/wallet.dat")
def run_test(self):
self.log.info("Generating initial blockchain")
@@ -154,9 +154,9 @@ class WalletBackupTest(BitcoinTestFramework):
shutil.rmtree(self.options.tmpdir + "/node2/regtest/chainstate")
# Restore wallets from backup
- shutil.copyfile(tmpdir + "/node0/wallet.bak", tmpdir + "/node0/regtest/wallet.dat")
- shutil.copyfile(tmpdir + "/node1/wallet.bak", tmpdir + "/node1/regtest/wallet.dat")
- shutil.copyfile(tmpdir + "/node2/wallet.bak", tmpdir + "/node2/regtest/wallet.dat")
+ shutil.copyfile(tmpdir + "/node0/wallet.bak", tmpdir + "/node0/regtest/wallets/wallet.dat")
+ shutil.copyfile(tmpdir + "/node1/wallet.bak", tmpdir + "/node1/regtest/wallets/wallet.dat")
+ shutil.copyfile(tmpdir + "/node2/wallet.bak", tmpdir + "/node2/regtest/wallets/wallet.dat")
self.log.info("Re-starting nodes")
self.start_three()
@@ -190,6 +190,16 @@ class WalletBackupTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getbalance(), balance1)
assert_equal(self.nodes[2].getbalance(), balance2)
+ # Backup to source wallet file must fail
+ sourcePaths = [
+ tmpdir + "/node0/regtest/wallets/wallet.dat",
+ tmpdir + "/node0/./regtest/wallets/wallet.dat",
+ tmpdir + "/node0/regtest/wallets/",
+ tmpdir + "/node0/regtest/wallets"]
+
+ for sourcePath in sourcePaths:
+ assert_raises_rpc_error(-4, "backup failed", self.nodes[0].backupwallet, sourcePath)
+
if __name__ == '__main__':
WalletBackupTest().main()
diff --git a/test/functional/zapwallettxes.py b/test/functional/zapwallettxes.py
index c001517a6d..8cd622dc8e 100755
--- a/test/functional/zapwallettxes.py
+++ b/test/functional/zapwallettxes.py
@@ -15,9 +15,11 @@
been zapped.
"""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (assert_equal,
- assert_raises_jsonrpc,
- )
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ wait_until,
+)
class ZapWalletTXesTest (BitcoinTestFramework):
def set_test_params(self):
@@ -56,6 +58,8 @@ class ZapWalletTXesTest (BitcoinTestFramework):
self.stop_node(0)
self.start_node(0, ["-persistmempool=1", "-zapwallettxes=2"])
+ wait_until(lambda: self.nodes[0].getmempoolinfo()['size'] == 1, timeout=3)
+
assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1)
assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2)
@@ -68,7 +72,7 @@ class ZapWalletTXesTest (BitcoinTestFramework):
assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1)
# This will raise an exception because the unconfirmed transaction has been zapped
- assert_raises_jsonrpc(-5, 'Invalid or non-wallet transaction id', self.nodes[0].gettransaction, txid2)
+ assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', self.nodes[0].gettransaction, txid2)
if __name__ == '__main__':
ZapWalletTXesTest().main()
diff --git a/test/functional/zmq_test.py b/test/functional/zmq_test.py
index 3f2668ee87..fa30318416 100755
--- a/test/functional/zmq_test.py
+++ b/test/functional/zmq_test.py
@@ -2,15 +2,37 @@
# 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 the ZMQ API."""
+"""Test the ZMQ notification interface."""
import configparser
import os
import struct
from test_framework.test_framework import BitcoinTestFramework, SkipTest
+from test_framework.mininode import CTransaction
from test_framework.util import (assert_equal,
bytes_to_hex_str,
- )
+ hash256,
+ )
+from io import BytesIO
+
+class ZMQSubscriber:
+ def __init__(self, socket, topic):
+ self.sequence = 0
+ self.socket = socket
+ self.topic = topic
+
+ import zmq
+ self.socket.setsockopt(zmq.SUBSCRIBE, self.topic)
+
+ def receive(self):
+ topic, body, seq = self.socket.recv_multipart()
+ # Topic should match the subscriber topic.
+ assert_equal(topic, self.topic)
+ # Sequence should be incremental.
+ assert_equal(struct.unpack('<I', seq)[-1], self.sequence)
+ self.sequence += 1
+ return body
+
class ZMQTest (BitcoinTestFramework):
def set_test_params(self):
@@ -23,23 +45,33 @@ class ZMQTest (BitcoinTestFramework):
except ImportError:
raise SkipTest("python3-zmq module not available.")
- # Check that bitcoin has been built with ZMQ enabled
+ # Check that bitcoin has been built with ZMQ enabled.
config = configparser.ConfigParser()
if not self.options.configfile:
- self.options.configfile = os.path.dirname(__file__) + "/../config.ini"
+ self.options.configfile = os.path.abspath(os.path.join(os.path.dirname(__file__), "../config.ini"))
config.read_file(open(self.options.configfile))
if not config["components"].getboolean("ENABLE_ZMQ"):
raise SkipTest("bitcoind has not been built with zmq enabled.")
- self.zmqContext = zmq.Context()
- self.zmqSubSocket = self.zmqContext.socket(zmq.SUB)
- self.zmqSubSocket.set(zmq.RCVTIMEO, 60000)
- self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashblock")
- self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx")
- ip_address = "tcp://127.0.0.1:28332"
- self.zmqSubSocket.connect(ip_address)
- self.extra_args = [['-zmqpubhashtx=%s' % ip_address, '-zmqpubhashblock=%s' % ip_address], []]
+ # Initialize ZMQ context and socket.
+ # All messages are received in the same socket which means
+ # that this test fails if the publishing order changes.
+ # Note that the publishing order is not defined in the documentation and
+ # is subject to change.
+ address = "tcp://127.0.0.1:28332"
+ self.zmq_context = zmq.Context()
+ socket = self.zmq_context.socket(zmq.SUB)
+ socket.set(zmq.RCVTIMEO, 60000)
+ socket.connect(address)
+
+ # Subscribe to all available topics.
+ self.hashblock = ZMQSubscriber(socket, b"hashblock")
+ self.hashtx = ZMQSubscriber(socket, b"hashtx")
+ self.rawblock = ZMQSubscriber(socket, b"rawblock")
+ self.rawtx = ZMQSubscriber(socket, b"rawtx")
+
+ self.extra_args = [["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]], []]
self.add_nodes(self.num_nodes, self.extra_args)
self.start_nodes()
@@ -47,67 +79,48 @@ class ZMQTest (BitcoinTestFramework):
try:
self._zmq_test()
finally:
- # Destroy the zmq context
- self.log.debug("Destroying zmq context")
- self.zmqContext.destroy(linger=None)
+ # Destroy the ZMQ context.
+ self.log.debug("Destroying ZMQ context")
+ self.zmq_context.destroy(linger=None)
def _zmq_test(self):
- genhashes = self.nodes[0].generate(1)
+ num_blocks = 5
+ self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks})
+ genhashes = self.nodes[0].generate(num_blocks)
self.sync_all()
- self.log.info("Wait for tx")
- msg = self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- assert_equal(topic, b"hashtx")
- body = msg[1]
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- assert_equal(msgSequence, 0) # must be sequence 0 on hashtx
-
- self.log.info("Wait for block")
- msg = self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- body = msg[1]
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- assert_equal(msgSequence, 0) # must be sequence 0 on hashblock
- blkhash = bytes_to_hex_str(body)
-
- assert_equal(genhashes[0], blkhash) # blockhash from generate must be equal to the hash received over zmq
-
- self.log.info("Generate 10 blocks (and 10 coinbase txes)")
- n = 10
- genhashes = self.nodes[1].generate(n)
- self.sync_all()
+ for x in range(num_blocks):
+ # Should receive the coinbase txid.
+ txid = self.hashtx.receive()
+
+ # Should receive the coinbase raw transaction.
+ hex = self.rawtx.receive()
+ tx = CTransaction()
+ tx.deserialize(BytesIO(hex))
+ tx.calc_sha256()
+ assert_equal(tx.hash, bytes_to_hex_str(txid))
+
+ # Should receive the generated block hash.
+ hash = bytes_to_hex_str(self.hashblock.receive())
+ assert_equal(genhashes[x], hash)
+ # The block should only have the coinbase txid.
+ assert_equal([bytes_to_hex_str(txid)], self.nodes[1].getblock(hash)["tx"])
- zmqHashes = []
- blockcount = 0
- for x in range(n * 2):
- msg = self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- body = msg[1]
- if topic == b"hashblock":
- zmqHashes.append(bytes_to_hex_str(body))
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- assert_equal(msgSequence, blockcount + 1)
- blockcount += 1
-
- for x in range(n):
- assert_equal(genhashes[x], zmqHashes[x]) # blockhash from generate must be equal to the hash received over zmq
+ # Should receive the generated raw block.
+ block = self.rawblock.receive()
+ assert_equal(genhashes[x], bytes_to_hex_str(hash256(block[:80])))
self.log.info("Wait for tx from second node")
- # test tx from a second node
- hashRPC = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
+ payment_txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
self.sync_all()
- # now we should receive a zmq msg because the tx was broadcast
- msg = self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- body = msg[1]
- assert_equal(topic, b"hashtx")
- hashZMQ = bytes_to_hex_str(body)
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- assert_equal(msgSequence, blockcount + 1)
+ # Should receive the broadcasted txid.
+ txid = self.hashtx.receive()
+ assert_equal(payment_txid, bytes_to_hex_str(txid))
- assert_equal(hashRPC, hashZMQ) # txid from sendtoaddress must be equal to the hash received over zmq
+ # Should receive the broadcasted raw transaction.
+ hex = self.rawtx.receive()
+ assert_equal(payment_txid, bytes_to_hex_str(hash256(hex)))
if __name__ == '__main__':
ZMQTest().main()
diff --git a/test/util/bitcoin-util-test.py b/test/util/bitcoin-util-test.py
index d15d6a6011..ef34955d90 100755
--- a/test/util/bitcoin-util-test.py
+++ b/test/util/bitcoin-util-test.py
@@ -9,9 +9,14 @@ Runs automatically during `make check`.
Can also be run manually."""
+from __future__ import division,print_function,unicode_literals
+
import argparse
import binascii
-import configparser
+try:
+ import configparser
+except ImportError:
+ import ConfigParser as configparser
import difflib
import json
import logging
@@ -22,7 +27,9 @@ import sys
def main():
config = configparser.ConfigParser()
- config.read_file(open(os.path.dirname(__file__) + "/../config.ini"))
+ config.optionxform = str
+ config.readfp(open(os.path.join(os.path.dirname(__file__), "../config.ini")))
+ env_conf = dict(config.items('environment'))
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('-v', '--verbose', action='store_true')
@@ -37,7 +44,7 @@ def main():
# Add the format/level to the logger
logging.basicConfig(format=formatter, level=level)
- bctester(config["environment"]["SRCDIR"] + "/test/util/data", "bitcoin-util-test.json", config["environment"])
+ bctester(os.path.join(env_conf["SRCDIR"], "test/util/data"), "bitcoin-util-test.json", env_conf)
def bctester(testDir, input_basename, buildenv):
""" Loads and parses the input file, runs all tests and reports results"""
diff --git a/test/util/data/bitcoin-util-test.json b/test/util/data/bitcoin-util-test.json
index b61a4f7f8f..89b28bba6c 100644
--- a/test/util/data/bitcoin-util-test.json
+++ b/test/util/data/bitcoin-util-test.json
@@ -263,6 +263,13 @@
},
{ "exec": "./bitcoin-tx",
"args":
+ ["-json", "-create", "outpubkey=0:047d1368ba7ae01c94bc32293efd70bd7e3be7aa7912d07d0b1c659c1008d179b8642f5fb90f47580feb29f045e216ff5a4716d3a0fed36da414d332046303c44a:WS", "nversion=1"],
+ "return_code": 1,
+ "error_txt": "error: Uncompressed pubkeys are not useable for SegWit outputs",
+ "description": "Creates a new transaction with a single pay-to-pub-key output, wrapped in P2SH (output as json)"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
["-create",
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
"outdata=4:badhexdata"],
@@ -388,5 +395,16 @@
"args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:WS", "nversion=1"],
"output_cmp": "txcreatemultisig4.json",
"description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output, wrapped in P2SH (output in json)"
+ },
+ { "exec": "./bitcoin-tx",
+ "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:047d1368ba7ae01c94bc32293efd70bd7e3be7aa7912d07d0b1c659c1008d179b8642f5fb90f47580feb29f045e216ff5a4716d3a0fed36da414d332046303c44a:S"],
+ "output_cmp": "txcreatemultisig5.json",
+ "description": "Uncompressed pubkeys should work just fine for non-witness outputs"
+ },
+ { "exec": "./bitcoin-tx",
+ "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:047d1368ba7ae01c94bc32293efd70bd7e3be7aa7912d07d0b1c659c1008d179b8642f5fb90f47580feb29f045e216ff5a4716d3a0fed36da414d332046303c44a:WS"],
+ "return_code": 1,
+ "error_txt": "error: Uncompressed pubkeys are not useable for SegWit outputs",
+ "description": "Ensure adding witness outputs with uncompressed pubkeys fails"
}
]
diff --git a/test/util/data/tt-delin1-out.json b/test/util/data/tt-delin1-out.json
index 0b3235dd4a..de647f98b6 100644
--- a/test/util/data/tt-delin1-out.json
+++ b/test/util/data/tt-delin1-out.json
@@ -14,7 +14,7 @@
"hex": "493046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "752f7f69b915637dc1c2f7aed1466ad676f6f3e24cf922809705f664e97ab3c1",
"vout": 1,
@@ -23,7 +23,7 @@
"hex": "473044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34"
},
"sequence": 4294967295
- },
+ },
{
"txid": "b0ac9cca2e69cd02410e31b1f4402a25758e71abd1ab06c265ef9077dc05d0ed",
"vout": 209,
@@ -32,7 +32,7 @@
"hex": "48304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "a135eafb595eaf4c1ea59ccb111cdc0eae1b2c979b226a1e5aa8b76fe2d628df",
"vout": 0,
@@ -41,7 +41,7 @@
"hex": "483045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52"
},
"sequence": 4294967295
- },
+ },
{
"txid": "a5d6bf53ba21140b8a4d554feb00fe8bb9a62430ff9e4624aa2f58a120232aae",
"vout": 1,
@@ -50,7 +50,7 @@
"hex": "493046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "1b299cf14f1a22e81ea56d71b7affbd7cf386807bf2b4d4b79a18a54125accb3",
"vout": 0,
@@ -59,7 +59,7 @@
"hex": "483045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52"
},
"sequence": 4294967295
- },
+ },
{
"txid": "071df1cdcb3f0070f9d6af7b0274f02d0be2324a274727cfd288383167531485",
"vout": 21,
@@ -68,7 +68,7 @@
"hex": "483045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "b012e500eb7adf7a13ed332dd6ece849f94f7a62bb3eac5babab356d1fc19282",
"vout": 9,
@@ -77,7 +77,7 @@
"hex": "48304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "58840fee9c833f2f2d40575842f30f4b8d2553094d06ad88b03d06869acf3d88",
"vout": 30,
@@ -86,7 +86,7 @@
"hex": "4730440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "e69f9cd16946e570a665245354428a3f507ea69f4568b581e4af98edb3db9766",
"vout": 114,
@@ -95,7 +95,7 @@
"hex": "47304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "595d1257f654ed2cbe5a65421e8aefd2b4d70b5b6c89a03f1d7e518221fc3f02",
"vout": 103,
@@ -104,7 +104,7 @@
"hex": "493046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "06fc818f9555a261248ecd7aad0993eafb5a82ceb2b5c87c3ddfb06671c7f816",
"vout": 1,
@@ -113,7 +113,7 @@
"hex": "483045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec766901801210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd"
},
"sequence": 4294967295
- },
+ },
{
"txid": "fb416c8155d6bb1d43f9395466ca90a638a7c2dd3ff617aadf3a7ac8f3967b19",
"vout": 0,
@@ -122,7 +122,7 @@
"hex": "49304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34"
},
"sequence": 4294967295
- },
+ },
{
"txid": "3940b9683bd6104ad24c978e640ba4095993cafdb27d2ed91baa27ee61a2d920",
"vout": 221,
@@ -131,7 +131,7 @@
"hex": "483045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "711b5714d3b5136147c02194cd95bde94a4648c4263ca6f972d86cd1d579f150",
"vout": 1,
@@ -140,7 +140,7 @@
"hex": "483045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba907401210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd"
},
"sequence": 4294967295
- },
+ },
{
"txid": "6364b5c5efe018430789e7fb4e338209546cae5d9c5f5e300aac68155d861b55",
"vout": 27,
@@ -149,7 +149,7 @@
"hex": "48304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "0bb57f6e38012c86d4c5a28c904f2675082859147921a707d48961015a3e5057",
"vout": 1095,
@@ -158,7 +158,7 @@
"hex": "48304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "9b34274814a2540bb062107117f8f3e75ef85d953e9372d8261a3e9dfbc1163f",
"vout": 37,
@@ -167,7 +167,7 @@
"hex": "483045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "b86b5cc0d8a7374d94e277850b0a249cb26a7b42ddf014f28a49b8859da64241",
"vout": 20,
@@ -176,7 +176,7 @@
"hex": "48304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "3d0a2353eeec44d3c10aed259038db321912122cd4150048f7bfa4c0ecfee236",
"vout": 242,
@@ -200,7 +200,7 @@
"1E7SGgAZFCHDnVZLuRViX3gUmxpMfdvd2o"
]
}
- },
+ },
{
"value": 0.01000001,
"n": 1,
diff --git a/test/util/data/tt-delout1-out.json b/test/util/data/tt-delout1-out.json
index 5b69d0cd86..067ffe74e7 100644
--- a/test/util/data/tt-delout1-out.json
+++ b/test/util/data/tt-delout1-out.json
@@ -14,7 +14,7 @@
"hex": "493046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "a72ec96bd0d022d1b0c2f9078cdd46b3725b8eecdd001e17b21e3ababad14ecb",
"vout": 0,
@@ -23,7 +23,7 @@
"hex": "493046022100a9b617843b68c284715d3e02fd120479cd0d96a6c43bf01e697fb0a460a21a3a022100ba0a12fbe8b993d4e7911fa3467615765dbe421ddf5c51b57a9c1ee19dcc00ba012103e633b4fa4ceb705c2da712390767199be8ef2448b3095dc01652e11b2b751505"
},
"sequence": 4294967295
- },
+ },
{
"txid": "752f7f69b915637dc1c2f7aed1466ad676f6f3e24cf922809705f664e97ab3c1",
"vout": 1,
@@ -32,7 +32,7 @@
"hex": "473044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34"
},
"sequence": 4294967295
- },
+ },
{
"txid": "b0ac9cca2e69cd02410e31b1f4402a25758e71abd1ab06c265ef9077dc05d0ed",
"vout": 209,
@@ -41,7 +41,7 @@
"hex": "48304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "a135eafb595eaf4c1ea59ccb111cdc0eae1b2c979b226a1e5aa8b76fe2d628df",
"vout": 0,
@@ -50,7 +50,7 @@
"hex": "483045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52"
},
"sequence": 4294967295
- },
+ },
{
"txid": "a5d6bf53ba21140b8a4d554feb00fe8bb9a62430ff9e4624aa2f58a120232aae",
"vout": 1,
@@ -59,7 +59,7 @@
"hex": "493046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "1b299cf14f1a22e81ea56d71b7affbd7cf386807bf2b4d4b79a18a54125accb3",
"vout": 0,
@@ -68,7 +68,7 @@
"hex": "483045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52"
},
"sequence": 4294967295
- },
+ },
{
"txid": "071df1cdcb3f0070f9d6af7b0274f02d0be2324a274727cfd288383167531485",
"vout": 21,
@@ -77,7 +77,7 @@
"hex": "483045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "b012e500eb7adf7a13ed332dd6ece849f94f7a62bb3eac5babab356d1fc19282",
"vout": 9,
@@ -86,7 +86,7 @@
"hex": "48304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "58840fee9c833f2f2d40575842f30f4b8d2553094d06ad88b03d06869acf3d88",
"vout": 30,
@@ -95,7 +95,7 @@
"hex": "4730440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "e69f9cd16946e570a665245354428a3f507ea69f4568b581e4af98edb3db9766",
"vout": 114,
@@ -104,7 +104,7 @@
"hex": "47304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "595d1257f654ed2cbe5a65421e8aefd2b4d70b5b6c89a03f1d7e518221fc3f02",
"vout": 103,
@@ -113,7 +113,7 @@
"hex": "493046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "06fc818f9555a261248ecd7aad0993eafb5a82ceb2b5c87c3ddfb06671c7f816",
"vout": 1,
@@ -122,7 +122,7 @@
"hex": "483045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec766901801210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd"
},
"sequence": 4294967295
- },
+ },
{
"txid": "fb416c8155d6bb1d43f9395466ca90a638a7c2dd3ff617aadf3a7ac8f3967b19",
"vout": 0,
@@ -131,7 +131,7 @@
"hex": "49304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34"
},
"sequence": 4294967295
- },
+ },
{
"txid": "3940b9683bd6104ad24c978e640ba4095993cafdb27d2ed91baa27ee61a2d920",
"vout": 221,
@@ -140,7 +140,7 @@
"hex": "483045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "711b5714d3b5136147c02194cd95bde94a4648c4263ca6f972d86cd1d579f150",
"vout": 1,
@@ -149,7 +149,7 @@
"hex": "483045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba907401210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd"
},
"sequence": 4294967295
- },
+ },
{
"txid": "6364b5c5efe018430789e7fb4e338209546cae5d9c5f5e300aac68155d861b55",
"vout": 27,
@@ -158,7 +158,7 @@
"hex": "48304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "0bb57f6e38012c86d4c5a28c904f2675082859147921a707d48961015a3e5057",
"vout": 1095,
@@ -167,7 +167,7 @@
"hex": "48304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "9b34274814a2540bb062107117f8f3e75ef85d953e9372d8261a3e9dfbc1163f",
"vout": 37,
@@ -176,7 +176,7 @@
"hex": "483045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "b86b5cc0d8a7374d94e277850b0a249cb26a7b42ddf014f28a49b8859da64241",
"vout": 20,
@@ -185,7 +185,7 @@
"hex": "48304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "3d0a2353eeec44d3c10aed259038db321912122cd4150048f7bfa4c0ecfee236",
"vout": 242,
diff --git a/test/util/data/tt-locktime317000-out.json b/test/util/data/tt-locktime317000-out.json
index cf1ebcdf38..af7903d1dd 100644
--- a/test/util/data/tt-locktime317000-out.json
+++ b/test/util/data/tt-locktime317000-out.json
@@ -14,7 +14,7 @@
"hex": "493046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "a72ec96bd0d022d1b0c2f9078cdd46b3725b8eecdd001e17b21e3ababad14ecb",
"vout": 0,
@@ -23,7 +23,7 @@
"hex": "493046022100a9b617843b68c284715d3e02fd120479cd0d96a6c43bf01e697fb0a460a21a3a022100ba0a12fbe8b993d4e7911fa3467615765dbe421ddf5c51b57a9c1ee19dcc00ba012103e633b4fa4ceb705c2da712390767199be8ef2448b3095dc01652e11b2b751505"
},
"sequence": 4294967295
- },
+ },
{
"txid": "752f7f69b915637dc1c2f7aed1466ad676f6f3e24cf922809705f664e97ab3c1",
"vout": 1,
@@ -32,7 +32,7 @@
"hex": "473044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34"
},
"sequence": 4294967295
- },
+ },
{
"txid": "b0ac9cca2e69cd02410e31b1f4402a25758e71abd1ab06c265ef9077dc05d0ed",
"vout": 209,
@@ -41,7 +41,7 @@
"hex": "48304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "a135eafb595eaf4c1ea59ccb111cdc0eae1b2c979b226a1e5aa8b76fe2d628df",
"vout": 0,
@@ -50,7 +50,7 @@
"hex": "483045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52"
},
"sequence": 4294967295
- },
+ },
{
"txid": "a5d6bf53ba21140b8a4d554feb00fe8bb9a62430ff9e4624aa2f58a120232aae",
"vout": 1,
@@ -59,7 +59,7 @@
"hex": "493046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "1b299cf14f1a22e81ea56d71b7affbd7cf386807bf2b4d4b79a18a54125accb3",
"vout": 0,
@@ -68,7 +68,7 @@
"hex": "483045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52"
},
"sequence": 4294967295
- },
+ },
{
"txid": "071df1cdcb3f0070f9d6af7b0274f02d0be2324a274727cfd288383167531485",
"vout": 21,
@@ -77,7 +77,7 @@
"hex": "483045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "b012e500eb7adf7a13ed332dd6ece849f94f7a62bb3eac5babab356d1fc19282",
"vout": 9,
@@ -86,7 +86,7 @@
"hex": "48304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "58840fee9c833f2f2d40575842f30f4b8d2553094d06ad88b03d06869acf3d88",
"vout": 30,
@@ -95,7 +95,7 @@
"hex": "4730440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "e69f9cd16946e570a665245354428a3f507ea69f4568b581e4af98edb3db9766",
"vout": 114,
@@ -104,7 +104,7 @@
"hex": "47304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "595d1257f654ed2cbe5a65421e8aefd2b4d70b5b6c89a03f1d7e518221fc3f02",
"vout": 103,
@@ -113,7 +113,7 @@
"hex": "493046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "06fc818f9555a261248ecd7aad0993eafb5a82ceb2b5c87c3ddfb06671c7f816",
"vout": 1,
@@ -122,7 +122,7 @@
"hex": "483045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec766901801210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd"
},
"sequence": 4294967295
- },
+ },
{
"txid": "fb416c8155d6bb1d43f9395466ca90a638a7c2dd3ff617aadf3a7ac8f3967b19",
"vout": 0,
@@ -131,7 +131,7 @@
"hex": "49304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34"
},
"sequence": 4294967295
- },
+ },
{
"txid": "3940b9683bd6104ad24c978e640ba4095993cafdb27d2ed91baa27ee61a2d920",
"vout": 221,
@@ -140,7 +140,7 @@
"hex": "483045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
},
"sequence": 4294967295
- },
+ },
{
"txid": "711b5714d3b5136147c02194cd95bde94a4648c4263ca6f972d86cd1d579f150",
"vout": 1,
@@ -149,7 +149,7 @@
"hex": "483045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba907401210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd"
},
"sequence": 4294967295
- },
+ },
{
"txid": "6364b5c5efe018430789e7fb4e338209546cae5d9c5f5e300aac68155d861b55",
"vout": 27,
@@ -158,7 +158,7 @@
"hex": "48304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "0bb57f6e38012c86d4c5a28c904f2675082859147921a707d48961015a3e5057",
"vout": 1095,
@@ -167,7 +167,7 @@
"hex": "48304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "9b34274814a2540bb062107117f8f3e75ef85d953e9372d8261a3e9dfbc1163f",
"vout": 37,
@@ -176,7 +176,7 @@
"hex": "483045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "b86b5cc0d8a7374d94e277850b0a249cb26a7b42ddf014f28a49b8859da64241",
"vout": 20,
@@ -185,7 +185,7 @@
"hex": "48304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
},
"sequence": 4294967295
- },
+ },
{
"txid": "3d0a2353eeec44d3c10aed259038db321912122cd4150048f7bfa4c0ecfee236",
"vout": 242,
@@ -209,7 +209,7 @@
"1E7SGgAZFCHDnVZLuRViX3gUmxpMfdvd2o"
]
}
- },
+ },
{
"value": 0.01000001,
"n": 1,
diff --git a/test/util/data/txcreate1.json b/test/util/data/txcreate1.json
index edb091f946..83a86649e0 100644
--- a/test/util/data/txcreate1.json
+++ b/test/util/data/txcreate1.json
@@ -14,7 +14,7 @@
"hex": ""
},
"sequence": 4294967295
- },
+ },
{
"txid": "bf829c6bcf84579331337659d31f89dfd138f7f7785802d5501c92333145ca7c",
"vout": 18,
@@ -23,7 +23,7 @@
"hex": ""
},
"sequence": 4294967295
- },
+ },
{
"txid": "22a6f904655d53ae2ff70e701a0bbd90aa3975c0f40bfc6cc996a9049e31cdfc",
"vout": 1,
@@ -47,7 +47,7 @@
"13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"
]
}
- },
+ },
{
"value": 4.00000000,
"n": 1,
diff --git a/test/util/data/txcreatedata1.json b/test/util/data/txcreatedata1.json
index e66a6bb9a5..15a4246ae5 100644
--- a/test/util/data/txcreatedata1.json
+++ b/test/util/data/txcreatedata1.json
@@ -29,7 +29,7 @@
"13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"
]
}
- },
+ },
{
"value": 4.00000000,
"n": 1,
diff --git a/test/util/data/txcreatedata2.json b/test/util/data/txcreatedata2.json
index 0f8edcafdd..cb93c27971 100644
--- a/test/util/data/txcreatedata2.json
+++ b/test/util/data/txcreatedata2.json
@@ -29,7 +29,7 @@
"13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"
]
}
- },
+ },
{
"value": 0.00000000,
"n": 1,
diff --git a/test/util/data/txcreatedata_seq1.json b/test/util/data/txcreatedata_seq1.json
index 771ff1bb10..dea48ba373 100644
--- a/test/util/data/txcreatedata_seq1.json
+++ b/test/util/data/txcreatedata_seq1.json
@@ -14,7 +14,7 @@
"hex": ""
},
"sequence": 4294967293
- },
+ },
{
"txid": "5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f",
"vout": 0,
diff --git a/test/util/data/txcreatemultisig1.json b/test/util/data/txcreatemultisig1.json
index 7c814dad83..72e20c8691 100644
--- a/test/util/data/txcreatemultisig1.json
+++ b/test/util/data/txcreatemultisig1.json
@@ -17,8 +17,8 @@
"reqSigs": 2,
"type": "multisig",
"addresses": [
- "1FoG2386FG2tAJS9acMuiDsKy67aGg9MKz",
- "1FXtz9KU8JNmQDyHdiEm5HDiALuP3zdHvV",
+ "1FoG2386FG2tAJS9acMuiDsKy67aGg9MKz",
+ "1FXtz9KU8JNmQDyHdiEm5HDiALuP3zdHvV",
"14LuavcBbXZYJ6Tsz3cAUQj9SuQoL2xCQX"
]
}
diff --git a/test/util/data/txcreatemultisig3.json b/test/util/data/txcreatemultisig3.json
index 06e093e224..6c5b49d876 100644
--- a/test/util/data/txcreatemultisig3.json
+++ b/test/util/data/txcreatemultisig3.json
@@ -14,7 +14,11 @@
"scriptPubKey": {
"asm": "0 e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05",
"hex": "0020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05",
- "type": "witness_v0_scripthash"
+ "reqSigs": 1,
+ "type": "witness_v0_scripthash",
+ "addresses": [
+ "bc1qu9dgdg330r6r84g5mw7wqshg04exv2uttmw2elfwx74h5tgntuzs44gyfg"
+ ]
}
}
],
diff --git a/test/util/data/txcreatemultisig5.json b/test/util/data/txcreatemultisig5.json
new file mode 100644
index 0000000000..20e9bb077b
--- /dev/null
+++ b/test/util/data/txcreatemultisig5.json
@@ -0,0 +1,26 @@
+{
+ "txid": "813cf75e1f08debd242ef7c8192b7d478fb651355209369499a0de779ba7eb2f",
+ "hash": "813cf75e1f08debd242ef7c8192b7d478fb651355209369499a0de779ba7eb2f",
+ "version": 2,
+ "size": 42,
+ "vsize": 42,
+ "locktime": 0,
+ "vin": [
+ ],
+ "vout": [
+ {
+ "value": 1.00000000,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_HASH160 a4051c02398868af83f28f083208fae99a769263 OP_EQUAL",
+ "hex": "a914a4051c02398868af83f28f083208fae99a76926387",
+ "reqSigs": 1,
+ "type": "scripthash",
+ "addresses": [
+ "3GeGs1eHUxPz5YyuFe9WPpXid2UsUb5Jos"
+ ]
+ }
+ }
+ ],
+ "hex": "02000000000100e1f5050000000017a914a4051c02398868af83f28f083208fae99a7692638700000000"
+}
diff --git a/test/util/data/txcreateoutpubkey2.json b/test/util/data/txcreateoutpubkey2.json
index 5144722230..4ba5dcb282 100644
--- a/test/util/data/txcreateoutpubkey2.json
+++ b/test/util/data/txcreateoutpubkey2.json
@@ -14,7 +14,11 @@
"scriptPubKey": {
"asm": "0 a2516e770582864a6a56ed21a102044e388c62e3",
"hex": "0014a2516e770582864a6a56ed21a102044e388c62e3",
- "type": "witness_v0_keyhash"
+ "reqSigs": 1,
+ "type": "witness_v0_keyhash",
+ "addresses": [
+ "bc1q5fgkuac9s2ry56jka5s6zqsyfcugcchry5cwu0"
+ ]
}
}
],
diff --git a/test/util/data/txcreatescript3.json b/test/util/data/txcreatescript3.json
index 980da2fb31..31b6459214 100644
--- a/test/util/data/txcreatescript3.json
+++ b/test/util/data/txcreatescript3.json
@@ -14,7 +14,11 @@
"scriptPubKey": {
"asm": "0 0bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6",
"hex": "00200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6",
- "type": "witness_v0_scripthash"
+ "reqSigs": 1,
+ "type": "witness_v0_scripthash",
+ "addresses": [
+ "bc1qp0lfxhnscvsu0j36l36uurgv5tuck4pzuqytkvwqp3kh78cupttqyf705v"
+ ]
}
}
],