aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml13
-rw-r--r--.cirrus.yml26
-rw-r--r--.gitignore9
-rw-r--r--.python-version2
-rw-r--r--.style.yapf261
-rw-r--r--.travis.yml75
-rwxr-xr-x.travis/lint_04_install.sh2
-rwxr-xr-x.travis/lint_06_script.sh4
-rwxr-xr-x.travis/test_04_install.sh9
-rwxr-xr-x.travis/test_06_script_b.sh14
-rw-r--r--.tx/config2
-rw-r--r--CONTRIBUTING.md37
-rw-r--r--Makefile.am12
-rw-r--r--README.md2
-rw-r--r--SECURITY.md20
-rw-r--r--build-aux/m4/ax_boost_base.m4252
-rw-r--r--build-aux/m4/ax_boost_system.m47
-rw-r--r--build-aux/m4/bitcoin_qt.m427
-rw-r--r--build_msvc/bench_bitcoin/bench_bitcoin.vcxproj10
-rw-r--r--build_msvc/bitcoin-cli/bitcoin-cli.vcxproj8
-rw-r--r--build_msvc/bitcoin-tx/bitcoin-tx.vcxproj8
-rw-r--r--build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj14
-rw-r--r--build_msvc/bitcoin_config.h10
-rw-r--r--build_msvc/bitcoind/bitcoind.vcxproj8
-rw-r--r--build_msvc/common.vcxproj2
-rw-r--r--build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj.in8
-rw-r--r--build_msvc/libbitcoin_common/libbitcoin_common.vcxproj.in8
-rw-r--r--build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj.in8
-rw-r--r--build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj33
-rw-r--r--build_msvc/libbitcoin_server/libbitcoin_server.vcxproj.in15
-rw-r--r--build_msvc/libbitcoin_util/libbitcoin_util.vcxproj.in8
-rw-r--r--build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj.in8
-rw-r--r--build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in8
-rw-r--r--build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj.in8
-rw-r--r--build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj9
-rw-r--r--build_msvc/libleveldb/libleveldb.vcxproj4
-rw-r--r--build_msvc/libsecp256k1/libsecp256k1.vcxproj4
-rw-r--r--build_msvc/libunivalue/libunivalue.vcxproj4
-rw-r--r--build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj4
-rw-r--r--build_msvc/test_bitcoin/test_bitcoin.vcxproj10
-rw-r--r--build_msvc/testconsensus/testconsensus.vcxproj4
-rw-r--r--configure.ac95
-rw-r--r--contrib/bitcoin-cli.bash-completion2
-rw-r--r--contrib/devtools/README.md6
-rwxr-xr-xcontrib/devtools/circular-dependencies.py9
-rwxr-xr-xcontrib/devtools/gen-manpages.sh3
-rwxr-xr-xcontrib/devtools/github-merge.py70
-rw-r--r--contrib/devtools/split-debug.sh.in2
-rwxr-xr-xcontrib/devtools/symbol-check.py2
-rwxr-xr-xcontrib/devtools/test-security-check.py12
-rwxr-xr-xcontrib/devtools/test_deterministic_coverage.sh152
-rwxr-xr-xcontrib/gitian-build.py118
-rw-r--r--contrib/gitian-descriptors/gitian-linux.yml20
-rw-r--r--contrib/gitian-descriptors/gitian-osx-signer.yml3
-rw-r--r--contrib/gitian-descriptors/gitian-osx.yml21
-rw-r--r--contrib/gitian-descriptors/gitian-win-signer.yml3
-rw-r--r--contrib/gitian-descriptors/gitian-win.yml19
-rw-r--r--contrib/gitian-keys/keys.txt1
-rw-r--r--contrib/init/bitcoind.service34
-rwxr-xr-xcontrib/install_db4.sh16
-rw-r--r--contrib/linearize/README.md3
-rwxr-xr-xcontrib/linearize/linearize-data.py6
-rwxr-xr-xcontrib/linearize/linearize-hashes.py4
-rwxr-xr-xcontrib/seeds/makeseeds.py27
-rw-r--r--contrib/verify-commits/README.md3
-rw-r--r--contrib/verify-commits/trusted-keys1
-rwxr-xr-xcontrib/verify-commits/verify-commits.py10
-rw-r--r--contrib/windeploy/win-codesign.cert187
-rw-r--r--contrib/zmq/zmq_sub3.4.py90
-rw-r--r--depends/Makefile9
-rw-r--r--depends/README.md8
-rw-r--r--depends/config.site.in6
-rw-r--r--depends/funcs.mk17
-rw-r--r--depends/packages.md40
-rw-r--r--depends/packages/bdb.mk2
-rw-r--r--depends/packages/boost.mk6
-rw-r--r--depends/packages/dbus.mk6
-rw-r--r--depends/packages/expat.mk7
-rw-r--r--depends/packages/fontconfig.mk6
-rw-r--r--depends/packages/freetype.mk6
-rw-r--r--depends/packages/libX11.mk11
-rw-r--r--depends/packages/libXau.mk6
-rw-r--r--depends/packages/libXext.mk35
-rw-r--r--depends/packages/libevent.mk1
-rw-r--r--depends/packages/libxcb.mk4
-rw-r--r--depends/packages/miniupnpc.mk2
-rw-r--r--depends/packages/native_biplist.mk6
-rw-r--r--depends/packages/native_cctools.mk6
-rw-r--r--depends/packages/native_cdrkit.mk2
-rw-r--r--depends/packages/native_ds_store.mk6
-rw-r--r--depends/packages/native_mac_alias.mk6
-rw-r--r--depends/packages/native_protobuf.mk2
-rw-r--r--depends/packages/packages.mk4
-rw-r--r--depends/packages/protobuf.mk2
-rw-r--r--depends/packages/qrencode.mk4
-rw-r--r--depends/packages/qt.mk8
-rw-r--r--depends/packages/rapidcheck.mk21
-rw-r--r--depends/packages/xcb_proto.mk2
-rw-r--r--depends/packages/xextproto.mk2
-rw-r--r--depends/packages/xproto.mk2
-rw-r--r--depends/packages/xtrans.mk4
-rw-r--r--depends/packages/zeromq.mk2
-rw-r--r--depends/packages/zlib.mk2
-rw-r--r--doc/JSON-RPC-interface.md12
-rw-r--r--doc/README.md2
-rw-r--r--doc/REST-interface.md4
-rw-r--r--doc/bips.md8
-rw-r--r--doc/build-freebsd.md56
-rw-r--r--doc/build-osx.md2
-rw-r--r--doc/build-unix.md10
-rw-r--r--doc/build-windows.md39
-rw-r--r--doc/dependencies.md22
-rw-r--r--doc/descriptors.md38
-rw-r--r--doc/developer-notes.md113
-rw-r--r--doc/fuzzing.md57
-rw-r--r--doc/init.md18
-rw-r--r--doc/man/Makefile.am6
-rw-r--r--doc/man/bitcoin-cli.16
-rw-r--r--doc/man/bitcoin-qt.111
-rw-r--r--doc/man/bitcoin-tx.18
-rw-r--r--doc/man/bitcoin-wallet.167
-rw-r--r--doc/man/bitcoind.112
-rw-r--r--doc/productivity.md217
-rw-r--r--doc/psbt.md11
-rw-r--r--doc/reduce-traffic.md13
-rw-r--r--doc/release-notes-14054.md7
-rw-r--r--doc/release-notes-14802.md3
-rw-r--r--doc/release-notes-14941.md5
-rw-r--r--doc/release-notes-14954.md3
-rw-r--r--doc/release-notes-14982.md5
-rw-r--r--doc/release-notes-15006.md4
-rw-r--r--doc/release-notes-15566.md3
-rw-r--r--doc/release-notes-15620.md13
-rw-r--r--doc/release-notes-15637.md3
-rw-r--r--doc/release-notes-15730.md5
-rw-r--r--doc/release-notes-15849.md6
-rw-r--r--doc/release-notes.md304
-rw-r--r--doc/release-notes/release-notes-0.12.0.md2
-rw-r--r--doc/release-notes/release-notes-0.18.0.md1224
-rw-r--r--doc/release-process.md117
-rw-r--r--doc/shared-libraries.md4
-rw-r--r--doc/tor.md7
-rw-r--r--doc/translation_process.md24
-rw-r--r--doc/translation_strings_policy.md16
-rw-r--r--doc/travis-ci.md42
-rw-r--r--share/examples/bitcoin.conf27
-rw-r--r--share/setup.nsi.in12
-rw-r--r--src/Makefile.am77
-rw-r--r--src/Makefile.bench.include18
-rw-r--r--src/Makefile.leveldb.include2
-rw-r--r--src/Makefile.qt.include3
-rw-r--r--src/Makefile.qttest.include4
-rw-r--r--src/Makefile.test.include342
-rw-r--r--src/addrman.cpp14
-rw-r--r--src/addrman.h3
-rw-r--r--src/arith_uint256.cpp1
-rw-r--r--src/bench/base58.cpp1
-rw-r--r--src/bench/bech32.cpp1
-rw-r--r--src/bench/bench.cpp20
-rw-r--r--src/bench/bench_bitcoin.cpp39
-rw-r--r--src/bench/block_assemble.cpp73
-rw-r--r--src/bench/ccoins_caching.cpp4
-rw-r--r--src/bench/chacha20.cpp45
-rw-r--r--src/bench/checkqueue.cpp1
-rw-r--r--src/bench/coin_selection.cpp4
-rw-r--r--src/bench/crypto_hash.cpp2
-rw-r--r--src/bench/duplicate_inputs.cpp40
-rw-r--r--src/bench/examples.cpp1
-rw-r--r--src/bench/poly1305.cpp41
-rw-r--r--src/bench/rollingbloom.cpp9
-rw-r--r--src/bench/rpc_mempool.cpp42
-rw-r--r--src/bench/util_time.cpp42
-rw-r--r--src/bench/verify_script.cpp1
-rw-r--r--src/bench/wallet_balance.cpp52
-rw-r--r--src/bitcoin-cli.cpp29
-rw-r--r--src/bitcoin-tx.cpp32
-rw-r--r--src/bitcoin-wallet.cpp21
-rw-r--r--src/bitcoind.cpp25
-rw-r--r--src/blockencodings.cpp2
-rw-r--r--src/blockfilter.cpp60
-rw-r--r--src/blockfilter.h18
-rw-r--r--src/bloom.cpp5
-rw-r--r--src/chain.cpp7
-rw-r--r--src/chain.h53
-rw-r--r--src/chainparams.cpp16
-rw-r--r--src/checkpoints.cpp32
-rw-r--r--src/checkpoints.h27
-rw-r--r--src/coins.h4
-rw-r--r--src/compat.h4
-rw-r--r--src/compat/assumptions.h65
-rw-r--r--src/consensus/merkle.cpp1
-rw-r--r--src/consensus/tx_check.cpp57
-rw-r--r--src/consensus/tx_check.h20
-rw-r--r--src/consensus/tx_verify.cpp60
-rw-r--r--src/consensus/tx_verify.h3
-rw-r--r--src/consensus/validation.h106
-rw-r--r--src/core_io.h2
-rw-r--r--src/core_read.cpp18
-rw-r--r--src/core_write.cpp1
-rw-r--r--src/crypto/aes.cpp63
-rw-r--r--src/crypto/aes.h51
-rw-r--r--src/crypto/chacha20.cpp132
-rw-r--r--src/crypto/chacha20.h18
-rw-r--r--src/crypto/hkdf_sha256_32.cpp21
-rw-r--r--src/crypto/hkdf_sha256_32.h25
-rw-r--r--src/crypto/poly1305.cpp141
-rw-r--r--src/crypto/poly1305.h17
-rw-r--r--src/crypto/ripemd160.cpp2
-rw-r--r--src/crypto/sha1.cpp2
-rw-r--r--src/crypto/sha256_avx2.cpp1
-rw-r--r--src/crypto/sha256_sse41.cpp1
-rw-r--r--src/crypto/sha512.cpp2
-rw-r--r--src/dbwrapper.cpp2
-rw-r--r--src/dummywallet.cpp41
-rw-r--r--src/flatfile.cpp98
-rw-r--r--src/flatfile.h96
-rw-r--r--src/fs.cpp4
-rw-r--r--src/fs.h1
-rw-r--r--src/httprpc.cpp1
-rw-r--r--src/httpserver.cpp35
-rw-r--r--src/httpserver.h2
-rw-r--r--src/index/base.cpp77
-rw-r--r--src/index/base.h21
-rw-r--r--src/index/blockfilterindex.cpp467
-rw-r--r--src/index/blockfilterindex.h94
-rw-r--r--src/index/txindex.cpp10
-rw-r--r--src/init.cpp283
-rw-r--r--src/interfaces/README.md4
-rw-r--r--src/interfaces/chain.cpp277
-rw-r--r--src/interfaces/chain.h204
-rw-r--r--src/interfaces/node.cpp22
-rw-r--r--src/interfaces/node.h8
-rw-r--r--src/interfaces/wallet.cpp290
-rw-r--r--src/interfaces/wallet.h12
-rw-r--r--src/key.cpp7
-rw-r--r--src/key.h3
-rw-r--r--src/key_io.cpp12
-rw-r--r--src/keystore.cpp9
-rw-r--r--src/logging.cpp34
-rw-r--r--src/logging.h10
-rw-r--r--src/merkleblock.cpp1
-rw-r--r--src/miner.cpp27
-rw-r--r--src/miner.h9
-rw-r--r--src/net.cpp63
-rw-r--r--src/net.h43
-rw-r--r--src/net_processing.cpp814
-rw-r--r--src/netaddress.cpp144
-rw-r--r--src/netaddress.h11
-rw-r--r--src/netbase.cpp7
-rw-r--r--src/node/README.md22
-rw-r--r--src/node/coin.cpp22
-rw-r--r--src/node/coin.h22
-rw-r--r--src/node/psbt.cpp134
-rw-r--r--src/node/psbt.h43
-rw-r--r--src/node/transaction.cpp78
-rw-r--r--src/node/transaction.h24
-rw-r--r--src/noui.cpp2
-rw-r--r--src/outputtype.cpp18
-rw-r--r--src/policy/fees.cpp39
-rw-r--r--src/policy/fees.h4
-rw-r--r--src/policy/policy.cpp28
-rw-r--r--src/policy/policy.h24
-rw-r--r--src/policy/rbf.cpp13
-rw-r--r--src/policy/rbf.h8
-rw-r--r--src/policy/settings.cpp14
-rw-r--r--src/policy/settings.h35
-rw-r--r--src/prevector.h16
-rw-r--r--src/primitives/block.cpp1
-rw-r--r--src/primitives/transaction.h4
-rw-r--r--src/psbt.cpp367
-rw-r--r--src/psbt.h599
-rw-r--r--src/qt/addressbookpage.cpp3
-rw-r--r--src/qt/addresstablemodel.cpp1
-rw-r--r--src/qt/bantablemodel.cpp2
-rw-r--r--src/qt/bitcoin.cpp28
-rw-r--r--src/qt/bitcoingui.cpp64
-rw-r--r--src/qt/bitcoingui.h7
-rw-r--r--src/qt/bitcoinstrings.cpp23
-rw-r--r--src/qt/bitcoinunits.cpp2
-rw-r--r--src/qt/clientmodel.cpp11
-rw-r--r--src/qt/coincontroldialog.cpp8
-rw-r--r--src/qt/forms/coincontroldialog.ui6
-rw-r--r--src/qt/forms/debugwindow.ui16
-rw-r--r--src/qt/forms/receivecoinsdialog.ui10
-rw-r--r--src/qt/forms/receiverequestdialog.ui2
-rw-r--r--src/qt/guiconstants.h6
-rw-r--r--src/qt/guiutil.cpp24
-rw-r--r--src/qt/guiutil.h5
-rw-r--r--src/qt/intro.cpp13
-rw-r--r--src/qt/intro.h5
-rw-r--r--src/qt/locale/bitcoin_af.ts196
-rw-r--r--src/qt/locale/bitcoin_af_ZA.ts92
-rw-r--r--src/qt/locale/bitcoin_am.ts451
-rw-r--r--src/qt/locale/bitcoin_ar.ts33
-rw-r--r--src/qt/locale/bitcoin_bg.ts40
-rw-r--r--src/qt/locale/bitcoin_cs.ts176
-rw-r--r--src/qt/locale/bitcoin_da.ts14
-rw-r--r--src/qt/locale/bitcoin_de.ts18
-rw-r--r--src/qt/locale/bitcoin_el_GR.ts166
-rw-r--r--src/qt/locale/bitcoin_en.ts550
-rw-r--r--src/qt/locale/bitcoin_en_GB.ts130
-rw-r--r--src/qt/locale/bitcoin_es.ts14
-rw-r--r--src/qt/locale/bitcoin_es_VE.ts96
-rw-r--r--src/qt/locale/bitcoin_eu_ES.ts32
-rw-r--r--src/qt/locale/bitcoin_fa.ts24
-rw-r--r--src/qt/locale/bitcoin_fa_IR.ts1246
-rw-r--r--src/qt/locale/bitcoin_fi.ts162
-rw-r--r--src/qt/locale/bitcoin_fr.ts26
-rw-r--r--src/qt/locale/bitcoin_he.ts32
-rw-r--r--src/qt/locale/bitcoin_hi_IN.ts108
-rw-r--r--src/qt/locale/bitcoin_hr.ts2143
-rw-r--r--src/qt/locale/bitcoin_id.ts969
-rw-r--r--src/qt/locale/bitcoin_id_ID.ts180
-rw-r--r--src/qt/locale/bitcoin_is.ts997
-rw-r--r--src/qt/locale/bitcoin_it.ts8
-rw-r--r--src/qt/locale/bitcoin_it_IT.ts12
-rw-r--r--src/qt/locale/bitcoin_ja.ts100
-rw-r--r--src/qt/locale/bitcoin_ka.ts8
-rw-r--r--src/qt/locale/bitcoin_ko.ts471
-rw-r--r--src/qt/locale/bitcoin_ko_KR.ts11
-rw-r--r--src/qt/locale/bitcoin_lt.ts374
-rw-r--r--src/qt/locale/bitcoin_ml.ts336
-rw-r--r--src/qt/locale/bitcoin_nl.ts14
-rw-r--r--src/qt/locale/bitcoin_pl.ts11
-rw-r--r--src/qt/locale/bitcoin_pt_BR.ts13
-rw-r--r--src/qt/locale/bitcoin_pt_PT.ts111
-rw-r--r--src/qt/locale/bitcoin_ro.ts4
-rw-r--r--src/qt/locale/bitcoin_ro_RO.ts144
-rw-r--r--src/qt/locale/bitcoin_ru.ts4
-rw-r--r--src/qt/locale/bitcoin_ru_RU.ts150
-rw-r--r--src/qt/locale/bitcoin_sk.ts86
-rw-r--r--src/qt/locale/bitcoin_sl_SI.ts4
-rw-r--r--src/qt/locale/bitcoin_sn.ts367
-rw-r--r--src/qt/locale/bitcoin_sv.ts180
-rw-r--r--src/qt/locale/bitcoin_szl.ts2123
-rw-r--r--src/qt/locale/bitcoin_tr.ts88
-rw-r--r--src/qt/locale/bitcoin_tr_TR.ts12
-rw-r--r--src/qt/locale/bitcoin_uk.ts6
-rw-r--r--src/qt/locale/bitcoin_ur_PK.ts60
-rw-r--r--src/qt/locale/bitcoin_vi.ts10
-rw-r--r--src/qt/locale/bitcoin_zh_CN.ts289
-rw-r--r--src/qt/locale/bitcoin_zh_TW.ts10
-rw-r--r--src/qt/modaloverlay.cpp8
-rw-r--r--src/qt/modaloverlay.h1
-rw-r--r--src/qt/optionsdialog.cpp8
-rw-r--r--src/qt/optionsmodel.cpp5
-rw-r--r--src/qt/paymentserver.cpp26
-rw-r--r--src/qt/peertablemodel.cpp1
-rw-r--r--src/qt/platformstyle.cpp2
-rw-r--r--src/qt/qrimagewidget.cpp141
-rw-r--r--src/qt/qrimagewidget.h45
-rw-r--r--src/qt/receivecoinsdialog.cpp8
-rw-r--r--src/qt/receiverequestdialog.cpp119
-rw-r--r--src/qt/receiverequestdialog.h30
-rw-r--r--src/qt/rpcconsole.cpp6
-rw-r--r--src/qt/sendcoinsdialog.cpp46
-rw-r--r--src/qt/sendcoinsdialog.h2
-rw-r--r--src/qt/signverifymessagedialog.cpp12
-rw-r--r--src/qt/test/addressbooktests.cpp7
-rw-r--r--src/qt/test/apptests.cpp6
-rw-r--r--src/qt/test/rpcnestedtests.cpp8
-rw-r--r--src/qt/test/wallettests.cpp16
-rw-r--r--src/qt/transactiondesc.cpp2
-rw-r--r--src/qt/transactionrecord.cpp3
-rw-r--r--src/qt/transactiontablemodel.cpp4
-rw-r--r--src/qt/transactionview.cpp3
-rw-r--r--src/qt/utilitydialog.cpp7
-rw-r--r--src/qt/walletcontroller.cpp78
-rw-r--r--src/qt/walletcontroller.h31
-rw-r--r--src/qt/walletframe.cpp24
-rw-r--r--src/qt/walletframe.h6
-rw-r--r--src/qt/walletmodel.cpp13
-rw-r--r--src/qt/walletmodel.h11
-rw-r--r--src/qt/walletmodeltransaction.cpp3
-rw-r--r--src/qt/walletview.cpp1
-rw-r--r--src/random.cpp236
-rw-r--r--src/random.h7
-rw-r--r--src/rest.cpp24
-rw-r--r--src/rpc/blockchain.cpp388
-rw-r--r--src/rpc/blockchain.h17
-rw-r--r--src/rpc/client.cpp9
-rw-r--r--src/rpc/mining.cpp136
-rw-r--r--src/rpc/mining.h18
-rw-r--r--src/rpc/misc.cpp188
-rw-r--r--src/rpc/net.cpp43
-rw-r--r--src/rpc/protocol.cpp1
-rw-r--r--src/rpc/rawtransaction.cpp973
-rw-r--r--src/rpc/rawtransaction.h22
-rw-r--r--src/rpc/rawtransaction_util.cpp282
-rw-r--r--src/rpc/rawtransaction_util.h32
-rw-r--r--src/rpc/server.cpp185
-rw-r--r--src/rpc/server.h73
-rw-r--r--src/rpc/util.cpp279
-rw-r--r--src/rpc/util.h98
-rw-r--r--src/scheduler.h6
-rw-r--r--src/script/descriptor.cpp229
-rw-r--r--src/script/descriptor.h19
-rw-r--r--src/script/ismine.cpp2
-rw-r--r--src/script/ismine.h29
-rw-r--r--src/script/script.cpp1
-rw-r--r--src/script/script.h9
-rw-r--r--src/script/sigcache.cpp1
-rw-r--r--src/script/sign.cpp244
-rw-r--r--src/script/sign.h562
-rw-r--r--src/script/standard.cpp18
-rw-r--r--src/script/standard.h25
-rw-r--r--src/secp256k1/.gitignore1
-rw-r--r--src/secp256k1/.travis.yml9
-rw-r--r--src/secp256k1/Makefile.am13
-rw-r--r--src/secp256k1/build-aux/m4/ax_jni_include_dir.m447
-rw-r--r--src/secp256k1/build-aux/m4/bitcoin_secp.m41
-rw-r--r--src/secp256k1/configure.ac89
-rw-r--r--src/secp256k1/include/secp256k1.h49
-rw-r--r--src/secp256k1/include/secp256k1_ecdh.h32
-rw-r--r--src/secp256k1/libsecp256k1.pc.in2
-rw-r--r--src/secp256k1/src/bench.h16
-rw-r--r--src/secp256k1/src/bench_ecdh.c10
-rw-r--r--src/secp256k1/src/bench_ecmult.c207
-rw-r--r--src/secp256k1/src/bench_internal.c83
-rw-r--r--src/secp256k1/src/bench_recover.c8
-rw-r--r--src/secp256k1/src/bench_sign.c12
-rw-r--r--src/secp256k1/src/eckey_impl.h2
-rw-r--r--src/secp256k1/src/ecmult.h19
-rw-r--r--src/secp256k1/src/ecmult_const.h4
-rw-r--r--src/secp256k1/src/ecmult_const_impl.h97
-rw-r--r--src/secp256k1/src/ecmult_gen_impl.h4
-rw-r--r--src/secp256k1/src/ecmult_impl.h929
-rw-r--r--src/secp256k1/src/field_10x26.h4
-rw-r--r--src/secp256k1/src/field_10x26_impl.h5
-rw-r--r--src/secp256k1/src/field_5x52.h4
-rw-r--r--src/secp256k1/src/field_5x52_impl.h2
-rw-r--r--src/secp256k1/src/field_5x52_int128_impl.h4
-rw-r--r--src/secp256k1/src/field_impl.h3
-rw-r--r--src/secp256k1/src/gen_context.c2
-rw-r--r--src/secp256k1/src/group.h10
-rw-r--r--src/secp256k1/src/group_impl.h75
-rw-r--r--src/secp256k1/src/hash.h26
-rw-r--r--src/secp256k1/src/hash_impl.h49
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java2
-rw-r--r--src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c6
-rw-r--r--src/secp256k1/src/modules/ecdh/main_impl.h41
-rw-r--r--src/secp256k1/src/modules/ecdh/tests_impl.h59
-rw-r--r--src/secp256k1/src/scalar_4x64_impl.h6
-rw-r--r--src/secp256k1/src/scratch.h39
-rw-r--r--src/secp256k1/src/scratch_impl.h86
-rw-r--r--src/secp256k1/src/secp256k1.c51
-rw-r--r--src/secp256k1/src/testrand_impl.h2
-rw-r--r--src/secp256k1/src/tests.c668
-rw-r--r--src/secp256k1/src/tests_exhaustive.c43
-rw-r--r--src/secp256k1/src/util.h10
-rw-r--r--src/serialize.h11
-rw-r--r--src/support/lockedpool.cpp4
-rw-r--r--src/sync.cpp39
-rw-r--r--src/sync.h24
-rw-r--r--src/test/README.md2
-rw-r--r--src/test/addrman_tests.cpp4
-rw-r--r--src/test/allocator_tests.cpp4
-rw-r--r--src/test/amount_tests.cpp4
-rw-r--r--src/test/arith_uint256_tests.cpp4
-rw-r--r--src/test/base32_tests.cpp4
-rw-r--r--src/test/base58_tests.cpp4
-rw-r--r--src/test/base64_tests.cpp4
-rw-r--r--src/test/bech32_tests.cpp2
-rw-r--r--src/test/bip32_tests.cpp4
-rw-r--r--src/test/blockchain_tests.cpp2
-rw-r--r--src/test/blockencodings_tests.cpp5
-rw-r--r--src/test/blockfilter_index_tests.cpp307
-rw-r--r--src/test/blockfilter_tests.cpp35
-rw-r--r--src/test/bloom_tests.cpp22
-rw-r--r--src/test/bswap_tests.cpp4
-rw-r--r--src/test/checkqueue_tests.cpp5
-rw-r--r--src/test/coins_tests.cpp15
-rw-r--r--src/test/compilerbug_tests.cpp43
-rw-r--r--src/test/compress_tests.cpp4
-rw-r--r--src/test/crypto_tests.cpp263
-rw-r--r--src/test/cuckoocache_tests.cpp4
-rw-r--r--src/test/data/blockfilters.json10
-rw-r--r--src/test/dbwrapper_tests.cpp4
-rw-r--r--src/test/denialofservice_tests.cpp15
-rw-r--r--src/test/descriptor_tests.cpp42
-rw-r--r--src/test/flatfile_tests.cpp123
-rw-r--r--src/test/fs_tests.cpp4
-rw-r--r--src/test/fuzz/deserialize.cpp (renamed from src/test/test_bitcoin_fuzzy.cpp)2
-rw-r--r--src/test/fuzz/fuzz.h2
-rw-r--r--src/test/fuzz/script_flags.cpp72
-rw-r--r--src/test/getarg_tests.cpp4
-rw-r--r--src/test/hash_tests.cpp4
-rw-r--r--src/test/key_io_tests.cpp4
-rw-r--r--src/test/key_properties.cpp4
-rw-r--r--src/test/key_tests.cpp44
-rw-r--r--src/test/limitedmap_tests.cpp4
-rw-r--r--src/test/main.cpp7
-rw-r--r--src/test/mempool_tests.cpp4
-rw-r--r--src/test/merkle_tests.cpp4
-rw-r--r--src/test/merkleblock_tests.cpp4
-rw-r--r--src/test/miner_tests.cpp73
-rw-r--r--src/test/multisig_tests.cpp5
-rw-r--r--src/test/net_tests.cpp4
-rw-r--r--src/test/netbase_tests.cpp5
-rw-r--r--src/test/pmt_tests.cpp4
-rw-r--r--src/test/policyestimator_tests.cpp4
-rw-r--r--src/test/pow_tests.cpp4
-rw-r--r--src/test/prevector_tests.cpp4
-rw-r--r--src/test/raii_event_tests.cpp4
-rw-r--r--src/test/random_tests.cpp14
-rw-r--r--src/test/reverselock_tests.cpp4
-rw-r--r--src/test/rpc_tests.cpp11
-rw-r--r--src/test/sanity_tests.cpp4
-rw-r--r--src/test/scheduler_tests.cpp4
-rw-r--r--src/test/script_p2sh_tests.cpp40
-rw-r--r--src/test/script_standard_tests.cpp72
-rw-r--r--src/test/script_tests.cpp10
-rw-r--r--src/test/scriptnum_tests.cpp4
-rw-r--r--src/test/serialize_tests.cpp4
-rw-r--r--src/test/setup_common.cpp (renamed from src/test/test_bitcoin.cpp)84
-rw-r--r--src/test/setup_common.h (renamed from src/test/test_bitcoin.h)15
-rw-r--r--src/test/sighash_tests.cpp6
-rw-r--r--src/test/sigopcount_tests.cpp14
-rw-r--r--src/test/skiplist_tests.cpp50
-rw-r--r--src/test/streams_tests.cpp4
-rw-r--r--src/test/sync_tests.cpp4
-rw-r--r--src/test/test_bitcoin_main.cpp30
-rw-r--r--src/test/timedata_tests.cpp4
-rw-r--r--src/test/torcontrol_tests.cpp6
-rw-r--r--src/test/transaction_tests.cpp37
-rw-r--r--src/test/txindex_tests.cpp15
-rw-r--r--src/test/txvalidation_tests.cpp9
-rw-r--r--src/test/txvalidationcache_tests.cpp37
-rw-r--r--src/test/uint256_tests.cpp4
-rw-r--r--src/test/util.cpp89
-rw-r--r--src/test/util.h70
-rw-r--r--src/test/util_tests.cpp381
-rw-r--r--src/test/util_threadnames_tests.cpp73
-rw-r--r--src/test/validation_block_tests.cpp9
-rw-r--r--src/test/validation_tests.cpp (renamed from src/test/main_tests.cpp)8
-rw-r--r--src/test/versionbits_tests.cpp34
-rw-r--r--src/threadsafety.h11
-rw-r--r--src/timedata.cpp1
-rw-r--r--src/tinyformat.h1
-rw-r--r--src/torcontrol.cpp2
-rw-r--r--src/txdb.cpp3
-rw-r--r--src/txdb.h2
-rw-r--r--src/txmempool.cpp19
-rw-r--r--src/txmempool.h17
-rw-r--r--src/ui_interface.cpp17
-rw-r--r--src/ui_interface.h14
-rw-r--r--src/util/bip32.cpp66
-rw-r--r--src/util/bip32.h19
-rw-r--r--src/util/error.cpp43
-rw-r--r--src/util/error.h38
-rw-r--r--src/util/fees.cpp42
-rw-r--r--src/util/fees.h16
-rw-r--r--src/util/rbf.cpp17
-rw-r--r--src/util/rbf.h18
-rw-r--r--src/util/strencodings.cpp57
-rw-r--r--src/util/strencodings.h23
-rw-r--r--src/util/system.cpp106
-rw-r--r--src/util/system.h30
-rw-r--r--src/util/threadnames.cpp62
-rw-r--r--src/util/threadnames.h21
-rw-r--r--src/util/time.cpp27
-rw-r--r--src/util/time.h28
-rw-r--r--src/util/url.cpp21
-rw-r--r--src/util/url.h12
-rw-r--r--src/util/validation.cpp20
-rw-r--r--src/util/validation.h18
-rw-r--r--src/validation.cpp1240
-rw-r--r--src/validation.h260
-rw-r--r--src/validationinterface.cpp13
-rw-r--r--src/validationinterface.h4
-rw-r--r--src/wallet/coincontrol.h4
-rw-r--r--src/wallet/crypter.cpp4
-rw-r--r--src/wallet/db.cpp35
-rw-r--r--src/wallet/db.h5
-rw-r--r--src/wallet/feebumper.cpp185
-rw-r--r--src/wallet/feebumper.h15
-rw-r--r--src/wallet/fees.cpp30
-rw-r--r--src/wallet/fees.h8
-rw-r--r--src/wallet/init.cpp123
-rw-r--r--src/wallet/load.cpp112
-rw-r--r--src/wallet/load.h38
-rw-r--r--src/wallet/psbtwallet.cpp60
-rw-r--r--src/wallet/psbtwallet.h34
-rw-r--r--src/wallet/rpcdump.cpp583
-rw-r--r--src/wallet/rpcwallet.cpp936
-rw-r--r--src/wallet/rpcwallet.h18
-rw-r--r--src/wallet/test/coinselector_tests.cpp12
-rw-r--r--src/wallet/test/db_tests.cpp4
-rw-r--r--src/wallet/test/init_test_fixture.h4
-rw-r--r--src/wallet/test/init_tests.cpp9
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp10
-rw-r--r--src/wallet/test/wallet_crypto_tests.cpp4
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp18
-rw-r--r--src/wallet/test/wallet_test_fixture.h6
-rw-r--r--src/wallet/test/wallet_tests.cpp139
-rw-r--r--src/wallet/wallet.cpp1245
-rw-r--r--src/wallet/wallet.h379
-rw-r--r--src/wallet/walletdb.cpp32
-rw-r--r--src/wallet/walletdb.h30
-rw-r--r--src/wallet/wallettool.cpp57
-rw-r--r--src/wallet/wallettool.h2
-rw-r--r--src/wallet/walletutil.cpp19
-rw-r--r--src/warnings.cpp9
-rw-r--r--src/zmq/zmqabstractnotifier.cpp1
-rw-r--r--src/zmq/zmqnotificationinterface.cpp1
-rw-r--r--test/README.md20
-rw-r--r--test/config.ini.in2
-rw-r--r--test/functional/README.md34
-rwxr-xr-xtest/functional/combine_logs.py54
-rw-r--r--test/functional/data/invalid_txs.py10
-rw-r--r--test/functional/data/rpc_getblockstats.json276
-rwxr-xr-xtest/functional/example_test.py6
-rwxr-xr-xtest/functional/feature_assumevalid.py12
-rwxr-xr-xtest/functional/feature_bip68_sequence.py38
-rwxr-xr-xtest/functional/feature_block.py232
-rwxr-xr-xtest/functional/feature_cltv.py6
-rwxr-xr-xtest/functional/feature_config_args.py18
-rwxr-xr-xtest/functional/feature_csv_activation.py68
-rwxr-xr-xtest/functional/feature_dbcrash.py17
-rwxr-xr-xtest/functional/feature_dersig.py6
-rwxr-xr-xtest/functional/feature_fee_estimation.py14
-rwxr-xr-xtest/functional/feature_filelock.py2
-rwxr-xr-xtest/functional/feature_notifications.py20
-rwxr-xr-xtest/functional/feature_nulldummy.py22
-rwxr-xr-xtest/functional/feature_proxy.py12
-rwxr-xr-xtest/functional/feature_pruning.py227
-rwxr-xr-xtest/functional/feature_rbf.py108
-rwxr-xr-xtest/functional/feature_segwit.py133
-rwxr-xr-xtest/functional/feature_versionbits_warning.py16
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py10
-rwxr-xr-xtest/functional/interface_http.py28
-rwxr-xr-xtest/functional/interface_rest.py57
-rwxr-xr-xtest/functional/interface_rpc.py19
-rwxr-xr-xtest/functional/interface_zmq.py15
-rwxr-xr-xtest/functional/mempool_accept.py67
-rwxr-xr-xtest/functional/mempool_limit.py6
-rwxr-xr-xtest/functional/mempool_packages.py34
-rwxr-xr-xtest/functional/mempool_persist.py17
-rwxr-xr-xtest/functional/mempool_resurrect.py8
-rwxr-xr-xtest/functional/mining_basic.py85
-rwxr-xr-xtest/functional/mining_getblocktemplate_longpoll.py12
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py28
-rwxr-xr-xtest/functional/p2p_blocksonly.py58
-rwxr-xr-xtest/functional/p2p_compactblocks.py269
-rwxr-xr-xtest/functional/p2p_feefilter.py16
-rwxr-xr-xtest/functional/p2p_invalid_messages.py16
-rwxr-xr-xtest/functional/p2p_leak.py8
-rwxr-xr-xtest/functional/p2p_node_network_limited.py18
-rwxr-xr-xtest/functional/p2p_segwit.py232
-rwxr-xr-xtest/functional/p2p_sendheaders.py9
-rwxr-xr-xtest/functional/p2p_unrequested_blocks.py18
-rwxr-xr-xtest/functional/rpc_bind.py2
-rwxr-xr-xtest/functional/rpc_blockchain.py20
-rwxr-xr-xtest/functional/rpc_createmultisig.py48
-rwxr-xr-xtest/functional/rpc_decodescript.py18
-rwxr-xr-xtest/functional/rpc_deprecated.py25
-rwxr-xr-xtest/functional/rpc_deriveaddresses.py55
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py32
-rwxr-xr-xtest/functional/rpc_getblockfilter.py59
-rwxr-xr-xtest/functional/rpc_getblockstats.py52
-rwxr-xr-xtest/functional/rpc_getchaintips.py5
-rwxr-xr-xtest/functional/rpc_invalidateblock.py7
-rwxr-xr-xtest/functional/rpc_misc.py58
-rwxr-xr-xtest/functional/rpc_named_arguments.py4
-rwxr-xr-xtest/functional/rpc_preciousblock.py13
-rwxr-xr-xtest/functional/rpc_psbt.py114
-rwxr-xr-xtest/functional/rpc_rawtransaction.py72
-rwxr-xr-xtest/functional/rpc_scantxoutset.py18
-rwxr-xr-xtest/functional/rpc_signmessage.py10
-rwxr-xr-xtest/functional/rpc_signrawtransaction.py34
-rwxr-xr-xtest/functional/rpc_uptime.py4
-rw-r--r--test/functional/test_framework/address.py18
-rw-r--r--test/functional/test_framework/authproxy.py33
-rw-r--r--test/functional/test_framework/blocktools.py13
-rw-r--r--test/functional/test_framework/descriptors.py55
-rw-r--r--test/functional/test_framework/key.py574
-rwxr-xr-xtest/functional/test_framework/messages.py31
-rwxr-xr-xtest/functional/test_framework/mininode.py19
-rw-r--r--test/functional/test_framework/netutil.py10
-rw-r--r--test/functional/test_framework/script.py9
-rw-r--r--test/functional/test_framework/socks5.py4
-rwxr-xr-xtest/functional/test_framework/test_framework.py188
-rwxr-xr-xtest/functional/test_framework/test_node.py147
-rw-r--r--test/functional/test_framework/util.py29
-rwxr-xr-xtest/functional/test_runner.py51
-rwxr-xr-xtest/functional/wallet_abandonconflict.py13
-rwxr-xr-xtest/functional/wallet_address_types.py85
-rwxr-xr-xtest/functional/wallet_backup.py25
-rwxr-xr-xtest/functional/wallet_balance.py144
-rwxr-xr-xtest/functional/wallet_basic.py94
-rwxr-xr-xtest/functional/wallet_bumpfee.py92
-rwxr-xr-xtest/functional/wallet_create_tx.py12
-rwxr-xr-xtest/functional/wallet_createwallet.py123
-rwxr-xr-xtest/functional/wallet_disable.py6
-rwxr-xr-xtest/functional/wallet_disableprivatekeys.py46
-rwxr-xr-xtest/functional/wallet_dump.py6
-rwxr-xr-xtest/functional/wallet_hd.py5
-rwxr-xr-xtest/functional/wallet_import_rescan.py38
-rwxr-xr-xtest/functional/wallet_importmulti.py312
-rwxr-xr-xtest/functional/wallet_keypool.py12
-rwxr-xr-xtest/functional/wallet_keypool_topup.py11
-rwxr-xr-xtest/functional/wallet_listreceivedby.py5
-rwxr-xr-xtest/functional/wallet_listsinceblock.py5
-rwxr-xr-xtest/functional/wallet_listtransactions.py32
-rwxr-xr-xtest/functional/wallet_multiwallet.py10
-rwxr-xr-xtest/functional/wallet_resendwallettransactions.py71
-rwxr-xr-xtest/functional/wallet_txn_clone.py24
-rwxr-xr-xtest/functional/wallet_txn_doublespend.py15
-rwxr-xr-xtest/fuzz/test_runner.py138
-rwxr-xr-xtest/lint/check-doc.py36
-rwxr-xr-xtest/lint/lint-all.sh8
-rwxr-xr-xtest/lint/lint-circular-dependencies.sh11
-rwxr-xr-xtest/lint/lint-format-strings.py56
-rwxr-xr-xtest/lint/lint-format-strings.sh1
-rwxr-xr-xtest/lint/lint-locale-dependence.sh6
-rwxr-xr-xtest/lint/lint-python-dead-code.sh2
-rwxr-xr-xtest/lint/lint-python.sh8
-rw-r--r--test/lint/lint-spelling.ignore-words.txt7
-rwxr-xr-xtest/lint/lint-whitespace.sh10
-rw-r--r--test/sanitizer_suppressions/ubsan2
720 files changed, 35101 insertions, 11790 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index eb2d32e1bc..0c43e61592 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -7,18 +7,19 @@ clone_depth: 5
environment:
APPVEYOR_SAVE_CACHE_ON_ERROR: true
CLCACHE_SERVER: 1
- PACKAGES: boost-filesystem boost-signals2 boost-test libevent openssl zeromq berkeleydb
+ PACKAGES: berkeleydb boost-filesystem boost-signals2 boost-test libevent openssl rapidcheck zeromq
PATH: 'C:\Python37-x64;C:\Python37-x64\Scripts;%PATH%'
PYTHONUTF8: 1
cache:
-- C:\tools\vcpkg\installed
-- C:\Users\appveyor\clcache
+- C:\tools\vcpkg\installed -> .appveyor.yml
+- C:\Users\appveyor\clcache -> .appveyor.yml, build_msvc\**, **\Makefile.am, **\*.vcxproj.in
install:
- cmd: pip install --quiet git+https://github.com/frerich/clcache.git@v4.2.0
# Disable zmq test for now since python zmq library on Windows would cause Access violation sometimes.
# - cmd: pip install zmq
+- cmd: echo set(VCPKG_BUILD_TYPE release) >> C:\tools\vcpkg\triplets\%PLATFORM%-windows-static.cmake
+- cmd: vcpkg remove --outdated --recurse
- cmd: vcpkg install --triplet %PLATFORM%-windows-static %PACKAGES% > NUL
-- cmd: del /s /q C:\Tools\vcpkg\installed\%PLATFORM%-windows-static\debug # Remove unused debug library
before_build:
- ps: clcache -M 536870912
- cmd: python build_msvc\msvc-autogen.py
@@ -27,7 +28,6 @@ before_build:
${content} = (Get-Content ${files}[${i}]);
${content} = ${content}.Replace("</RuntimeLibrary>", "</RuntimeLibrary><DebugInformationFormat>None</DebugInformationFormat>");
${content} = ${content}.Replace("<WholeProgramOptimization>true", "<WholeProgramOptimization>false");
- ${content} = ${content}.Replace("NDEBUG;", "");
Set-Content ${files}[${i}] ${content};
}
- ps: Start-Process clcache-server
@@ -39,6 +39,7 @@ after_build:
- ps: clcache -z
before_test:
- ps: ${conf_ini} = (Get-Content([IO.Path]::Combine(${env:APPVEYOR_BUILD_FOLDER}, "test", "config.ini.in")))
+- ps: ${conf_ini} = ${conf_ini}.Replace("@PACKAGE_NAME@", "Bitcoin Core")
- ps: ${conf_ini} = ${conf_ini}.Replace("@abs_top_srcdir@", ${env:APPVEYOR_BUILD_FOLDER})
- ps: ${conf_ini} = ${conf_ini}.Replace("@abs_top_builddir@", ${env:APPVEYOR_BUILD_FOLDER})
- ps: ${conf_ini} = ${conf_ini}.Replace("@EXEEXT@", ".exe")
@@ -54,5 +55,5 @@ test_script:
- cmd: src\bench_bitcoin.exe -evals=1 -scaling=0 > NUL
- ps: python test\util\bitcoin-util-test.py
- cmd: python test\util\rpcauth-test.py
-- cmd: python test\functional\test_runner.py --ci --force --quiet --combinedlogslen=4000 --failfast
+- cmd: python test\functional\test_runner.py --ci --quiet --combinedlogslen=4000 --failfast
deploy: off
diff --git a/.cirrus.yml b/.cirrus.yml
new file mode 100644
index 0000000000..9104a0a3d1
--- /dev/null
+++ b/.cirrus.yml
@@ -0,0 +1,26 @@
+task:
+ name: "FreeBsd 12.0 amd64 [GOAL: install] [no depends, only system libs]"
+ freebsd_instance:
+ image: freebsd-12-0-release-amd64
+ ccache_cache:
+ folder: "/tmp/ccache_dir"
+ env:
+ MAKEJOBS: "-j3"
+ CONFIGURE_OPTS: "--disable-dependency-tracking"
+ GOAL: "install"
+ CCACHE_SIZE: "200M"
+ CCACHE_COMPRESS: 1
+ CCACHE_DIR: "/tmp/ccache_dir"
+ install_script:
+ - pkg install -y autoconf automake boost-libs git gmake libevent libtool openssl pkgconf python3 ccache
+ - ./contrib/install_db4.sh $(pwd)
+ - ccache --max-size=${CCACHE_SIZE}
+ configure_script:
+ - ./autogen.sh
+ - ./configure ${CONFIGURE_OPTS} BDB_LIBS="-L$(pwd)/db4/lib -ldb_cxx-4.8" BDB_CFLAGS="-I$(pwd)/db4/include" || ( cat config.log && false)
+ make_script:
+ - gmake ${MAKEJOBS} ${GOAL} || ( echo "Build failure. Verbose build follows." && gmake ${GOAL} V=1 ; false )
+ check_script:
+ - gmake check ${MAKEJOBS} VERBOSE=1
+ functional_test_script:
+ - ./test/functional/test_runner.py --ci --combinedlogslen=1000 --quiet --failfast
diff --git a/.gitignore b/.gitignore
index 994d67402f..3ebf00ed52 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,10 +62,10 @@ src/qt/bitcoin-qt.includes
*.pyc
*.o
*.o-*
-*.patch
*.a
*.pb.cc
*.pb.h
+*.dat
*.log
*.trs
@@ -74,6 +74,10 @@ src/qt/bitcoin-qt.includes
*.json.h
*.raw.h
+# Only ignore unexpected patches
+*.patch
+!depends/patches/*.patch
+
#libtool object files
*.lo
*.la
@@ -120,3 +124,6 @@ contrib/devtools/split-debug.sh
# Output from running db4 installation
db4/
+
+# clang-check
+*.plist
diff --git a/.python-version b/.python-version
index 7bcbb3808b..c49282585a 100644
--- a/.python-version
+++ b/.python-version
@@ -1 +1 @@
-3.4.9
+3.5.6
diff --git a/.style.yapf b/.style.yapf
new file mode 100644
index 0000000000..69d8c6aee4
--- /dev/null
+++ b/.style.yapf
@@ -0,0 +1,261 @@
+[style]
+# Align closing bracket with visual indentation.
+align_closing_bracket_with_visual_indent=True
+
+# Allow dictionary keys to exist on multiple lines. For example:
+#
+# x = {
+# ('this is the first element of a tuple',
+# 'this is the second element of a tuple'):
+# value,
+# }
+allow_multiline_dictionary_keys=False
+
+# Allow lambdas to be formatted on more than one line.
+allow_multiline_lambdas=False
+
+# Allow splits before the dictionary value.
+allow_split_before_dict_value=True
+
+# Number of blank lines surrounding top-level function and class
+# definitions.
+blank_lines_around_top_level_definition=2
+
+# Insert a blank line before a class-level docstring.
+blank_line_before_class_docstring=False
+
+# Insert a blank line before a module docstring.
+blank_line_before_module_docstring=False
+
+# Insert a blank line before a 'def' or 'class' immediately nested
+# within another 'def' or 'class'. For example:
+#
+# class Foo:
+# # <------ this blank line
+# def method():
+# ...
+blank_line_before_nested_class_or_def=False
+
+# Do not split consecutive brackets. Only relevant when
+# dedent_closing_brackets is set. For example:
+#
+# call_func_that_takes_a_dict(
+# {
+# 'key1': 'value1',
+# 'key2': 'value2',
+# }
+# )
+#
+# would reformat to:
+#
+# call_func_that_takes_a_dict({
+# 'key1': 'value1',
+# 'key2': 'value2',
+# })
+coalesce_brackets=False
+
+# The column limit.
+column_limit=160
+
+# The style for continuation alignment. Possible values are:
+#
+# - SPACE: Use spaces for continuation alignment. This is default behavior.
+# - FIXED: Use fixed number (CONTINUATION_INDENT_WIDTH) of columns
+# (ie: CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs) for continuation
+# alignment.
+# - LESS: Slightly left if cannot vertically align continuation lines with
+# indent characters.
+# - VALIGN-RIGHT: Vertically align continuation lines with indent
+# characters. Slightly right (one more indent character) if cannot
+# vertically align continuation lines with indent characters.
+#
+# For options FIXED, and VALIGN-RIGHT are only available when USE_TABS is
+# enabled.
+continuation_align_style=SPACE
+
+# Indent width used for line continuations.
+continuation_indent_width=4
+
+# Put closing brackets on a separate line, dedented, if the bracketed
+# expression can't fit in a single line. Applies to all kinds of brackets,
+# including function definitions and calls. For example:
+#
+# config = {
+# 'key1': 'value1',
+# 'key2': 'value2',
+# } # <--- this bracket is dedented and on a separate line
+#
+# time_series = self.remote_client.query_entity_counters(
+# entity='dev3246.region1',
+# key='dns.query_latency_tcp',
+# transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
+# start_ts=now()-timedelta(days=3),
+# end_ts=now(),
+# ) # <--- this bracket is dedented and on a separate line
+dedent_closing_brackets=False
+
+# Disable the heuristic which places each list element on a separate line
+# if the list is comma-terminated.
+disable_ending_comma_heuristic=False
+
+# Place each dictionary entry onto its own line.
+each_dict_entry_on_separate_line=True
+
+# The regex for an i18n comment. The presence of this comment stops
+# reformatting of that line, because the comments are required to be
+# next to the string they translate.
+i18n_comment=
+
+# The i18n function call names. The presence of this function stops
+# reformattting on that line, because the string it has cannot be moved
+# away from the i18n comment.
+i18n_function_call=
+
+# Indent the dictionary value if it cannot fit on the same line as the
+# dictionary key. For example:
+#
+# config = {
+# 'key1':
+# 'value1',
+# 'key2': value1 +
+# value2,
+# }
+indent_dictionary_value=False
+
+# The number of columns to use for indentation.
+indent_width=4
+
+# Join short lines into one line. E.g., single line 'if' statements.
+join_multiple_lines=True
+
+# Do not include spaces around selected binary operators. For example:
+#
+# 1 + 2 * 3 - 4 / 5
+#
+# will be formatted as follows when configured with "*,/":
+#
+# 1 + 2*3 - 4/5
+#
+no_spaces_around_selected_binary_operators=
+
+# Use spaces around default or named assigns.
+spaces_around_default_or_named_assign=False
+
+# Use spaces around the power operator.
+spaces_around_power_operator=False
+
+# The number of spaces required before a trailing comment.
+spaces_before_comment=2
+
+# Insert a space between the ending comma and closing bracket of a list,
+# etc.
+space_between_ending_comma_and_closing_bracket=True
+
+# Split before arguments
+split_all_comma_separated_values=False
+
+# Split before arguments if the argument list is terminated by a
+# comma.
+split_arguments_when_comma_terminated=False
+
+# Set to True to prefer splitting before '&', '|' or '^' rather than
+# after.
+split_before_bitwise_operator=True
+
+# Split before the closing bracket if a list or dict literal doesn't fit on
+# a single line.
+split_before_closing_bracket=True
+
+# Split before a dictionary or set generator (comp_for). For example, note
+# the split before the 'for':
+#
+# foo = {
+# variable: 'Hello world, have a nice day!'
+# for variable in bar if variable != 42
+# }
+split_before_dict_set_generator=True
+
+# Split before the '.' if we need to split a longer expression:
+#
+# foo = ('This is a really long string: {}, {}, {}, {}'.format(a, b, c, d))
+#
+# would reformat to something like:
+#
+# foo = ('This is a really long string: {}, {}, {}, {}'
+# .format(a, b, c, d))
+split_before_dot=False
+
+# Split after the opening paren which surrounds an expression if it doesn't
+# fit on a single line.
+split_before_expression_after_opening_paren=False
+
+# If an argument / parameter list is going to be split, then split before
+# the first argument.
+split_before_first_argument=False
+
+# Set to True to prefer splitting before 'and' or 'or' rather than
+# after.
+split_before_logical_operator=True
+
+# Split named assignments onto individual lines.
+split_before_named_assigns=True
+
+# Set to True to split list comprehensions and generators that have
+# non-trivial expressions and multiple clauses before each of these
+# clauses. For example:
+#
+# result = [
+# a_long_var + 100 for a_long_var in xrange(1000)
+# if a_long_var % 10]
+#
+# would reformat to something like:
+#
+# result = [
+# a_long_var + 100
+# for a_long_var in xrange(1000)
+# if a_long_var % 10]
+split_complex_comprehension=False
+
+# The penalty for splitting right after the opening bracket.
+split_penalty_after_opening_bracket=30
+
+# The penalty for splitting the line after a unary operator.
+split_penalty_after_unary_operator=10000
+
+# The penalty for splitting right before an if expression.
+split_penalty_before_if_expr=0
+
+# The penalty of splitting the line around the '&', '|', and '^'
+# operators.
+split_penalty_bitwise_operator=300
+
+# The penalty for splitting a list comprehension or generator
+# expression.
+split_penalty_comprehension=80
+
+# The penalty for characters over the column limit.
+split_penalty_excess_character=7000
+
+# The penalty incurred by adding a line split to the unwrapped line. The
+# more line splits added the higher the penalty.
+split_penalty_for_added_line_split=30
+
+# The penalty of splitting a list of "import as" names. For example:
+#
+# from a_very_long_or_indented_module_name_yada_yad import (long_argument_1,
+# long_argument_2,
+# long_argument_3)
+#
+# would reformat to something like:
+#
+# from a_very_long_or_indented_module_name_yada_yad import (
+# long_argument_1, long_argument_2, long_argument_3)
+split_penalty_import_names=0
+
+# The penalty of splitting the line around the 'and' and 'or'
+# operators.
+split_penalty_logical_operator=300
+
+# Use the Tab character for indentation.
+use_tabs=False
+
diff --git a/.travis.yml b/.travis.yml
index b43418f84e..adf2140642 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,38 @@
+# The test build matrix (stage: test) is constructed to test a wide range of
+# configurations, rather than a single pass/fail. This helps to catch build
+# failures and logic errors that present on platforms other than the ones the
+# author has tested.
+#
+# Some builders use the dependency-generator in `./depends`, rather than using
+# apt-get to install build dependencies. This guarantees that the tester is
+# using the same versions as Gitian, so the build results are nearly identical
+# to what would be found in a final release.
+#
+# In order to avoid rebuilding all dependencies for each build, the binaries
+# are cached and re-used when possible. Changes in the dependency-generator
+# will trigger cache-invalidation and rebuilds as necessary.
+#
+# These caches can be manually removed if necessary. This is one of the very
+# few manual operations that is possible with Travis, and it can be done by a
+# Bitcoin Core GitHub member via the Travis web interface [0].
+#
+# Travis CI uploads the cache after the script phase of the build [1].
+# However, the build is terminated without saving the chache if it takes over
+# 50 minutes [2]. Thus, if we spent too much time in early build stages, fail
+# with an error and save the cache.
+#
+# [0] https://travis-ci.org/bitcoin/bitcoin/caches
+# [1] https://docs.travis-ci.com/user/caching/#build-phases
+# [2] https://docs.travis-ci.com/user/customizing-the-build#build-timeouts
+
dist: xenial
os: linux
language: minimal
cache:
ccache: true
directories:
- - depends/built
- - depends/sdk-sources
+ - $TRAVIS_BUILD_DIR/depends/built
+ - $TRAVIS_BUILD_DIR/depends/sdk-sources
- $HOME/.ccache
stages:
- lint
@@ -15,6 +42,7 @@ env:
- MAKEJOBS=-j3
- RUN_UNIT_TESTS=true
- RUN_FUNCTIONAL_TESTS=true
+ - RUN_FUZZ_TESTS=false
- DOCKER_NAME_TAG=ubuntu:18.04
- BOOST_TEST_RANDOM=1$TRAVIS_BUILD_ID
- CCACHE_SIZE=100M
@@ -25,6 +53,7 @@ env:
- SDK_URL=https://bitcoincore.org/depends-sources/sdks
- WINEDEBUG=fixme-all
- DOCKER_PACKAGES="build-essential libtool autotools-dev automake pkg-config bsdmainutils curl git ca-certificates ccache"
+ - CACHE_ERR_MSG="Error! Initial build successful, but not enough time remains to run later build stages and tests. Please manually re-run this job by using the travis restart button or asking a bitcoin maintainer to restart. The next run should not time out because the build cache has been saved."
before_install:
- set -o errexit; source .travis/test_03_before_install.sh
install:
@@ -32,8 +61,11 @@ install:
before_script:
- set -o errexit; source .travis/test_05_before_script.sh
script:
- - if [ $SECONDS -gt 1200 ]; then set +o errexit; echo "Travis early exit to cache current state"; false; else set -o errexit; source .travis/test_06_script_a.sh; fi
- - if [ $SECONDS -gt 1500 ]; then set +o errexit; echo "Travis early exit to cache current state"; false; else set -o errexit; source .travis/test_06_script_b.sh; fi
+ - export CONTINUE=1
+ - if [ $SECONDS -gt 1200 ]; then export CONTINUE=0; fi # Likely the depends build took very long
+ - if [ $CONTINUE = "1" ]; then set -o errexit; source .travis/test_06_script_a.sh; else set +o errexit; echo "$CACHE_ERR_MSG"; false; fi
+ - if [ $SECONDS -gt 2000 ]; then export CONTINUE=0; fi # Likely the build took very long; The tests take about 1000s, so we should abort if we have less than 50*60-1000=2000s left
+ - if [ $CONTINUE = "1" ]; then set -o errexit; source .travis/test_06_script_b.sh; else set +o errexit; echo "$CACHE_ERR_MSG"; false; fi
after_script:
- echo $TRAVIS_COMMIT_RANGE
- echo $TRAVIS_COMMIT_LOG
@@ -45,7 +77,7 @@ jobs:
env:
cache: false
language: python
- python: '3.4' # Oldest supported version according to doc/dependencies.md
+ python: '3.5' # Oldest supported version according to doc/dependencies.md
install:
- set -o errexit; source .travis/lint_04_install.sh
before_script:
@@ -66,29 +98,21 @@ jobs:
BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CXXFLAGS=-Wno-psabi"
- stage: test
- name: 'Win32 [GOAL: deploy] [no gui tests]'
- env: >-
- HOST=i686-w64-mingw32
- DPKG_ADD_ARCH="i386"
- PACKAGES="python3 nsis g++-mingw-w64-i686 wine-binfmt wine32"
- GOAL="deploy"
- BITCOIN_CONFIG="--enable-reduce-exports --disable-gui-tests"
-
- - stage: test
- name: 'Win64 [GOAL: deploy] [no gui tests]'
+ name: 'Win64 [GOAL: deploy] [no gui or functional tests]'
env: >-
HOST=x86_64-w64-mingw32
PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64"
+ RUN_FUNCTIONAL_TESTS=false
GOAL="deploy"
BITCOIN_CONFIG="--enable-reduce-exports --disable-gui-tests"
- stage: test
- name: '32-bit + dash [GOAL: install]'
+ name: '32-bit + dash [GOAL: install] [GUI: no BIP70]'
env: >-
HOST=i686-pc-linux-gnu
PACKAGES="g++-multilib python3-zmq"
GOAL="install"
- BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++"
+ BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --disable-bip70 --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++"
CONFIG_SHELL="/bin/dash"
- stage: test
@@ -97,8 +121,9 @@ jobs:
HOST=x86_64-unknown-linux-gnu
PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools protobuf-compiler libdbus-1-dev libharfbuzz-dev libprotobuf-dev"
DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1"
+ TEST_RUNNER_EXTRA="--coverage --extended --exclude feature_dbcrash" # Run extended tests so that coverage does not fail, but exclude the very slow dbcrash
GOAL="install"
- BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-fuzz --enable-glibc-back-compat --enable-reduce-exports --enable-debug CXXFLAGS=\"-g0 -O2\""
+ BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-debug CXXFLAGS=\"-g0 -O2\""
- stage: test
name: 'x86_64 Linux [GOAL: install] [trusty] [no functional tests, no depends, only system libs]'
@@ -131,6 +156,18 @@ jobs:
BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=address,integer,undefined CC=clang CXX=clang++"
- stage: test
+ name: 'x86_64 Linux [GOAL: install] [bionic] [no depends, only system libs, sanitizers: fuzzer,address]'
+ env: >-
+ HOST=x86_64-unknown-linux-gnu
+ PACKAGES="clang llvm python3 libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev"
+ NO_DEPENDS=1
+ RUN_UNIT_TESTS=false
+ RUN_FUNCTIONAL_TESTS=false
+ RUN_FUZZ_TESTS=true
+ GOAL="install"
+ BITCOIN_CONFIG="--disable-wallet --disable-bench --with-utils=no --with-daemon=no --with-libs=no --with-gui=no --enable-fuzz --with-sanitizers=fuzzer,address CC=clang CXX=clang++"
+
+ - stage: test
name: 'x86_64 Linux [GOAL: install] [bionic] [no wallet]'
env: >-
HOST=x86_64-unknown-linux-gnu
@@ -143,7 +180,7 @@ jobs:
name: 'macOS 10.10 [GOAL: deploy] [no functional tests]'
env: >-
HOST=x86_64-apple-darwin14
- PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
+ PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools"
OSX_SDK=10.11
RUN_UNIT_TESTS=false
RUN_FUNCTIONAL_TESTS=false
diff --git a/.travis/lint_04_install.sh b/.travis/lint_04_install.sh
index 9a22773e57..62174620f2 100755
--- a/.travis/lint_04_install.sh
+++ b/.travis/lint_04_install.sh
@@ -6,7 +6,7 @@
export LC_ALL=C
-travis_retry pip install codespell==1.13.0
+travis_retry pip install codespell==1.15.0
travis_retry pip install flake8==3.5.0
travis_retry pip install vulture==0.29
diff --git a/.travis/lint_06_script.sh b/.travis/lint_06_script.sh
index 701e6d8005..eeebc80ec0 100755
--- a/.travis/lint_06_script.sh
+++ b/.travis/lint_06_script.sh
@@ -20,6 +20,6 @@ test/lint/lint-all.sh
if [ "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_EVENT_TYPE" = "cron" ]; then
git log --merges --before="2 days ago" -1 --format='%H' > ./contrib/verify-commits/trusted-sha512-root-commit
- while read -r LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys &&
- travis_wait 50 contrib/verify-commits/verify-commits.py --clean-merge=2;
+ travis_retry gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys $(<contrib/verify-commits/trusted-keys) &&
+ ./contrib/verify-commits/verify-commits.py --clean-merge=2;
fi
diff --git a/.travis/test_04_install.sh b/.travis/test_04_install.sh
index a111387f10..b589ee7a16 100755
--- a/.travis/test_04_install.sh
+++ b/.travis/test_04_install.sh
@@ -7,6 +7,11 @@
export LC_ALL=C.UTF-8
travis_retry docker pull "$DOCKER_NAME_TAG"
+
+export DIR_FUZZ_IN=${TRAVIS_BUILD_DIR}/qa-assets
+git clone https://github.com/bitcoin-core/qa-assets ${DIR_FUZZ_IN}
+export DIR_FUZZ_IN=${DIR_FUZZ_IN}/fuzz_seed_corpus/
+
mkdir -p "${TRAVIS_BUILD_DIR}/sanitizer-output/"
export ASAN_OPTIONS=""
export LSAN_OPTIONS="suppressions=${TRAVIS_BUILD_DIR}/test/sanitizer_suppressions/lsan"
@@ -24,10 +29,6 @@ DOCKER_EXEC () {
docker exec $DOCKER_ID bash -c "cd $PWD && $*"
}
-if [ -n "$DPKG_ADD_ARCH" ]; then
- DOCKER_EXEC dpkg --add-architecture "$DPKG_ADD_ARCH"
-fi
-
travis_retry DOCKER_EXEC apt-get update
travis_retry DOCKER_EXEC apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES $DOCKER_PACKAGES
diff --git a/.travis/test_06_script_b.sh b/.travis/test_06_script_b.sh
index 472448a658..e40055a6ee 100755
--- a/.travis/test_06_script_b.sh
+++ b/.travis/test_06_script_b.sh
@@ -14,12 +14,16 @@ if [ "$RUN_UNIT_TESTS" = "true" ]; then
END_FOLD
fi
-if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then
- extended="--extended --exclude feature_pruning"
-fi
-
if [ "$RUN_FUNCTIONAL_TESTS" = "true" ]; then
BEGIN_FOLD functional-tests
- DOCKER_EXEC test/functional/test_runner.py --ci --combinedlogslen=4000 --coverage --quiet --failfast ${extended}
+ DOCKER_EXEC test/functional/test_runner.py --ci --combinedlogslen=4000 ${TEST_RUNNER_EXTRA} --quiet --failfast
+ END_FOLD
+fi
+
+if [ "$RUN_FUZZ_TESTS" = "true" ]; then
+ BEGIN_FOLD fuzz-tests
+ DOCKER_EXEC test/fuzz/test_runner.py -l DEBUG ${DIR_FUZZ_IN}
END_FOLD
fi
+
+cd ${TRAVIS_BUILD_DIR} || (echo "could not enter travis build dir $TRAVIS_BUILD_DIR"; exit 1)
diff --git a/.tx/config b/.tx/config
index c0931c0f34..743510a7f2 100644
--- a/.tx/config
+++ b/.tx/config
@@ -1,7 +1,7 @@
[main]
host = https://www.transifex.com
-[bitcoin.qt-translation-017x]
+[bitcoin.qt-translation-018x]
file_filter = src/qt/locale/bitcoin_<lang>.ts
source_file = src/qt/locale/bitcoin_en.ts
source_lang = en
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 139fe7dc52..a2456f5b8c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -62,7 +62,7 @@ Commit messages should be verbose by default consisting of a short subject line
paragraph(s), unless the title alone is self-explanatory (like "Corrected typo
in init.cpp") in which case a single title line is sufficient. Commit messages should be
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/).
+your decisions. Further explanation [here](https://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
@@ -220,6 +220,7 @@ In general, all pull requests must:
- 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.
+ - Change relevant comments and documentation when behaviour of code changes.
Patches that change Bitcoin consensus rules are considerably more involved than
normal because they affect the entire ecosystem and so must be preceded by
@@ -236,21 +237,35 @@ request. Typically reviewers will review the code for obvious errors, as well as
test out the patch set and opine on the technical merits of the patch. Project
maintainers take into account the peer review when determining if there is
consensus to merge a pull request (remember that discussions may have been
-spread out over GitHub, mailing list and IRC discussions). The following
+spread out over GitHub, mailing list and IRC discussions).
+
+#### Conceptual Review
+
+A review can be a conceptual review, where the reviewer leaves a comment
+ * `Concept (N)ACK`, meaning "I do (not) agree in the general goal of this pull
+ request",
+ * `Approach (N)ACK`, meaning `Concept ACK`, but "I do (not) agree with the
+ approach of this change".
+
+A `NACK` needs to include a rationale why the change is not worthwhile.
+NACKs without accompanying reasoning may be disregarded.
+
+#### Code Review
+
+After conceptual agreement on the change, code review can be provided. It is
+starting with `ACK BRANCH_COMMIT`, where `BRANCH_COMMIT` is the top of the
+topic branch. The review is followed by a description of how the reviewer did
+the review. The following
language is used within pull-request comments:
- - ACK means "I have tested the code and I agree it should be merged";
- - NACK means "I disagree this should be merged", and must be accompanied by
- sound technical justification (or in certain cases of copyright/patent/licensing
- issues, legal justification). NACKs without accompanying reasoning may be
- disregarded;
- - utACK means "I have not tested the code, but I have reviewed it and it looks
+ - "I have tested the code", involving
+ change-specific manual testing in addition to running the unit and functional
+ tests, and in case it is not obvious how the manual testing was done, it should
+ be described;
+ - "I have not tested the code, but I have reviewed it and it looks
OK, I agree it can be merged";
- - Concept ACK means "I agree in the general principle of this pull request";
- Nit refers to trivial, often non-blocking issues.
-Reviewers should include the commit hash which they reviewed in their comments.
-
Project maintainers reserve the right to weigh the opinions of peer reviewers
using common sense judgement and also may weight based on meritocracy: Those
that have demonstrated a deeper commitment and understanding towards the project
diff --git a/Makefile.am b/Makefile.am
index 9a6e15f0dd..ec0743c3fa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -20,6 +20,7 @@ BITCOIND_BIN=$(top_builddir)/src/$(BITCOIN_DAEMON_NAME)$(EXEEXT)
BITCOIN_QT_BIN=$(top_builddir)/src/qt/$(BITCOIN_GUI_NAME)$(EXEEXT)
BITCOIN_CLI_BIN=$(top_builddir)/src/$(BITCOIN_CLI_NAME)$(EXEEXT)
BITCOIN_TX_BIN=$(top_builddir)/src/$(BITCOIN_TX_NAME)$(EXEEXT)
+BITCOIN_WALLET_BIN=$(top_builddir)/src/$(BITCOIN_WALLET_TOOL_NAME)$(EXEEXT)
BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT)
empty :=
@@ -42,6 +43,7 @@ DIST_DOCS = $(wildcard doc/*.md) $(wildcard doc/release-notes/*.md)
DIST_CONTRIB = $(top_srcdir)/contrib/bitcoin-cli.bash-completion \
$(top_srcdir)/contrib/bitcoin-tx.bash-completion \
$(top_srcdir)/contrib/bitcoind.bash-completion \
+ $(top_srcdir)/contrib/debian/copyright \
$(top_srcdir)/contrib/init \
$(top_srcdir)/contrib/install_db4.sh
DIST_SHARE = \
@@ -76,6 +78,7 @@ $(BITCOIN_WIN_INSTALLER): all-recursive
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_QT_BIN) $(top_builddir)/release
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_CLI_BIN) $(top_builddir)/release
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_TX_BIN) $(top_builddir)/release
+ STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_WALLET_BIN) $(top_builddir)/release
@test -f $(MAKENSIS) && $(MAKENSIS) -V2 $(top_builddir)/share/setup.nsi || \
echo error: could not build $@
@echo built $@
@@ -172,6 +175,9 @@ $(BITCOIN_CLI_BIN): FORCE
$(BITCOIN_TX_BIN): FORCE
$(MAKE) -C src $(@F)
+$(BITCOIN_WALLET_BIN): FORCE
+ $(MAKE) -C src $(@F)
+
if USE_LCOV
LCOV_FILTER_PATTERN=-p "/usr/include/" -p "/usr/lib/" -p "src/leveldb/" -p "src/bench/" -p "src/univalue" -p "src/crypto/ctaes" -p "src/secp256k1"
@@ -220,7 +226,11 @@ endif
dist_noinst_SCRIPTS = autogen.sh
-EXTRA_DIST = $(DIST_SHARE) test/functional/test_runner.py test/functional $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS)
+EXTRA_DIST = $(DIST_SHARE) $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS)
+
+EXTRA_DIST += \
+ test/functional \
+ test/fuzz
EXTRA_DIST += \
test/util/bitcoin-util-test.py \
diff --git a/README.md b/README.md
index 23577d54f9..db79043665 100644
--- a/README.md
+++ b/README.md
@@ -66,7 +66,7 @@ Translations
------------
Changes to translations as well as new translations can be submitted to
-[Bitcoin Core's Transifex page](https://www.transifex.com/projects/p/bitcoin/).
+[Bitcoin Core's Transifex page](https://www.transifex.com/bitcoin/bitcoin/).
Translations are periodically pulled from Transifex and merged into the git repository. See the
[translation process](doc/translation_process.md) for details on how this works.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000000..7ed96c7cea
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,20 @@
+# Security Policy
+
+## Supported Versions
+
+See our website for versions of Bitcoin Core that are currently supported with
+security updates: https://bitcoincore.org/en/lifecycle/#schedule
+
+## Reporting a Vulnerability
+
+To report security issues send an email to security@bitcoincore.org (not for support).
+
+The following keys may be used to communicate sensitive information to developers:
+
+| Name | Fingerprint |
+|------|-------------|
+| Wladimir van der Laan | 71A3 B167 3540 5025 D447 E8F2 7481 0B01 2346 C9A6 |
+| Jonas Schnelli | 32EE 5C4C 3FA1 5CCA DB46 ABE5 29D4 BCB6 416F 53EC |
+| Pieter Wuille | 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320 |
+
+You can import a key by running the following command with that individual’s fingerprint: `gpg --recv-keys "<fingerprint>"` Ensure that you put quotes around fingerprints containing spaces.
diff --git a/build-aux/m4/ax_boost_base.m4 b/build-aux/m4/ax_boost_base.m4
index 650c94fa64..d540395763 100644
--- a/build-aux/m4/ax_boost_base.m4
+++ b/build-aux/m4/ax_boost_base.m4
@@ -1,5 +1,5 @@
# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html
+# https://www.gnu.org/software/autoconf-archive/ax_boost_base.html
# ===========================================================================
#
# SYNOPSIS
@@ -33,7 +33,15 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 27
+#serial 45
+
+# example boost program (need to pass version)
+m4_define([_AX_BOOST_BASE_PROGRAM],
+ [AC_LANG_PROGRAM([[
+#include <boost/version.hpp>
+]],[[
+(void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < ($1))]));
+]])])
AC_DEFUN([AX_BOOST_BASE],
[
@@ -44,110 +52,121 @@ AC_ARG_WITH([boost],
or disable it (ARG=no)
@<:@ARG=yes@:>@ ])],
[
- if test "$withval" = "no"; then
- want_boost="no"
- elif test "$withval" = "yes"; then
- want_boost="yes"
- ac_boost_path=""
- else
- want_boost="yes"
- ac_boost_path="$withval"
- fi
+ AS_CASE([$withval],
+ [no],[want_boost="no";_AX_BOOST_BASE_boost_path=""],
+ [yes],[want_boost="yes";_AX_BOOST_BASE_boost_path=""],
+ [want_boost="yes";_AX_BOOST_BASE_boost_path="$withval"])
],
[want_boost="yes"])
AC_ARG_WITH([boost-libdir],
- AS_HELP_STRING([--with-boost-libdir=LIB_DIR],
- [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]),
- [
- if test -d "$withval"
- then
- ac_boost_lib_path="$withval"
- else
- AC_MSG_ERROR(--with-boost-libdir expected directory name)
- fi
- ],
- [ac_boost_lib_path=""]
-)
+ [AS_HELP_STRING([--with-boost-libdir=LIB_DIR],
+ [Force given directory for boost libraries.
+ Note that this will override library path detection,
+ so use this parameter only if default library detection fails
+ and you know exactly where your boost libraries are located.])],
+ [
+ AS_IF([test -d "$withval"],
+ [_AX_BOOST_BASE_boost_lib_path="$withval"],
+ [AC_MSG_ERROR([--with-boost-libdir expected directory name])])
+ ],
+ [_AX_BOOST_BASE_boost_lib_path=""])
-if test "x$want_boost" = "xyes"; then
- boost_lib_version_req=ifelse([$1], ,1.20.0,$1)
- boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'`
- boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'`
- boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'`
- boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'`
- if test "x$boost_lib_version_req_sub_minor" = "x" ; then
- boost_lib_version_req_sub_minor="0"
- fi
- WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor`
- AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req)
+BOOST_LDFLAGS=""
+BOOST_CPPFLAGS=""
+AS_IF([test "x$want_boost" = "xyes"],
+ [_AX_BOOST_BASE_RUNDETECT([$1],[$2],[$3])])
+AC_SUBST(BOOST_CPPFLAGS)
+AC_SUBST(BOOST_LDFLAGS)
+])
+
+
+# convert a version string in $2 to numeric and affect to polymorphic var $1
+AC_DEFUN([_AX_BOOST_BASE_TONUMERICVERSION],[
+ AS_IF([test "x$2" = "x"],[_AX_BOOST_BASE_TONUMERICVERSION_req="1.20.0"],[_AX_BOOST_BASE_TONUMERICVERSION_req="$2"])
+ _AX_BOOST_BASE_TONUMERICVERSION_req_shorten=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '\([[0-9]]*\.[[0-9]]*\)'`
+ _AX_BOOST_BASE_TONUMERICVERSION_req_major=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '\([[0-9]]*\)'`
+ AS_IF([test "x$_AX_BOOST_BASE_TONUMERICVERSION_req_major" = "x"],
+ [AC_MSG_ERROR([You should at least specify libboost major version])])
+ _AX_BOOST_BASE_TONUMERICVERSION_req_minor=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '[[0-9]]*\.\([[0-9]]*\)'`
+ AS_IF([test "x$_AX_BOOST_BASE_TONUMERICVERSION_req_minor" = "x"],
+ [_AX_BOOST_BASE_TONUMERICVERSION_req_minor="0"])
+ _AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'`
+ AS_IF([test "X$_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor" = "X"],
+ [_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor="0"])
+ _AX_BOOST_BASE_TONUMERICVERSION_RET=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req_major \* 100000 \+ $_AX_BOOST_BASE_TONUMERICVERSION_req_minor \* 100 \+ $_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor`
+ AS_VAR_SET($1,$_AX_BOOST_BASE_TONUMERICVERSION_RET)
+])
+
+dnl Run the detection of boost should be run only if $want_boost
+AC_DEFUN([_AX_BOOST_BASE_RUNDETECT],[
+ _AX_BOOST_BASE_TONUMERICVERSION(WANT_BOOST_VERSION,[$1])
succeeded=no
+
+ AC_REQUIRE([AC_CANONICAL_HOST])
dnl On 64-bit systems check for system libraries in both lib64 and lib.
dnl The former is specified by FHS, but e.g. Debian does not adhere to
dnl this (as it rises problems for generic multi-arch support).
dnl The last entry in the list is chosen by default when no libraries
dnl are found, e.g. when only header-only libraries are installed!
- libsubdirs="lib"
- ax_arch=`uname -m`
- case $ax_arch in
- x86_64)
- libsubdirs="lib64 libx32 lib lib64"
- ;;
- ppc64|s390x|sparc64|aarch64|ppc64le)
- libsubdirs="lib64 lib lib64"
- ;;
- esac
+ AS_CASE([${host_cpu}],
+ [x86_64],[libsubdirs="lib64 libx32 lib lib64"],
+ [ppc64|powerpc64|s390x|sparc64|aarch64|ppc64le|powerpc64le|riscv64],[libsubdirs="lib64 lib lib64"],
+ [libsubdirs="lib"]
+ )
dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give
dnl them priority over the other paths since, if libs are found there, they
dnl are almost assuredly the ones desired.
- AC_REQUIRE([AC_CANONICAL_HOST])
- libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs"
-
- case ${host_cpu} in
- i?86)
- libsubdirs="lib/i386-${host_os} $libsubdirs"
- ;;
- esac
-
- dnl some arches may advertise a cpu type that doesn't line up with their
- dnl prefix's cpu type. For example, uname may report armv7l while libs are
- dnl installed to /usr/lib/arm-linux-gnueabihf. Try getting the compiler's
- dnl value for an extra chance of finding the correct path.
- libsubdirs="lib/`$CXX -dumpmachine 2>/dev/null` $libsubdirs"
+ AS_CASE([${host_cpu}],
+ [i?86],[multiarch_libsubdir="lib/i386-${host_os}"],
+ [multiarch_libsubdir="lib/${host_cpu}-${host_os}"]
+ )
dnl first we check the system location for boost libraries
dnl this location ist chosen if boost libraries are installed with the --layout=system option
dnl or if you install boost with RPM
- if test "$ac_boost_path" != ""; then
- BOOST_CPPFLAGS="-I$ac_boost_path/include"
- for ac_boost_path_tmp in $libsubdirs; do
- if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then
- BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp"
- break
- fi
- done
- elif test "$cross_compiling" != yes; then
- for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do
- if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then
- for libsubdir in $libsubdirs ; do
- if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
+ AS_IF([test "x$_AX_BOOST_BASE_boost_path" != "x"],[
+ AC_MSG_CHECKING([for boostlib >= $1 ($WANT_BOOST_VERSION) includes in "$_AX_BOOST_BASE_boost_path/include"])
+ AS_IF([test -d "$_AX_BOOST_BASE_boost_path/include" && test -r "$_AX_BOOST_BASE_boost_path/include"],[
+ AC_MSG_RESULT([yes])
+ BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path/include"
+ for _AX_BOOST_BASE_boost_path_tmp in $multiarch_libsubdir $libsubdirs; do
+ AC_MSG_CHECKING([for boostlib >= $1 ($WANT_BOOST_VERSION) lib path in "$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp"])
+ AS_IF([test -d "$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp" && test -r "$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp" ],[
+ AC_MSG_RESULT([yes])
+ BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp";
+ break;
+ ],
+ [AC_MSG_RESULT([no])])
+ done],[
+ AC_MSG_RESULT([no])])
+ ],[
+ if test X"$cross_compiling" = Xyes; then
+ search_libsubdirs=$multiarch_libsubdir
+ else
+ search_libsubdirs="$multiarch_libsubdir $libsubdirs"
+ fi
+ for _AX_BOOST_BASE_boost_path_tmp in /usr /usr/local /opt /opt/local ; do
+ if test -d "$_AX_BOOST_BASE_boost_path_tmp/include/boost" && test -r "$_AX_BOOST_BASE_boost_path_tmp/include/boost" ; then
+ for libsubdir in $search_libsubdirs ; do
+ if ls "$_AX_BOOST_BASE_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
done
- BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir"
- BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include"
+ BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_path_tmp/$libsubdir"
+ BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path_tmp/include"
break;
fi
done
- fi
+ ])
dnl overwrite ld flags if we have required special directory with
dnl --with-boost-libdir parameter
- if test "$ac_boost_lib_path" != ""; then
- BOOST_LDFLAGS="-L$ac_boost_lib_path"
- fi
+ AS_IF([test "x$_AX_BOOST_BASE_boost_lib_path" != "x"],
+ [BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_lib_path"])
+ AC_MSG_CHECKING([for boostlib >= $1 ($WANT_BOOST_VERSION)])
CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
export CPPFLAGS
@@ -158,15 +177,7 @@ if test "x$want_boost" = "xyes"; then
AC_REQUIRE([AC_PROG_CXX])
AC_LANG_PUSH(C++)
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
- @%:@include <boost/version.hpp>
- ]], [[
- #if BOOST_VERSION >= $WANT_BOOST_VERSION
- // Everything is okay
- #else
- # error Boost version is too old
- #endif
- ]])],[
+ AC_COMPILE_IFELSE([_AX_BOOST_BASE_PROGRAM($WANT_BOOST_VERSION)],[
AC_MSG_RESULT(yes)
succeeded=yes
found_system=yes
@@ -178,40 +189,50 @@ if test "x$want_boost" = "xyes"; then
dnl if we found no boost with system layout we search for boost libraries
dnl built and installed without the --layout=system option or for a staged(not installed) version
- if test "x$succeeded" != "xyes"; then
+ if test "x$succeeded" != "xyes" ; then
CPPFLAGS="$CPPFLAGS_SAVED"
LDFLAGS="$LDFLAGS_SAVED"
BOOST_CPPFLAGS=
- BOOST_LDFLAGS=
+ if test -z "$_AX_BOOST_BASE_boost_lib_path" ; then
+ BOOST_LDFLAGS=
+ fi
_version=0
- if test "$ac_boost_path" != ""; then
- if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
- for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
- _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
+ if test -n "$_AX_BOOST_BASE_boost_path" ; then
+ if test -d "$_AX_BOOST_BASE_boost_path" && test -r "$_AX_BOOST_BASE_boost_path"; then
+ for i in `ls -d $_AX_BOOST_BASE_boost_path/include/boost-* 2>/dev/null`; do
+ _version_tmp=`echo $i | sed "s#$_AX_BOOST_BASE_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
V_CHECK=`expr $_version_tmp \> $_version`
- if test "$V_CHECK" = "1" ; then
+ if test "x$V_CHECK" = "x1" ; then
_version=$_version_tmp
fi
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
- BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE"
+ BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path/include/boost-$VERSION_UNDERSCORE"
done
dnl if nothing found search for layout used in Windows distributions
if test -z "$BOOST_CPPFLAGS"; then
- if test -d "$ac_boost_path/boost" && test -r "$ac_boost_path/boost"; then
- BOOST_CPPFLAGS="-I$ac_boost_path"
+ if test -d "$_AX_BOOST_BASE_boost_path/boost" && test -r "$_AX_BOOST_BASE_boost_path/boost"; then
+ BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path"
fi
fi
+ dnl if we found something and BOOST_LDFLAGS was unset before
+ dnl (because "$_AX_BOOST_BASE_boost_lib_path" = ""), set it here.
+ if test -n "$BOOST_CPPFLAGS" && test -z "$BOOST_LDFLAGS"; then
+ for libsubdir in $libsubdirs ; do
+ if ls "$_AX_BOOST_BASE_boost_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
+ done
+ BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_path/$libsubdir"
+ fi
fi
else
- if test "$cross_compiling" != yes; then
- for ac_boost_path in /usr /usr/local /opt /opt/local ; do
- if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
- for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
- _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
+ if test "x$cross_compiling" != "xyes" ; then
+ for _AX_BOOST_BASE_boost_path in /usr /usr/local /opt /opt/local ; do
+ if test -d "$_AX_BOOST_BASE_boost_path" && test -r "$_AX_BOOST_BASE_boost_path" ; then
+ for i in `ls -d $_AX_BOOST_BASE_boost_path/include/boost-* 2>/dev/null`; do
+ _version_tmp=`echo $i | sed "s#$_AX_BOOST_BASE_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
V_CHECK=`expr $_version_tmp \> $_version`
- if test "$V_CHECK" = "1" ; then
+ if test "x$V_CHECK" = "x1" ; then
_version=$_version_tmp
- best_path=$ac_boost_path
+ best_path=$_AX_BOOST_BASE_boost_path
fi
done
fi
@@ -219,7 +240,7 @@ if test "x$want_boost" = "xyes"; then
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE"
- if test "$ac_boost_lib_path" = ""; then
+ if test -z "$_AX_BOOST_BASE_boost_lib_path" ; then
for libsubdir in $libsubdirs ; do
if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
done
@@ -227,7 +248,7 @@ if test "x$want_boost" = "xyes"; then
fi
fi
- if test "x$BOOST_ROOT" != "x"; then
+ if test -n "$BOOST_ROOT" ; then
for libsubdir in $libsubdirs ; do
if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
done
@@ -236,7 +257,7 @@ if test "x$want_boost" = "xyes"; then
stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'`
stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'`
V_CHECK=`expr $stage_version_shorten \>\= $_version`
- if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then
+ if test "x$V_CHECK" = "x1" && test -z "$_AX_BOOST_BASE_boost_lib_path" ; then
AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT)
BOOST_CPPFLAGS="-I$BOOST_ROOT"
BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir"
@@ -251,15 +272,7 @@ if test "x$want_boost" = "xyes"; then
export LDFLAGS
AC_LANG_PUSH(C++)
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
- @%:@include <boost/version.hpp>
- ]], [[
- #if BOOST_VERSION >= $WANT_BOOST_VERSION
- // Everything is okay
- #else
- # error Boost version is too old
- #endif
- ]])],[
+ AC_COMPILE_IFELSE([_AX_BOOST_BASE_PROGRAM($WANT_BOOST_VERSION)],[
AC_MSG_RESULT(yes)
succeeded=yes
found_system=yes
@@ -268,17 +281,15 @@ if test "x$want_boost" = "xyes"; then
AC_LANG_POP([C++])
fi
- if test "$succeeded" != "yes" ; then
- if test "$_version" = "0" ; then
- AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.]])
+ if test "x$succeeded" != "xyes" ; then
+ if test "x$_version" = "x0" ; then
+ AC_MSG_NOTICE([[We could not detect the boost libraries (version $1 or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.]])
else
AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).])
fi
# execute ACTION-IF-NOT-FOUND (if present):
ifelse([$3], , :, [$3])
else
- AC_SUBST(BOOST_CPPFLAGS)
- AC_SUBST(BOOST_LDFLAGS)
AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available])
# execute ACTION-IF-FOUND (if present):
ifelse([$2], , :, [$2])
@@ -286,6 +297,5 @@ if test "x$want_boost" = "xyes"; then
CPPFLAGS="$CPPFLAGS_SAVED"
LDFLAGS="$LDFLAGS_SAVED"
-fi
])
diff --git a/build-aux/m4/ax_boost_system.m4 b/build-aux/m4/ax_boost_system.m4
index 1c05450cbe..207d7be8de 100644
--- a/build-aux/m4/ax_boost_system.m4
+++ b/build-aux/m4/ax_boost_system.m4
@@ -1,5 +1,5 @@
# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_boost_system.html
+# https://www.gnu.org/software/autoconf-archive/ax_boost_system.html
# ===========================================================================
#
# SYNOPSIS
@@ -31,7 +31,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 18
+#serial 19
AC_DEFUN([AX_BOOST_SYSTEM],
[
@@ -84,7 +84,6 @@ AC_DEFUN([AX_BOOST_SYSTEM],
LDFLAGS_SAVE=$LDFLAGS
if test "x$ax_boost_user_system_lib" = "x"; then
- ax_lib=
for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do
ax_lib=${libextension}
AC_CHECK_LIB($ax_lib, exit,
@@ -109,7 +108,7 @@ AC_DEFUN([AX_BOOST_SYSTEM],
fi
if test "x$ax_lib" = "x"; then
- AC_MSG_ERROR(Could not find a version of the boost_system library!)
+ AC_MSG_ERROR(Could not find a version of the library!)
fi
if test "x$link_system" = "xno"; then
AC_MSG_ERROR(Could not link against $ax_lib !)
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4
index 90a2cd9875..1a7c5d5f7d 100644
--- a/build-aux/m4/bitcoin_qt.m4
+++ b/build-aux/m4/bitcoin_qt.m4
@@ -116,24 +116,6 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
if test "x$bitcoin_cv_static_qt" = xyes; then
_BITCOIN_QT_FIND_STATIC_PLUGINS
AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static])
- AC_CACHE_CHECK(for Qt < 5.4, bitcoin_cv_need_acc_widget,[
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
- #include <QtCore/qconfig.h>
- #ifndef QT_VERSION
- # include <QtCore/qglobal.h>
- #endif
- ]],
- [[
- #if QT_VERSION >= 0x050400
- choke
- #endif
- ]])],
- [bitcoin_cv_need_acc_widget=yes],
- [bitcoin_cv_need_acc_widget=no])
- ])
- if test "x$bitcoin_cv_need_acc_widget" = xyes; then
- _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(AccessibleFactory)], [-lqtaccessiblewidgets])
- fi
_BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin)],[-lqminimal])
AC_DEFINE(QT_QPA_PLATFORM_MINIMAL, 1, [Define this symbol if the minimal qt platform exists])
if test "x$TARGET_OS" = xwindows; then
@@ -264,7 +246,7 @@ dnl All macros below are internal and should _not_ be used from the main
dnl configure.ac.
dnl ----
-dnl Internal. Check if the included version of Qt is Qt5.
+dnl Internal. Check included version of Qt against minimum specified in doc/dependencies.md
dnl Requires: INCLUDES must be populated as necessary.
dnl Output: bitcoin_cv_qt5=yes|no
AC_DEFUN([_BITCOIN_QT_CHECK_QT5],[
@@ -276,7 +258,7 @@ AC_DEFUN([_BITCOIN_QT_CHECK_QT5],[
#endif
]],
[[
- #if QT_VERSION < 0x050200 || QT_VERSION_MAJOR < 5
+ #if QT_VERSION < 0x050501
choke
#endif
]])],
@@ -374,9 +356,7 @@ AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[
fi
if test "x$TARGET_OS" = xlinux; then
PKG_CHECK_MODULES([X11XCB], [x11-xcb], [QT_LIBS="$X11XCB_LIBS $QT_LIBS"])
- if ${PKG_CONFIG} --exists "Qt5Core >= 5.5" 2>/dev/null; then
- PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"])
- fi
+ PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"])
elif test "x$TARGET_OS" = xdarwin; then
PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport], [QT_LIBS="-lQt5ClipboardSupport $QT_LIBS"])
PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport], [QT_LIBS="-lQt5GraphicsSupport $QT_LIBS"])
@@ -527,4 +507,3 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[
CXXFLAGS="$TEMP_CXXFLAGS"
LIBS="$TEMP_LIBS"
])
-
diff --git a/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj b/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj
index 771e8a56f4..723e230d3a 100644
--- a/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj
+++ b/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj
@@ -20,6 +20,10 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
+ <ClCompile Include="..\..\src\test\util.h" />
+ <ClCompile Include="..\..\src\test\util.cpp" />
+ <ClCompile Include="..\..\src\test\setup_common.h" />
+ <ClCompile Include="..\..\src\test\setup_common.cpp" />
<ClCompile Include="..\..\src\bench\base58.cpp" />
<ClCompile Include="..\..\src\bench\bech32.cpp" />
<ClCompile Include="..\..\src\bench\bench.cpp" />
@@ -32,8 +36,10 @@
<ClCompile Include="..\..\src\bench\examples.cpp" />
<ClCompile Include="..\..\src\bench\lockedpool.cpp" />
<ClCompile Include="..\..\src\bench\mempool_eviction.cpp" />
+ <ClCompile Include="..\..\src\bench\rpc_mempool.cpp" />
<ClCompile Include="..\..\src\bench\merkle_root.cpp" />
<ClCompile Include="..\..\src\bench\rollingbloom.cpp" />
+ <ClCompile Include="..\..\src\bench\wallet_balance.cpp" />
<ClCompile Include="..\..\src\bench\verify_script.cpp" />
</ItemGroup>
<ItemGroup>
@@ -146,7 +152,6 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NOMINMAX;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@@ -164,7 +169,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>NOMINMAX;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -180,7 +184,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>NOMINMAX;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -198,7 +201,6 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NOMINMAX;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
diff --git a/build_msvc/bitcoin-cli/bitcoin-cli.vcxproj b/build_msvc/bitcoin-cli/bitcoin-cli.vcxproj
index 32f0354fad..6b668054a3 100644
--- a/build_msvc/bitcoin-cli/bitcoin-cli.vcxproj
+++ b/build_msvc/bitcoin-cli/bitcoin-cli.vcxproj
@@ -111,9 +111,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@@ -131,7 +130,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -147,7 +145,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -163,9 +160,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
diff --git a/build_msvc/bitcoin-tx/bitcoin-tx.vcxproj b/build_msvc/bitcoin-tx/bitcoin-tx.vcxproj
index 86fd614990..1d0174e319 100644
--- a/build_msvc/bitcoin-tx/bitcoin-tx.vcxproj
+++ b/build_msvc/bitcoin-tx/bitcoin-tx.vcxproj
@@ -114,9 +114,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@@ -134,7 +133,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -150,7 +148,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -166,9 +163,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
diff --git a/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj b/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
index b618aca817..fa86da7bae 100644
--- a/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
+++ b/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
@@ -53,6 +53,12 @@
<ProjectReference Include="..\libbitcoin_wallet_tool\libbitcoin_wallet_tool.vcxproj">
<Project>{f91ac55e-6f5e-4c58-9ac5-b40db7deef93}</Project>
</ProjectReference>
+ <ProjectReference Include="..\libsecp256k1\libsecp256k1.vcxproj">
+ <Project>{bb493552-3b8c-4a8c-bf69-a6e7a51d2ea6}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libleveldb\libleveldb.vcxproj">
+ <Project>{18430fef-6b61-4c53-b396-718e02850f1b}</Project>
+ </ProjectReference>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
@@ -126,9 +132,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@@ -146,7 +151,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -162,7 +166,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -178,9 +181,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
diff --git a/build_msvc/bitcoin_config.h b/build_msvc/bitcoin_config.h
index 4ac27dae3f..ab13f73539 100644
--- a/build_msvc/bitcoin_config.h
+++ b/build_msvc/bitcoin_config.h
@@ -11,10 +11,10 @@
#define CLIENT_VERSION_IS_RELEASE false
/* Major version */
-#define CLIENT_VERSION_MAJOR 1
+#define CLIENT_VERSION_MAJOR 0
/* Minor version */
-#define CLIENT_VERSION_MINOR 17
+#define CLIENT_VERSION_MINOR 18
/* Build revision */
#define CLIENT_VERSION_REVISION 99
@@ -29,7 +29,7 @@
#define COPYRIGHT_HOLDERS_SUBSTITUTION "Bitcoin Core"
/* Copyright year */
-#define COPYRIGHT_YEAR 2018
+#define COPYRIGHT_YEAR 2019
/* Define to 1 to enable wallet functions */
#define ENABLE_WALLET 1
@@ -346,7 +346,7 @@
#define PACKAGE_NAME "Bitcoin Core"
/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "Bitcoin Core 0.17.99"
+#define PACKAGE_STRING "Bitcoin Core 0.18.99"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "bitcoin"
@@ -355,7 +355,7 @@
#define PACKAGE_URL "https://bitcoincore.org/"
/* Define to the version of this package. */
-#define PACKAGE_VERSION "0.17.99"
+#define PACKAGE_VERSION "0.18.99"
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
diff --git a/build_msvc/bitcoind/bitcoind.vcxproj b/build_msvc/bitcoind/bitcoind.vcxproj
index 9a071f3bdd..bb212af52e 100644
--- a/build_msvc/bitcoind/bitcoind.vcxproj
+++ b/build_msvc/bitcoind/bitcoind.vcxproj
@@ -79,7 +79,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -96,7 +95,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -113,9 +111,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@@ -136,9 +133,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
diff --git a/build_msvc/common.vcxproj b/build_msvc/common.vcxproj
index 985b0becaa..889dc6c2ad 100644
--- a/build_msvc/common.vcxproj
+++ b/build_msvc/common.vcxproj
@@ -23,6 +23,8 @@
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>4018;4244;4267;4715;4805;</DisableSpecificWarnings>
<TreatWarningAsError>true</TreatWarningAsError>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\secp256k1\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
</Project>
diff --git a/build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj.in b/build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj.in
index b7265054fb..5849e463a6 100644
--- a/build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj.in
+++ b/build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj.in
@@ -97,9 +97,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@@ -116,7 +115,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -131,7 +129,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -146,9 +143,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
diff --git a/build_msvc/libbitcoin_common/libbitcoin_common.vcxproj.in b/build_msvc/libbitcoin_common/libbitcoin_common.vcxproj.in
index b51cdb2991..292e193f5d 100644
--- a/build_msvc/libbitcoin_common/libbitcoin_common.vcxproj.in
+++ b/build_msvc/libbitcoin_common/libbitcoin_common.vcxproj.in
@@ -91,7 +91,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\secp256k1\include;</AdditionalIncludeDirectories>
<SuppressStartupBanner>false</SuppressStartupBanner>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
@@ -109,7 +108,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\secp256k1\include;</AdditionalIncludeDirectories>
<SuppressStartupBanner>false</SuppressStartupBanner>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
@@ -127,9 +125,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\secp256k1\include;</AdditionalIncludeDirectories>
<SuppressStartupBanner>false</SuppressStartupBanner>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
@@ -149,9 +146,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\secp256k1\include;</AdditionalIncludeDirectories>
<SuppressStartupBanner>false</SuppressStartupBanner>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
diff --git a/build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj.in b/build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj.in
index a05125723a..e7002036ad 100644
--- a/build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj.in
+++ b/build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj.in
@@ -89,7 +89,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -104,7 +103,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -119,9 +117,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@@ -138,9 +135,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
diff --git a/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj b/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj
index 2e8ecb2d2c..73ba90aa88 100644
--- a/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj
+++ b/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj
@@ -61,6 +61,9 @@
<None Include="..\..\src\qt\locale\bitcoin_af_ZA.ts">
<DeploymentContent>true</DeploymentContent>
</None>
+ <None Include="..\..\src\qt\locale\bitcoin_am.ts">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
<None Include="..\..\src\qt\locale\bitcoin_ar.ts">
<DeploymentContent>true</DeploymentContent>
</None>
@@ -73,10 +76,10 @@
<None Include="..\..\src\qt\locale\bitcoin_bg_BG.ts">
<DeploymentContent>true</DeploymentContent>
</None>
- <None Include="..\..\src\qt\locale\bitcoin_ca%40valencia.ts">
+ <None Include="..\..\src\qt\locale\bitcoin_ca.ts">
<DeploymentContent>true</DeploymentContent>
</None>
- <None Include="..\..\src\qt\locale\bitcoin_ca.ts">
+ <None Include="..\..\src\qt\locale\bitcoin_ca%40valencia.ts">
<DeploymentContent>true</DeploymentContent>
</None>
<None Include="..\..\src\qt\locale\bitcoin_ca_ES.ts">
@@ -178,9 +181,15 @@
<None Include="..\..\src\qt\locale\bitcoin_hu.ts">
<DeploymentContent>true</DeploymentContent>
</None>
+ <None Include="..\..\src\qt\locale\bitcoin_id.ts">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
<None Include="..\..\src\qt\locale\bitcoin_id_ID.ts">
<DeploymentContent>true</DeploymentContent>
</None>
+ <None Include="..\..\src\qt\locale\bitcoin_is.ts">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
<None Include="..\..\src\qt\locale\bitcoin_it.ts">
<DeploymentContent>true</DeploymentContent>
</None>
@@ -196,6 +205,9 @@
<None Include="..\..\src\qt\locale\bitcoin_kk_KZ.ts">
<DeploymentContent>true</DeploymentContent>
</None>
+ <None Include="..\..\src\qt\locale\bitcoin_ko.ts">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
<None Include="..\..\src\qt\locale\bitcoin_ko_KR.ts">
<DeploymentContent>true</DeploymentContent>
</None>
@@ -217,6 +229,9 @@
<None Include="..\..\src\qt\locale\bitcoin_mk_MK.ts">
<DeploymentContent>true</DeploymentContent>
</None>
+ <None Include="..\..\src\qt\locale\bitcoin_ml.ts">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
<None Include="..\..\src\qt\locale\bitcoin_mn.ts">
<DeploymentContent>true</DeploymentContent>
</None>
@@ -262,18 +277,24 @@
<None Include="..\..\src\qt\locale\bitcoin_sl_SI.ts">
<DeploymentContent>true</DeploymentContent>
</None>
- <None Include="..\..\src\qt\locale\bitcoin_sq.ts">
+ <None Include="..\..\src\qt\locale\bitcoin_sn.ts">
<DeploymentContent>true</DeploymentContent>
</None>
- <None Include="..\..\src\qt\locale\bitcoin_sr%40latin.ts">
+ <None Include="..\..\src\qt\locale\bitcoin_sq.ts">
<DeploymentContent>true</DeploymentContent>
</None>
<None Include="..\..\src\qt\locale\bitcoin_sr.ts">
<DeploymentContent>true</DeploymentContent>
</None>
+ <None Include="..\..\src\qt\locale\bitcoin_sr%40latin.ts">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
<None Include="..\..\src\qt\locale\bitcoin_sv.ts">
<DeploymentContent>true</DeploymentContent>
</None>
+ <None Include="..\..\src\qt\locale\bitcoin_szl.ts">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
<None Include="..\..\src\qt\locale\bitcoin_ta.ts">
<DeploymentContent>true</DeploymentContent>
</None>
@@ -852,7 +873,7 @@
<Optimization>Disabled</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>false</IntrinsicFunctions>
- <PreprocessorDefinitions>_X86_;WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>_X86_;WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>.\GeneratedFiles;..\..\src;..\..\src\univalue\include;.\QtGenerated\mocheaders</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -901,7 +922,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>.\GeneratedFiles;..\..\src;..\..\src\univalue\include;.\QtGenerated\mocheaders</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
diff --git a/build_msvc/libbitcoin_server/libbitcoin_server.vcxproj.in b/build_msvc/libbitcoin_server/libbitcoin_server.vcxproj.in
index acb827bd95..0b4aa1e407 100644
--- a/build_msvc/libbitcoin_server/libbitcoin_server.vcxproj.in
+++ b/build_msvc/libbitcoin_server/libbitcoin_server.vcxproj.in
@@ -89,9 +89,8 @@
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -104,9 +103,8 @@
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<SuppressStartupBanner>false</SuppressStartupBanner>
</ClCompile>
@@ -122,9 +120,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@@ -141,9 +138,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@@ -155,6 +151,9 @@
</ItemDefinitionGroup>
<ItemGroup>
@SOURCE_FILES@
+ <ClCompile Include="..\..\src\wallet\init.cpp">
+ <ObjectFileName>$(IntDir)wallet_init.obj</ObjectFileName>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
diff --git a/build_msvc/libbitcoin_util/libbitcoin_util.vcxproj.in b/build_msvc/libbitcoin_util/libbitcoin_util.vcxproj.in
index dc17c98e98..dbd91cf4db 100644
--- a/build_msvc/libbitcoin_util/libbitcoin_util.vcxproj.in
+++ b/build_msvc/libbitcoin_util/libbitcoin_util.vcxproj.in
@@ -92,7 +92,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_CONFIG_H;WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -112,7 +111,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_CONFIG_H;WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -132,9 +130,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>HAVE_CONFIG_H;WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@@ -156,9 +153,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>HAVE_CONFIG_H;WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
diff --git a/build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj.in b/build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj.in
index 1bb7be6f7f..33f4054546 100644
--- a/build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj.in
+++ b/build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj.in
@@ -97,7 +97,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\wallet;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
@@ -113,7 +112,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\wallet;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
@@ -129,9 +127,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\wallet;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
@@ -149,9 +146,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\wallet;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
diff --git a/build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in b/build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in
index b9cf68aee0..187d955687 100644
--- a/build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in
+++ b/build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in
@@ -97,7 +97,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<SuppressStartupBanner>false</SuppressStartupBanner>
</ClCompile>
@@ -113,7 +112,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<SuppressStartupBanner>false</SuppressStartupBanner>
</ClCompile>
@@ -129,9 +127,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<SuppressStartupBanner>false</SuppressStartupBanner>
</ClCompile>
@@ -149,9 +146,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<SuppressStartupBanner>false</SuppressStartupBanner>
</ClCompile>
diff --git a/build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj.in b/build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj.in
index e396c1ad0c..c877a280c0 100644
--- a/build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj.in
+++ b/build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj.in
@@ -97,7 +97,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<SuppressStartupBanner>false</SuppressStartupBanner>
</ClCompile>
@@ -113,7 +112,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<SuppressStartupBanner>false</SuppressStartupBanner>
</ClCompile>
@@ -129,9 +127,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<SuppressStartupBanner>false</SuppressStartupBanner>
</ClCompile>
@@ -149,9 +146,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<SuppressStartupBanner>false</SuppressStartupBanner>
</ClCompile>
diff --git a/build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj b/build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj
index 961225f9ba..227b1ebcd2 100644
--- a/build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj
+++ b/build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj
@@ -22,6 +22,7 @@
<ItemGroup>
<ClCompile Include="..\..\src\arith_uint256.cpp" />
<ClCompile Include="..\..\src\consensus\merkle.cpp" />
+ <ClCompile Include="..\..\src\consensus\tx_check.cpp" />
<ClCompile Include="..\..\src\crypto\aes.cpp" />
<ClCompile Include="..\..\src\crypto\chacha20.cpp" />
<ClCompile Include="..\..\src\crypto\hmac_sha256.cpp" />
@@ -126,7 +127,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>false</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\secp256k1\include;</AdditionalIncludeDirectories>
<ExceptionHandling>Sync</ExceptionHandling>
<SuppressStartupBanner>false</SuppressStartupBanner>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@@ -143,7 +143,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>false</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\secp256k1\include;</AdditionalIncludeDirectories>
<ExceptionHandling>Sync</ExceptionHandling>
<SuppressStartupBanner>false</SuppressStartupBanner>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@@ -160,9 +159,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>false</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\secp256k1\include;</AdditionalIncludeDirectories>
<ExceptionHandling>Sync</ExceptionHandling>
<SuppressStartupBanner>false</SuppressStartupBanner>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -181,9 +179,8 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>false</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\secp256k1\include;</AdditionalIncludeDirectories>
<ExceptionHandling>Sync</ExceptionHandling>
<SuppressStartupBanner>false</SuppressStartupBanner>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
diff --git a/build_msvc/libleveldb/libleveldb.vcxproj b/build_msvc/libleveldb/libleveldb.vcxproj
index 6de3a2b282..545508001e 100644
--- a/build_msvc/libleveldb/libleveldb.vcxproj
+++ b/build_msvc/libleveldb/libleveldb.vcxproj
@@ -162,7 +162,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;LEVELDB_PLATFORM_WINDOWS;LEVELDB_ATOMIC_PRESENT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;LEVELDB_PLATFORM_WINDOWS;LEVELDB_ATOMIC_PRESENT;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\..\src\leveldb;..\..\src\leveldb\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -181,7 +181,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;LEVELDB_PLATFORM_WINDOWS;LEVELDB_ATOMIC_PRESENT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;LEVELDB_PLATFORM_WINDOWS;LEVELDB_ATOMIC_PRESENT;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\..\src\leveldb;..\..\src\leveldb\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
diff --git a/build_msvc/libsecp256k1/libsecp256k1.vcxproj b/build_msvc/libsecp256k1/libsecp256k1.vcxproj
index 699c7d6871..b4c9ec28ee 100644
--- a/build_msvc/libsecp256k1/libsecp256k1.vcxproj
+++ b/build_msvc/libsecp256k1/libsecp256k1.vcxproj
@@ -122,7 +122,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;HAVE_CONFIG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;HAVE_CONFIG_H;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\..\src\secp256k1;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -141,7 +141,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;HAVE_CONFIG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;HAVE_CONFIG_H;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\..\src\secp256k1;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
diff --git a/build_msvc/libunivalue/libunivalue.vcxproj b/build_msvc/libunivalue/libunivalue.vcxproj
index 57d469debf..c3799b6408 100644
--- a/build_msvc/libunivalue/libunivalue.vcxproj
+++ b/build_msvc/libunivalue/libunivalue.vcxproj
@@ -125,7 +125,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -144,7 +144,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\..\src\univalue\include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
diff --git a/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj b/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj
index b2d4c118f3..a5d666c114 100644
--- a/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj
+++ b/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj
@@ -89,7 +89,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
@@ -132,7 +132,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
diff --git a/build_msvc/test_bitcoin/test_bitcoin.vcxproj b/build_msvc/test_bitcoin/test_bitcoin.vcxproj
index 8f39132a22..92fe95bddc 100644
--- a/build_msvc/test_bitcoin/test_bitcoin.vcxproj
+++ b/build_msvc/test_bitcoin/test_bitcoin.vcxproj
@@ -21,9 +21,11 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\test\*_tests.cpp" />
+ <ClCompile Include="..\..\src\test\*_properties.cpp" />
+ <ClCompile Include="..\..\src\test\gen\*_gen.cpp" />
<ClCompile Include="..\..\src\wallet\test\*_tests.cpp" />
- <ClCompile Include="..\..\src\test\test_bitcoin.cpp" />
- <ClCompile Include="..\..\src\test\test_bitcoin_main.cpp" />
+ <ClCompile Include="..\..\src\test\setup_common.cpp" />
+ <ClCompile Include="..\..\src\test\main.cpp" />
<ClCompile Include="..\..\src\wallet\test\*_fixture.cpp" />
</ItemGroup>
<ItemGroup>
@@ -133,7 +135,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NOMINMAX;WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\leveldb\include;..\..\src\test;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -150,7 +151,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NOMINMAX;WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\leveldb\include;..\..\src\test;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -168,7 +168,6 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NOMINMAX;WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\leveldb\include;..\..\src\test;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@@ -188,7 +187,6 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NOMINMAX;WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\leveldb\include;..\..\src\test;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
diff --git a/build_msvc/testconsensus/testconsensus.vcxproj b/build_msvc/testconsensus/testconsensus.vcxproj
index db2f8a6216..a2f5659049 100644
--- a/build_msvc/testconsensus/testconsensus.vcxproj
+++ b/build_msvc/testconsensus/testconsensus.vcxproj
@@ -95,7 +95,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_CONFIG_H;WIN32;_SCL_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src\;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -110,7 +109,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src\;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
@@ -129,7 +127,6 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src\;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@@ -148,7 +145,6 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>HAVE_CONFIG_H;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>..\..\src\;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
diff --git a/configure.ac b/configure.ac
index 8ac5cb02d1..5e1a5e14a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 0)
-define(_CLIENT_VERSION_MINOR, 17)
+define(_CLIENT_VERSION_MINOR, 18)
define(_CLIENT_VERSION_REVISION, 99)
define(_CLIENT_VERSION_BUILD, 0)
define(_CLIENT_VERSION_RC, 0)
@@ -85,8 +85,8 @@ AC_PATH_TOOL(RANLIB, ranlib)
AC_PATH_TOOL(STRIP, strip)
AC_PATH_TOOL(GCOV, gcov)
AC_PATH_PROG(LCOV, lcov)
-dnl Python 3.x is supported from 3.4 on (see https://github.com/bitcoin/bitcoin/issues/7893)
-AC_PATH_PROGS([PYTHON], [python3.7 python3.6 python3.5 python3.4 python3 python])
+dnl Python 3.5 is specified in .python-version and should be used if available, see doc/dependencies.md
+AC_PATH_PROGS([PYTHON], [python3.5 python3.6 python3.7 python3.8 python3 python])
AC_PATH_PROG(GENHTML, genhtml)
AC_PATH_PROG([GIT], [git])
AC_PATH_PROG(CCACHE,ccache)
@@ -194,9 +194,15 @@ AC_ARG_ENABLE([glibc-back-compat],
[use_glibc_compat=$enableval],
[use_glibc_compat=no])
+AC_ARG_ENABLE([threadlocal],
+ [AS_HELP_STRING([--enable-threadlocal],
+ [enable features that depend on the c++ thread_local keyword (currently just thread names in debug logs). (default is to enabled if there is platform support and glibc-back-compat is not enabled)])],
+ [use_thread_local=$enableval],
+ [use_thread_local=auto])
+
AC_ARG_ENABLE([asm],
- [AS_HELP_STRING([--enable-asm],
- [Enable assembly routines (default is yes)])],
+ [AS_HELP_STRING([--disable-asm],
+ [disable assembly routines (enabled by default)])],
[use_asm=$enableval],
[use_asm=yes])
@@ -339,6 +345,13 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
AX_CHECK_COMPILE_FLAG([-Wimplicit-fallthrough],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-implicit-fallthrough"],,[[$CXXFLAG_WERROR]])
fi
+enable_hwcrc32=no
+enable_sse41=no
+enable_avx2=no
+enable_shani=no
+
+if test "x$use_asm" = "xyes"; then
+
# Check for optional instruction set support. Enabling these does _not_ imply that all code will
# be compiled with them, rather that specific objects/libs may use them after checking for runtime
# compatibility.
@@ -416,6 +429,8 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
)
CXXFLAGS="$TEMP_CXXFLAGS"
+fi
+
CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS"
AC_ARG_WITH([utils],
@@ -496,7 +511,7 @@ case $host in
AC_MSG_ERROR("windres not found")
fi
- CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB"
+ CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -D_WIN32_WINNT=0x0601"
LEVELDB_TARGET_FLAGS="-DOS_WINDOWS"
if test "x$CXXFLAGS_overridden" = "xno"; then
CXXFLAGS="$CXXFLAGS -w"
@@ -725,6 +740,10 @@ if test x$TARGET_OS != xwindows; then
AX_CHECK_COMPILE_FLAG([-fPIC],[PIC_FLAGS="-fPIC"])
fi
+# All versions of gcc that we commonly use for building are subject to bug
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90348. To work around that, set
+# -fstack-reuse=none for all gcc builds. (Only gcc understands this flag)
+AX_CHECK_COMPILE_FLAG([-fstack-reuse=none],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-reuse=none"])
if test x$use_hardening != xno; then
use_hardening=yes
AX_CHECK_COMPILE_FLAG([-Wstack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"])
@@ -814,27 +833,49 @@ 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"
+if test "x$use_thread_local" = xyes || { test "x$use_thread_local" = xauto && test "x$use_glibc_compat" = xno; }; then
+ 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;
+ }
+ ])],
+ [
+ case $host in
+ *mingw*)
+ # mingw32's implementation of thread_local has also been shown to behave
+ # erroneously under concurrent usage; see:
+ # https://gist.github.com/jamesob/fe9a872051a88b2025b1aa37bfa98605
+ AC_MSG_RESULT(no)
+ ;;
+ *darwin*)
+ # TODO enable thread_local on later versions of Darwin where it is
+ # supported (per https://stackoverflow.com/a/29929949)
+ AC_MSG_RESULT(no)
+ ;;
+ *freebsd*)
+ # FreeBSD's implementation of thread_local is also buggy (per
+ # https://groups.google.com/d/msg/bsdmailinglist/22ncTZAbDp4/Dii_pII5AwAJ)
+ AC_MSG_RESULT(no)
+ ;;
+ *)
+ AC_DEFINE(HAVE_THREAD_LOCAL,1,[Define if thread_local is supported.])
+ AC_MSG_RESULT(yes)
+ ;;
+ esac
+ ],
+ [
+ AC_MSG_RESULT(no)
+ ]
+ )
+ LDFLAGS="$TEMP_LDFLAGS"
+fi
# Check for different ways of gathering OS randomness
AC_MSG_CHECKING(for Linux getrandom syscall)
diff --git a/contrib/bitcoin-cli.bash-completion b/contrib/bitcoin-cli.bash-completion
index 732981fe7c..f4cac84182 100644
--- a/contrib/bitcoin-cli.bash-completion
+++ b/contrib/bitcoin-cli.bash-completion
@@ -50,7 +50,7 @@ _bitcoin_cli() {
COMPREPLY=( $( compgen -W "true false" -- "$cur" ) )
return 0
;;
- signrawtransaction)
+ signrawtransactionwithkey|signrawtransactionwithwallet)
COMPREPLY=( $( compgen -W "ALL NONE SINGLE ALL|ANYONECANPAY NONE|ANYONECANPAY SINGLE|ANYONECANPAY" -- "$cur" ) )
return 0
;;
diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md
index 6ee65f40be..4994d7f0a5 100644
--- a/contrib/devtools/README.md
+++ b/contrib/devtools/README.md
@@ -7,6 +7,8 @@ clang-format-diff.py
A script to format unified git diffs according to [.clang-format](../../src/.clang-format).
+Requires `clang-format`, installed e.g. via `brew install clang-format` on macOS.
+
For instance, to format the last commit with 0 lines of context,
the script should be called from the git root folder as follows.
@@ -142,7 +144,7 @@ Then do:
Create and verify timestamps of merge commits
---------------------------------------------
To create or verify timestamps on the merge commits, install the OpenTimestamps
-client via `pip3 install opentimestamps-client`. Then, dowload the gpg wrapper
+client via `pip3 install opentimestamps-client`. Then, download the gpg wrapper
`ots-git-gpg-wrapper.sh` and set it as git's `gpg.program`. See
[the ots git integration documentation](https://github.com/opentimestamps/opentimestamps-client/blob/master/doc/git-integration.md#usage)
for further details.
@@ -167,7 +169,7 @@ still compatible with the minimum supported Linux distribution versions.
Example usage after a gitian build:
- find ../gitian-builder/build -type f -executable | xargs python contrib/devtools/symbol-check.py
+ find ../gitian-builder/build -type f -executable | xargs python3 contrib/devtools/symbol-check.py
If only supported symbols are used the return value will be 0 and the output will be empty.
diff --git a/contrib/devtools/circular-dependencies.py b/contrib/devtools/circular-dependencies.py
index abfa5ed5ae..2e4657f1dd 100755
--- a/contrib/devtools/circular-dependencies.py
+++ b/contrib/devtools/circular-dependencies.py
@@ -8,9 +8,18 @@ MAPPING = {
'core_write.cpp': 'core_io.cpp',
}
+# Directories with header-based modules, where the assumption that .cpp files
+# define functions and variables declared in corresponding .h files is
+# incorrect.
+HEADER_MODULE_PATHS = [
+ 'interfaces/'
+]
+
def module_name(path):
if path in MAPPING:
path = MAPPING[path]
+ if any(path.startswith(dirpath) for dirpath in HEADER_MODULE_PATHS):
+ return path
if path.endswith(".h"):
return path[:-2]
if path.endswith(".c"):
diff --git a/contrib/devtools/gen-manpages.sh b/contrib/devtools/gen-manpages.sh
index b5de5a395f..63b9847100 100755
--- a/contrib/devtools/gen-manpages.sh
+++ b/contrib/devtools/gen-manpages.sh
@@ -10,6 +10,7 @@ MANDIR=${MANDIR:-$TOPDIR/doc/man}
BITCOIND=${BITCOIND:-$BINDIR/bitcoind}
BITCOINCLI=${BITCOINCLI:-$BINDIR/bitcoin-cli}
BITCOINTX=${BITCOINTX:-$BINDIR/bitcoin-tx}
+WALLET_TOOL=${WALLET_TOOL:-$BINDIR/bitcoin-wallet}
BITCOINQT=${BITCOINQT:-$BINDIR/qt/bitcoin-qt}
[ ! -x $BITCOIND ] && echo "$BITCOIND not found or not executable." && exit 1
@@ -23,7 +24,7 @@ BTCVER=($($BITCOINCLI --version | head -n1 | awk -F'[ -]' '{ print $6, $7 }'))
echo "[COPYRIGHT]" > footer.h2m
$BITCOIND --version | sed -n '1!p' >> footer.h2m
-for cmd in $BITCOIND $BITCOINCLI $BITCOINTX $BITCOINQT; do
+for cmd in $BITCOIND $BITCOINCLI $BITCOINTX $WALLET_TOOL $BITCOINQT; do
cmdname="${cmd##*/}"
help2man -N --version-string=${BTCVER[0]} --include=footer.h2m -o ${MANDIR}/${cmdname}.1 ${cmd}
sed -i "s/\\\-${BTCVER[1]}//g" ${MANDIR}/${cmdname}.1
diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py
index ab834b7623..03179cec24 100755
--- a/contrib/devtools/github-merge.py
+++ b/contrib/devtools/github-merge.py
@@ -32,11 +32,11 @@ BASH = os.getenv('BASH','bash')
# OS specific configuration for terminal attributes
ATTR_RESET = ''
ATTR_PR = ''
-COMMIT_FORMAT = '%h %s (%an)%d'
+COMMIT_FORMAT = '%H %s (%an)%d'
if os.name == 'posix': # if posix, assume we can use basic terminal escapes
ATTR_RESET = '\033[0m'
ATTR_PR = '\033[1;36m'
- COMMIT_FORMAT = '%C(bold blue)%h%Creset %s %C(cyan)(%an)%Creset%C(green)%d%Creset'
+ COMMIT_FORMAT = '%C(bold blue)%H%Creset %s %C(cyan)(%an)%Creset%C(green)%d%Creset'
def git_config_get(option, default=None):
'''
@@ -47,18 +47,36 @@ def git_config_get(option, default=None):
except subprocess.CalledProcessError:
return default
-def retrieve_pr_info(repo,pull,ghtoken):
+def get_response(req_url, ghtoken):
+ req = Request(req_url)
+ if ghtoken is not None:
+ req.add_header('Authorization', 'token ' + ghtoken)
+ return urlopen(req)
+
+def retrieve_json(req_url, ghtoken, use_pagination=False):
'''
- Retrieve pull request information from github.
- Return None if no title can be found, or an error happens.
+ Retrieve json from github.
+ Return None if an error happens.
'''
try:
- req = Request("https://api.github.com/repos/"+repo+"/pulls/"+pull)
- if ghtoken is not None:
- req.add_header('Authorization', 'token ' + ghtoken)
- result = urlopen(req)
reader = codecs.getreader('utf-8')
- obj = json.load(reader(result))
+ if not use_pagination:
+ return json.load(reader(get_response(req_url, ghtoken)))
+
+ obj = []
+ page_num = 1
+ while True:
+ req_url_page = '{}?page={}'.format(req_url, page_num)
+ result = get_response(req_url_page, ghtoken)
+ obj.extend(json.load(reader(result)))
+
+ link = result.headers.get('link', None)
+ if link is not None:
+ link_next = [l for l in link.split(',') if 'rel="next"' in l]
+ if len(link_next) > 0:
+ page_num = int(link_next[0][link_next[0].find("page=")+5:link_next[0].find(">")])
+ continue
+ break
return obj
except HTTPError as e:
error_message = e.read()
@@ -69,6 +87,18 @@ def retrieve_pr_info(repo,pull,ghtoken):
print('Warning: unable to retrieve pull information from github: %s' % e)
return None
+def retrieve_pr_info(repo,pull,ghtoken):
+ req_url = "https://api.github.com/repos/"+repo+"/pulls/"+pull
+ return retrieve_json(req_url,ghtoken)
+
+def retrieve_pr_comments(repo,pull,ghtoken):
+ req_url = "https://api.github.com/repos/"+repo+"/issues/"+pull+"/comments"
+ return retrieve_json(req_url,ghtoken,use_pagination=True)
+
+def retrieve_pr_reviews(repo,pull,ghtoken):
+ req_url = "https://api.github.com/repos/"+repo+"/pulls/"+pull+"/reviews"
+ return retrieve_json(req_url,ghtoken,use_pagination=True)
+
def ask_prompt(text):
print(text,end=" ",file=stderr)
stderr.flush()
@@ -133,6 +163,16 @@ def tree_sha512sum(commit='HEAD'):
raise IOError('Non-zero return value executing git cat-file')
return overall.hexdigest()
+def get_acks_from_comments(head_commit, comments):
+ assert len(head_commit) == 6
+ ack_str ='\n\nACKs for commit {}:\n'.format(head_commit)
+ for c in comments:
+ review = [l for l in c['body'].split('\r\n') if 'ACK' in l and head_commit in l]
+ if review:
+ ack_str += ' {}:\n'.format(c['user']['login'])
+ ack_str += ' {}\n'.format(review[0])
+ return ack_str
+
def print_merge_details(pull, title, branch, base_branch, head_branch):
print('%s#%s%s %s %sinto %s%s' % (ATTR_RESET+ATTR_PR,pull,ATTR_RESET,title,ATTR_RESET+ATTR_PR,branch,ATTR_RESET))
subprocess.check_call([GIT,'log','--graph','--topo-order','--pretty=format:'+COMMIT_FORMAT,base_branch+'..'+head_branch])
@@ -185,6 +225,9 @@ def main():
info = retrieve_pr_info(repo,pull,ghtoken)
if info is None:
sys.exit(1)
+ comments = retrieve_pr_comments(repo,pull,ghtoken) + retrieve_pr_reviews(repo,pull,ghtoken)
+ if comments is None:
+ sys.exit(1)
title = info['title'].strip()
body = info['body'].strip()
# precedence order for destination branch argument:
@@ -236,10 +279,11 @@ def main():
else:
firstline = 'Merge #%s' % (pull,)
message = firstline + '\n\n'
- message += subprocess.check_output([GIT,'log','--no-merges','--topo-order','--pretty=format:%h %s (%an)',base_branch+'..'+head_branch]).decode('utf-8')
+ message += subprocess.check_output([GIT,'log','--no-merges','--topo-order','--pretty=format:%H %s (%an)',base_branch+'..'+head_branch]).decode('utf-8')
message += '\n\nPull request description:\n\n ' + body.replace('\n', '\n ') + '\n'
+ message += get_acks_from_comments(head_commit=subprocess.check_output([GIT,'log','-1','--pretty=format:%H',head_branch]).decode('utf-8')[:6], comments=comments)
try:
- subprocess.check_call([GIT,'merge','-q','--commit','--no-edit','--no-ff','-m',message.encode('utf-8'),head_branch])
+ subprocess.check_call([GIT,'merge','-q','--commit','--no-edit','--no-ff','--no-gpg-sign','-m',message.encode('utf-8'),head_branch])
except subprocess.CalledProcessError:
print("ERROR: Cannot be merged cleanly.",file=stderr)
subprocess.check_call([GIT,'merge','--abort'])
@@ -263,7 +307,7 @@ def main():
print("ERROR: Unable to compute tree hash")
sys.exit(4)
try:
- subprocess.check_call([GIT,'commit','--amend','-m',message.encode('utf-8')])
+ subprocess.check_call([GIT,'commit','--amend','--no-gpg-sign','-m',message.encode('utf-8')])
except subprocess.CalledProcessError:
print("ERROR: Cannot update message.", file=stderr)
sys.exit(4)
diff --git a/contrib/devtools/split-debug.sh.in b/contrib/devtools/split-debug.sh.in
index deda49cc54..92b72b1446 100644
--- a/contrib/devtools/split-debug.sh.in
+++ b/contrib/devtools/split-debug.sh.in
@@ -1,5 +1,5 @@
#!/bin/sh
-
+set -e
if [ $# -ne 3 ];
then echo "usage: $0 <input> <stripped-binary> <debug-binary>"
fi
diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py
index c6158c9422..7729dd7257 100755
--- a/contrib/devtools/symbol-check.py
+++ b/contrib/devtools/symbol-check.py
@@ -9,7 +9,7 @@ still compatible with the minimum supported Linux distribution versions.
Example usage:
- find ../gitian-builder/build -type f -executable | xargs python contrib/devtools/symbol-check.py
+ find ../gitian-builder/build -type f -executable | xargs python3 contrib/devtools/symbol-check.py
'''
import subprocess
import re
diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py
index fd374f6328..bb864bfc0c 100755
--- a/contrib/devtools/test-security-check.py
+++ b/contrib/devtools/test-security-check.py
@@ -43,18 +43,6 @@ class TestSecurityChecks(unittest.TestCase):
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE']),
(0, ''))
- def test_32bit_PE(self):
- source = 'test1.c'
- executable = 'test1.exe'
- cc = 'i686-w64-mingw32-gcc'
- write_testcode(source)
-
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--no-nxcompat','-Wl,--no-dynamicbase']),
- (1, executable+': failed DYNAMIC_BASE NX'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--no-dynamicbase']),
- (1, executable+': failed DYNAMIC_BASE'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase']),
- (0, ''))
def test_64bit_PE(self):
source = 'test1.c'
executable = 'test1.exe'
diff --git a/contrib/devtools/test_deterministic_coverage.sh b/contrib/devtools/test_deterministic_coverage.sh
new file mode 100755
index 0000000000..88ac850021
--- /dev/null
+++ b/contrib/devtools/test_deterministic_coverage.sh
@@ -0,0 +1,152 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 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 for deterministic coverage across unit test runs.
+
+export LC_ALL=C
+
+# Use GCOV_EXECUTABLE="gcov" if compiling with gcc.
+# Use GCOV_EXECUTABLE="llvm-cov gcov" if compiling with clang.
+GCOV_EXECUTABLE="gcov"
+
+# Disable tests known to cause non-deterministic behaviour and document the source or point of non-determinism.
+NON_DETERMINISTIC_TESTS=(
+ "blockfilter_index_tests/blockfilter_index_initial_sync" # src/checkqueue.h: In CCheckQueue::Loop(): while (queue.empty()) { ... }
+ "coinselector_tests/knapsack_solver_test" # coinselector_tests.cpp: if (equal_sets(setCoinsRet, setCoinsRet2))
+ "denialofservice_tests/DoS_mapOrphans" # denialofservice_tests.cpp: it = mapOrphanTransactions.lower_bound(InsecureRand256());
+ "fs_tests/fsbridge_fstream" # deterministic test failure?
+ "miner_tests/CreateNewBlock_validity" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+ "scheduler_tests/manythreads" # scheduler.cpp: CScheduler::serviceQueue()
+ "scheduler_tests/singlethreadedscheduler_ordered" # scheduler.cpp: CScheduler::serviceQueue()
+ "tx_validationcache_tests/checkinputs_test" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+ "tx_validationcache_tests/tx_mempool_block_doublespend" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+ "txindex_tests/txindex_initial_sync" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+ "txvalidation_tests/tx_mempool_reject_coinbase" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+ "validation_block_tests/processnewblock_signals_ordering" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+ "wallet_tests/coin_mark_dirty_immature_credit" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+ "wallet_tests/dummy_input_size_test" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+ "wallet_tests/importmulti_rescan" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+ "wallet_tests/importwallet_rescan" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+ "wallet_tests/ListCoins" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+ "wallet_tests/scan_for_wallet_transactions" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+ "wallet_tests/wallet_disableprivkeys" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
+)
+
+TEST_BITCOIN_BINARY="src/test/test_bitcoin"
+
+print_usage() {
+ echo "Usage: $0 [custom test filter (default: all but known non-deterministic tests)] [number of test runs (default: 2)]"
+}
+
+N_TEST_RUNS=2
+BOOST_TEST_RUN_FILTERS=""
+if [[ $# != 0 ]]; then
+ if [[ $1 == "--help" ]]; then
+ print_usage
+ exit
+ fi
+ PARSED_ARGUMENTS=0
+ if [[ $1 =~ [a-z] ]]; then
+ BOOST_TEST_RUN_FILTERS=$1
+ PARSED_ARGUMENTS=$((PARSED_ARGUMENTS + 1))
+ shift
+ fi
+ if [[ $1 =~ ^[0-9]+$ ]]; then
+ N_TEST_RUNS=$1
+ PARSED_ARGUMENTS=$((PARSED_ARGUMENTS + 1))
+ shift
+ fi
+ if [[ ${PARSED_ARGUMENTS} == 0 || $# -gt 2 || ${N_TEST_RUNS} -lt 2 ]]; then
+ print_usage
+ exit
+ fi
+fi
+if [[ ${BOOST_TEST_RUN_FILTERS} == "" ]]; then
+ BOOST_TEST_RUN_FILTERS="$(IFS=":"; echo "!${NON_DETERMINISTIC_TESTS[*]}" | sed 's/:/:!/g')"
+else
+ echo "Using Boost test filter: ${BOOST_TEST_RUN_FILTERS}"
+ echo
+fi
+
+if ! command -v gcov > /dev/null; then
+ echo "Error: gcov not installed. Exiting."
+ exit 1
+fi
+
+if ! command -v gcovr > /dev/null; then
+ echo "Error: gcovr not installed. Exiting."
+ exit 1
+fi
+
+if [[ ! -e ${TEST_BITCOIN_BINARY} ]]; then
+ echo "Error: Executable ${TEST_BITCOIN_BINARY} not found. Run \"./configure --enable-lcov\" and compile."
+ exit 1
+fi
+
+get_file_suffix_count() {
+ find src/ -type f -name "*.$1" | wc -l
+}
+
+if [[ $(get_file_suffix_count gcno) == 0 ]]; then
+ echo "Error: Could not find any *.gcno files. The *.gcno files are generated by the compiler. Run \"./configure --enable-lcov\" and re-compile."
+ exit 1
+fi
+
+get_covr_filename() {
+ echo "gcovr.run-$1.txt"
+}
+
+TEST_RUN_ID=0
+while [[ ${TEST_RUN_ID} -lt ${N_TEST_RUNS} ]]; do
+ TEST_RUN_ID=$((TEST_RUN_ID + 1))
+ echo "[$(date +"%Y-%m-%d %H:%M:%S")] Measuring coverage, run #${TEST_RUN_ID} of ${N_TEST_RUNS}"
+ find src/ -type f -name "*.gcda" -exec rm {} \;
+ if [[ $(get_file_suffix_count gcda) != 0 ]]; then
+ echo "Error: Stale *.gcda files found. Exiting."
+ exit 1
+ fi
+ TEST_OUTPUT_TEMPFILE=$(mktemp)
+ if ! BOOST_TEST_RUN_FILTERS="${BOOST_TEST_RUN_FILTERS}" ${TEST_BITCOIN_BINARY} > "${TEST_OUTPUT_TEMPFILE}" 2>&1; then
+ cat "${TEST_OUTPUT_TEMPFILE}"
+ rm "${TEST_OUTPUT_TEMPFILE}"
+ exit 1
+ fi
+ rm "${TEST_OUTPUT_TEMPFILE}"
+ if [[ $(get_file_suffix_count gcda) == 0 ]]; then
+ echo "Error: Running the test suite did not create any *.gcda files. The gcda files are generated when the instrumented test programs are executed. Run \"./configure --enable-lcov\" and re-compile."
+ exit 1
+ fi
+ GCOVR_TEMPFILE=$(mktemp)
+ if ! gcovr --gcov-executable "${GCOV_EXECUTABLE}" -r src/ > "${GCOVR_TEMPFILE}"; then
+ echo "Error: gcovr failed. Output written to ${GCOVR_TEMPFILE}. Exiting."
+ exit 1
+ fi
+ GCOVR_FILENAME=$(get_covr_filename ${TEST_RUN_ID})
+ mv "${GCOVR_TEMPFILE}" "${GCOVR_FILENAME}"
+ if grep -E "^TOTAL *0 *0 " "${GCOVR_FILENAME}"; then
+ echo "Error: Spurious gcovr output. Make sure the correct GCOV_EXECUTABLE variable is set in $0 (\"gcov\" for gcc, \"llvm-cov gcov\" for clang)."
+ exit 1
+ fi
+ if [[ ${TEST_RUN_ID} != 1 ]]; then
+ COVERAGE_DIFF=$(diff -u "$(get_covr_filename 1)" "${GCOVR_FILENAME}")
+ if [[ ${COVERAGE_DIFF} != "" ]]; then
+ echo
+ echo "The line coverage is non-deterministic between runs. Exiting."
+ echo
+ echo "The test suite must be deterministic in the sense that the set of lines executed at least"
+ echo "once must be identical between runs. This is a necessary condition for meaningful"
+ echo "coverage measuring."
+ echo
+ echo "${COVERAGE_DIFF}"
+ exit 1
+ fi
+ rm "${GCOVR_FILENAME}"
+ fi
+done
+
+echo
+echo "Coverage test passed: Deterministic coverage across ${N_TEST_RUNS} runs."
+exit
diff --git a/contrib/gitian-build.py b/contrib/gitian-build.py
index fc7fbb764d..570d4906cd 100755
--- a/contrib/gitian-build.py
+++ b/contrib/gitian-build.py
@@ -7,20 +7,20 @@ import sys
def setup():
global args, workdir
- programs = ['ruby', 'git', 'apt-cacher-ng', 'make', 'wget']
+ programs = ['ruby', 'git', 'make', 'wget', 'curl']
if args.kvm:
- programs += ['python-vm-builder', 'qemu-kvm', 'qemu-utils']
- elif args.docker:
+ programs += ['apt-cacher-ng', 'python-vm-builder', 'qemu-kvm', 'qemu-utils']
+ elif args.docker and not os.path.isfile('/lib/systemd/system/docker.service'):
dockers = ['docker.io', 'docker-ce']
for i in dockers:
return_code = subprocess.call(['sudo', 'apt-get', 'install', '-qq', i])
if return_code == 0:
break
if return_code != 0:
- print('Cannot find any way to install docker', file=sys.stderr)
- exit(1)
+ print('Cannot find any way to install Docker.', file=sys.stderr)
+ sys.exit(1)
else:
- programs += ['lxc', 'debootstrap']
+ programs += ['apt-cacher-ng', 'lxc', 'debootstrap']
subprocess.check_call(['sudo', 'apt-get', 'install', '-qq'] + programs)
if not os.path.isdir('gitian.sigs'):
subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin-core/gitian.sigs.git'])
@@ -41,7 +41,7 @@ def setup():
if args.is_bionic and not args.kvm and not args.docker:
subprocess.check_call(['sudo', 'sed', '-i', 's/lxcbr0/br0/', '/etc/default/lxc-net'])
print('Reboot is required')
- exit(0)
+ sys.exit(0)
def build():
global args, workdir
@@ -68,14 +68,14 @@ def build():
subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'bitcoin='+args.commit, '--url', 'bitcoin='+args.url, '../bitcoin/contrib/gitian-descriptors/gitian-win.yml'])
subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-unsigned', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-win.yml'])
subprocess.check_call('mv build/out/bitcoin-*-win-unsigned.tar.gz inputs/', shell=True)
- subprocess.check_call('mv build/out/bitcoin-*.zip build/out/bitcoin-*.exe ../bitcoin-binaries/'+args.version, shell=True)
+ subprocess.check_call('mv build/out/bitcoin-*.zip build/out/bitcoin-*.exe build/out/src/bitcoin-*.tar.gz ../bitcoin-binaries/'+args.version, shell=True)
if args.macos:
print('\nCompiling ' + args.version + ' MacOS')
subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'bitcoin='+args.commit, '--url', 'bitcoin='+args.url, '../bitcoin/contrib/gitian-descriptors/gitian-osx.yml'])
subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-unsigned', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-osx.yml'])
subprocess.check_call('mv build/out/bitcoin-*-osx-unsigned.tar.gz inputs/', shell=True)
- subprocess.check_call('mv build/out/bitcoin-*.tar.gz build/out/bitcoin-*.dmg ../bitcoin-binaries/'+args.version, shell=True)
+ subprocess.check_call('mv build/out/bitcoin-*.tar.gz build/out/bitcoin-*.dmg build/out/src/bitcoin-*.tar.gz ../bitcoin-binaries/'+args.version, shell=True)
os.chdir(workdir)
@@ -95,15 +95,14 @@ def sign():
if args.windows:
print('\nSigning ' + args.version + ' Windows')
subprocess.check_call('cp inputs/bitcoin-' + args.version + '-win-unsigned.tar.gz inputs/bitcoin-win-unsigned.tar.gz', shell=True)
- subprocess.check_call(['bin/gbuild', '-i', '--commit', 'signature='+args.commit, '../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml'])
+ subprocess.check_call(['bin/gbuild', '--skip-image', '--upgrade', '--commit', 'signature='+args.commit, '../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml'])
subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-signed', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml'])
subprocess.check_call('mv build/out/bitcoin-*win64-setup.exe ../bitcoin-binaries/'+args.version, shell=True)
- subprocess.check_call('mv build/out/bitcoin-*win32-setup.exe ../bitcoin-binaries/'+args.version, shell=True)
if args.macos:
print('\nSigning ' + args.version + ' MacOS')
subprocess.check_call('cp inputs/bitcoin-' + args.version + '-osx-unsigned.tar.gz inputs/bitcoin-osx-unsigned.tar.gz', shell=True)
- subprocess.check_call(['bin/gbuild', '-i', '--commit', 'signature='+args.commit, '../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml'])
+ subprocess.check_call(['bin/gbuild', '--skip-image', '--upgrade', '--commit', 'signature='+args.commit, '../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml'])
subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-signed', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml'])
subprocess.check_call('mv build/out/bitcoin-osx-signed.dmg ../bitcoin-binaries/'+args.version+'/bitcoin-'+args.version+'-osx.dmg', shell=True)
@@ -119,25 +118,41 @@ def sign():
def verify():
global args, workdir
+ rc = 0
os.chdir('gitian-builder')
print('\nVerifying v'+args.version+' Linux\n')
- subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-linux', '../bitcoin/contrib/gitian-descriptors/gitian-linux.yml'])
+ if subprocess.call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-linux', '../bitcoin/contrib/gitian-descriptors/gitian-linux.yml']):
+ print('Verifying v'+args.version+' Linux FAILED\n')
+ rc = 1
+
print('\nVerifying v'+args.version+' Windows\n')
- subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-unsigned', '../bitcoin/contrib/gitian-descriptors/gitian-win.yml'])
+ if subprocess.call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-unsigned', '../bitcoin/contrib/gitian-descriptors/gitian-win.yml']):
+ print('Verifying v'+args.version+' Windows FAILED\n')
+ rc = 1
+
print('\nVerifying v'+args.version+' MacOS\n')
- subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-unsigned', '../bitcoin/contrib/gitian-descriptors/gitian-osx.yml'])
+ if subprocess.call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-unsigned', '../bitcoin/contrib/gitian-descriptors/gitian-osx.yml']):
+ print('Verifying v'+args.version+' MacOS FAILED\n')
+ rc = 1
+
print('\nVerifying v'+args.version+' Signed Windows\n')
- subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-signed', '../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml'])
+ if subprocess.call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-signed', '../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml']):
+ print('Verifying v'+args.version+' Signed Windows FAILED\n')
+ rc = 1
+
print('\nVerifying v'+args.version+' Signed MacOS\n')
- subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-signed', '../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml'])
+ if subprocess.call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-signed', '../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml']):
+ print('Verifying v'+args.version+' Signed MacOS FAILED\n')
+ rc = 1
os.chdir(workdir)
+ return rc
def main():
global args, workdir
- parser = argparse.ArgumentParser(usage='%(prog)s [options] signer version')
+ parser = argparse.ArgumentParser(description='Script for running full Gitian builds.')
parser.add_argument('-c', '--commit', action='store_true', dest='commit', help='Indicate that the version argument is for a commit or branch')
parser.add_argument('-p', '--pull', action='store_true', dest='pull', help='Indicate that the version argument is the number of a github repository pull request')
parser.add_argument('-u', '--url', dest='url', default='https://github.com/bitcoin/bitcoin', help='Specify the URL of the repository. Default is %(default)s')
@@ -150,64 +165,70 @@ def main():
parser.add_argument('-m', '--memory', dest='memory', default='2000', help='Memory to allocate in MiB. Default %(default)s')
parser.add_argument('-k', '--kvm', action='store_true', dest='kvm', help='Use KVM instead of LXC')
parser.add_argument('-d', '--docker', action='store_true', dest='docker', help='Use Docker instead of LXC')
- parser.add_argument('-S', '--setup', action='store_true', dest='setup', help='Set up the Gitian building environment. Uses LXC. If you want to use KVM, use the --kvm option. Only works on Debian-based systems (Ubuntu, Debian)')
+ parser.add_argument('-S', '--setup', action='store_true', dest='setup', help='Set up the Gitian building environment. Only works on Debian-based systems (Ubuntu, Debian)')
parser.add_argument('-D', '--detach-sign', action='store_true', dest='detach_sign', help='Create the assert file for detached signing. Will not commit anything.')
parser.add_argument('-n', '--no-commit', action='store_false', dest='commit_files', help='Do not commit anything to git')
- parser.add_argument('signer', help='GPG signer to sign each build assert file')
- parser.add_argument('version', help='Version number, commit, or branch to build. If building a commit or branch, the -c option must be specified')
+ parser.add_argument('signer', nargs='?', help='GPG signer to sign each build assert file')
+ parser.add_argument('version', nargs='?', help='Version number, commit, or branch to build. If building a commit or branch, the -c option must be specified')
args = parser.parse_args()
workdir = os.getcwd()
- args.linux = 'l' in args.os
- args.windows = 'w' in args.os
- args.macos = 'm' in args.os
-
args.is_bionic = b'bionic' in subprocess.check_output(['lsb_release', '-cs'])
- if args.buildsign:
- args.build=True
- args.sign=True
-
if args.kvm and args.docker:
raise Exception('Error: cannot have both kvm and docker')
- args.sign_prog = 'true' if args.detach_sign else 'gpg --detach-sign'
-
- # Set environment variable USE_LXC or USE_DOCKER, let gitian-builder know that we use lxc or docker
+ # Ensure no more than one environment variable for gitian-builder (USE_LXC, USE_VBOX, USE_DOCKER) is set as they
+ # can interfere (e.g., USE_LXC being set shadows USE_DOCKER; for details see gitian-builder/libexec/make-clean-vm).
+ os.environ['USE_LXC'] = ''
+ os.environ['USE_VBOX'] = ''
+ os.environ['USE_DOCKER'] = ''
if args.docker:
os.environ['USE_DOCKER'] = '1'
elif not args.kvm:
os.environ['USE_LXC'] = '1'
- if not 'GITIAN_HOST_IP' in os.environ.keys():
+ if 'GITIAN_HOST_IP' not in os.environ.keys():
os.environ['GITIAN_HOST_IP'] = '10.0.3.1'
- if not 'LXC_GUEST_IP' in os.environ.keys():
+ if 'LXC_GUEST_IP' not in os.environ.keys():
os.environ['LXC_GUEST_IP'] = '10.0.3.5'
+ if args.setup:
+ setup()
+
+ if args.buildsign:
+ args.build = True
+ args.sign = True
+
+ if not args.build and not args.sign and not args.verify:
+ sys.exit(0)
+
+ args.linux = 'l' in args.os
+ args.windows = 'w' in args.os
+ args.macos = 'm' in args.os
+
# Disable for MacOS if no SDK found
if args.macos and not os.path.isfile('gitian-builder/inputs/MacOSX10.11.sdk.tar.gz'):
print('Cannot build for MacOS, SDK does not exist. Will build for other OSes')
args.macos = False
+ args.sign_prog = 'true' if args.detach_sign else 'gpg --detach-sign'
+
script_name = os.path.basename(sys.argv[0])
- # Signer and version shouldn't be empty
- if args.signer == '':
- print(script_name+': Missing signer.')
+ if not args.signer:
+ print(script_name+': Missing signer')
print('Try '+script_name+' --help for more information')
- exit(1)
- if args.version == '':
- print(script_name+': Missing version.')
+ sys.exit(1)
+ if not args.version:
+ print(script_name+': Missing version')
print('Try '+script_name+' --help for more information')
- exit(1)
+ sys.exit(1)
# Add leading 'v' for tags
if args.commit and args.pull:
raise Exception('Cannot have both commit and pull')
args.commit = ('' if args.commit else 'v') + args.version
- if args.setup:
- setup()
-
os.chdir('bitcoin')
if args.pull:
subprocess.check_call(['git', 'fetch', args.url, 'refs/pull/'+args.version+'/merge'])
@@ -220,6 +241,10 @@ def main():
subprocess.check_call(['git', 'checkout', args.commit])
os.chdir(workdir)
+ os.chdir('gitian-builder')
+ subprocess.check_call(['git', 'pull'])
+ os.chdir(workdir)
+
if args.build:
build()
@@ -227,7 +252,10 @@ def main():
sign()
if args.verify:
- verify()
+ os.chdir('gitian.sigs')
+ subprocess.check_call(['git', 'pull'])
+ os.chdir(workdir)
+ sys.exit(verify())
if __name__ == '__main__':
main()
diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml
index e97072c80a..c50750ed44 100644
--- a/contrib/gitian-descriptors/gitian-linux.yml
+++ b/contrib/gitian-descriptors/gitian-linux.yml
@@ -1,6 +1,7 @@
---
-name: "bitcoin-linux-0.18"
+name: "bitcoin-core-linux-0.19"
enable_cache: true
+distro: "ubuntu"
suites:
- "bionic"
architectures:
@@ -30,12 +31,13 @@ packages:
- "faketime"
- "bsdmainutils"
- "ca-certificates"
-- "python"
+- "python3"
remotes:
- "url": "https://github.com/bitcoin/bitcoin.git"
"dir": "bitcoin"
files: []
script: |
+ set -e -o pipefail
WRAP_DIR=$HOME/wrapped
HOSTS="i686-pc-linux-gnu x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu riscv64-linux-gnu"
@@ -48,8 +50,6 @@ script: |
export QT_RCC_TEST=1
export QT_RCC_SOURCE_DATE_OVERRIDE=1
- export GZIP="-9n"
- export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME""
export TZ="UTC"
export BUILD_DIR=`pwd`
mkdir -p ${WRAP_DIR}
@@ -148,8 +148,8 @@ script: |
# Correct tar file order
mkdir -p temp
pushd temp
- tar xf ../$SOURCEDIST
- find bitcoin-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST
+ tar -xf ../$SOURCEDIST
+ find bitcoin-* | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST
popd
# Workaround for tarball not building with the bare tag version (prep)
@@ -179,11 +179,11 @@ script: |
find . -name "lib*.la" -delete
find . -name "lib*.a" -delete
rm -rf ${DISTNAME}/lib/pkgconfig
- find ${DISTNAME}/bin -type f -executable -exec ../contrib/devtools/split-debug.sh {} {} {}.dbg \;
- find ${DISTNAME}/lib -type f -exec ../contrib/devtools/split-debug.sh {} {} {}.dbg \;
+ find ${DISTNAME}/bin -type f -executable -print0 | xargs -0 -n1 -I{} ../contrib/devtools/split-debug.sh {} {} {}.dbg
+ find ${DISTNAME}/lib -type f -print0 | xargs -0 -n1 -I{} ../contrib/devtools/split-debug.sh {} {} {}.dbg
cp ../doc/README.md ${DISTNAME}/
- find ${DISTNAME} -not -name "*.dbg" | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz
- find ${DISTNAME} -name "*.dbg" | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}-debug.tar.gz
+ find ${DISTNAME} -not -name "*.dbg" | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz
+ find ${DISTNAME} -name "*.dbg" | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}-debug.tar.gz
cd ../../
rm -rf distsrc-${i}
done
diff --git a/contrib/gitian-descriptors/gitian-osx-signer.yml b/contrib/gitian-descriptors/gitian-osx-signer.yml
index 297a136fae..4cfca403b1 100644
--- a/contrib/gitian-descriptors/gitian-osx-signer.yml
+++ b/contrib/gitian-descriptors/gitian-osx-signer.yml
@@ -1,5 +1,6 @@
---
name: "bitcoin-dmg-signer"
+distro: "ubuntu"
suites:
- "bionic"
architectures:
@@ -12,6 +13,8 @@ remotes:
files:
- "bitcoin-osx-unsigned.tar.gz"
script: |
+ set -e -o pipefail
+
WRAP_DIR=$HOME/wrapped
mkdir -p ${WRAP_DIR}
export PATH=`pwd`:$PATH
diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml
index 87d8007ccb..8c51ce7159 100644
--- a/contrib/gitian-descriptors/gitian-osx.yml
+++ b/contrib/gitian-descriptors/gitian-osx.yml
@@ -1,6 +1,7 @@
---
-name: "bitcoin-osx-0.18"
+name: "bitcoin-core-osx-0.19"
enable_cache: true
+distro: "ubuntu"
suites:
- "bionic"
architectures:
@@ -23,9 +24,9 @@ packages:
- "libcap-dev"
- "libz-dev"
- "libbz2-dev"
-- "python"
-- "python-dev"
-- "python-setuptools"
+- "python3"
+- "python3-dev"
+- "python3-setuptools"
- "fonts-tuffy"
remotes:
- "url": "https://github.com/bitcoin/bitcoin.git"
@@ -33,6 +34,8 @@ remotes:
files:
- "MacOSX10.11.sdk.tar.gz"
script: |
+ set -e -o pipefail
+
WRAP_DIR=$HOME/wrapped
HOSTS="x86_64-apple-darwin14"
CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests GENISOIMAGE=$WRAP_DIR/genisoimage"
@@ -41,8 +44,6 @@ script: |
export QT_RCC_TEST=1
export QT_RCC_SOURCE_DATE_OVERRIDE=1
- export GZIP="-9n"
- export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME""
export TZ="UTC"
export BUILD_DIR=`pwd`
mkdir -p ${WRAP_DIR}
@@ -111,8 +112,8 @@ script: |
# Correct tar file order
mkdir -p temp
pushd temp
- tar xf ../$SOURCEDIST
- find bitcoin-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST
+ tar -xf ../$SOURCEDIST
+ find bitcoin-* | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST
popd
# Workaround for tarball not building with the bare tag version (prep)
@@ -149,7 +150,7 @@ script: |
cp ${BASEPREFIX}/${i}/native/bin/${i}-pagestuff unsigned-app-${i}/pagestuff
mv dist unsigned-app-${i}
pushd unsigned-app-${i}
- find . | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-osx-unsigned.tar.gz
+ find . | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-osx-unsigned.tar.gz
popd
make deploy
@@ -159,7 +160,7 @@ script: |
find . -name "lib*.la" -delete
find . -name "lib*.a" -delete
rm -rf ${DISTNAME}/lib/pkgconfig
- find ${DISTNAME} | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz
+ find ${DISTNAME} | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz
cd ../../
done
mkdir -p $OUTDIR/src
diff --git a/contrib/gitian-descriptors/gitian-win-signer.yml b/contrib/gitian-descriptors/gitian-win-signer.yml
index 045be873e9..656c6d9b7a 100644
--- a/contrib/gitian-descriptors/gitian-win-signer.yml
+++ b/contrib/gitian-descriptors/gitian-win-signer.yml
@@ -1,5 +1,6 @@
---
name: "bitcoin-win-signer"
+distro: "ubuntu"
suites:
- "bionic"
architectures:
@@ -16,6 +17,8 @@ files:
- "osslsigncode-Backports-to-1.7.1.patch"
- "bitcoin-win-unsigned.tar.gz"
script: |
+ set -e -o pipefail
+
BUILD_DIR=`pwd`
SIGDIR=${BUILD_DIR}/signature/win
UNSIGNED_DIR=${BUILD_DIR}/unsigned
diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml
index 31b9c309c7..f227c6ad92 100644
--- a/contrib/gitian-descriptors/gitian-win.yml
+++ b/contrib/gitian-descriptors/gitian-win.yml
@@ -1,6 +1,7 @@
---
-name: "bitcoin-win-0.18"
+name: "bitcoin-core-win-0.19"
enable_cache: true
+distro: "ubuntu"
suites:
- "bionic"
architectures:
@@ -20,15 +21,17 @@ packages:
- "nsis"
- "zip"
- "ca-certificates"
-- "python"
+- "python3"
- "rename"
remotes:
- "url": "https://github.com/bitcoin/bitcoin.git"
"dir": "bitcoin"
files: []
script: |
+ set -e -o pipefail
+
WRAP_DIR=$HOME/wrapped
- HOSTS="i686-w64-mingw32 x86_64-w64-mingw32"
+ HOSTS="x86_64-w64-mingw32"
CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests"
FAKETIME_HOST_PROGS="ar ranlib nm windres strip objcopy"
FAKETIME_PROGS="date makensis zip"
@@ -37,8 +40,6 @@ script: |
export QT_RCC_TEST=1
export QT_RCC_SOURCE_DATE_OVERRIDE=1
- export GZIP="-9n"
- export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME""
export TZ="UTC"
export BUILD_DIR=`pwd`
mkdir -p ${WRAP_DIR}
@@ -127,8 +128,8 @@ script: |
# Correct tar file order
mkdir -p temp
pushd temp
- tar xf ../$SOURCEDIST
- find bitcoin-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST
+ tar -xf ../$SOURCEDIST
+ find bitcoin-* | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST
mkdir -p $OUTDIR/src
cp ../$SOURCEDIST $OUTDIR/src
popd
@@ -174,8 +175,6 @@ script: |
cd $BUILD_DIR/windeploy
mkdir unsigned
cp $OUTDIR/bitcoin-*setup-unsigned.exe unsigned/
- find . | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz
+ find . | sort | tar --mtime="$REFERENCE_DATETIME" --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz
mv ${OUTDIR}/${DISTNAME}-x86_64-*-debug.zip ${OUTDIR}/${DISTNAME}-win64-debug.zip
- mv ${OUTDIR}/${DISTNAME}-i686-*-debug.zip ${OUTDIR}/${DISTNAME}-win32-debug.zip
mv ${OUTDIR}/${DISTNAME}-x86_64-*.zip ${OUTDIR}/${DISTNAME}-win64.zip
- mv ${OUTDIR}/${DISTNAME}-i686-*.zip ${OUTDIR}/${DISTNAME}-win32.zip
diff --git a/contrib/gitian-keys/keys.txt b/contrib/gitian-keys/keys.txt
index 80c18aa889..33f0f7e5b0 100644
--- a/contrib/gitian-keys/keys.txt
+++ b/contrib/gitian-keys/keys.txt
@@ -6,6 +6,7 @@ C519EBCF3B926298946783EFF6430754120EC2F4 Christian Decker (cdecker)
F20F56EF6A067F70E8A5C99FFF95FAA971697405 centaur
C060A6635913D98A3587D7DB1C2491FFEB0EF770 Cory Fields
BF6273FAEF7CC0BA1F562E50989F6B3048A116B5 Dev Random
+6D3170C1DC2C6FD0AEEBCA6743811D1A26623924 Douglas Roark
9A1689B60D1B3CCE9262307A2F40A9BF167FBA47 Erik Mossberg (erkmos)
D35176BE9264832E4ACA8986BF0792FBE95DC863 fivepiece
01CDF4627A3B88AAE4A571C87588242FBE38D3A8 Gavin Andresen
diff --git a/contrib/init/bitcoind.service b/contrib/init/bitcoind.service
index 877abafd19..cfc5f77580 100644
--- a/contrib/init/bitcoind.service
+++ b/contrib/init/bitcoind.service
@@ -5,21 +5,45 @@
# See "man systemd.service" for details.
# Note that almost all daemon options could be specified in
-# /etc/bitcoin/bitcoin.conf
+# /etc/bitcoin/bitcoin.conf, except for those explicitly specified as arguments
+# in ExecStart=
[Unit]
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
+ExecStart=/usr/bin/bitcoind -daemon \
+ -pid=/run/bitcoind/bitcoind.pid \
+ -conf=/etc/bitcoin/bitcoin.conf \
+ -datadir=/var/lib/bitcoind
+
+# Process management
+####################
+
Type=forking
PIDFile=/run/bitcoind/bitcoind.pid
Restart=on-failure
+# Directory creation and permissions
+####################################
+
+# Run as bitcoin:bitcoin
+User=bitcoin
+Group=bitcoin
+
+# /run/bitcoind
+RuntimeDirectory=bitcoind
+RuntimeDirectoryMode=0710
+
+# /etc/bitcoin
+ConfigurationDirectory=bitcoin
+ConfigurationDirectoryMode=0710
+
+# /var/lib/bitcoind
+StateDirectory=bitcoind
+StateDirectoryMode=0710
+
# Hardening measures
####################
diff --git a/contrib/install_db4.sh b/contrib/install_db4.sh
index e90b8af7e1..47594a5b0a 100755
--- a/contrib/install_db4.sh
+++ b/contrib/install_db4.sh
@@ -6,7 +6,7 @@ export LC_ALL=C
set -e
if [ -z "${1}" ]; then
- echo "Usage: ./install_db4.sh <base-dir> [<extra-bdb-configure-flag> ...]"
+ echo "Usage: $0 <base-dir> [<extra-bdb-configure-flag> ...]"
echo
echo "Must specify a single argument: the directory in which db4 will be built."
echo "This is probably \`pwd\` if you're at the root of the bitcoin repository."
@@ -70,6 +70,20 @@ CLANG_CXX11_PATCH_HASH='7a9a47b03fd5fb93a16ef42235fa9512db9b0829cfc3bdf90edd3ec1
http_get "${CLANG_CXX11_PATCH_URL}" clang.patch "${CLANG_CXX11_PATCH_HASH}"
patch -p2 < clang.patch
+# The packaged config.guess and config.sub are ancient (2009) and can cause build issues.
+# Replace them with modern versions.
+# See https://github.com/bitcoin/bitcoin/issues/16064
+CONFIG_GUESS_URL='https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=55eaf3e779455c4e5cc9f82efb5278be8f8f900b'
+CONFIG_GUESS_HASH='2d1ff7bca773d2ec3c6217118129220fa72d8adda67c7d2bf79994b3129232c1'
+CONFIG_SUB_URL='https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=55eaf3e779455c4e5cc9f82efb5278be8f8f900b'
+CONFIG_SUB_HASH='3a4befde9bcdf0fdb2763fc1bfa74e8696df94e1ad7aac8042d133c8ff1d2e32'
+
+rm -f "dist/config.guess"
+rm -f "dist/config.sub"
+
+http_get "${CONFIG_GUESS_URL}" dist/config.guess "${CONFIG_GUESS_HASH}"
+http_get "${CONFIG_SUB_URL}" dist/config.sub "${CONFIG_SUB_HASH}"
+
cd build_unix/
"${BDB_PREFIX}/${BDB_VERSION}/dist/configure" \
diff --git a/contrib/linearize/README.md b/contrib/linearize/README.md
index 2985106982..25a1c7351a 100644
--- a/contrib/linearize/README.md
+++ b/contrib/linearize/README.md
@@ -1,6 +1,5 @@
# Linearize
-Construct a linear, no-fork, best version of the Bitcoin blockchain. The scripts
-run using Python 3 but are compatible with Python 2.
+Construct a linear, no-fork, best version of the Bitcoin blockchain.
## Step 1: Download hash list
diff --git a/contrib/linearize/linearize-data.py b/contrib/linearize/linearize-data.py
index 56c1fbfc92..468aec04b5 100755
--- a/contrib/linearize/linearize-data.py
+++ b/contrib/linearize/linearize-data.py
@@ -16,7 +16,7 @@ import hashlib
import datetime
import time
from collections import namedtuple
-from binascii import hexlify, unhexlify
+from binascii import unhexlify
settings = {}
@@ -61,7 +61,7 @@ def calc_hash_str(blk_hdr):
hash = calc_hdr_hash(blk_hdr)
hash = bufreverse(hash)
hash = wordreverse(hash)
- hash_str = hexlify(hash).decode('utf-8')
+ hash_str = hash.hex()
return hash_str
def get_blk_dt(blk_hdr):
@@ -213,7 +213,7 @@ class BlockDataCopier:
inMagic = inhdr[:4]
if (inMagic != self.settings['netmagic']):
- print("Invalid magic: " + hexlify(inMagic).decode('utf-8'))
+ print("Invalid magic: " + inMagic.hex())
return
inLenLE = inhdr[4:]
su = struct.unpack("<I", inLenLE)
diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py
index e10b46d831..8529470e09 100755
--- a/contrib/linearize/linearize-hashes.py
+++ b/contrib/linearize/linearize-hashes.py
@@ -7,7 +7,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
-from http.client import HttpConnection
+from http.client import HTTPConnection
import json
import re
import base64
@@ -27,7 +27,7 @@ class BitcoinRPC:
authpair = "%s:%s" % (username, password)
authpair = authpair.encode('utf-8')
self.authhdr = b"Basic " + base64.b64encode(authpair)
- self.conn = HttpConnection(host, port=port, timeout=30)
+ self.conn = HTTPConnection(host, port=port, timeout=30)
def execute(self, obj):
try:
diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py
index 6527deccf2..523386e393 100755
--- a/contrib/seeds/makeseeds.py
+++ b/contrib/seeds/makeseeds.py
@@ -109,18 +109,30 @@ def filtermultiport(ips):
# Based on Greg Maxwell's seed_filter.py
def filterbyasn(ips, max_per_asn, max_total):
# Sift out ips by type
- ips_ipv4 = [ip for ip in ips if ip['net'] == 'ipv4']
- ips_ipv6 = [ip for ip in ips if ip['net'] == 'ipv6']
+ ips_ipv46 = [ip for ip in ips if ip['net'] in ['ipv4', 'ipv6']]
ips_onion = [ip for ip in ips if ip['net'] == 'onion']
- # Filter IPv4 by ASN
+ # Filter IPv46 by ASN
result = []
asn_count = {}
- for ip in ips_ipv4:
+ for ip in ips_ipv46:
if len(result) == max_total:
break
try:
- asn = int([x.to_text() for x in dns.resolver.query('.'.join(reversed(ip['ip'].split('.'))) + '.origin.asn.cymru.com', 'TXT').response.answer][0].split('\"')[1].split(' ')[0])
+ if ip['net'] == 'ipv4':
+ ipaddr = ip['ip']
+ prefix = '.origin'
+ else: # http://www.team-cymru.com/IP-ASN-mapping.html
+ res = str() # 2001:4860:b002:23::68
+ for nb in ip['ip'].split(':')[:4]: # pick the first 4 nibbles
+ for c in nb.zfill(4): # right padded with '0'
+ res += c + '.' # 2001 4860 b002 0023
+ ipaddr = res.rstrip('.') # 2.0.0.1.4.8.6.0.b.0.0.2.0.0.2.3
+ prefix = '.origin6'
+
+ asn = int([x.to_text() for x in dns.resolver.query('.'.join(
+ reversed(ipaddr.split('.'))) + prefix + '.asn.cymru.com',
+ 'TXT').response.answer][0].split('\"')[1].split(' ')[0])
if asn not in asn_count:
asn_count[asn] = 0
if asn_count[asn] == max_per_asn:
@@ -130,10 +142,7 @@ def filterbyasn(ips, max_per_asn, max_total):
except:
sys.stderr.write('ERR: Could not resolve ASN for "' + ip['ip'] + '"\n')
- # TODO: filter IPv6 by ASN
-
- # Add back non-IPv4
- result.extend(ips_ipv6)
+ # Add back Onions
result.extend(ips_onion)
return result
diff --git a/contrib/verify-commits/README.md b/contrib/verify-commits/README.md
index 27ca15acb4..1215962a16 100644
--- a/contrib/verify-commits/README.md
+++ b/contrib/verify-commits/README.md
@@ -37,7 +37,8 @@ Configuration files
Import trusted keys
-------------------
-In order to check the commit signatures you must add the trusted PGP keys to your machine. This can be done in Linux by running
+In order to check the commit signatures, you must add the trusted PGP keys to your machine. [GnuPG](https://gnupg.org/) may be used to import the trusted keys by running the following command:
+
```sh
gpg --recv-keys $(<contrib/verify-commits/trusted-keys)
```
diff --git a/contrib/verify-commits/trusted-keys b/contrib/verify-commits/trusted-keys
index a10da9d822..27fede6277 100644
--- a/contrib/verify-commits/trusted-keys
+++ b/contrib/verify-commits/trusted-keys
@@ -3,3 +3,4 @@
32EE5C4C3FA15CCADB46ABE529D4BCB6416F53EC
B8B3F1C0E58C15DB6A81D30C3648A882F4316B9B
CA03882CB1FC067B5D3ACFE4D300116E1C875A3D
+E777299FC265DD04793070EB944D35F9AC3DB76A
diff --git a/contrib/verify-commits/verify-commits.py b/contrib/verify-commits/verify-commits.py
index 6bbed01073..255ce75092 100755
--- a/contrib/verify-commits/verify-commits.py
+++ b/contrib/verify-commits/verify-commits.py
@@ -5,6 +5,7 @@
"""Verify commits against a trusted keys list."""
import argparse
import hashlib
+import logging
import os
import subprocess
import sys
@@ -66,6 +67,11 @@ def tree_sha512sum(commit='HEAD'):
return overall.hexdigest()
def main():
+
+ # Enable debug logging if running in CI
+ if 'CI' in os.environ and os.environ['CI'].lower() == "true":
+ logging.getLogger().setLevel(logging.DEBUG)
+
# Parse arguments
parser = argparse.ArgumentParser(usage='%(prog)s [options] [commit id]')
parser.add_argument('--disable-tree-check', action='store_false', dest='verify_tree', help='disable SHA-512 tree check')
@@ -95,6 +101,10 @@ def main():
# Iterate through commits
while True:
+
+ # Log a message to prevent Travis from timing out
+ logging.debug("verify-commits: [in-progress] processing commit {}".format(current_commit[:8]))
+
if current_commit == verified_root:
print('There is a valid path from "{}" to {} where all commits are signed!'.format(initial_commit, verified_root))
sys.exit(0)
diff --git a/contrib/windeploy/win-codesign.cert b/contrib/windeploy/win-codesign.cert
index 200b30a3f0..5bc5dc5809 100644
--- a/contrib/windeploy/win-codesign.cert
+++ b/contrib/windeploy/win-codesign.cert
@@ -1,99 +1,100 @@
-----BEGIN CERTIFICATE-----
-MIIFTTCCBDWgAwIBAgIRALlW05RLwG2hMQMX5d/o5J8wDQYJKoZIhvcNAQELBQAw
-fTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
-A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxIzAhBgNV
-BAMTGkNPTU9ETyBSU0EgQ29kZSBTaWduaW5nIENBMB4XDTE2MDIwMzAwMDAwMFoX
-DTE5MDMwNTIzNTk1OVowgbUxCzAJBgNVBAYTAlVTMQ4wDAYDVQQRDAU5ODEwNDEL
-MAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxEDAOBgNVBAkMB1N0ZSAzMDAx
-FzAVBgNVBAkMDjcxIENvbHVtYmlhIFN0MSUwIwYDVQQKDBxUaGUgQml0Y29pbiBG
-b3VuZGF0aW9uLCBJbmMuMSUwIwYDVQQDDBxUaGUgQml0Y29pbiBGb3VuZGF0aW9u
-LCBJbmMuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw37Vrv9Gbku0
-+kuV0t89TuyxtAcmT7QE4GcwESKKjmkxfzD9a0qlhqk8GfQ+fw4DHNN+nLKNv7xB
-bk6aS7J2v2DcXkOjrP99P9jqgTkp7MC04VtG3OqVRGB+gum0pptRovYZUQXIdkY7
-GJOok/NDagwKiiUe2V2meZ7UctsZNvYeilQdTgKIIhrMB9NowCOhT8ocVL4Ki55/
-l7hukJn3fueCM3fHTwY2/1gaGsOHoCkFRsD7vokjAVpiY+8rUgvHjb0gxgojiVGd
-6a6/F5XJwKJacvUyN4Hfc2K5lRMQjTTmo4aWNWIa0iJ3TK9BHpdSLJBqerMPvmnM
-kkapS+ZTNQIDAQABo4IBjTCCAYkwHwYDVR0jBBgwFoAUKZFg/4pN+uv5pmq4z/nm
-S71JzhIwHQYDVR0OBBYEFONpQ+cV82URVe+V8G57377KxxexMA4GA1UdDwEB/wQE
-AwIHgDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMBEGCWCGSAGG
-+EIBAQQEAwIEEDBGBgNVHSAEPzA9MDsGDCsGAQQBsjEBAgEDAjArMCkGCCsGAQUF
-BwIBFh1odHRwczovL3NlY3VyZS5jb21vZG8ubmV0L0NQUzBDBgNVHR8EPDA6MDig
-NqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9SU0FDb2RlU2lnbmlu
-Z0NBLmNybDB0BggrBgEFBQcBAQRoMGYwPgYIKwYBBQUHMAKGMmh0dHA6Ly9jcnQu
-Y29tb2RvY2EuY29tL0NPTU9ET1JTQUNvZGVTaWduaW5nQ0EuY3J0MCQGCCsGAQUF
-BzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQELBQADggEB
-AGnBSi9K/9rgTAyKFKrfGWSfNOwAghmsnsvpZSQ7QyoGWBFKSgCs/70kErl18oHA
-g7Y8loQB1yukZmJaCa3OvGud7smn45TCh0TMf4EpP20Wxf4rMQTxwAatasHL3+vi
-I+Nl5bsRZ09kWjvayqLII5upjS/yq0JfpmyGl5k2C/fIpztq0iOLvqWlXcL4+51r
-cMUAfX6E6EaZQm//ikp+w2+7MEXTKguOuV3gwsrTy0DsvkZl4YDgx/FA4ImzXopv
-d+3KJPLvO+OSBqUD3JPwXHnuJqGAbLBFyyCa/feGUjLlR8cxcNWLWdp4qxtoIUPG
-3wTsC9YgrglS0F7FKMXlNRY=
+MIIFcTCCBFmgAwIBAgIRALWcUnSOxv9FQW3xdaMDO6swDQYJKoZIhvcNAQELBQAw
+fDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSQwIgYDVQQD
+ExtTZWN0aWdvIFJTQSBDb2RlIFNpZ25pbmcgQ0EwHhcNMTkwMzI3MDAwMDAwWhcN
+MjAwMzI2MjM1OTU5WjCBtDELMAkGA1UEBhMCQ0gxDTALBgNVBBEMBDgwMDUxCzAJ
+BgNVBAgMAlpIMRAwDgYDVQQHDAdaw7xyaWNoMRcwFQYDVQQJDA5NYXR0ZW5nYXNz
+ZSAyNzEuMCwGA1UECgwlQml0Y29pbiBDb3JlIENvZGUgU2lnbmluZyBBc3NvY2lh
+dGlvbjEuMCwGA1UEAwwlQml0Y29pbiBDb3JlIENvZGUgU2lnbmluZyBBc3NvY2lh
+dGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK04VDwiY1wxcW3E
+WTTGmnbciCwETwC96DG4qcoH2PPNsVy3dfwGh0C02Qj2vL64IfwIGUFSgREvyjZk
+CNhEuJO2e0nO0rKNNH5v/JO+P7/VYPZkF5a3uUz9ulmihULXioieHB/q0l6BmiJL
++cYaMVfidL9Y+IJwgiTqjnpRhv1Ik083SPsu6GcfQT9MJfY/+xse2EP0l4GfdFE6
+DRcWjiC8UHpfpGYcImzSFZZpbFbqoAyhueCl28QU4f8QAbS6BqNfaAK9MMACWDcK
+eTz3C5JK6CiUxOnGIxilXhljuybFUjR4jGl5eTRpuPWk95NTTYS36q+bx/1nYelx
+0n4nnDMCAwEAAaOCAbMwggGvMB8GA1UdIwQYMBaAFA7hOqhTOjHVir7Bu61nGgOF
+rTQOMB0GA1UdDgQWBBRbN7ECrPCdVvh58enwy3Dix46h2jAOBgNVHQ8BAf8EBAMC
+B4AwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAzARBglghkgBhvhC
+AQEEBAMCBBAwQAYDVR0gBDkwNzA1BgwrBgEEAbIxAQIBAwIwJTAjBggrBgEFBQcC
+ARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwQwYDVR0fBDwwOjA4oDagNIYyaHR0
+cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdvUlNBQ29kZVNpZ25pbmdDQS5jcmww
+cwYIKwYBBQUHAQEEZzBlMD4GCCsGAQUFBzAChjJodHRwOi8vY3J0LnNlY3RpZ28u
+Y29tL1NlY3RpZ29SU0FDb2RlU2lnbmluZ0NBLmNydDAjBggrBgEFBQcwAYYXaHR0
+cDovL29jc3Auc2VjdGlnby5jb20wKwYDVR0RBCQwIoEgam9uYXNAYml0Y29pbmNv
+cmVjb2Rlc2lnbmluZy5vcmcwDQYJKoZIhvcNAQELBQADggEBAF/AIXcFBWCC2Red
+SHN4Cvko5mdSkDNgzjVFc+OwAJ5RdOgbERde4PnHm3Qmrnx+uMetVnmrC8Fv1Iwb
+kkR0bdbWBj6lF6zMsClIN6WJEfY+qfj1qi7wyucu+3OElYRC9bm5Lf0mEHQr8lJ1
+lGvAjPh+/hmxoVNbHFMZ1Ea+BrbjVwiSznt0gzdMh0CispBZKLWCIwRwi+hFjQrw
+Z7RLH8HeCJ5Ojl/OTDQqh6AylQ7l9w9KHsUt4Jqy/AnCCyAj2/6xjdwnuo3tCZwb
+g/9CydiAacD/83odphEeC2iBa+0wsj9bWmyYKY7S9n0u+wm3wBfZbSVMDDPk/la1
+3qCUDLk=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIF4DCCA8igAwIBAgIQLnyHzA6TSlL+lP0ct800rzANBgkqhkiG9w0BAQwFADCB
-hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
-A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
-BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTMwNTA5
-MDAwMDAwWhcNMjgwNTA4MjM1OTU5WjB9MQswCQYDVQQGEwJHQjEbMBkGA1UECBMS
-R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD
-T01PRE8gQ0EgTGltaXRlZDEjMCEGA1UEAxMaQ09NT0RPIFJTQSBDb2RlIFNpZ25p
-bmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmmJBjd5E0f4rR
-3elnMRHrzB79MR2zuWJXP5O8W+OfHiQyESdrvFGRp8+eniWzX4GoGA8dHiAwDvth
-e4YJs+P9omidHCydv3Lj5HWg5TUjjsmK7hoMZMfYQqF7tVIDSzqwjiNLS2PgIpQ3
-e9V5kAoUGFEs5v7BEvAcP2FhCoyi3PbDMKrNKBh1SMF5WgjNu4xVjPfUdpA6M0ZQ
-c5hc9IVKaw+A3V7Wvf2pL8Al9fl4141fEMJEVTyQPDFGy3CuB6kK46/BAW+QGiPi
-XzjbxghdR7ODQfAuADcUuRKqeZJSzYcPe9hiKaR+ML0btYxytEjy4+gh+V5MYnmL
-Agaff9ULAgMBAAGjggFRMIIBTTAfBgNVHSMEGDAWgBS7r34CPfqm8TyEjq3uOJjs
-2TIy1DAdBgNVHQ4EFgQUKZFg/4pN+uv5pmq4z/nmS71JzhIwDgYDVR0PAQH/BAQD
-AgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYD
-VR0gBAowCDAGBgRVHSAAMEwGA1UdHwRFMEMwQaA/oD2GO2h0dHA6Ly9jcmwuY29t
-b2RvY2EuY29tL0NPTU9ET1JTQUNlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHEG
-CCsGAQUFBwEBBGUwYzA7BggrBgEFBQcwAoYvaHR0cDovL2NydC5jb21vZG9jYS5j
-b20vQ09NT0RPUlNBQWRkVHJ1c3RDQS5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v
-Y3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAAj8COcPu+Mo7id4M
-bU2x8U6ST6/COCwEzMVjEasJY6+rotcCP8xvGcM91hoIlP8l2KmIpysQGuCbsQci
-GlEcOtTh6Qm/5iR0rx57FjFuI+9UUS1SAuJ1CAVM8bdR4VEAxof2bO4QRHZXavHf
-WGshqknUfDdOvf+2dVRAGDZXZxHNTwLk/vPa/HUX2+y392UJI0kfQ1eD6n4gd2HI
-TfK7ZU2o94VFB696aSdlkClAi997OlE5jKgfcHmtbUIgos8MbAOMTM1zB5TnWo46
-BLqioXwfy2M6FafUFRunUkcyqfS/ZEfRqh9TTjIwc8Jvt3iCnVz/RrtrIh2IC/gb
-qjSm/Iz13X9ljIwxVzHQNuxHoc/Li6jvHBhYxQZ3ykubUa9MCEp6j+KjUuKOjswm
-5LLY5TjCqO3GgZw1a6lYYUoKl7RLQrZVnb6Z53BtWfhtKgx/GWBfDJqIbDCsUgmQ
-Fhv/K53b0CDKieoofjKOGd97SDMe12X4rsn4gxSTdn1k0I7OvjV9/3IxTZ+evR5s
-L6iPDAZQ+4wns3bJ9ObXwzTijIchhmH+v1V04SF3AwpobLvkyanmz1kl63zsRQ55
-ZmjoIs2475iFTZYRPAmK0H+8KCgT+2rKVI2SXM3CZZgGns5IW9S1N5NGQXwH3c/6
-Q++6Z2H/fUnguzB9XIDj5hY5S6c=
+MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
+iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
+cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
+BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
+MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
+BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
+aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
+AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
+3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
+tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
+Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
+VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
+79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
+c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
+Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
+c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
+UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
+Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
+BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
+A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
+Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
+VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
+ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
+8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
+iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
+Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
+XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
+qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
+VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
+L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
+jjxDah2nGN59PRbxYvnKkKj9
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
-hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
-A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
-BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5
-MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
-EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
-Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh
-dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR
-6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X
-pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC
-9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV
-/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf
-Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z
-+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w
-qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah
-SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC
-u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf
-Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq
-crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
-FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
-/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl
-wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM
-4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV
-2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna
-FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ
-CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK
-boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke
-jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL
-S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb
-QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl
-0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB
-NVOFBkpdn627G190
+MIIF9TCCA92gAwIBAgIQHaJIMG+bJhjQguCWfTPTajANBgkqhkiG9w0BAQwFADCB
+iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
+cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
+BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx
+MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjB8MQswCQYDVQQGEwJHQjEbMBkGA1UE
+CBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRgwFgYDVQQK
+Ew9TZWN0aWdvIExpbWl0ZWQxJDAiBgNVBAMTG1NlY3RpZ28gUlNBIENvZGUgU2ln
+bmluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIYijTKFehif
+SfCWL2MIHi3cfJ8Uz+MmtiVmKUCGVEZ0MWLFEO2yhyemmcuVMMBW9aR1xqkOUGKl
+UZEQauBLYq798PgYrKf/7i4zIPoMGYmobHutAMNhodxpZW0fbieW15dRhqb0J+V8
+aouVHltg1X7XFpKcAC9o95ftanK+ODtj3o+/bkxBXRIgCFnoOc2P0tbPBrRXBbZO
+oT5Xax+YvMRi1hsLjcdmG0qfnYHEckC14l/vC0X/o84Xpi1VsLewvFRqnbyNVlPG
+8Lp5UEks9wO5/i9lNfIi6iwHr0bZ+UYc3Ix8cSjz/qfGFN1VkW6KEQ3fBiSVfQ+n
+oXw62oY1YdMCAwEAAaOCAWQwggFgMB8GA1UdIwQYMBaAFFN5v1qqK0rPVIDh2JvA
+nfKyA2bLMB0GA1UdDgQWBBQO4TqoUzox1Yq+wbutZxoDha00DjAOBgNVHQ8BAf8E
+BAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUEFjAUBggrBgEFBQcDAwYI
+KwYBBQUHAwgwEQYDVR0gBAowCDAGBgRVHSAAMFAGA1UdHwRJMEcwRaBDoEGGP2h0
+dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9u
+QXV0aG9yaXR5LmNybDB2BggrBgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6
+Ly9jcnQudXNlcnRydXN0LmNvbS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAl
+BggrBgEFBQcwAYYZaHR0cDovL29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0B
+AQwFAAOCAgEATWNQ7Uc0SmGk295qKoyb8QAAHh1iezrXMsL2s+Bjs/thAIiaG20Q
+BwRPvrjqiXgi6w9G7PNGXkBGiRL0C3danCpBOvzW9Ovn9xWVM8Ohgyi33i/klPeF
+M4MtSkBIv5rCT0qxjyT0s4E307dksKYjalloUkJf/wTr4XRleQj1qZPea3FAmZa6
+ePG5yOLDCBaxq2NayBWAbXReSnV+pbjDbLXP30p5h1zHQE1jNfYw08+1Cg4LBH+g
+S667o6XQhACTPlNdNKUANWlsvp8gJRANGftQkGG+OY96jk32nw4e/gdREmaDJhlI
+lc5KycF/8zoFm/lv34h/wCOe0h5DekUxwZxNqfBZslkZ6GqNKQQCd3xLS81wvjqy
+VVp4Pry7bwMQJXcVNIr5NsxDkuS6T/FikyglVyn7URnHoSVAaoRXxrKdsbwcCtp8
+Z359LukoTBh+xHsxQXGaSynsCz1XUNLK3f2eBVHlRHjdAd6xdZgNVCT98E7j4viD
+vXK6yz067vBeF5Jobchh+abxKgoLpbn0nu6YMgWFnuv5gynTxix9vTp3Los3QqBq
+gu07SqqUEKThDfgXxbZaeTMYkuO1dfih6Y4KJR7kHvGfWocj/5+kUZ77OYARzdu1
+xKeogG/lU9Tg46LC0lsa+jImLWpXcBw8pFguo/NbSwfcMlnzh6cabVg=
-----END CERTIFICATE-----
diff --git a/contrib/zmq/zmq_sub3.4.py b/contrib/zmq/zmq_sub3.4.py
deleted file mode 100644
index 66fdf7887f..0000000000
--- a/contrib/zmq/zmq_sub3.4.py
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-"""
- ZMQ example using python3's asyncio
-
- Bitcoin should be started with the command line arguments:
- bitcoind -testnet -daemon \
- -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
-
- We use the asyncio library here. `self.handle()` installs itself as a
- future at the end of the function. Since it never returns with the event
- loop having an empty stack of futures, this creates an infinite loop. An
- alternative is to wrap the contents of `handle` inside `while True`.
-
- The `@asyncio.coroutine` decorator and the `yield from` syntax found here
- was introduced in python 3.4 and has been deprecated in favor of the `async`
- and `await` keywords respectively.
-
- A blocking example using python 2.7 can be obtained from the git history:
- https://github.com/bitcoin/bitcoin/blob/37a7fe9e440b83e2364d5498931253937abe9294/contrib/zmq/zmq_sub.py
-"""
-
-import binascii
-import asyncio
-import zmq
-import zmq.asyncio
-import signal
-import struct
-import sys
-
-if (sys.version_info.major, sys.version_info.minor) < (3, 4):
- print("This example only works with Python 3.4 and greater")
- sys.exit(1)
-
-port = 28332
-
-class ZMQHandler():
- def __init__(self):
- self.loop = asyncio.get_event_loop()
- self.zmqContext = zmq.asyncio.Context()
-
- self.zmqSubSocket = self.zmqContext.socket(zmq.SUB)
- self.zmqSubSocket.setsockopt(zmq.RCVHWM, 0)
- self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "hashblock")
- self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "hashtx")
- self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "rawblock")
- self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "rawtx")
- self.zmqSubSocket.connect("tcp://127.0.0.1:%i" % port)
-
- @asyncio.coroutine
- def handle(self) :
- msg = yield from self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- body = msg[1]
- sequence = "Unknown"
- if len(msg[-1]) == 4:
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- sequence = str(msgSequence)
- if topic == b"hashblock":
- print('- HASH BLOCK ('+sequence+') -')
- print(binascii.hexlify(body))
- elif topic == b"hashtx":
- print('- HASH TX ('+sequence+') -')
- print(binascii.hexlify(body))
- elif topic == b"rawblock":
- print('- RAW BLOCK HEADER ('+sequence+') -')
- print(binascii.hexlify(body[:80]))
- elif topic == b"rawtx":
- print('- RAW TX ('+sequence+') -')
- print(binascii.hexlify(body))
- # schedule ourselves to receive the next message
- asyncio.ensure_future(self.handle())
-
- def start(self):
- self.loop.add_signal_handler(signal.SIGINT, self.stop)
- self.loop.create_task(self.handle())
- self.loop.run_forever()
-
- def stop(self):
- self.loop.stop()
- self.zmqContext.destroy()
-
-daemon = ZMQHandler()
-daemon.start()
diff --git a/depends/Makefile b/depends/Makefile
index 50cc77ddeb..70af875189 100644
--- a/depends/Makefile
+++ b/depends/Makefile
@@ -7,6 +7,7 @@ SDK_PATH ?= $(BASEDIR)/SDKs
NO_QT ?=
RAPIDCHECK ?=
NO_WALLET ?=
+NO_ZMQ ?=
NO_UPNP ?=
FALLBACK_DOWNLOAD_PATH ?= https://bitcoincore.org/depends-sources
@@ -93,6 +94,7 @@ $(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null)
qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch)_$(host_os)_packages)
wallet_packages_$(NO_WALLET) = $(wallet_packages)
upnp_packages_$(NO_UPNP) = $(upnp_packages)
+zmq_packages_$(NO_ZMQ) = $(zmq_packages)
rapidcheck_packages_$(RAPIDCHECK) = $(rapidcheck_packages)
@@ -103,6 +105,10 @@ ifneq ($(qt_packages_),)
native_packages += $(qt_native_packages)
endif
+ifneq ($(zmq_packages_),)
+packages += $(zmq_packages)
+endif
+
ifeq ($(rapidcheck_packages_),)
packages += $(rapidcheck_packages)
endif
@@ -143,6 +149,7 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_
-e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \
-e 's|@allow_host_packages@|$(ALLOW_HOST_PACKAGES)|' \
-e 's|@no_qt@|$(NO_QT)|' \
+ -e 's|@no_zmq@|$(NO_ZMQ)|' \
-e 's|@no_wallet@|$(NO_WALLET)|' \
-e 's|@no_upnp@|$(NO_UPNP)|' \
-e 's|@debug@|$(DEBUG)|' \
@@ -192,4 +199,6 @@ download-win:
@$(MAKE) -s HOST=x86_64-w64-mingw32 download-one
download: download-osx download-linux download-win
+$(foreach package,$(all_packages),$(eval $(call ext_add_stages,$(package))))
+
.PHONY: install cached clean clean-all download-one download-osx download-linux download-win download check-packages check-sources
diff --git a/depends/README.md b/depends/README.md
index 693bc36197..6dbe365545 100644
--- a/depends/README.md
+++ b/depends/README.md
@@ -20,7 +20,6 @@ created. To use it for Bitcoin:
Common `host-platform-triplets` for cross compilation are:
-- `i686-w64-mingw32` for Win32
- `x86_64-w64-mingw32` for Win64
- `x86_64-apple-darwin14` for macOS
- `arm-linux-gnueabihf` for Linux ARM 32 bit
@@ -34,7 +33,7 @@ No other options are needed, the paths are automatically configured.
#### For macOS cross compilation
- sudo apt-get install curl librsvg2-bin libtiff-tools bsdmainutils cmake imagemagick libcap-dev libz-dev libbz2-dev python-setuptools
+ sudo apt-get install curl librsvg2-bin libtiff-tools bsdmainutils cmake imagemagick libcap-dev libz-dev libbz2-dev python3-setuptools
#### For Win32/Win64 cross compilation
@@ -44,7 +43,7 @@ No other options are needed, the paths are automatically configured.
Common linux dependencies:
- sudo apt-get install make automake cmake curl g++-multilib libtool binutils-gold bsdmainutils pkg-config python3
+ sudo apt-get install make automake cmake curl g++-multilib libtool binutils-gold bsdmainutils pkg-config python3 patch
For linux ARM cross compilation:
@@ -69,10 +68,11 @@ The following can be set when running make: make FOO=bar
SDK_PATH: Path where sdk's can be found (used by macOS)
FALLBACK_DOWNLOAD_PATH: If a source file can't be fetched, try here before giving up
NO_QT: Don't download/build/cache qt and its dependencies
+ NO_ZMQ: Don't download/build/cache packages needed for enabling zeromq
NO_WALLET: Don't download/build/cache libs needed to enable the wallet
NO_UPNP: Don't download/build/cache packages needed for enabling upnp
DEBUG: disable some optimizations and enable more runtime checking
- RAPIDCHECK: build rapidcheck (experimental)
+ RAPIDCHECK: build rapidcheck (experimental, requires cmake)
HOST_ID_SALT: Optional salt to use when generating host package ids
BUILD_ID_SALT: Optional salt to use when generating build package ids
diff --git a/depends/config.site.in b/depends/config.site.in
index b7a5e795c8..e633752066 100644
--- a/depends/config.site.in
+++ b/depends/config.site.in
@@ -33,6 +33,10 @@ if test -z $with_gui && test -n "@no_qt@"; then
with_gui=no
fi
+if test -z $enable_zmq && test -n "@no_zmq@"; then
+ enable_zmq=no
+fi
+
if test x@host_os@ = xdarwin; then
BREW=no
PORT=no
@@ -67,7 +71,7 @@ fi
if test -n "@CXX@" -a -z "${CXX}"; then
CXX="@CXX@"
fi
-PYTHONPATH=$depends_prefix/native/lib/python/dist-packages:$PYTHONPATH
+PYTHONPATH=$depends_prefix/native/lib/python3/dist-packages:$PYTHONPATH
if test -n "@AR@"; then
AR=@AR@
diff --git a/depends/funcs.mk b/depends/funcs.mk
index 15e404e42d..8f03c5f37a 100644
--- a/depends/funcs.mk
+++ b/depends/funcs.mk
@@ -76,8 +76,9 @@ $(1)_download_path_fixed=$(subst :,\:,$$($(1)_download_path))
#default commands
+# The default behavior for tar will try to set ownership when running as uid 0 and may not succeed, --no-same-owner disables this behavior
$(1)_fetch_cmds ?= $(call fetch_file,$(1),$(subst \:,:,$$($(1)_download_path_fixed)),$$($(1)_download_file),$($(1)_file_name),$($(1)_sha256_hash))
-$(1)_extract_cmds ?= mkdir -p $$($(1)_extract_dir) && echo "$$($(1)_sha256_hash) $$($(1)_source)" > $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_SHA256SUM) -c $$($(1)_extract_dir)/.$$($(1)_file_name).hash && tar --strip-components=1 -xf $$($(1)_source)
+$(1)_extract_cmds ?= mkdir -p $$($(1)_extract_dir) && echo "$$($(1)_sha256_hash) $$($(1)_source)" > $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_SHA256SUM) -c $$($(1)_extract_dir)/.$$($(1)_file_name).hash && tar --no-same-owner --strip-components=1 -xf $$($(1)_source)
$(1)_preprocess_cmds ?=
$(1)_build_cmds ?=
$(1)_config_cmds ?=
@@ -170,15 +171,15 @@ $($(1)_extracted): | $($(1)_fetched)
$(AT)mkdir -p $$(@D)
$(AT)cd $$(@D); $(call $(1)_extract_cmds,$(1))
$(AT)touch $$@
-$($(1)_preprocessed): | $($(1)_dependencies) $($(1)_extracted)
+$($(1)_preprocessed): | $($(1)_extracted)
$(AT)echo Preprocessing $(1)...
$(AT)mkdir -p $$(@D) $($(1)_patch_dir)
$(AT)$(foreach patch,$($(1)_patches),cd $(PATCHES_PATH)/$(1); cp $(patch) $($(1)_patch_dir) ;)
$(AT)cd $$(@D); $(call $(1)_preprocess_cmds, $(1))
$(AT)touch $$@
-$($(1)_configured): | $($(1)_preprocessed)
+$($(1)_configured): | $($(1)_dependencies) $($(1)_preprocessed)
$(AT)echo Configuring $(1)...
- $(AT)rm -rf $(host_prefix); mkdir -p $(host_prefix)/lib; cd $(host_prefix); $(foreach package,$($(1)_all_dependencies), tar xf $($(package)_cached); )
+ $(AT)rm -rf $(host_prefix); mkdir -p $(host_prefix)/lib; cd $(host_prefix); $(foreach package,$($(1)_all_dependencies), tar --no-same-owner -xf $($(package)_cached); )
$(AT)mkdir -p $$(@D)
$(AT)+cd $$(@D); $($(1)_config_env) $(call $(1)_config_cmds, $(1))
$(AT)touch $$@
@@ -213,6 +214,14 @@ $(1): | $($(1)_cached_checksum)
endef
+stages = fetched extracted preprocessed configured built staged postprocessed cached cached_checksum
+
+define ext_add_stages
+$(foreach stage,$(stages),
+ $(1)_$(stage): $($(1)_$(stage))
+ .PHONY: $(1)_$(stage))
+endef
+
# These functions create the build targets for each package. They must be
# broken down into small steps so that each part is done for all packages
# before moving on to the next step. Otherwise, a package's info
diff --git a/depends/packages.md b/depends/packages.md
index 7c80362509..2c592885b6 100644
--- a/depends/packages.md
+++ b/depends/packages.md
@@ -5,6 +5,10 @@ The package "mylib" will be used here as an example
General tips:
- mylib_foo is written as $(package)_foo in order to make recipes more similar.
+- Secondary dependency packages relative to the bitcoin binaries/libraries (i.e.
+ those not in `ALLOWED_LIBRARIES` in `contrib/devtools/symbol-check.py`) don't
+ need to be shared and should be built statically whenever possible. See
+ [below](#secondary-dependencies) for more details.
## Identifiers
Each package is required to define at least these variables:
@@ -14,8 +18,9 @@ Each package is required to define at least these variables:
placeholder such as 1.0 can be used.
$(package)_download_path:
- Location of the upstream source, without the file-name. Usually http or
- ftp.
+ Location of the upstream source, without the file-name. Usually http, https
+ or ftp. Secure transmission options like https should be preferred if
+ available.
$(package)_file_name:
The upstream source filename available at the download path.
@@ -145,3 +150,34 @@ $($(package)_config_opts) will be appended.
Most autotools projects can be properly staged using:
$(MAKE) DESTDIR=$($(package)_staging_dir) install
+
+## Build outputs:
+
+In general, the output of a depends package should not contain any libtool
+archives. Instead, the package should output `.pc` (`pkg-config`) files where
+possible.
+
+From the [Gentoo Wiki entry](https://wiki.gentoo.org/wiki/Project:Quality_Assurance/Handling_Libtool_Archives):
+
+> Libtool pulls in all direct and indirect dependencies into the .la files it
+> creates. This leads to massive overlinking, which is toxic to the Gentoo
+> ecosystem, as it leads to a massive number of unnecessary rebuilds.
+
+## Secondary dependencies:
+
+Secondary dependency packages relative to the bitcoin binaries/libraries (i.e.
+those not in `ALLOWED_LIBRARIES` in `contrib/devtools/symbol-check.py`) don't
+need to be shared and should be built statically whenever possible. This
+improves general build reliability as illustrated by the following example:
+
+When linking an executable against a shared library `libprimary` that has its
+own shared dependency `libsecondary`, we may need to specify the path to
+`libsecondary` on the link command using the `-rpath/-rpath-link` options, it is
+not sufficient to just say `libprimary`.
+
+For us, it's much easier to just link a static `libsecondary` into a shared
+`libprimary`. Especially because in our case, we are linking against a dummy
+`libprimary` anyway that we'll throw away. We don't care if the end-user has a
+static or dynamic `libseconday`, that's not our concern. With a static
+`libseconday`, when we need to link `libprimary` into our executable, there's no
+dependency chain to worry about as `libprimary` has all the symbols.
diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk
index 3cd2e28858..6cdb79592b 100644
--- a/depends/packages/bdb.mk
+++ b/depends/packages/bdb.mk
@@ -1,6 +1,6 @@
package=bdb
$(package)_version=4.8.30
-$(package)_download_path=http://download.oracle.com/berkeley-db
+$(package)_download_path=https://download.oracle.com/berkeley-db
$(package)_file_name=db-$($(package)_version).NC.tar.gz
$(package)_sha256_hash=12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef
$(package)_build_subdir=build_unix
diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk
index 61806c7509..5df49b2af8 100644
--- a/depends/packages/boost.mk
+++ b/depends/packages/boost.mk
@@ -1,8 +1,8 @@
package=boost
-$(package)_version=1_64_0
-$(package)_download_path=https://dl.bintray.com/boostorg/release/1.64.0/source/
+$(package)_version=1_70_0
+$(package)_download_path=https://dl.bintray.com/boostorg/release/1.70.0/source/
$(package)_file_name=$(package)_$($(package)_version).tar.bz2
-$(package)_sha256_hash=7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332
+$(package)_sha256_hash=430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778
define $(package)_set_vars
$(package)_config_opts_release=variant=release
diff --git a/depends/packages/dbus.mk b/depends/packages/dbus.mk
index bbe0375409..ad10b0fdd7 100644
--- a/depends/packages/dbus.mk
+++ b/depends/packages/dbus.mk
@@ -6,7 +6,7 @@ $(package)_sha256_hash=6049ddd5f3f3e2618f615f1faeda0a115104423a7996b7aa73e2f36e3
$(package)_dependencies=expat
define $(package)_set_vars
- $(package)_config_opts=--disable-tests --disable-doxygen-docs --disable-xml-docs --disable-static --without-x
+ $(package)_config_opts=--disable-tests --disable-doxygen-docs --disable-xml-docs --disable-shared --without-x
endef
define $(package)_config_cmds
@@ -21,3 +21,7 @@ define $(package)_stage_cmds
$(MAKE) -C dbus DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-dbusincludeHEADERS install-nodist_dbusarchincludeHEADERS && \
$(MAKE) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA
endef
+
+define $(package)_postprocess_cmds
+ rm lib/*.la
+endef
diff --git a/depends/packages/expat.mk b/depends/packages/expat.mk
index 8d06882cdb..b811f84a38 100644
--- a/depends/packages/expat.mk
+++ b/depends/packages/expat.mk
@@ -5,7 +5,8 @@ $(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=17b43c2716d521369f82fc2dc70f359860e90fa440bea65b3b85f0b246ea81f2
define $(package)_set_vars
-$(package)_config_opts=--disable-static --without-docbook
+ $(package)_config_opts=--disable-shared --without-docbook
+ $(package)_config_opts_linux=--with-pic
endef
define $(package)_config_cmds
@@ -19,3 +20,7 @@ endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
+
+define $(package)_postprocess_cmds
+ rm lib/*.la
+endef
diff --git a/depends/packages/fontconfig.mk b/depends/packages/fontconfig.mk
index 12695db4b9..293631739d 100644
--- a/depends/packages/fontconfig.mk
+++ b/depends/packages/fontconfig.mk
@@ -1,6 +1,6 @@
package=fontconfig
$(package)_version=2.12.1
-$(package)_download_path=http://www.freedesktop.org/software/fontconfig/release/
+$(package)_download_path=https://www.freedesktop.org/software/fontconfig/release/
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=b449a3e10c47e1d1c7a6ec6e2016cca73d3bd68fbbd4f0ae5cc6b573f7d6c7f3
$(package)_dependencies=freetype expat
@@ -26,3 +26,7 @@ endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
+
+define $(package)_postprocess_cmds
+ rm lib/*.la
+endef
diff --git a/depends/packages/freetype.mk b/depends/packages/freetype.mk
index 41e02e2030..f24fc69d81 100644
--- a/depends/packages/freetype.mk
+++ b/depends/packages/freetype.mk
@@ -1,6 +1,6 @@
package=freetype
$(package)_version=2.7.1
-$(package)_download_path=http://download.savannah.gnu.org/releases/$(package)
+$(package)_download_path=https://download.savannah.gnu.org/releases/$(package)
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=3a3bb2c4e15ffb433f2032f50a5b5a92558206822e22bfe8cbe339af4aa82f88
@@ -20,3 +20,7 @@ endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
+
+define $(package)_postprocess_cmds
+ rm lib/*.la
+endef
diff --git a/depends/packages/libX11.mk b/depends/packages/libX11.mk
index 298616bea4..f46bd9219e 100644
--- a/depends/packages/libX11.mk
+++ b/depends/packages/libX11.mk
@@ -1,13 +1,14 @@
package=libX11
$(package)_version=1.6.2
-$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/
+$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=2aa027e837231d2eeea90f3a4afe19948a6eb4c8b2bec0241eba7dbc8106bd16
$(package)_dependencies=libxcb xtrans xextproto xproto
define $(package)_set_vars
-$(package)_config_opts=--disable-xkb --disable-static
-$(package)_config_opts_linux=--with-pic
+ # See libXext for --disable-malloc0returnsnull rationale.
+ $(package)_config_opts=--disable-xkb --disable-static --disable-malloc0returnsnull
+ $(package)_config_opts_linux=--with-pic
endef
define $(package)_preprocess_cmds
@@ -25,3 +26,7 @@ endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
+
+define $(package)_postprocess_cmds
+ rm lib/*.la
+endef
diff --git a/depends/packages/libXau.mk b/depends/packages/libXau.mk
index 304494e3c5..0e4a60ad25 100644
--- a/depends/packages/libXau.mk
+++ b/depends/packages/libXau.mk
@@ -1,6 +1,6 @@
package=libXau
$(package)_version=1.0.8
-$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/
+$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=fdd477320aeb5cdd67272838722d6b7d544887dfe7de46e1e7cc0c27c2bea4f2
$(package)_dependencies=xproto
@@ -25,3 +25,7 @@ endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
+
+define $(package)_postprocess_cmds
+ rm lib/*.la
+endef
diff --git a/depends/packages/libXext.mk b/depends/packages/libXext.mk
index c0565dd672..77f32a340e 100644
--- a/depends/packages/libXext.mk
+++ b/depends/packages/libXext.mk
@@ -1,12 +1,35 @@
package=libXext
-$(package)_version=1.3.2
-$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/
+$(package)_version=1.3.3
+$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
-$(package)_sha256_hash=f829075bc646cdc085fa25d98d5885d83b1759ceb355933127c257e8e50432e0
+$(package)_sha256_hash=b518d4d332231f313371fdefac59e3776f4f0823bcb23cf7c7305bfb57b16e35
$(package)_dependencies=xproto xextproto libX11 libXau
define $(package)_set_vars
- $(package)_config_opts=--disable-static
+ # A number of steps in the autoconfig process implicitly assume that the build
+ # system and the host system are the same. For example, library components
+ # want to build and run test programs to determine the behavior of certain
+ # host system elements. This is clearly impossible when crosscompiling. To
+ # work around these issues, the --enable-malloc0returnsnull (or
+ # --disable-malloc0returnsnull, depending on the host system) must be passed
+ # to configure.
+ # -- https://www.x.org/wiki/CrossCompilingXorg/
+ #
+ # Concretely, between the releases of libXext 1.3.2 and 1.3.3,
+ # XORG_CHECK_MALLOC_ZERO from xorg-macros was changed to use the autoconf
+ # cache, expecting cross-compilation environments to seed this cache as there
+ # is no single correct value when cross compiling (think uclibc, musl, etc.).
+ # You can see the actual change in commit 72fdc868b56fe2b7bdc9a69872651baeca72
+ # in the freedesktop/xorg-macros repo.
+ #
+ # As a result of this change, if we don't seed the cache and we don't use
+ # either --{en,dis}able-malloc0returnsnull, the AC_RUN_IFELSE block has no
+ # optional action-if-cross-compiling argument and configure prints an error
+ # message and exits as documented in the autoconf manual. Prior to this
+ # commit, the AC_RUN_IFELSE block had an action-if-cross-compiling argument
+ # which set the more pessimistic default value MALLOC_ZERO_RETURNS_NULL=yes.
+ # This is why the flag was not required prior to libXext 1.3.3.
+ $(package)_config_opts=--disable-static --disable-malloc0returnsnull
endef
define $(package)_preprocess_cmds
@@ -24,3 +47,7 @@ endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
+
+define $(package)_postprocess_cmds
+ rm lib/*.la
+endef
diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk
index 5f622f8e6e..a3ade899b7 100644
--- a/depends/packages/libevent.mk
+++ b/depends/packages/libevent.mk
@@ -27,4 +27,5 @@ define $(package)_stage_cmds
endef
define $(package)_postprocess_cmds
+ rm lib/*.la
endef
diff --git a/depends/packages/libxcb.mk b/depends/packages/libxcb.mk
index 3f346d9728..9402951826 100644
--- a/depends/packages/libxcb.mk
+++ b/depends/packages/libxcb.mk
@@ -1,6 +1,6 @@
package=libxcb
$(package)_version=1.10
-$(package)_download_path=http://xcb.freedesktop.org/dist
+$(package)_download_path=https://xcb.freedesktop.org/dist
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=98d9ab05b636dd088603b64229dd1ab2d2cc02ab807892e107d674f9c3f2d5b5
$(package)_dependencies=xcb_proto libXau xproto
@@ -32,5 +32,5 @@ define $(package)_stage_cmds
endef
define $(package)_postprocess_cmds
- rm -rf share/man share/doc
+ rm -rf share/man share/doc lib/*.la
endef
diff --git a/depends/packages/miniupnpc.mk b/depends/packages/miniupnpc.mk
index 5ad2b580d2..fdbe22cda6 100644
--- a/depends/packages/miniupnpc.mk
+++ b/depends/packages/miniupnpc.mk
@@ -1,6 +1,6 @@
package=miniupnpc
$(package)_version=2.0.20180203
-$(package)_download_path=http://miniupnp.free.fr/files
+$(package)_download_path=https://miniupnp.tuxfamily.org/files/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=90dda8c7563ca6cd4a83e23b3c66dbbea89603a1675bfdb852897c2c9cc220b7
diff --git a/depends/packages/native_biplist.mk b/depends/packages/native_biplist.mk
index 5f247e9bf3..c3054cbd1a 100644
--- a/depends/packages/native_biplist.mk
+++ b/depends/packages/native_biplist.mk
@@ -3,13 +3,13 @@ $(package)_version=1.0.3
$(package)_download_path=https://bitbucket.org/wooster/biplist/downloads
$(package)_file_name=biplist-$($(package)_version).tar.gz
$(package)_sha256_hash=4c0549764c5fe50b28042ec21aa2e14fe1a2224e239a1dae77d9e7f3932aa4c6
-$(package)_install_libdir=$(build_prefix)/lib/python/dist-packages
+$(package)_install_libdir=$(build_prefix)/lib/python3/dist-packages
define $(package)_build_cmds
- python setup.py build
+ python3 setup.py build
endef
define $(package)_stage_cmds
mkdir -p $($(package)_install_libdir) && \
- python setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir)
+ python3 setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir)
endef
diff --git a/depends/packages/native_cctools.mk b/depends/packages/native_cctools.mk
index 44d238cc4c..a065256c1c 100644
--- a/depends/packages/native_cctools.mk
+++ b/depends/packages/native_cctools.mk
@@ -5,7 +5,7 @@ $(package)_file_name=$($(package)_version).tar.gz
$(package)_sha256_hash=a09c9ba4684670a0375e42d9d67e7f12c1f62581a27f28f7c825d6d7032ccc6a
$(package)_build_subdir=cctools
$(package)_clang_version=3.7.1
-$(package)_clang_download_path=http://llvm.org/releases/$($(package)_clang_version)
+$(package)_clang_download_path=https://llvm.org/releases/$($(package)_clang_version)
$(package)_clang_download_file=clang+llvm-$($(package)_clang_version)-x86_64-linux-gnu-ubuntu-14.04.tar.xz
$(package)_clang_file_name=clang-llvm-$($(package)_clang_version)-x86_64-linux-gnu-ubuntu-14.04.tar.xz
$(package)_clang_sha256_hash=99b28a6b48e793705228a390471991386daa33a9717cd9ca007fcdde69608fd9
@@ -22,12 +22,12 @@ define $(package)_extract_cmds
echo "$($(package)_clang_sha256_hash) $($(package)_source_dir)/$($(package)_clang_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \
$(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \
mkdir -p toolchain/bin toolchain/lib/clang/3.5/include && \
- tar --strip-components=1 -C toolchain -xf $($(package)_source_dir)/$($(package)_clang_file_name) && \
+ tar --no-same-owner --strip-components=1 -C toolchain -xf $($(package)_source_dir)/$($(package)_clang_file_name) && \
rm -f toolchain/lib/libc++abi.so* && \
echo "#!/bin/sh" > toolchain/bin/$(host)-dsymutil && \
echo "exit 0" >> toolchain/bin/$(host)-dsymutil && \
chmod +x toolchain/bin/$(host)-dsymutil && \
- tar --strip-components=1 -xf $($(package)_source)
+ tar --no-same-owner --strip-components=1 -xf $($(package)_source)
endef
define $(package)_set_vars
diff --git a/depends/packages/native_cdrkit.mk b/depends/packages/native_cdrkit.mk
index cf694edb30..8243458ec8 100644
--- a/depends/packages/native_cdrkit.mk
+++ b/depends/packages/native_cdrkit.mk
@@ -1,6 +1,6 @@
package=native_cdrkit
$(package)_version=1.1.11
-$(package)_download_path=http://distro.ibiblio.org/fatdog/source/600/c
+$(package)_download_path=https://distro.ibiblio.org/fatdog/source/600/c
$(package)_file_name=cdrkit-$($(package)_version).tar.bz2
$(package)_sha256_hash=b50d64c214a65b1a79afe3a964c691931a4233e2ba605d793eb85d0ac3652564
$(package)_patches=cdrkit-deterministic.patch
diff --git a/depends/packages/native_ds_store.mk b/depends/packages/native_ds_store.mk
index 116fa25d38..f99b689ecd 100644
--- a/depends/packages/native_ds_store.mk
+++ b/depends/packages/native_ds_store.mk
@@ -3,14 +3,14 @@ $(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)_install_libdir=$(build_prefix)/lib/python3/dist-packages
$(package)_dependencies=native_biplist
define $(package)_build_cmds
- python setup.py build
+ python3 setup.py build
endef
define $(package)_stage_cmds
mkdir -p $($(package)_install_libdir) && \
- python setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir)
+ python3 setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir)
endef
diff --git a/depends/packages/native_mac_alias.mk b/depends/packages/native_mac_alias.mk
index 306c835656..e60b99dccc 100644
--- a/depends/packages/native_mac_alias.mk
+++ b/depends/packages/native_mac_alias.mk
@@ -3,13 +3,13 @@ $(package)_version=2.0.7
$(package)_download_path=https://github.com/al45tair/mac_alias/archive/
$(package)_file_name=v$($(package)_version).tar.gz
$(package)_sha256_hash=6f606d3b6bccd2112aeabf1a063f5b5ece87005a5d7e97c8faca23b916e88838
-$(package)_install_libdir=$(build_prefix)/lib/python/dist-packages
+$(package)_install_libdir=$(build_prefix)/lib/python3/dist-packages
define $(package)_build_cmds
- python setup.py build
+ python3 setup.py build
endef
define $(package)_stage_cmds
mkdir -p $($(package)_install_libdir) && \
- python setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir)
+ python3 setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir)
endef
diff --git a/depends/packages/native_protobuf.mk b/depends/packages/native_protobuf.mk
index ce50b366fa..1de8c37d36 100644
--- a/depends/packages/native_protobuf.mk
+++ b/depends/packages/native_protobuf.mk
@@ -5,7 +5,7 @@ $(package)_file_name=protobuf-$($(package)_version).tar.bz2
$(package)_sha256_hash=ee445612d544d885ae240ffbcbf9267faa9f593b7b101f21d58beceb92661910
define $(package)_set_vars
-$(package)_config_opts=--disable-shared
+$(package)_config_opts=--disable-shared --without-zlib
endef
define $(package)_config_cmds
diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk
index 38329d16d7..9a08e30892 100644
--- a/depends/packages/packages.mk
+++ b/depends/packages/packages.mk
@@ -1,4 +1,4 @@
-packages:=boost openssl libevent zeromq
+packages:=boost openssl libevent
qt_native_packages = native_protobuf
qt_packages = qrencode protobuf zlib
@@ -12,6 +12,8 @@ qt_mingw32_packages=qt
wallet_packages=bdb
+zmq_packages=zeromq
+
upnp_packages=miniupnpc
darwin_native_packages = native_biplist native_ds_store native_mac_alias
diff --git a/depends/packages/protobuf.mk b/depends/packages/protobuf.mk
index d201d1183f..52975b14ec 100644
--- a/depends/packages/protobuf.mk
+++ b/depends/packages/protobuf.mk
@@ -30,5 +30,5 @@ define $(package)_stage_cmds
endef
define $(package)_postprocess_cmds
- rm lib/libprotoc.a
+ rm lib/libprotoc.a lib/*.la
endef
diff --git a/depends/packages/qrencode.mk b/depends/packages/qrencode.mk
index 313e4adf2a..56681b52a1 100644
--- a/depends/packages/qrencode.mk
+++ b/depends/packages/qrencode.mk
@@ -24,3 +24,7 @@ endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
+
+define $(package)_postprocess_cmds
+ rm lib/*.la
+endef
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index deebf13e98..23cde9ee6d 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -125,11 +125,11 @@ define $(package)_extract_cmds
echo "$($(package)_qttools_sha256_hash) $($(package)_source_dir)/$($(package)_qttools_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \
$(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \
mkdir qtbase && \
- tar --strip-components=1 -xf $($(package)_source) -C qtbase && \
+ tar --no-same-owner --strip-components=1 -xf $($(package)_source) -C qtbase && \
mkdir qttranslations && \
- tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttranslations_file_name) -C qttranslations && \
+ tar --no-same-owner --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttranslations_file_name) -C qttranslations && \
mkdir qttools && \
- tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttools_file_name) -C qttools
+ tar --no-same-owner --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttools_file_name) -C qttools
endef
define $(package)_preprocess_cmds
@@ -138,7 +138,7 @@ define $(package)_preprocess_cmds
sed -i.old "/updateqm.depends =/d" qttranslations/translations/translations.pro && \
sed -i.old "s/src_plugins.depends = src_sql src_network/src_plugins.depends = src_network/" qtbase/src/src.pro && \
sed -i.old "s|X11/extensions/XIproto.h|X11/X.h|" qtbase/src/plugins/platforms/xcb/qxcbxsettings.cpp && \
- sed -i.old 's/if \[ "$$$$XPLATFORM_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/if \[ "$$$$BUILD_ON_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/' qtbase/configure && \
+ sed -i.old -e 's/if \[ "$$$$XPLATFORM_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/if \[ "$$$$BUILD_ON_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/' -e 's|/bin/pwd|pwd|' qtbase/configure && \
sed -i.old 's/CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0)/CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, kCGMouseButtonLeft)/' qtbase/src/plugins/platforms/cocoa/qcocoacursor.mm && \
mkdir -p qtbase/mkspecs/macx-clang-linux &&\
cp -f qtbase/mkspecs/macx-clang/Info.plist.lib qtbase/mkspecs/macx-clang-linux/ &&\
diff --git a/depends/packages/rapidcheck.mk b/depends/packages/rapidcheck.mk
index 19cf1cae2e..a35e091c80 100644
--- a/depends/packages/rapidcheck.mk
+++ b/depends/packages/rapidcheck.mk
@@ -1,18 +1,17 @@
package=rapidcheck
-$(package)_version=10fc0cb
-$(package)_download_path=https://github.com/MarcoFalke/rapidcheck/archive
-$(package)_file_name=$(package)-$($(package)_version).tar.gz
-$(package)_sha256_hash=9640926223c00af45bce4c7df8b756b5458a89b2ba74cfe3e404467f13ce26df
+$(package)_version=3eb9b4ff69f4ff2d9932e8f852c2b2a61d7c20d3
+$(package)_download_path=https://github.com/emil-e/rapidcheck/archive
+$(package)_file_name=$($(package)_version).tar.gz
+$(package)_sha256_hash=5fbf82755c9a647127e62563be079448ff8b1db9ca80a52a673dd9a88fdb714b
define $(package)_config_cmds
- cmake -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true .
+ cmake -DCMAKE_INSTALL_PREFIX=$($(package)_staging_dir)$(host_prefix) -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true -DRC_INSTALL_ALL_EXTRAS=ON
endef
define $(package)_build_cmds
- $(MAKE) && \
- mkdir -p $($(package)_staging_dir)$(host_prefix)/include && \
- cp -a include/* $($(package)_staging_dir)$(host_prefix)/include/ && \
- cp -a extras/boost_test/include/rapidcheck/* $($(package)_staging_dir)$(host_prefix)/include/rapidcheck/ && \
- mkdir -p $($(package)_staging_dir)$(host_prefix)/lib && \
- cp -a librapidcheck.a $($(package)_staging_dir)$(host_prefix)/lib/
+ $(MAKE) rapidcheck
+endef
+
+define $(package)_stage_cmds
+ $(MAKE) rapidcheck install
endef
diff --git a/depends/packages/xcb_proto.mk b/depends/packages/xcb_proto.mk
index 0c7c958d62..44110394bd 100644
--- a/depends/packages/xcb_proto.mk
+++ b/depends/packages/xcb_proto.mk
@@ -1,6 +1,6 @@
package=xcb_proto
$(package)_version=1.10
-$(package)_download_path=http://xcb.freedesktop.org/dist
+$(package)_download_path=https://xcb.freedesktop.org/dist
$(package)_file_name=xcb-proto-$($(package)_version).tar.bz2
$(package)_sha256_hash=7ef40ddd855b750bc597d2a435da21e55e502a0fefa85b274f2c922800baaf05
diff --git a/depends/packages/xextproto.mk b/depends/packages/xextproto.mk
index 7065237bd5..157b76edf6 100644
--- a/depends/packages/xextproto.mk
+++ b/depends/packages/xextproto.mk
@@ -1,6 +1,6 @@
package=xextproto
$(package)_version=7.3.0
-$(package)_download_path=http://xorg.freedesktop.org/releases/individual/proto
+$(package)_download_path=https://xorg.freedesktop.org/releases/individual/proto
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=f3f4b23ac8db9c3a9e0d8edb591713f3d70ef9c3b175970dd8823dfc92aa5bb0
diff --git a/depends/packages/xproto.mk b/depends/packages/xproto.mk
index 5328ec8481..23ad5ffa10 100644
--- a/depends/packages/xproto.mk
+++ b/depends/packages/xproto.mk
@@ -1,6 +1,6 @@
package=xproto
$(package)_version=7.0.26
-$(package)_download_path=http://xorg.freedesktop.org/releases/individual/proto
+$(package)_download_path=https://xorg.freedesktop.org/releases/individual/proto
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=636162c1759805a5a0114a369dffdeccb8af8c859ef6e1445f26a4e6e046514f
diff --git a/depends/packages/xtrans.mk b/depends/packages/xtrans.mk
index c313b1f609..1993ff8344 100644
--- a/depends/packages/xtrans.mk
+++ b/depends/packages/xtrans.mk
@@ -1,12 +1,12 @@
package=xtrans
$(package)_version=1.3.4
-$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/
+$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=054d4ee3efd52508c753e9f7bc655ef185a29bd2850dd9e2fc2ccc33544f583a
$(package)_dependencies=
define $(package)_set_vars
-$(package)_config_opts_linux=--with-pic --disable-static
+$(package)_config_opts_linux=--with-pic --disable-shared
endef
define $(package)_preprocess_cmds
diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk
index dfbc50580c..e69a37e093 100644
--- a/depends/packages/zeromq.mk
+++ b/depends/packages/zeromq.mk
@@ -31,5 +31,5 @@ endef
define $(package)_postprocess_cmds
sed -i.old "s/ -lstdc++//" lib/pkgconfig/libzmq.pc && \
- rm -rf bin share
+ rm -rf bin share lib/*.la
endef
diff --git a/depends/packages/zlib.mk b/depends/packages/zlib.mk
index 589490800f..1600b11a01 100644
--- a/depends/packages/zlib.mk
+++ b/depends/packages/zlib.mk
@@ -1,6 +1,6 @@
package=zlib
$(package)_version=1.2.11
-$(package)_download_path=http://www.zlib.net
+$(package)_download_path=https://www.zlib.net
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
diff --git a/doc/JSON-RPC-interface.md b/doc/JSON-RPC-interface.md
index 982afd5d56..a0cfe84a3e 100644
--- a/doc/JSON-RPC-interface.md
+++ b/doc/JSON-RPC-interface.md
@@ -5,6 +5,18 @@ The headless daemon `bitcoind` has the JSON-RPC API enabled by default, the GUI
option. In the GUI it is possible to execute RPC methods in the Debug Console
Dialog.
+## Versioning
+
+The RPC interface might change from one major version of Bitcoin Core to the
+next. This makes the RPC interface implicitly versioned on the major version.
+The version tuple can be retrieved by e.g. the `getnetworkinfo` RPC in
+`version`.
+
+Usually deprecated features can be re-enabled during the grace-period of one
+major version via the `-deprecatedrpc=` command line option. The release notes
+of a new major release come with detailed instructions on what RPC features
+were deprecated and how to re-enable them temporarily.
+
## Security
The RPC interface allows other programs to control Bitcoin Core,
diff --git a/doc/README.md b/doc/README.md
index 51950d4a13..b4fa933c8e 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -51,12 +51,12 @@ Development
The Bitcoin repo's [root README](/README.md) contains relevant information on the development process and automated testing.
- [Developer Notes](developer-notes.md)
+- [Productivity Notes](productivity.md)
- [Release Notes](release-notes.md)
- [Release Process](release-process.md)
- [Source Code Documentation (External Link)](https://dev.visucore.com/bitcoin/doxygen/)
- [Translation Process](translation_process.md)
- [Translation Strings Policy](translation_strings_policy.md)
-- [Travis CI](travis-ci.md)
- [JSON-RPC Interface](JSON-RPC-interface.md)
- [Unauthenticated REST Interface](REST-interface.md)
- [Shared Libraries](shared-libraries.md)
diff --git a/doc/REST-interface.md b/doc/REST-interface.md
index d21df36130..c96871ab5f 100644
--- a/doc/REST-interface.md
+++ b/doc/REST-interface.md
@@ -20,7 +20,8 @@ Supported API
Given a transaction hash: returns a transaction in binary, hex-encoded binary, or JSON formats.
-For full TX query capability, one must enable the transaction index via "txindex=1" command line / configuration option.
+By default, this endpoint will only search the mempool.
+To query for a confirmed transaction, enable the transaction index via "txindex=1" command line / configuration option.
#### Blocks
`GET /rest/block/<BLOCK-HASH>.<bin|hex|json>`
@@ -100,6 +101,7 @@ $ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff76
Returns various information about the TX mempool.
Only supports JSON as output format.
+* loaded : (boolean) if the mempool is fully loaded
* size : (numeric) the number of transactions in the TX mempool
* bytes : (numeric) size of the TX mempool in bytes
* usage : (numeric) total TX mempool memory usage
diff --git a/doc/bips.md b/doc/bips.md
index 76edc94c29..eb24ce6f66 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -1,4 +1,4 @@
-BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.17.0**):
+BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.18.0**):
* [`BIP 9`](https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki): The changes allowing multiple soft-forks to be deployed in parallel have been implemented since **v0.12.1** ([PR #7575](https://github.com/bitcoin/bitcoin/pull/7575))
* [`BIP 11`](https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki): Multisig outputs are standard since **v0.6.0** ([PR #669](https://github.com/bitcoin/bitcoin/pull/669)).
@@ -15,11 +15,11 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.17.0**):
* [`BIP 35`](https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki): The 'mempool' protocol message (and the protocol version bump to 60002) has been implemented since **v0.7.0** ([PR #1641](https://github.com/bitcoin/bitcoin/pull/1641)).
* [`BIP 37`](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki): The bloom filtering for transaction relaying, partial Merkle trees for blocks, and the protocol version bump to 70001 (enabling low-bandwidth SPV clients) has been implemented since **v0.8.0** ([PR #1795](https://github.com/bitcoin/bitcoin/pull/1795)).
* [`BIP 42`](https://github.com/bitcoin/bips/blob/master/bip-0042.mediawiki): The bug that would have caused the subsidy schedule to resume after block 13440000 was fixed in **v0.9.2** ([PR #3842](https://github.com/bitcoin/bitcoin/pull/3842)).
-* [`BIP 61`](https://github.com/bitcoin/bips/blob/master/bip-0061.mediawiki): The 'reject' protocol message (and the protocol version bump to 70002) was added in **v0.9.0** ([PR #3185](https://github.com/bitcoin/bitcoin/pull/3185)). Starting *v0.17.0*, whether to send reject messages can be configured with the `-enablebip61` option.
+* [`BIP 61`](https://github.com/bitcoin/bips/blob/master/bip-0061.mediawiki): The 'reject' protocol message (and the protocol version bump to 70002) was added in **v0.9.0** ([PR #3185](https://github.com/bitcoin/bitcoin/pull/3185)). Starting **v0.17.0**, whether to send reject messages can be configured with the `-enablebip61` option, and support is deprecated as of **v0.18.0**.
* [`BIP 65`](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki): The CHECKLOCKTIMEVERIFY softfork was merged in **v0.12.0** ([PR #6351](https://github.com/bitcoin/bitcoin/pull/6351)), and backported to **v0.11.2** and **v0.10.4**. Mempool-only CLTV was added in [PR #6124](https://github.com/bitcoin/bitcoin/pull/6124).
* [`BIP 66`](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki): The strict DER rules and associated version 3 blocks have been implemented since **v0.10.0** ([PR #5713](https://github.com/bitcoin/bitcoin/pull/5713)).
* [`BIP 68`](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki): Sequence locks have been implemented as of **v0.12.1** ([PR #7184](https://github.com/bitcoin/bitcoin/pull/7184)), and have been activated since *block 419328*.
-* [`BIP 70`](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki) [`71`](https://github.com/bitcoin/bips/blob/master/bip-0071.mediawiki) [`72`](https://github.com/bitcoin/bips/blob/master/bip-0072.mediawiki): Payment Protocol support has been available in Bitcoin Core GUI since **v0.9.0** ([PR #5216](https://github.com/bitcoin/bitcoin/pull/5216)).
+* [`BIP 70`](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki) [`71`](https://github.com/bitcoin/bips/blob/master/bip-0071.mediawiki) [`72`](https://github.com/bitcoin/bips/blob/master/bip-0072.mediawiki): Payment Protocol support has been available in Bitcoin Core GUI since **v0.9.0** ([PR #5216](https://github.com/bitcoin/bitcoin/pull/5216)). Support can be optionally disabled at build time since **v0.18.0** ([PR 14451](https://github.com/bitcoin/bitcoin/pull/14451)).
* [`BIP 90`](https://github.com/bitcoin/bips/blob/master/bip-0090.mediawiki): Trigger mechanism for activation of BIPs 34, 65, and 66 has been simplified to block height checks since **v0.14.0** ([PR #8391](https://github.com/bitcoin/bitcoin/pull/8391)).
* [`BIP 111`](https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki): `NODE_BLOOM` service bit added, and enforced for all peer versions as of **v0.13.0** ([PR #6579](https://github.com/bitcoin/bitcoin/pull/6579) and [PR #6641](https://github.com/bitcoin/bitcoin/pull/6641)).
* [`BIP 112`](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki): The CHECKSEQUENCEVERIFY opcode has been implemented since **v0.12.1** ([PR #7524](https://github.com/bitcoin/bitcoin/pull/7524)) and has been activated since *block 419328*.
@@ -33,7 +33,7 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.17.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 11740](https://github.com/bitcoin/bitcoin/pull/11740)).
+* [`BIP 159`](https://github.com/bitcoin/bips/blob/master/bip-0159.mediawiki): The NODE_NETWORK_LIMITED service bit is signalled as of **v0.16.0** ([PR 11740](https://github.com/bitcoin/bitcoin/pull/11740)), and such nodes are connected to as of **v0.17.0** ([PR 10387](https://github.com/bitcoin/bitcoin/pull/10387)).
* [`BIP 173`](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki): Bech32 addresses for native Segregated Witness outputs are supported as of **v0.16.0** ([PR 11167](https://github.com/bitcoin/bitcoin/pull/11167)).
* [`BIP 174`](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki): RPCs to operate on Partially Signed Bitcoin Transactions (PSBT) are present as of **v0.17.0** ([PR 13557](https://github.com/bitcoin/bitcoin/pull/13557)).
* [`BIP 176`](https://github.com/bitcoin/bips/blob/master/bip-0176.mediawiki): Bits Denomination [QT only] is supported as of **v0.16.0** ([PR 12035](https://github.com/bitcoin/bitcoin/pull/12035)).
diff --git a/doc/build-freebsd.md b/doc/build-freebsd.md
index 70f5dfc882..d22b6e8383 100644
--- a/doc/build-freebsd.md
+++ b/doc/build-freebsd.md
@@ -1,6 +1,6 @@
FreeBSD build guide
======================
-(updated for FreeBSD 11.1)
+(updated for FreeBSD 12.0)
This guide describes how to build bitcoind and command-line utilities on FreeBSD.
@@ -10,55 +10,51 @@ This guide does not contain instructions for building the GUI.
You will need the following dependencies, which can be installed as root via pkg:
-```
+```shell
pkg install autoconf automake boost-libs git gmake libevent libtool openssl pkgconf
+
+git clone https://github.com/bitcoin/bitcoin.git
```
In order to run the test suite (recommended), you will need to have Python 3 installed:
-```
+```shell
pkg install python3
```
-For the wallet (optional):
-```
-./contrib/install_db4.sh `pwd`
-export BDB_PREFIX="$PWD/db4"
-```
-
See [dependencies.md](dependencies.md) for a complete overview.
-Download the source code:
-```
-git clone https://github.com/bitcoin/bitcoin
+### Building BerkeleyDB
+
+BerkeleyDB is only necessary for the wallet functionality. To skip this, pass
+`--disable-wallet` to `./configure` and skip to the next section.
+
+```shell
+./contrib/install_db4.sh `pwd`
+export BDB_PREFIX="$PWD/db4"
```
## Building Bitcoin Core
**Important**: Use `gmake` (the non-GNU `make` will exit with an error):
-```
+With wallet:
+```shell
./autogen.sh
-
-./configure # to build with wallet OR
-./configure --disable-wallet # to build without wallet
+./configure --with-gui=no \
+ BDB_LIBS="-L${BDB_PREFIX}/lib -ldb_cxx-4.8" \
+ BDB_CFLAGS="-I${BDB_PREFIX}/include"
```
-followed by either:
-
-```
-gmake
+Without wallet:
+```shell
+./autogen.sh
+./configure --with-gui=no --disable-wallet
```
-to build without testing, or
+followed by:
+```shell
+gmake # use -jX here for parallelism
+gmake check # Run tests if Python 3 is available
```
-gmake check
-```
-
-to also run the test suite (recommended, if Python 3 is installed).
-
-*Note on debugging*: The version of `gdb` installed by default is [ancient and considered harmful](https://wiki.freebsd.org/GdbRetirement).
-It is not suitable for debugging a multi-threaded C++ program, not even for getting backtraces. Please install the package `gdb` and
-use the versioned gdb command (e.g. `gdb7111`).
-
diff --git a/doc/build-osx.md b/doc/build-osx.md
index 119896dc67..d28a3d97aa 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -82,6 +82,8 @@ Bitcoin Core is now available at `./src/bitcoind`
Before running, you may create an empty configuration file:
+ mkdir -p "/Users/${USER}/Library/Application Support/Bitcoin"
+
touch "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf"
chmod 600 "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf"
diff --git a/doc/build-unix.md b/doc/build-unix.md
index 522e3069ce..da65bc347a 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -78,19 +78,13 @@ Now, you can either build from self-compiled [depends](/depends/README.md) or in
BerkeleyDB is required for the wallet.
-**For Ubuntu only:** db4.8 packages are available [here](https://launchpad.net/~bitcoin/+archive/bitcoin).
-You can add the repository and install using the following commands:
-
- sudo apt-get install software-properties-common
- sudo add-apt-repository ppa:bitcoin/bitcoin
- sudo apt-get update
- sudo apt-get install libdb4.8-dev libdb4.8++-dev
-
Ubuntu and Debian have their own libdb-dev and libdb++-dev packages, but these will install
BerkeleyDB 5.1 or later. This will break binary wallet compatibility with the distributed executables, which
are based on BerkeleyDB 4.8. If you do not care about wallet compatibility,
pass `--with-incompatible-bdb` to configure.
+Otherwise, you can build from self-compiled `depends` (see above).
+
To build Bitcoin Core without wallet, see [*Disable-wallet mode*](/doc/build-unix.md#disable-wallet-mode)
diff --git a/doc/build-windows.md b/doc/build-windows.md
index 9641e0d3fd..5ca9f98475 100644
--- a/doc/build-windows.md
+++ b/doc/build-windows.md
@@ -71,6 +71,11 @@ If you want to build the windows installer with `make deploy` you need [NSIS](ht
sudo apt install nsis
+Acquire the source in the usual way:
+
+ git clone https://github.com/bitcoin/bitcoin.git
+ cd bitcoin
+
## Building for 64-bit Windows
The first step is to install the mingw-w64 cross-compilation tool chain:
@@ -87,11 +92,7 @@ Note that for WSL the Bitcoin Core source path MUST be somewhere in the default
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 is located directly on the host Windows file system to perform the build.
-Acquire the source in the usual way:
-
- git clone https://github.com/bitcoin/bitcoin.git
-
-Once the source code is ready the build steps are below:
+Build using:
PATH=$(echo "$PATH" | sed -e 's/:\/mnt.*//g') # strip out problematic Windows %PATH% imported var
cd depends
@@ -101,34 +102,6 @@ Once the source code is ready the build steps are below:
CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site ./configure --prefix=/
make
-## Building for 32-bit Windows
-
-To build executables for Windows 32-bit, install the following dependencies:
-
- sudo apt install g++-mingw-w64-i686 mingw-w64-i686-dev
-
-For Ubuntu Bionic 18.04 and Windows Subsystem for Linux <sup>[1](#footnote1)</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.
-
-Acquire the source in the usual way:
-
- git clone https://github.com/bitcoin/bitcoin.git
-
-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 ..
- ./autogen.sh # not required when building from tarball
- CONFIG_SITE=$PWD/depends/i686-w64-mingw32/share/config.site ./configure --prefix=/
- make
-
## Depends system
For further documentation on the depends system see [README.md](../depends/README.md) in the depends directory.
diff --git a/doc/dependencies.md b/doc/dependencies.md
index b833e9151f..4c0e8d2567 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -6,7 +6,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
| Dependency | Version used | Minimum required | CVEs | Shared | [Bundled Qt library](https://doc.qt.io/qt-5/configure-options.html#third-party-libraries) |
| --- | --- | --- | --- | --- | --- |
| Berkeley DB | [4.8.30](https://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.x | No | | |
-| Boost | [1.64.0](https://www.boost.org/users/download/) | [1.47.0](https://github.com/bitcoin/bitcoin/pull/8920) | No | | |
+| Boost | [1.70.0](https://www.boost.org/users/download/) | [1.47.0](https://github.com/bitcoin/bitcoin/pull/8920) | No | | |
| Clang | | [3.3+](https://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.6](https://libexpat.github.io/) | | No | Yes | |
@@ -17,14 +17,30 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
| 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#L65) |
| libpng | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L64) |
+| librsvg | | | | | |
| MiniUPnPc | [2.0.20180203](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#L66) |
| protobuf | [2.6.1](https://github.com/google/protobuf/releases) | | No | | |
-| Python (tests) | | [3.4](https://www.python.org/downloads) | | | |
+| Python (tests) | | [3.5](https://www.python.org/downloads) | | | |
| qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | |
-| Qt | [5.9.7](https://download.qt.io/official_releases/qt/) | [5.2](https://github.com/bitcoin/bitcoin/pull/14725) | No | | |
+| Qt | [5.9.7](https://download.qt.io/official_releases/qt/) | [5.5.1](https://github.com/bitcoin/bitcoin/issues/13478) | No | | |
| XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L87) (Linux only) |
| xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L86) (Linux only) |
| ZeroMQ | [4.3.1](https://github.com/zeromq/libzmq/releases) | 4.0.0 | No | | |
| zlib | [1.2.11](https://zlib.net/) | | | | No |
+
+Controlling dependencies
+------------------------
+Some dependencies are not needed in all configurations. The following are some factors that affect the dependency list.
+
+#### Options passed to `./configure`
+* MiniUPnPc is not needed with `--with-miniupnpc=no`.
+* Berkeley DB is not needed with `--disable-wallet`.
+* protobuf is not needed with `--disable-bip70`.
+* Qt is not needed with `--without-gui`.
+* If the qrencode dependency is absent, QR support won't be added. To force an error when that happens, pass `--with-qrencode`.
+* ZeroMQ is needed only with the `--with-zmq` option.
+
+#### Other
+* librsvg is only needed if you need to run `make deploy` on (cross-compilation to) macOS.
diff --git a/doc/descriptors.md b/doc/descriptors.md
index de4d4e574f..dbdac2c5b6 100644
--- a/doc/descriptors.md
+++ b/doc/descriptors.md
@@ -1,11 +1,18 @@
# Support for Output Descriptors in Bitcoin Core
-Since Bitcoin Core v0.17, there is support for Output Descriptors in the
-`scantxoutset` RPC call. This is a simple language which can be used to
-describe collections of output scripts.
-
-This document describes the language. For the specifics on usage for scanning
-the UTXO set, see the `scantxoutset` RPC help.
+Since Bitcoin Core v0.17, there is support for Output Descriptors. This is a
+simple language which can be used to describe collections of output scripts.
+Supporting RPCs are:
+- `scantxoutset` takes as input descriptors to scan for, and also reports
+ specialized descriptors for the matching UTXOs.
+- `getdescriptorinfo` analyzes a descriptor, and reports a canonicalized version
+ with checksum added.
+- `deriveaddresses` takes as input a descriptor and computes the corresponding
+ addresses.
+- `listunspent` outputs a specialized descriptor for the reported unspent outputs.
+
+This document describes the language. For the specifics on usage, see the RPC
+documentation for the functions mentioned above.
## Features
@@ -39,7 +46,7 @@ Output descriptors currently support:
## Reference
-Descriptors consist of several types of expressions. The top level expression is always a `SCRIPT`.
+Descriptors consist of several types of expressions. The top level expression is either a `SCRIPT`, or `SCRIPT#CHECKSUM` where `CHECKSUM` is an 8-character alphanumeric descriptor checksum.
`SCRIPT` expressions:
- `sh(SCRIPT)` (top level only): P2SH embed the argument.
@@ -169,3 +176,20 @@ existing Bitcoin Core wallets, a convenience function `combo` is
provided, which takes as input a public key, and describes a set of P2PK,
P2PKH, P2WPKH, and P2SH-P2WPH scripts for that key. In case the key is
uncompressed, the set only includes P2PK and P2PKH scripts.
+
+### Checksums
+
+Descriptors can optionally be suffixed with a checksum to protect against
+typos or copy-paste errors.
+
+These checksums consist of 8 alphanumeric characters. As long as errors are
+restricted to substituting characters in `0123456789()[],'/*abcdefgh@:$%{}`
+for others in that set and changes in letter case, up to 4 errors will always
+be detected in descriptors up to 501 characters, and up to 3 errors in longer
+ones. For larger numbers of errors, or other types of errors, there is a
+roughly 1 in a trillion chance of not detecting the errors.
+
+All RPCs in Bitcoin Core will include the checksum in their output. Only
+certain RPCs require checksums on input, including `deriveaddress` and
+`importmulti`. The checksum for a descriptor without one can be computed
+using the `getdescriptorinfo` RPC.
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index ec29310ffe..f4fc55427d 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -17,6 +17,7 @@ Developer Notes
- [DEBUG_LOCKORDER](#debug_lockorder)
- [Valgrind suppressions file](#valgrind-suppressions-file)
- [Compiling for test coverage](#compiling-for-test-coverage)
+ - [Performance profiling with perf](#performance-profiling-with-perf)
- [Locking/mutex usage notes](#lockingmutex-usage-notes)
- [Threads](#threads)
- [Ignoring IDE/editor files](#ignoring-ideeditor-files)
@@ -143,7 +144,7 @@ For example, to describe a function use:
*/
bool function(int arg1, const char *arg2)
```
-A complete list of `@xxx` commands can be found at http://www.stack.nl/~dimitri/doxygen/manual/commands.html.
+A complete list of `@xxx` commands can be found at http://www.doxygen.nl/manual/commands.html.
As Doxygen recognizes the comments by the delimiters (`/**` and `*/` in this case), you don't
*need* to provide any commands for a comment to be valid; just a description text is fine.
@@ -184,7 +185,7 @@ Not OK (used plenty in the current source, but not picked up):
//
```
-A full list of comment syntaxes picked up by Doxygen can be found at https://www.stack.nl/~dimitri/doxygen/manual/docblocks.html,
+A full list of comment syntaxes picked up by Doxygen can be found at http://www.doxygen.nl/manual/docblocks.html,
but the above styles are favored.
Documentation can be generated with `make docs` and cleaned up with `make clean-docs`. The resulting files are located in `doc/doxygen/html`; open `index.html` to view the homepage.
@@ -265,6 +266,51 @@ make cov
# A coverage report will now be accessible at `./test_bitcoin.coverage/index.html`.
```
+### Performance profiling with perf
+
+Profiling is a good way to get a precise idea of where time is being spent in
+code. One tool for doing profiling on Linux platforms is called
+[`perf`](http://www.brendangregg.com/perf.html), and has been integrated into
+the functional test framework. Perf can observe a running process and sample
+(at some frequency) where its execution is.
+
+Perf installation is contingent on which kernel version you're running; see
+[this StackExchange
+thread](https://askubuntu.com/questions/50145/how-to-install-perf-monitoring-tool)
+for specific instructions.
+
+Certain kernel parameters may need to be set for perf to be able to inspect the
+running process's stack.
+
+```sh
+$ sudo sysctl -w kernel.perf_event_paranoid=-1
+$ sudo sysctl -w kernel.kptr_restrict=0
+```
+
+Make sure you [understand the security
+trade-offs](https://lwn.net/Articles/420403/) of setting these kernel
+parameters.
+
+To profile a running bitcoind process for 60 seconds, you could use an
+invocation of `perf record` like this:
+
+```sh
+$ perf record \
+ -g --call-graph dwarf --per-thread -F 140 \
+ -p `pgrep bitcoind` -- sleep 60
+```
+
+You could then analyze the results by running
+
+```sh
+perf report --stdio | c++filt | less
+```
+
+or using a graphical tool like [Hotspot](https://github.com/KDAB/hotspot).
+
+See the functional test documentation for how to invoke perf within tests.
+
+
**Sanitizers**
Bitcoin Core can be compiled with various "sanitizers" enabled, which add
@@ -432,6 +478,14 @@ Wallet
General C++
-------------
+For general C++ guidelines, you may refer to the [C++ Core
+Guidelines](https://isocpp.github.io/CppCoreGuidelines/).
+
+Common misconceptions are clarified in those sections:
+
+- Passing (non-)fundamental types in the [C++ Core
+ Guideline](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-conventional)
+
- Assertions should not have side-effects
- *Rationale*: Even though the source code is set to refuse to compile
@@ -574,8 +628,8 @@ class AddressBookPage
Mode m_mode;
}
-AddressBookPage::AddressBookPage(Mode _mode) :
- m_mode(_mode)
+AddressBookPage::AddressBookPage(Mode _mode)
+ : m_mode(_mode)
...
```
@@ -798,54 +852,6 @@ would be to revert the upstream fix before applying the updates to Bitcoin's
copy of LevelDB. In general you should be wary of any upstream changes affecting
what data is returned from LevelDB queries.
-Git and GitHub tips
----------------------
-
-- For resolving merge/rebase conflicts, it can be useful to enable diff3 style using
- `git config merge.conflictstyle diff3`. Instead of
-
- <<<
- yours
- ===
- theirs
- >>>
-
- you will see
-
- <<<
- yours
- |||
- original
- ===
- theirs
- >>>
-
- This may make it much clearer what caused the conflict. In this style, you can often just look
- at what changed between *original* and *theirs*, and mechanically apply that to *yours* (or the other way around).
-
-- When reviewing patches which change indentation in C++ files, use `git diff -w` and `git show -w`. This makes
- the diff algorithm ignore whitespace changes. This feature is also available on github.com, by adding `?w=1`
- at the end of any URL which shows a diff.
-
-- When reviewing patches that change symbol names in many places, use `git diff --word-diff`. This will instead
- of showing the patch as deleted/added *lines*, show deleted/added *words*.
-
-- When reviewing patches that move code around, try using
- `git diff --patience commit~:old/file.cpp commit:new/file/name.cpp`, and ignoring everything except the
- moved body of code which should show up as neither `+` or `-` lines. In case it was not a pure move, this may
- even work when combined with the `-w` or `--word-diff` options described above.
-
-- When looking at other's pull requests, it may make sense to add the following section to your `.git/config`
- file:
-
- [remote "upstream-pull"]
- fetch = +refs/pull/*:refs/remotes/upstream-pull/*
- url = git@github.com:bitcoin/bitcoin.git
-
- This will add an `upstream-pull` remote to your git repository, which can be fetched using `git fetch --all`
- 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
--------------
@@ -954,8 +960,7 @@ 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 `signrawtransaction` that differ in behavior
- based on presence of a wallet.
+ introduce new methods 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
diff --git a/doc/fuzzing.md b/doc/fuzzing.md
index 08b73d3b3c..f9221dde5b 100644
--- a/doc/fuzzing.md
+++ b/doc/fuzzing.md
@@ -5,6 +5,29 @@ A special test harness in `src/test/fuzz/` is provided for each fuzz target to
provide an easy entry point for fuzzers and the like. In this document we'll
describe how to use it with AFL and libFuzzer.
+## Preparing fuzzing
+
+AFL needs an input directory with examples, and an output directory where it
+will place examples that it found. These can be anywhere in the file system,
+we'll define environment variables to make it easy to reference them.
+
+libFuzzer will use the input directory as output directory.
+
+Extract the example seeds (or other starting inputs) into the inputs
+directory before starting fuzzing.
+
+```
+git clone https://github.com/bitcoin-core/qa-assets
+export DIR_FUZZ_IN=$PWD/qa-assets/fuzz_seed_corpus
+```
+
+Only for AFL:
+
+```
+mkdir outputs
+export AFLOUT=$PWD/outputs
+```
+
## AFL
### Building AFL
@@ -23,7 +46,7 @@ export AFLPATH=$PWD
To build Bitcoin Core using AFL instrumentation (this assumes that the
`AFLPATH` was set as above):
```
-./configure --disable-ccache --disable-shared --enable-tests --enable-fuzz CC=${AFLPATH}/afl-gcc CXX=${AFLPATH}/afl-g++
+./configure --disable-ccache --disable-shared --enable-tests --enable-fuzz --disable-wallet --disable-bench --with-utils=no --with-daemon=no --with-libs=no --with-gui=no CC=${AFLPATH}/afl-gcc CXX=${AFLPATH}/afl-g++
export AFL_HARDEN=1
cd src/
make
@@ -39,31 +62,14 @@ binary will be instrumented in such a way that the AFL
features "persistent mode" and "deferred forkserver" can be used. See
https://github.com/mcarpenter/afl/tree/master/llvm_mode for details.
-### Preparing fuzzing
-
-AFL needs an input directory with examples, and an output directory where it
-will place examples that it found. These can be anywhere in the file system,
-we'll define environment variables to make it easy to reference them.
-
-```
-mkdir inputs
-AFLIN=$PWD/inputs
-mkdir outputs
-AFLOUT=$PWD/outputs
-```
-
-Example inputs are available from:
-
-- https://download.visucore.com/bitcoin/bitcoin_fuzzy_in.tar.xz
-- http://strateman.ninja/fuzzing.tar.xz
-
-Extract these (or other starting inputs) into the `inputs` directory before starting fuzzing.
-
### Fuzzing
To start the actual fuzzing use:
+
```
-$AFLPATH/afl-fuzz -i ${AFLIN} -o ${AFLOUT} -m52 -- test/fuzz/fuzz_target_foo
+export FUZZ_TARGET=fuzz_target_foo # Pick a fuzz_target
+mkdir ${AFLOUT}/${FUZZ_TARGET}
+$AFLPATH/afl-fuzz -i ${DIR_FUZZ_IN}/${FUZZ_TARGET} -o ${AFLOUT}/${FUZZ_TARGET} -m52 -- test/fuzz/${FUZZ_TARGET}
```
You may have to change a few kernel parameters to test optimally - `afl-fuzz`
@@ -74,10 +80,10 @@ will print an error and suggestion if so.
A recent version of `clang`, the address sanitizer and libFuzzer is needed (all
found in the `compiler-rt` runtime libraries package).
-To build the `test/test_bitcoin_fuzzy` executable run
+To build all fuzz targets with libFuzzer, run
```
-./configure --disable-ccache --enable-fuzz --with-sanitizers=fuzzer,address CC=clang CXX=clang++
+./configure --disable-ccache --disable-wallet --disable-bench --with-utils=no --with-daemon=no --with-libs=no --with-gui=no --enable-fuzz --with-sanitizers=fuzzer,address CC=clang CXX=clang++
make
```
@@ -86,3 +92,6 @@ interchangeably between libFuzzer and AFL.
See https://llvm.org/docs/LibFuzzer.html#running on how to run the libFuzzer
instrumented executable.
+
+Alternatively run the script in `./test/fuzz/test_runner.py` and provide it
+with the `${DIR_FUZZ_IN}` created earlier.
diff --git a/doc/init.md b/doc/init.md
index 5778b09d05..a6c9bb94d8 100644
--- a/doc/init.md
+++ b/doc/init.md
@@ -56,7 +56,7 @@ All three configurations assume several paths that might need to be adjusted.
Binary: `/usr/bin/bitcoind`
Configuration file: `/etc/bitcoin/bitcoin.conf`
Data directory: `/var/lib/bitcoind`
-PID file: `/var/run/bitcoind/bitcoind.pid` (OpenRC and Upstart) or `/var/lib/bitcoind/bitcoind.pid` (systemd)
+PID file: `/var/run/bitcoind/bitcoind.pid` (OpenRC and Upstart) or `/run/bitcoind/bitcoind.pid` (systemd)
Lock file: `/var/lock/subsys/bitcoind` (CentOS)
The configuration file, PID directory (if applicable) and data directory
@@ -65,6 +65,22 @@ reasons to make the configuration file and data directory only readable by the
bitcoin user and group. Access to bitcoin-cli and other bitcoind rpc clients
can then be controlled by group membership.
+NOTE: When using the systemd .service file, the creation of the aforementioned
+directories and the setting of their permissions is automatically handled by
+systemd. Directories are given a permission of 710, giving the bitcoin group
+access to files under it _if_ the files themselves give permission to the
+bitcoin group to do so (e.g. when `-sysperms` is specified). This does not allow
+for the listing of files under the directory.
+
+NOTE: It is not currently possible to override `datadir` in
+`/etc/bitcoin/bitcoin.conf` with the current systemd, OpenRC, and Upstart init
+files out-of-the-box. This is because the command line options specified in the
+init files take precedence over the configurations in
+`/etc/bitcoin/bitcoin.conf`. However, some init systems have their own
+configuration mechanisms that would allow for overriding the command line
+options specified in the init files (e.g. setting `BITCOIND_DATADIR` for
+OpenRC).
+
### macOS
Binary: `/usr/local/bin/bitcoind`
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 9b36319e64..edbc0911a1 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -15,3 +15,9 @@ endif
if BUILD_BITCOIN_TX
dist_man1_MANS+=bitcoin-tx.1
endif
+
+if ENABLE_WALLET
+if BUILD_BITCOIN_WALLET
+ dist_man1_MANS+=bitcoin-wallet.1
+endif
+endif
diff --git a/doc/man/bitcoin-cli.1 b/doc/man/bitcoin-cli.1
index 553addfa84..95c1d24dff 100644
--- a/doc/man/bitcoin-cli.1
+++ b/doc/man/bitcoin-cli.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
-.TH BITCOIN-CLI "1" "December 2018" "bitcoin-cli v0.17.99.0" "User Commands"
+.TH BITCOIN-CLI "1" "February 2019" "bitcoin-cli v0.17.99.0" "User Commands"
.SH NAME
bitcoin-cli \- manual page for bitcoin-cli v0.17.99.0
.SH SYNOPSIS
@@ -20,7 +20,7 @@ Bitcoin Core RPC client version v0.17.99.0
.HP
\-?
.IP
-This help message
+Print this help message and exit
.HP
\fB\-conf=\fR<file>
.IP
@@ -104,7 +104,7 @@ Chain selection options:
.IP
Use the test chain
.SH COPYRIGHT
-Copyright (C) 2009-2018 The Bitcoin Core developers
+Copyright (C) 2009-2019 The Bitcoin Core developers
Please contribute if you find Bitcoin Core useful. Visit
<https://bitcoincore.org> for further information about the software.
diff --git a/doc/man/bitcoin-qt.1 b/doc/man/bitcoin-qt.1
index 1d87acd3de..1e8443b1d3 100644
--- a/doc/man/bitcoin-qt.1
+++ b/doc/man/bitcoin-qt.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
-.TH BITCOIN-QT "1" "December 2018" "bitcoin-qt v0.17.99.0" "User Commands"
+.TH BITCOIN-QT "1" "February 2019" "bitcoin-qt v0.17.99.0" "User Commands"
.SH NAME
bitcoin-qt \- manual page for bitcoin-qt v0.17.99.0
.SH SYNOPSIS
@@ -56,7 +56,7 @@ Specify data directory
.HP
\fB\-dbcache=\fR<n>
.IP
-Set database cache size in megabytes (4 to 16384, default: 450)
+Set database cache size in MiB (4 to 16384, default: 450)
.HP
\fB\-debuglogfile=\fR<file>
.IP
@@ -488,9 +488,6 @@ Relay and mine data carrier transactions (default: 1)
Maximum size of data in data carrier transactions we relay and mine
(default: 83)
.HP
-\fB\-mempoolreplacement\fR
-.IP
-Enable transaction replacement in the memory pool (default: 1)
.HP
\fB\-minrelaytxfee=\fR<amt>
.IP
@@ -500,7 +497,7 @@ mining and transaction creation (default: 0.00001)
\fB\-whitelistforcerelay\fR
.IP
Force relay of transactions from whitelisted peers even if they violate
-local relay policy (default: 1)
+local relay policy (default: 0)
.HP
\fB\-whitelistrelay\fR
.IP
@@ -606,7 +603,7 @@ Set SSL root certificates for payment request (default: \fB\-system\-\fR)
.IP
Show splash screen on startup (default: 1)
.SH COPYRIGHT
-Copyright (C) 2009-2018 The Bitcoin Core developers
+Copyright (C) 2009-2019 The Bitcoin Core developers
Please contribute if you find Bitcoin Core useful. Visit
<https://bitcoincore.org> for further information about the software.
diff --git a/doc/man/bitcoin-tx.1 b/doc/man/bitcoin-tx.1
index f16c68ca14..6b6071d9b7 100644
--- a/doc/man/bitcoin-tx.1
+++ b/doc/man/bitcoin-tx.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
-.TH BITCOIN-TX "1" "December 2018" "bitcoin-tx v0.17.99.0" "User Commands"
+.TH BITCOIN-TX "1" "February 2019" "bitcoin-tx v0.17.99.0" "User Commands"
.SH NAME
bitcoin-tx \- manual page for bitcoin-tx v0.17.99.0
.SH SYNOPSIS
@@ -14,7 +14,7 @@ Bitcoin Core bitcoin\-tx utility version v0.17.99.0
.HP
\-?
.IP
-This help message
+Print this help message and exit
.HP
\fB\-create\fR
.IP
@@ -92,7 +92,7 @@ sign=SIGHASH\-FLAGS
.IP
Add zero or more signatures to transaction. This command requires JSON
registers:prevtxs=JSON object, privatekeys=JSON object. See
-signrawtransaction docs for format of sighash flags, JSON
+signrawtransactionwithkey docs for format of sighash flags, JSON
objects.
.PP
Register Commands:
@@ -105,7 +105,7 @@ set=NAME:JSON\-STRING
.IP
Set register NAME to given JSON\-STRING
.SH COPYRIGHT
-Copyright (C) 2009-2018 The Bitcoin Core developers
+Copyright (C) 2009-2019 The Bitcoin Core developers
Please contribute if you find Bitcoin Core useful. Visit
<https://bitcoincore.org> for further information about the software.
diff --git a/doc/man/bitcoin-wallet.1 b/doc/man/bitcoin-wallet.1
new file mode 100644
index 0000000000..1cb8cdebcd
--- /dev/null
+++ b/doc/man/bitcoin-wallet.1
@@ -0,0 +1,67 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
+.TH BITCOIN-WALLET "1" "February 2019" "bitcoin-wallet v0.17.99.0" "User Commands"
+.SH NAME
+bitcoin-wallet \- manual page for bitcoin-wallet v0.17.99.0
+.SH DESCRIPTION
+Bitcoin Core bitcoin\-wallet version v0.17.99.0
+.PP
+wallet\-tool is an offline tool for creating and interacting with Bitcoin Core wallet files.
+By default wallet\-tool will act on wallets in the default mainnet wallet directory in the datadir.
+To change the target wallet, use the \fB\-datadir\fR, \fB\-wallet\fR and \fB\-testnet\fR/\-regtest arguments.
+.SS "Usage:"
+.IP
+bitcoin\-wallet [options] <command>
+.SH OPTIONS
+.HP
+\-?
+.IP
+Print this help message and exit
+.HP
+\fB\-datadir=\fR<dir>
+.IP
+Specify data directory
+.HP
+\fB\-wallet=\fR<wallet\-name>
+.IP
+Specify wallet name
+.PP
+Debugging/Testing options:
+.HP
+\fB\-debug=\fR<category>
+.IP
+Output debugging information (default: 0).
+.HP
+\fB\-printtoconsole\fR
+.IP
+Send trace/debug info to console (default: 1 when no \fB\-debug\fR is true, 0
+otherwise.
+.PP
+Chain selection options:
+.HP
+\fB\-testnet\fR
+.IP
+Use the test chain
+.PP
+Commands:
+.IP
+create
+.IP
+Create new wallet file
+.IP
+info
+.IP
+Get wallet info
+.SH COPYRIGHT
+Copyright (C) 2009-2019 The Bitcoin Core developers
+
+Please contribute if you find Bitcoin Core useful. Visit
+<https://bitcoincore.org> for further information about the software.
+The source code is available from <https://github.com/bitcoin/bitcoin>.
+
+This is experimental software.
+Distributed under the MIT software license, see the accompanying file COPYING
+or <https://opensource.org/licenses/MIT>
+
+This product includes software developed by the OpenSSL Project for use in the
+OpenSSL Toolkit <https://www.openssl.org> and cryptographic software written by
+Eric Young and UPnP software written by Thomas Bernard.
diff --git a/doc/man/bitcoind.1 b/doc/man/bitcoind.1
index 5c4b1cd03b..2a79b6cb46 100644
--- a/doc/man/bitcoind.1
+++ b/doc/man/bitcoind.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
-.TH BITCOIND "1" "December 2018" "bitcoind v0.17.99.0" "User Commands"
+.TH BITCOIND "1" "February 2019" "bitcoind v0.17.99.0" "User Commands"
.SH NAME
bitcoind \- manual page for bitcoind v0.17.99.0
.SH SYNOPSIS
@@ -56,7 +56,7 @@ Specify data directory
.HP
\fB\-dbcache=\fR<n>
.IP
-Set database cache size in megabytes (4 to 16384, default: 450)
+Set database cache size in MiB (4 to 16384, default: 450)
.HP
\fB\-debuglogfile=\fR<file>
.IP
@@ -488,10 +488,6 @@ Relay and mine data carrier transactions (default: 1)
Maximum size of data in data carrier transactions we relay and mine
(default: 83)
.HP
-\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,
@@ -500,7 +496,7 @@ mining and transaction creation (default: 0.00001)
\fB\-whitelistforcerelay\fR
.IP
Force relay of transactions from whitelisted peers even if they violate
-local relay policy (default: 1)
+local relay policy (default: 0)
.HP
\fB\-whitelistrelay\fR
.IP
@@ -580,7 +576,7 @@ Username for JSON\-RPC connections
.IP
Accept command line and JSON\-RPC commands
.SH COPYRIGHT
-Copyright (C) 2009-2018 The Bitcoin Core developers
+Copyright (C) 2009-2019 The Bitcoin Core developers
Please contribute if you find Bitcoin Core useful. Visit
<https://bitcoincore.org> for further information about the software.
diff --git a/doc/productivity.md b/doc/productivity.md
new file mode 100644
index 0000000000..b25ddc94e5
--- /dev/null
+++ b/doc/productivity.md
@@ -0,0 +1,217 @@
+Productivity Notes
+==================
+
+Table of Contents
+-----------------
+
+* [General](#general)
+ * [Cache compilations with `ccache`](#cache-compilations-with-ccache)
+ * [Disable features with `./configure`](#disable-features-with-configure)
+ * [Make use of your threads with `make -j`](#make-use-of-your-threads-with-make--j)
+ * [Only build what you need](#only-build-what-you-need)
+ * [Multiple working directories with `git worktrees`](#multiple-working-directories-with-git-worktrees)
+ * [Interactive "dummy rebases" for fixups and execs with `git merge-base`](#interactive-dummy-rebases-for-fixups-and-execs-with-git-merge-base)
+* [Writing code](#writing-code)
+ * [Format C/C++/Protobuf diffs with `clang-format-diff.py`](#format-ccprotobuf-diffs-with-clang-format-diffpy)
+ * [Format Python diffs with `yapf-diff.py`](#format-python-diffs-with-yapf-diffpy)
+* [Rebasing/Merging code](#rebasingmerging-code)
+ * [More conflict context with `merge.conflictstyle diff3`](#more-conflict-context-with-mergeconflictstyle-diff3)
+* [Reviewing code](#reviewing-code)
+ * [Reduce mental load with `git diff` options](#reduce-mental-load-with-git-diff-options)
+ * [Reference PRs easily with `refspec`s](#reference-prs-easily-with-refspecs)
+ * [Diff the diffs with `git range-diff`](#diff-the-diffs-with-git-range-diff)
+
+General
+------
+
+### Cache compilations with `ccache`
+
+The easiest way to faster compile times is to cache compiles. `ccache` is a way to do so, from its description at the time of writing:
+
+> ccache is a compiler cache. It speeds up recompilation by caching the result of previous compilations and detecting when the same compilation is being done again. Supported languages are C, C++, Objective-C and Objective-C++.
+
+Install `ccache` through your distribution's package manager, and run `./configure` with your normal flags to pick it up.
+
+To use ccache for all your C/C++ projects, follow the symlinks method [here](https://ccache.samba.org/manual/latest.html#_run_modes) to set it up.
+
+To get the most out of ccache, put something like this in `~/.ccache/ccache.conf`:
+
+```
+max_size = 50.0G # or whatever cache size you prefer; default is 5G; 0 means unlimited
+base_dir = /home/yourname # or wherever you keep your source files
+```
+
+Note: base_dir is required for ccache to share cached compiles of the same file across different repositories / paths; it will only do this for paths under base_dir. So this option is required for effective use of ccache with git worktrees (described below).
+
+You _must not_ set base_dir to "/", or anywhere that contains system headers (according to the ccache docs).
+
+### Disable features with `./configure`
+
+After running `./autogen.sh`, which generates the `./configure` file, use `./configure --help` to identify features that you can disable to save on compilation time. A few common flags:
+
+```sh
+--without-miniupnpc
+--disable-bench
+--disable-wallet
+--without-gui
+```
+
+If you do need the wallet enabled, it is common for devs to add `--with-incompatible-bdb`. This uses your system bdb version for the wallet, so you don't have to find a copy of bdb 4.8. Wallets from such a build will be incompatible with any release binary (and vice versa), so use with caution on mainnet.
+
+### Make use of your threads with `make -j`
+
+If you have multiple threads on your machine, you can tell `make` to utilize all of them with:
+
+```sh
+make -j"$(($(nproc)+1))"
+```
+
+### Only build what you need
+
+When rebuilding during development, note that running `make`, without giving a target, will do a lot of work you probably don't need. It will build the GUI (unless you've disabled it) and all the tests (which take much longer to build than the app does).
+
+Obviously, it is important to build and run the tests at appropriate times -- but when you just want a quick compile to check your work, consider picking one or a set of build targets relevant to what you're working on, e.g.:
+
+```sh
+make src/bitcoind src/bitcoin-cli
+make src/qt/bitcoin-qt
+make -C src bitcoin_bench
+```
+
+(You can and should combine this with `-j`, as above, for a parallel build.)
+
+### Multiple working directories with `git worktrees`
+
+If you work with multiple branches or multiple copies of the repository, you should try `git worktrees`.
+
+To create a new branch that lives under a new working directory without disrupting your current working directory (useful for creating pull requests):
+```sh
+git worktree add -b my-shiny-new-branch ../living-at-my-new-working-directory based-on-my-crufty-old-commit-ish
+```
+
+To simply check out a commit-ish under a new working directory without disrupting your current working directory (useful for reviewing pull requests):
+```sh
+git worktree add --checkout ../where-my-checkout-commit-ish-will-live my-checkout-commit-ish
+```
+
+### Interactive "dummy rebases" for fixups and execs with `git merge-base`
+
+When rebasing, we often want to do a "dummy rebase," whereby we are not rebasing over an updated master but rather over the last common commit with master. This might be useful for rearranging commits, `rebase --autosquash`ing, or `rebase --exec`ing without introducing conflicts that arise from an updated master. In these situations, we can use `git merge-base` to identify the last common commit with master, and rebase off of that.
+
+To squash in `git commit --fixup` commits without rebasing over an updated master, we can do the following:
+
+```sh
+git rebase -i --autosquash "$(git merge-base master HEAD)"
+```
+
+To execute `make check` on every commit since last diverged from master, but without rebasing over an updated master, we can do the following:
+```sh
+git rebase -i --exec "make check" "$(git merge-base master HEAD)"
+```
+
+-----
+
+This synergizes well with [`ccache`](#cache-compilations-with-ccache) as objects resulting from unchanged code will most likely hit the cache and won't need to be recompiled.
+
+You can also set up [upstream refspecs](#reference-prs-easily-with-refspecs) to refer to pull requests easier in the above `git worktree` commands.
+
+Writing code
+------------
+
+### Format C/C++/Protobuf diffs with `clang-format-diff.py`
+
+See [contrib/devtools/README.md](/contrib/devtools/README.md#clang-format-diff.py).
+
+### Format Python diffs with `yapf-diff.py`
+
+Usage is exactly the same as [`clang-format-diff.py`](#format-ccprotobuf-diffs-with-clang-format-diffpy). You can get it [here](https://github.com/MarcoFalke/yapf-diff).
+
+Rebasing/Merging code
+-------------
+
+### More conflict context with `merge.conflictstyle diff3`
+
+For resolving merge/rebase conflicts, it can be useful to enable diff3 style using `git config merge.conflictstyle diff3`. Instead of
+
+```diff
+<<<
+yours
+===
+theirs
+>>>
+```
+
+ you will see
+
+```diff
+<<<
+yours
+|||
+original
+===
+theirs
+>>>
+```
+
+This may make it much clearer what caused the conflict. In this style, you can often just look at what changed between *original* and *theirs*, and mechanically apply that to *yours* (or the other way around).
+
+Reviewing code
+--------------
+
+### Reduce mental load with `git diff` options
+
+When reviewing patches which change indentation in C++ files, use `git diff -w` and `git show -w`. This makes the diff algorithm ignore whitespace changes. This feature is also available on github.com, by adding `?w=1` at the end of any URL which shows a diff.
+
+When reviewing patches that change symbol names in many places, use `git diff --word-diff`. This will instead of showing the patch as deleted/added *lines*, show deleted/added *words*.
+
+When reviewing patches that move code around, try using `git diff --patience commit~:old/file.cpp commit:new/file/name.cpp`, and ignoring everything except the moved body of code which should show up as neither `+` or `-` lines. In case it was not a pure move, this may even work when combined with the `-w` or `--word-diff` options described above. `--color-moved=dimmed-zebra` will also dim the coloring of moved hunks in the diff on compatible terminals.
+
+### Reference PRs easily with `refspec`s
+
+When looking at other's pull requests, it may make sense to add the following section to your `.git/config` file:
+
+```
+[remote "upstream-pull"]
+ fetch = +refs/pull/*:refs/remotes/upstream-pull/*
+ url = git@github.com:bitcoin/bitcoin.git
+```
+
+This will add an `upstream-pull` remote to your git repository, which can be fetched using `git fetch --all` 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.
+
+### Diff the diffs with `git range-diff`
+
+It is very common for contributors to rebase their pull requests, or make changes to commits (perhaps in response to review) that are not at the head of their branch. This poses a problem for reviewers as when the contributor force pushes, the reviewer is no longer sure that his previous reviews of commits are still valid (as the commit hashes can now be different even though the diff is semantically the same). [git range-diff](https://git-scm.com/docs/git-range-diff) (Git >= 2.19) can help solve this problem by diffing the diffs.
+
+For example, to identify the differences between your previously reviewed diffs P1-5, and the new diffs P1-2,N3-4 as illustrated below:
+```
+ P1--P2--P3--P4--P5 <-- previously-reviewed-head
+ /
+...--m <-- master
+ \
+ P1--P2--N3--N4--N5 <-- new-head (with P3 slightly modified)
+```
+
+You can do:
+```sh
+git range-diff master previously-reviewed-head new-head
+```
+
+Note that `git range-diff` also work for rebases:
+
+```
+ P1--P2--P3--P4--P5 <-- previously-reviewed-head
+ /
+...--m--m1--m2--m3 <-- master
+ \
+ P1--P2--N3--N4 <-- new-head (with P3 modified, P4 & P5 squashed)
+
+PREV=P5 N=4 && git range-diff `git merge-base --all HEAD $PREV`...$PREV HEAD~$N...HEAD
+```
+
+Where `P5` is the commit you last reviewed and `4` is the number of commits in the new version.
+
+-----
+
+`git range-diff` also accepts normal `git diff` options, see [Reduce mental load with `git diff` options](#reduce-mental-load-with-git-diff-options) for useful `git diff` options.
+
+You can also set up [upstream refspecs](#reference-prs-easily-with-refspecs) to refer to pull requests easier in the above `git range-diff` commands.
diff --git a/doc/psbt.md b/doc/psbt.md
index 560b45ef31..c411b31d5d 100644
--- a/doc/psbt.md
+++ b/doc/psbt.md
@@ -67,6 +67,9 @@ hardware implementations will typically implement multiple roles simultaneously.
input a PSBT, adds UTXO, key, and script data to inputs and outputs that miss
it, and optionally signs inputs. Where possible it also finalizes the partial
signatures.
+- **`utxoupdatepsbt` (Updater)** is a node RPC that takes a PSBT and updates it
+ to include information available from the UTXO set (works only for SegWit
+ inputs).
- **`finalizepsbt` (Finalizer, Extractor)** is a utility RPC that finalizes any
partial signatures, and if all inputs are finalized, converts the result to a
fully signed transaction which can be broadcast with `sendrawtransaction`.
@@ -74,8 +77,16 @@ hardware implementations will typically implement multiple roles simultaneously.
can be used at any point in the workflow to merge information added to
different versions of the same PSBT. In particular it is useful to combine the
output of multiple Updaters or Signers.
+- **`joinpsbts`** (Creator) is a utility RPC that joins multiple PSBTs together,
+ concatenating the inputs and outputs. This can be used to construct CoinJoin
+ transactions.
- **`decodepsbt`** is a diagnostic utility RPC which will show all information in
a PSBT in human-readable form, as well as compute its eventual fee if known.
+- **`analyzepsbt`** is a utility RPC that examines a PSBT and reports the
+ current status of its inputs, the next step in the workflow if known, and if
+ possible, computes the fee of the resulting transaction and estimates the
+ final weight and feerate.
+
### Workflows
diff --git a/doc/reduce-traffic.md b/doc/reduce-traffic.md
index dd1469f563..5a71f62e0f 100644
--- a/doc/reduce-traffic.md
+++ b/doc/reduce-traffic.md
@@ -35,3 +35,16 @@ blocks and transactions to fewer nodes.
Reducing the maximum connected nodes to a minimum could be desirable if traffic
limits are tiny. Keep in mind that bitcoin's trustless model works best if you are
connected to a handful of nodes.
+
+## 4. Turn off transaction relay (`-blocksonly`)
+
+Forwarding transactions to peers increases the P2P traffic. To only sync blocks
+with other peers, you can disable transaction relay.
+
+Be reminded of the effects of this setting.
+
+- Fee estimation will no longer work.
+- Not relaying other's transactions could hurt your privacy if used while a
+ wallet is loaded or if you use the node to broadcast transactions.
+- It makes block propagation slower because compact block relay can only be
+ used when transaction relay is enabled.
diff --git a/doc/release-notes-14054.md b/doc/release-notes-14054.md
new file mode 100644
index 0000000000..d8cad369c5
--- /dev/null
+++ b/doc/release-notes-14054.md
@@ -0,0 +1,7 @@
+P2P changes
+-----------
+
+BIP 61 reject messages were deprecated in v0.18. They are now disabled by
+default, but can be enabled by setting the `-enablebip61` command line option.
+BIP 61 reject messages will be removed entirely in a future version of
+Bitcoin Core.
diff --git a/doc/release-notes-14802.md b/doc/release-notes-14802.md
new file mode 100644
index 0000000000..1fcc38866a
--- /dev/null
+++ b/doc/release-notes-14802.md
@@ -0,0 +1,3 @@
+RPC changes
+-----------
+The `getblockstats` RPC is faster for fee calculation by using BlockUndo data. Also, `-txindex` is no longer required and `getblockstats` works for all non-pruned blocks.
diff --git a/doc/release-notes-14941.md b/doc/release-notes-14941.md
deleted file mode 100644
index c3820d0368..0000000000
--- a/doc/release-notes-14941.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Miscellaneous RPC changes
-------------
-
-- The `unloadwallet` RPC is now synchronous, meaning that it blocks until the
- wallet is fully unloaded.
diff --git a/doc/release-notes-14954.md b/doc/release-notes-14954.md
new file mode 100644
index 0000000000..4bb0fcca74
--- /dev/null
+++ b/doc/release-notes-14954.md
@@ -0,0 +1,3 @@
+Build system changes
+--------------------
+Python >=3.5 is now required by all aspects of the project. This includes the build systems, test framework and linters. The previously supported minimum (3.4), was EOL in March 2019. See #14954 for more details. \ No newline at end of file
diff --git a/doc/release-notes-14982.md b/doc/release-notes-14982.md
deleted file mode 100644
index 3f0bf8aacd..0000000000
--- a/doc/release-notes-14982.md
+++ /dev/null
@@ -1,5 +0,0 @@
-New RPCs
---------
-
-- The RPC `getrpcinfo` returns runtime details of the RPC server. At the moment
- it returns the active commands and the corresponding execution time.
diff --git a/doc/release-notes-15006.md b/doc/release-notes-15006.md
new file mode 100644
index 0000000000..76ed3247a6
--- /dev/null
+++ b/doc/release-notes-15006.md
@@ -0,0 +1,4 @@
+Miscellaneous RPC changes
+------------
+
+- `createwallet` can now create encrypted wallets if a non-empty passphrase is specified.
diff --git a/doc/release-notes-15566.md b/doc/release-notes-15566.md
new file mode 100644
index 0000000000..49964d7550
--- /dev/null
+++ b/doc/release-notes-15566.md
@@ -0,0 +1,3 @@
+Miscellaneous CLI Changes
+-------------------------
+- The `testnet` field in `bitcoin-cli -getinfo` has been renamed to `chain` and now returns the current network name as defined in BIP70 (main, test, regtest). \ No newline at end of file
diff --git a/doc/release-notes-15620.md b/doc/release-notes-15620.md
new file mode 100644
index 0000000000..bf89a70a4e
--- /dev/null
+++ b/doc/release-notes-15620.md
@@ -0,0 +1,13 @@
+Updated RPCs
+------------
+
+* The -maxtxfee setting no longer has any effect on non-wallet RPCs.
+
+ The `sendrawtransaction` and `testmempoolaccept` RPC methods previously
+ accepted an `allowhighfees` parameter to fail the mempool acceptance in case
+ the transaction's fee would exceed the value of the command line argument
+ `-maxtxfee`. To uncouple the RPCs from the global option, they now have a
+ hardcoded default for the maximum transaction fee, that can be changed for
+ both RPCs on a per-call basis with the `maxfeerate` parameter. The
+ `allowhighfees` boolean option has been removed and replaced by the
+ `maxfeerate` numeric option.
diff --git a/doc/release-notes-15637.md b/doc/release-notes-15637.md
new file mode 100644
index 0000000000..048d5e7218
--- /dev/null
+++ b/doc/release-notes-15637.md
@@ -0,0 +1,3 @@
+RPC changes
+-----------
+In getmempoolancestors, getmempooldescendants, getmempoolentry and getrawmempool RPCs, to be consistent with the returned value and other RPCs such as getrawtransaction, vsize has been added and size is now deprecated. size will only be returned if bitcoind is started with `-deprecatedrpc=size`. \ No newline at end of file
diff --git a/doc/release-notes-15730.md b/doc/release-notes-15730.md
new file mode 100644
index 0000000000..7a4a60b1ee
--- /dev/null
+++ b/doc/release-notes-15730.md
@@ -0,0 +1,5 @@
+RPC changes
+-----------
+The RPC `getwalletinfo` response now includes the `scanning` key with an object
+if there is a scanning in progress or `false` otherwise. Currently the object
+has the scanning duration and progress.
diff --git a/doc/release-notes-15849.md b/doc/release-notes-15849.md
new file mode 100644
index 0000000000..a1df31f250
--- /dev/null
+++ b/doc/release-notes-15849.md
@@ -0,0 +1,6 @@
+Thread names in logs
+--------------------
+
+On platforms supporting `thread_local`, log lines can be prefixed with the name
+of the thread that caused the log. To enable this behavior, use
+`-logthreadnames=1`.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index a54b08848f..9efb6cbabb 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,5 +1,19 @@
-(note: this is a temporary file, to be added-to by anybody, and moved to
-release-notes at release time)
+*After branching off for a major version release of Bitcoin Core, use this
+template to create the initial release notes draft.*
+
+*The release notes draft is a temporary file that can be added to by anyone. See
+[/doc/developer-notes.md#release-notes](/doc/developer-notes.md#release-notes)
+for the process.*
+
+*Create the draft, named* "*version* Release Notes Draft"
+*(e.g. "0.20.0 Release Notes Draft"), as a collaborative wiki in:*
+
+https://github.com/bitcoin-core/bitcoin-devwiki/wiki/
+
+*Before the final release, move the notes back to this git repository.*
+
+*version* Release Notes Draft
+===============================
Bitcoin Core version *version* is now available from:
@@ -24,14 +38,9 @@ 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.
-
-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.
+Upgrading directly from a version of Bitcoin Core that has reached its EOL is
+possible, but might take some time if the datadir needs to be migrated. Old
+wallet versions of Bitcoin Core are generally supported.
Downgrading warning
-------------------
@@ -66,255 +75,62 @@ platform.
Notable changes
===============
-Mining
-------
-
-- Calls to `getblocktemplate` will fail if the segwit rule is not specified.
- Calling `getblocktemplate` without segwit specified is almost certainly
- a misconfiguration since doing so results in lower rewards for the miner.
- Failed calls will produce an error message describing how to enable the
- segwit rule.
-
-Configuration option changes
-----------------------------
-
-- A warning is printed if an unrecognized section name is used in the
- configuration file. Recognized sections are `[test]`, `[main]`, and
- `[regtest]`.
-
-- Four new options are available for configuring the maximum number of
- messages that ZMQ will queue in memory (the "high water mark") before
- dropping additional messages. The default value is 1,000, the same as
- was used for previous releases. See the [ZMQ
- documentation](https://github.com/bitcoin/bitcoin/blob/master/doc/zmq.md#usage)
- for details.
-
-- The `enablebip61` option (introduced in Bitcoin Core 0.17.0) is
- used to toggle sending of BIP 61 reject messages. Reject messages have no use
- case on the P2P network and are only logged for debugging by most network
- nodes. The option will now by default be off for improved privacy and security
- as well as reduced upload usage. The option can explicitly be turned on for
- local-network debugging purposes.
-
-- The `rpcallowip` option can no longer be used to automatically listen
- on all network interfaces. Instead, the `rpcbind` parameter must also
- be used to specify the IP addresses to listen on. Listening for RPC
- commands over a public network connection is insecure and should be
- disabled, so a warning is now printed if a user selects such a
- configuration. If you need to expose RPC in order to use a tool
- like Docker, ensure you only bind RPC to your localhost, e.g. `docker
- run [...] -p 127.0.0.1:8332:8332` (this is an extra `:8332` over the
- normal Docker port specification).
-
-- The `rpcpassword` option now causes a startup error if the password
- set in the configuration file contains a hash character (#), as it's
- ambiguous whether the hash character is meant for the password or as a
- comment.
-
-- The `whitelistforcerelay` option is used to relay transactions from
- whitelisted peers even when not accepted to the mempool. This option now
- defaults to being off, so that changes in policy and disconnect/ban behavior
- will not cause a node that is whitelisting another to be dropped by peers.
- Users can still explicitly enable this behavior with the command line option
- (and may want to consider letting the Bitcoin Core project know about their
- use-case, as this feature could be deprecated in the future).
-
-Documentation
--------------
-
-- A new short
- [document](https://github.com/bitcoin/bitcoin/blob/master/doc/JSON-RPC-interface.md)
- about the JSON-RPC interface describes cases where the results of an
- RPC might contain inconsistencies between data sourced from different
- subsystems, such as wallet state and mempool state. A note is added
- to the [REST interface documentation](https://github.com/bitcoin/bitcoin/blob/master/doc/REST-interface.md)
- indicating that the same rules apply.
-
-- A new [document](https://github.com/bitcoin/bitcoin/blob/master/doc/bitcoin-conf.md)
- about the `bitcoin.conf` file describes how to use it to configure
- Bitcoin Core.
-
-- A new document introduces Bitcoin Core's BIP174
- [Partially-Signed Bitcoin Transactions (PSBT)](https://github.com/bitcoin/bitcoin/blob/master/doc/psbt.md)
- interface, which is used to allow multiple programs to collaboratively
- work to create, sign, and broadcast new transactions. This is useful
- for offline (cold storage) wallets, multisig wallets, coinjoin
- implementations, and many other cases where two or more programs need
- to interact to generate a complete transaction.
-
-- The [output script descriptor](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md)
- documentation has been updated with information about new features in
- this still-developing language for describing the output scripts that
- a wallet or other program wants to receive notifications for, such as
- which addresses it wants to know received payments. The language is
- currently used in the `scantxoutset` RPC and is expected to be adapted
- to other RPCs and to the underlying wallet structure.
-
-Build system changes
---------------------
-
-- A new `--disable-bip70` option may be passed to `./configure` to
- prevent Bitcoin-Qt from being built with support for the BIP70 payment
- protocol or from linking libssl. As the payment protocol has exposed
- Bitcoin Core to libssl vulnerabilities in the past, builders who don't
- need BIP70 support are encouraged to use this option to reduce their
- exposure to future vulnerabilities.
-
-Deprecated or removed RPCs
---------------------------
-
-- The `signrawtransaction` RPC is removed after being deprecated and
- hidden behind a special configuration option in version 0.17.0.
-
-- The 'account' API is removed after being deprecated in v0.17. The
- 'label' API was introduced in v0.17 as a replacement for accounts.
- See the [release notes from v0.17](https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.17.0.md#label-and-account-apis-for-wallet)
- for a full description of the changes from the 'account' API to the
- 'label' API.
-
-- The `addwitnessaddress` RPC is removed after being deprecated in
- version 0.13.0.
-
-- The wallet's `generate` RPC method is deprecated and will be fully
- removed in a subsequent major version. This RPC is only used for
- testing, but its implementation reached across multiple subsystems
- (wallet and mining), so it is being deprecated to simplify the
- wallet-node interface. Projects that are using `generate` for testing
- purposes should transition to using the `generatetoaddress` RPC, which
- does not require or use the wallet component. Calling
- `generatetoaddress` with an address returned by the `getnewaddress`
- RPC gives the same functionality as the old `generate` RPC. To
- continue using `generate` in this version, restart bitcoind with the
- `-deprecatedrpc=generate` configuration option.
-
New RPCs
--------
-- A new `getnodeaddresses` RPC returns peer addresses known to this
- node. It may be used to find nodes to connect to without using a DNS
- seeder.
-
-- A new `listwalletdir` RPC returns a list of wallets in the wallet
- directory (either the default wallet directory or the directory
- configured by the `-walletdir` parameter).
+- `getbalances` returns an object with all balances (`mine`,
+ `untrusted_pending` and `immature`). Please refer to the RPC help of
+ `getbalances` for details. The new RPC is intended to replace
+ `getunconfirmedbalance` and the balance fields in `getwalletinfo`, as well as
+ `getbalance`. The old calls may be removed in a future version.
Updated RPCs
------------
-Note: some low-level RPC changes mainly useful for testing are described
-in the Low-level Changes section below.
-
-- The `getpeerinfo` RPC now returns an additional `minfeefilter` field
- set to the peer's BIP133 fee filter. You can use this to detect that
- you have peers that are willing to accept transactions below the
- default minimum relay fee.
-
-- The mempool RPCs, such as `getrawmempool` with `verbose=true`, now
- return an additional "bip125-replaceable" value indicating whether the
- transaction (or its unconfirmed ancestors) opts-in to asking nodes and
- miners to replace it with a higher-feerate transaction spending any of
- the same inputs.
-
-- The `settxfee` RPC previously silently ignored attempts to set the fee
- below the allowed minimums. It now prints a warning. The special
- value of "0" may still be used to request the minimum value.
-
-- The `getaddressinfo` RPC now provides an `ischange` field indicating
- whether the wallet used the address in a change output.
-
-- The `importmulti` RPC has been updated to support P2WSH, P2WPKH,
- P2SH-P2WPKH, and P2SH-P2WSH. Requests for P2WSH and P2SH-P2WSH accept
- an additional `witnessscript` parameter.
-
-- The `importmulti` RPC now returns an additional `warnings` field for
- each request with an array of strings explaining when fields are being
- ignored or are inconsistent, if there are any.
-
-- The `getaddressinfo` RPC now returns an additional `solvable` boolean
- field when Bitcoin Core knows enough about the address's scriptPubKey,
- optional redeemScript, and optional witnessScript in order for the
- wallet to be able to generate an unsigned input spending funds sent to
- that address.
-
-- The `getaddressinfo`, `listunspent`, and `scantxoutset` RPCs now
- return an additional `desc` field that contains an output descriptor
- containing all key paths and signing information for the address
- (except for the private key). The `desc` field is only returned for
- `getaddressinfo` and `listunspent` when the address is solvable.
-
-- The `importprivkey` RPC will preserve previously-set labels for
- addresses or public keys corresponding to the private key being
- imported. For example, if you imported a watch-only address with the
- label "cold wallet" in earlier releases of Bitcoin Core, subsequently
- importing the private key would default to resetting the address's
- label to the default empty-string label (""). In this release, the
- previous label of "cold wallet" will be retained. If you optionally
- specify any label besides the default when calling `importprivkey`,
- the new label will be applied to the address.
-
-- See the [Mining](#mining) section for changes to `getblocktemplate`.
-
-- The `getrawtransaction` RPC no longer checks the unspent UTXO set for
- a transaction. The remaining behaviors are as follows: 1. If a
- blockhash is provided, check the corresponding block. 2. If no
- blockhash is provided, check the mempool. 3. If no blockhash is
- provided but txindex is enabled, also check txindex.
-
-Graphical User Interface (GUI)
-------------------------------
-
-- A new Window menu is added alongside the existing File, Settings, and
- Help menus. Several items from the other menus that opened new
- windows have been moved to this new Window menu.
-
-- In the Send tab, the checkbox for "pay only the required fee"
- has been removed. Instead, the user can simply decrease the value in
- the Custom Feerate field all the way down to the node's configured
- minimum relay fee.
-
-- In the Overview tab, the watch-only balance will be the only
- balance shown if the wallet was created using the `createwallet` RPC
- and the `disable_private_keys` parameter was set to true.
-
-- The launch-on-startup option is no longer available on macOS if
- compiled with macosx min version greater than 10.11 (use
- CXXFLAGS="-mmacosx-version-min=10.11"
- CFLAGS="-mmacosx-version-min=10.11" for setting the deployment
- sdk version)
+Note: some low-level RPC changes mainly useful for testing are described in the
+Low-level Changes section below.
+
+- The `sendmany` RPC had an argument `minconf` that was not well specified and
+ would lead to RPC errors even when the wallet's coin selection would succeed.
+ The `sendtoaddress` RPC never had this check, so to normalize the behavior,
+ `minconf` is now ignored in `sendmany`. If the coin selection does not
+ succeed due to missing coins, it will still throw an RPC error. Be reminded
+ that coin selection is influenced by the `-spendzeroconfchange`,
+ `-limitancestorcount`, `-limitdescendantcount` and `-walletrejectlongchains`
+ command line arguments.
+
Low-level changes
=================
-RPC
----
-
-- The `submitblock` RPC previously returned the reason a rejected block
- was invalid the first time it processed that block but returned a
- generic "duplicate" rejection message on subsequent occasions it
- processed the same block. It now always returns the fundamental
- reason for rejecting an invalid block and only returns "duplicate" for
- valid blocks it has already accepted.
+Configuration
+------------
-- A new `submitheader` RPC allows submitting block headers independently
- from their block. This is likely only useful for testing.
+- An error is issued where previously a warning was issued when a setting in
+ the config file was specified in the default section, but not overridden for
+ the selected network. This change takes only effect if the selected network
+ is not mainnet.
-Configuration
--------------
+Network
+-------
-- The `-usehd` configuration option was removed in version 0.16. From
- that version onwards, all new wallets created are hierarchical
- deterministic wallets. This release makes specifying `-usehd` an
- invalid configuration option.
+- When fetching a transaction announced by multiple peers, previous versions of
+ Bitcoin Core would sequentially attempt to download the transaction from each
+ announcing peer until the transaction is received, in the order that those
+ peers' announcements were received. In this release, the download logic has
+ changed to randomize the fetch order across peers and to prefer sending
+ download requests to outbound peers over inbound peers. This fixes an issue
+ where inbound peers can prevent a node from getting a transaction.
-Changes for particular platforms
---------------------------------
+Wallet
+------
-- On macOS, Bitcoin Core now opts out of application CPU throttling
- ("app nap") during initial blockchain download, when catching up from
- over 100 blocks behind the current chain tip, or when reindexing chain
- data. This helps prevent these operations from taking an excessively
- long time because the operating system is attempting to conserve
- power.
+- When in pruned mode, a rescan that was triggered by an `importwallet`,
+ `importpubkey`, `importaddress`, or `importprivkey` RPC will only fail when
+ blocks have been pruned. Previously it would fail when `-prune` has been set.
+ This change allows to set `-prune` to a high value (e.g. the disk size) and
+ the calls to any of the import RPCs would fail when the first block is
+ pruned.
Credits
=======
@@ -322,4 +138,4 @@ Credits
Thanks to everyone who directly contributed to this release:
-As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/).
+As well as everyone that helped translating on [Transifex](https://www.transifex.com/bitcoin/bitcoin/).
diff --git a/doc/release-notes/release-notes-0.12.0.md b/doc/release-notes/release-notes-0.12.0.md
index cf74a17975..bc0d5ea3b0 100644
--- a/doc/release-notes/release-notes-0.12.0.md
+++ b/doc/release-notes/release-notes-0.12.0.md
@@ -127,7 +127,7 @@ minimum relay feerate. The initial minimum relay feerate is set to
Bitcoin Core 0.12 also introduces new default policy limits on the length and
size of unconfirmed transaction chains that are allowed in the mempool
(generally limiting the length of unconfirmed chains to 25 transactions, with a
-total size of 101 KB). These limits can be overriden using command line
+total size of 101 KB). These limits can be overridden using command line
arguments; see the extended help (`--help -help-debug`) for more information.
Opt-in Replace-by-fee transactions
diff --git a/doc/release-notes/release-notes-0.18.0.md b/doc/release-notes/release-notes-0.18.0.md
new file mode 100644
index 0000000000..3ca7d52243
--- /dev/null
+++ b/doc/release-notes/release-notes-0.18.0.md
@@ -0,0 +1,1224 @@
+Bitcoin Core version 0.18.0 is now available from:
+
+ <https://bitcoincore.org/bin/bitcoin-core-0.18.0/>
+
+This is a new major version release, including new features, various bug
+fixes 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 newer, 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.
+
+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 or later. Upgrading directly from 0.7.x and earlier without
+redownloading the blockchain is not supported. However, as usual, old
+wallet versions are still supported.
+
+Compatibility
+==============
+
+Bitcoin Core is supported and extensively tested on operating systems
+using the Linux kernel, macOS 10.10+, and Windows 7 and newer. It is not
+recommended to use Bitcoin Core on unsupported systems.
+
+Bitcoin Core should also work on most other Unix-like systems but is not
+as frequently tested on them.
+
+From 0.17.0 onwards, macOS <10.10 is no longer supported. 0.17.0 is
+built using Qt 5.9.x, which doesn't support versions of macOS older than
+10.10. Additionally, Bitcoin Core does not yet change appearance when
+macOS "dark mode" is activated.
+
+In addition to previously-supported CPU platforms, this release's
+pre-compiled distribution also provides binaries for the RISC-V
+platform.
+
+If you are using the `systemd` unit configuration file located at
+`contrib/init/bitcoind.service`, it has been changed to use
+`/var/lib/bitcoind` as the data directory instead of
+`~bitcoin/.bitcoin`. When switching over to the new configuration file,
+please make sure that the filesystem on which `/var/lib/bitcoind` will
+exist has enough space (check using `df -h /var/lib/bitcoind`), and
+optionally copy over your existing data directory. See the [systemd init
+file section](#systemd-init-file) for more details.
+
+Known issues
+============
+
+Wallet GUI
+----------
+
+For advanced users who have both (1) enabled coin control features, and
+(2) are using multiple wallets loaded at the same time: The coin control
+input selection dialog can erroneously retain wrong-wallet state when
+switching wallets using the dropdown menu. For now, it is recommended
+not to use coin control features with multiple wallets loaded.
+
+Notable changes
+===============
+
+Mining
+------
+
+- Calls to `getblocktemplate` will fail if the segwit rule is not
+ specified. Calling `getblocktemplate` without segwit specified is
+ almost certainly a misconfiguration since doing so results in lower
+ rewards for the miner. Failed calls will produce an error message
+ describing how to enable the segwit rule.
+
+Configuration option changes
+----------------------------
+
+- A warning is printed if an unrecognized section name is used in the
+ configuration file. Recognized sections are `[test]`, `[main]`, and
+ `[regtest]`.
+
+- Four new options are available for configuring the maximum number of
+ messages that ZMQ will queue in memory (the "high water mark") before
+ dropping additional messages. The default value is 1,000, the same as
+ was used for previous releases. See the [ZMQ
+ documentation](https://github.com/bitcoin/bitcoin/blob/master/doc/zmq.md#usage)
+ for details.
+
+- The `rpcallowip` option can no longer be used to automatically listen
+ on all network interfaces. Instead, the `rpcbind` parameter must be
+ used to specify the IP addresses to listen on. Listening for RPC
+ commands over a public network connection is insecure and should be
+ disabled, so a warning is now printed if a user selects such a
+ configuration. If you need to expose RPC in order to use a tool like
+ Docker, ensure you only bind RPC to your localhost, e.g. `docker run
+ [...] -p 127.0.0.1:8332:8332` (this is an extra `:8332` over the
+ normal Docker port specification).
+
+- The `rpcpassword` option now causes a startup error if the password
+ set in the configuration file contains a hash character (#), as it's
+ ambiguous whether the hash character is meant for the password or as a
+ comment.
+
+- The `whitelistforcerelay` option is used to relay transactions from
+ whitelisted peers even when not accepted to the mempool. This option
+ now defaults to being off, so that changes in policy and
+ disconnect/ban behavior will not cause a node that is whitelisting
+ another to be dropped by peers. Users can still explicitly enable
+ this behavior with the command line option (and may want to consider
+ [contacting](https://bitcoincore.org/en/contact/) the Bitcoin Core
+ project to let us know about their use-case, as this feature could be
+ deprecated in the future).
+
+systemd init file
+-----------------
+
+The systemd init file (`contrib/init/bitcoind.service`) has been changed
+to use `/var/lib/bitcoind` as the data directory instead of
+`~bitcoin/.bitcoin`. This change makes Bitcoin Core more consistent with
+other services, and makes the systemd init config more consistent with
+existing Upstart and OpenRC configs.
+
+The configuration, PID, and data directories are now completely managed
+by systemd, which will take care of their creation, permissions, etc.
+See [`systemd.exec(5)`](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RuntimeDirectory=)
+for more details.
+
+When using the provided init files under `contrib/init`, overriding the
+`datadir` option in `/etc/bitcoin/bitcoin.conf` will have no effect.
+This is because the command line arguments specified in the init files
+take precedence over the options specified in
+`/etc/bitcoin/bitcoin.conf`.
+
+
+Documentation
+-------------
+
+- A new short [document](https://github.com/bitcoin/bitcoin/blob/master/doc/JSON-RPC-interface.md)
+ about the JSON-RPC interface describes cases where the results of an
+ RPC might contain inconsistencies between data sourced from different
+ subsystems, such as wallet state and mempool state. A note is added
+ to the [REST interface documentation](https://github.com/bitcoin/bitcoin/blob/master/doc/REST-interface.md)
+ indicating that the same rules apply.
+
+- Further information is added to the [JSON-RPC
+ documentation](https://github.com/bitcoin/bitcoin/blob/master/doc/JSON-RPC-interface.md)
+ about how to secure this interface.
+
+- A new [document](https://github.com/bitcoin/bitcoin/blob/master/doc/bitcoin-conf.md)
+ about the `bitcoin.conf` file describes how to use it to configure
+ Bitcoin Core.
+
+- A new document introduces Bitcoin Core's BIP174 [Partially-Signed
+ Bitcoin Transactions
+ (PSBT)](https://github.com/bitcoin/bitcoin/blob/master/doc/psbt.md)
+ interface, which is used to allow multiple programs to collaboratively
+ work to create, sign, and broadcast new transactions. This is useful
+ for offline (cold storage) wallets, multisig wallets, coinjoin
+ implementations, and many other cases where two or more programs need
+ to interact to generate a complete transaction.
+
+- The [output script
+ descriptor](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md)
+ documentation has been updated with information about new features in
+ this still-developing language for describing the output scripts that
+ a wallet or other program wants to receive notifications for, such as
+ which addresses it wants to know received payments. The language is
+ currently used in multiple new and updated RPCs described in these
+ release notes and is expected to be adapted to other RPCs and to the
+ underlying wallet structure.
+
+Build system changes
+--------------------
+
+- A new `--disable-bip70` option may be passed to `./configure` to
+ prevent Bitcoin-Qt from being built with support for the BIP70 payment
+ protocol or from linking libssl. As the payment protocol has exposed
+ Bitcoin Core to libssl vulnerabilities in the past, builders who don't
+ need BIP70 support are encouraged to use this option to reduce their
+ exposure to future vulnerabilities.
+
+- The minimum required version of Qt (when building the GUI) has been
+ increased from 5.2 to 5.5.1 (the [depends
+ system](https://github.com/bitcoin/bitcoin/blob/master/depends/README.md)
+ provides 5.9.7)
+
+New RPCs
+--------
+
+- `getnodeaddresses` returns peer addresses known to this node. It may
+ be used to find nodes to connect to without using a DNS seeder.
+
+- `listwalletdir` returns a list of wallets in the wallet directory
+ (either the default wallet directory or the directory configured by
+ the `-walletdir` parameter).
+
+- `getrpcinfo` returns runtime details of the RPC server. At the moment,
+ it returns an array of the currently active commands and how long
+ they've been running.
+
+- `deriveaddresses` returns one or more addresses corresponding to an
+ [output descriptor](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md).
+
+- `getdescriptorinfo` accepts a descriptor and returns information about
+ it, including its computed checksum.
+
+- `joinpsbts` merges multiple distinct PSBTs into a single PSBT. The
+ multiple PSBTs must have different inputs. The resulting PSBT will
+ contain every input and output from all of the PSBTs. Any signatures
+ provided in any of the PSBTs will be dropped.
+
+- `analyzepsbt` examines a PSBT and provides information about what
+ the PSBT contains and the next steps that need to be taken in order
+ to complete the transaction. For each input of a PSBT, `analyzepsbt`
+ provides information about what information is missing for that
+ input, including whether a UTXO needs to be provided, what pubkeys
+ still need to be provided, which scripts need to be provided, and
+ what signatures are still needed. Every input will also list which
+ role is needed to complete that input, and `analyzepsbt` will also
+ list the next role in general needed to complete the PSBT.
+ `analyzepsbt` will also provide the estimated fee rate and estimated
+ virtual size of the completed transaction if it has enough
+ information to do so.
+
+- `utxoupdatepsbt` searches the set of Unspent Transaction Outputs
+ (UTXOs) to find the outputs being spent by the partial transaction.
+ PSBTs need to have the UTXOs being spent to be provided because
+ the signing algorithm requires information from the UTXO being spent.
+ For segwit inputs, only the UTXO itself is necessary. For
+ non-segwit outputs, the entire previous transaction is needed so
+ that signers can be sure that they are signing the correct thing.
+ Unfortunately, because the UTXO set only contains UTXOs and not full
+ transactions, `utxoupdatepsbt` will only add the UTXO for segwit
+ inputs.
+
+Updated RPCs
+------------
+
+Note: some low-level RPC changes mainly useful for testing are described
+in the Low-level Changes section below.
+
+- `getpeerinfo` now returns an additional `minfeefilter` field set to
+ the peer's BIP133 fee filter. You can use this to detect that you
+ have peers that are willing to accept transactions below the default
+ minimum relay fee.
+
+- The mempool RPCs, such as `getrawmempool` with `verbose=true`, now
+ return an additional "bip125-replaceable" value indicating whether the
+ transaction (or its unconfirmed ancestors) opts-in to asking nodes and
+ miners to replace it with a higher-feerate transaction spending any of
+ the same inputs.
+
+- `settxfee` previously silently ignored attempts to set the fee below
+ the allowed minimums. It now prints a warning. The special value of
+ "0" may still be used to request the minimum value.
+
+- `getaddressinfo` now provides an `ischange` field indicating whether
+ the wallet used the address in a change output.
+
+- `importmulti` has been updated to support P2WSH, P2WPKH, P2SH-P2WPKH,
+ and P2SH-P2WSH. Requests for P2WSH and P2SH-P2WSH accept an additional
+ `witnessscript` parameter.
+
+- `importmulti` now returns an additional `warnings` field for each
+ request with an array of strings explaining when fields are being
+ ignored or are inconsistent, if there are any.
+
+- `getaddressinfo` now returns an additional `solvable` boolean field
+ when Bitcoin Core knows enough about the address's scriptPubKey,
+ optional redeemScript, and optional witnessScript in order for the
+ wallet to be able to generate an unsigned input spending funds sent to
+ that address.
+
+- The `getaddressinfo`, `listunspent`, and `scantxoutset` RPCs now
+ return an additional `desc` field that contains an output descriptor
+ containing all key paths and signing information for the address
+ (except for the private key). The `desc` field is only returned for
+ `getaddressinfo` and `listunspent` when the address is solvable.
+
+- `importprivkey` will preserve previously-set labels for addresses or
+ public keys corresponding to the private key being imported. For
+ example, if you imported a watch-only address with the label "cold
+ wallet" in earlier releases of Bitcoin Core, subsequently importing
+ the private key would default to resetting the address's label to the
+ default empty-string label (""). In this release, the previous label
+ of "cold wallet" will be retained. If you optionally specify any
+ label besides the default when calling `importprivkey`, the new label
+ will be applied to the address.
+
+- See the [Mining](#mining) section for changes to `getblocktemplate`.
+
+- `getmininginfo` now omits `currentblockweight` and `currentblocktx`
+ when a block was never assembled via RPC on this node.
+
+- The `getrawtransaction` RPC & REST endpoints no longer check the
+ unspent UTXO set for a transaction. The remaining behaviors are as
+ follows: 1. If a blockhash is provided, check the corresponding block.
+ 2. If no blockhash is provided, check the mempool. 3. If no blockhash
+ is provided but txindex is enabled, also check txindex.
+
+- `unloadwallet` is now synchronous, meaning it will not return until
+ the wallet is fully unloaded.
+
+- `importmulti` now supports importing of addresses from descriptors. A
+ "desc" parameter can be provided instead of the "scriptPubKey" in a
+ request, as well as an optional range for ranged descriptors to
+ specify the start and end of the range to import. Descriptors with key
+ origin information imported through `importmulti` will have their key
+ origin information stored in the wallet for use with creating PSBTs.
+ More information about descriptors can be found
+ [here](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md).
+
+- `listunspent` has been modified so that it also returns
+ `witnessScript`, the witness script in the case of a P2WSH or
+ P2SH-P2WSH output.
+
+- `createwallet` now has an optional `blank` argument that can be used
+ to create a blank wallet. Blank wallets do not have any keys or HD
+ seed. They cannot be opened in software older than 0.18. Once a blank
+ wallet has a HD seed set (by using `sethdseed`) or private keys,
+ scripts, addresses, and other watch only things have been imported,
+ the wallet is no longer blank and can be opened in 0.17.x. Encrypting
+ a blank wallet will also set a HD seed for it.
+
+Deprecated or removed RPCs
+--------------------------
+
+- `signrawtransaction` is removed after being deprecated and hidden
+ behind a special configuration option in version 0.17.0.
+
+- The 'account' API is removed after being deprecated in v0.17. The
+ 'label' API was introduced in v0.17 as a replacement for accounts.
+ See the [release notes from
+ v0.17](https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.17.0.md#label-and-account-apis-for-wallet)
+ for a full description of the changes from the 'account' API to the
+ 'label' API.
+
+- `addwitnessaddress` is removed after being deprecated in version
+ 0.16.0.
+
+- `generate` is deprecated and will be fully removed in a subsequent
+ major version. This RPC is only used for testing, but its
+ implementation reached across multiple subsystems (wallet and mining),
+ so it is being deprecated to simplify the wallet-node interface.
+ Projects that are using `generate` for testing purposes should
+ transition to using the `generatetoaddress` RPC, which does not
+ require or use the wallet component. Calling `generatetoaddress` with
+ an address returned by the `getnewaddress` RPC gives the same
+ functionality as the old `generate` RPC. To continue using `generate`
+ in this version, restart bitcoind with the `-deprecatedrpc=generate`
+ configuration option.
+
+- Be reminded that parts of the `validateaddress` command have been
+ deprecated and moved to `getaddressinfo`. The following deprecated
+ fields have moved to `getaddressinfo`: `ismine`, `iswatchonly`,
+ `script`, `hex`, `pubkeys`, `sigsrequired`, `pubkey`, `embedded`,
+ `iscompressed`, `label`, `timestamp`, `hdkeypath`, `hdmasterkeyid`.
+
+- The `addresses` field has been removed from the `validateaddress`
+ and `getaddressinfo` RPC methods. This field was confusing since
+ it referred to public keys using their P2PKH address. Clients
+ should use the `embedded.address` field for P2SH or P2WSH wrapped
+ addresses, and `pubkeys` for inspecting multisig participants.
+
+REST changes
+------------
+
+- A new `/rest/blockhashbyheight/` endpoint is added for fetching the
+ hash of the block in the current best blockchain based on its height
+ (how many blocks it is after the Genesis Block).
+
+Graphical User Interface (GUI)
+------------------------------
+
+- A new Window menu is added alongside the existing File, Settings, and
+ Help menus. Several items from the other menus that opened new
+ windows have been moved to this new Window menu.
+
+- In the Send tab, the checkbox for "pay only the required fee" has been
+ removed. Instead, the user can simply decrease the value in the
+ Custom Feerate field all the way down to the node's configured minimum
+ relay fee.
+
+- In the Overview tab, the watch-only balance will be the only balance
+ shown if the wallet was created using the `createwallet` RPC and the
+ `disable_private_keys` parameter was set to true.
+
+- The launch-on-startup option is no longer available on macOS if
+ compiled with macosx min version greater than 10.11 (use
+ CXXFLAGS="-mmacosx-version-min=10.11"
+ CFLAGS="-mmacosx-version-min=10.11" for setting the deployment sdk
+ version)
+
+Tools
+-----
+
+- A new `bitcoin-wallet` tool is now distributed alongside Bitcoin
+ Core's other executables. Without needing to use any RPCs, this tool
+ can currently create a new wallet file or display some basic
+ information about an existing wallet, such as whether the wallet is
+ encrypted, whether it uses an HD seed, how many transactions it
+ contains, and how many address book entries it has.
+
+Planned changes
+===============
+
+This section describes planned changes to Bitcoin Core that may affect
+other Bitcoin software and services.
+
+- Since version 0.16.0, Bitcoin Core’s built-in wallet has defaulted to
+ generating P2SH-wrapped segwit addresses when users want to receive
+ payments. These addresses are backwards compatible with all
+ widely-used software. Starting with Bitcoin Core 0.20 (expected about
+ a year after 0.18), Bitcoin Core will default to native segwit
+ addresses (bech32) that provide additional fee savings and other
+ benefits. Currently, many wallets and services already support sending
+ to bech32 addresses, and if the Bitcoin Core project sees enough
+ additional adoption, it will instead default to bech32 receiving
+ addresses in Bitcoin Core 0.19 (approximately November 2019).
+ P2SH-wrapped segwit addresses will continue to be provided if the user
+ requests them in the GUI or by RPC, and anyone who doesn’t want the
+ update will be able to configure their default address type.
+ (Similarly, pioneering users who want to change their default now may
+ set the `addresstype=bech32` configuration option in any Bitcoin Core
+ release from 0.16.0 up.)
+
+Deprecated P2P messages
+-----------------------
+
+- BIP 61 reject messages are now deprecated. Reject messages have no use
+ case on the P2P network and are only logged for debugging by most
+ network nodes. Furthermore, they increase bandwidth and can be harmful
+ for privacy and security. It has been possible to disable BIP 61
+ messages since v0.17 with the `-enablebip61=0` option. BIP 61 messages
+ will be disabled by default in a future version, before being removed
+ entirely.
+
+Low-level changes
+=================
+
+This section describes RPC changes mainly useful for testing, mostly not
+relevant in production. The changes are mentioned for completeness.
+
+RPC
+---
+
+- The `submitblock` RPC previously returned the reason a rejected block
+ was invalid the first time it processed that block, but returned a
+ generic "duplicate" rejection message on subsequent occasions it
+ processed the same block. It now always returns the fundamental
+ reason for rejecting an invalid block and only returns "duplicate" for
+ valid blocks it has already accepted.
+
+- A new `submitheader` RPC allows submitting block headers independently
+ from their block. This is likely only useful for testing.
+
+- The `signrawtransactionwithkey` and `signrawtransactionwithwallet`
+ RPCs have been modified so that they also optionally accept a
+ `witnessScript`, the witness script in the case of a P2WSH or
+ P2SH-P2WSH output. This is compatible with the change to
+ `listunspent`.
+
+- For the `walletprocesspsbt` and `walletcreatefundedpsbt` RPCs, if the
+ `bip32derivs` parameter is set to true but the key metadata for a
+ public key has not been updated yet, then that key will have a
+ derivation path as if it were just an independent key (i.e. no
+ derivation path and its master fingerprint is itself).
+
+Configuration
+-------------
+
+- The `-usehd` configuration option was removed in version 0.16. From
+ that version onwards, all new wallets created are hierarchical
+ deterministic wallets. This release makes specifying `-usehd` an
+ invalid configuration option.
+
+Network
+-------
+
+- This release allows peers that your node automatically disconnected
+ for misbehavior (e.g. sending invalid data) to reconnect to your node
+ if you have unused incoming connection slots. If your slots fill up,
+ a misbehaving node will be disconnected to make room for nodes without
+ a history of problems (unless the misbehaving node helps your node in
+ some other way, such as by connecting to a part of the Internet from
+ which you don't have many other peers). Previously, Bitcoin Core
+ banned the IP addresses of misbehaving peers for a period of time
+ (default of 1 day); this was easily circumvented by attackers with
+ multiple IP addresses. If you manually ban a peer, such as by using
+ the `setban` RPC, all connections from that peer will still be
+ rejected.
+
+Wallet
+-------
+
+- The key metadata will need to be upgraded the first time that the HD
+ seed is available. For unencrypted wallets this will occur on wallet
+ loading. For encrypted wallets this will occur the first time the
+ wallet is unlocked.
+
+- Newly encrypted wallets will no longer require restarting the
+ software. Instead such wallets will be completely unloaded and
+ reloaded to achieve the same effect.
+
+- A sub-project of Bitcoin Core now provides Hardware Wallet Interaction
+ (HWI) scripts that allow command-line users to use several popular
+ hardware key management devices with Bitcoin Core. See their [project
+ page](https://github.com/bitcoin-core/HWI#readme) for details.
+
+Security
+--------
+
+- This release changes the Random Number Generator (RNG) used from
+ OpenSSL to Bitcoin Core's own implementation, although entropy
+ gathered by Bitcoin Core is fed out to OpenSSL and then read back in
+ when the program needs strong randomness. This moves Bitcoin Core a
+ little closer to no longer needing to depend on OpenSSL, a dependency
+ that has caused security issues in the past. The new implementation
+ gathers entropy from multiple sources, including from hardware
+ supporting the rdseed CPU instruction.
+
+Changes for particular platforms
+--------------------------------
+
+- On macOS, Bitcoin Core now opts out of application CPU throttling
+ ("app nap") during initial blockchain download, when catching up from
+ over 100 blocks behind the current chain tip, or when reindexing chain
+ data. This helps prevent these operations from taking an excessively
+ long time because the operating system is attempting to conserve
+ power.
+
+0.18.0 change log
+=================
+
+### Consensus
+- #14247 Fix crash bug with duplicate inputs within a transaction (TheBlueMatt)
+
+### Mining
+- #14811 Mining: Enforce that segwit option must be set in GBT (jnewbery)
+
+### Block and transaction handling
+- #13310 Report progress in ReplayBlocks while rolling forward (promag)
+- #13783 validation: Pass tx pool reference into CheckSequenceLocks (MarcoFalke)
+- #14834 validation: Assert that pindexPrev is non-null when required (kallewoof)
+- #14085 index: Fix for indexers skipping genesis block (jimpo)
+- #14963 mempool, validation: Explain `cs_main` locking semantics (MarcoFalke)
+- #15193 Default `-whitelistforcerelay` to off (sdaftuar)
+- #15429 Update `assumevalid`, `minimumchainwork`, and `getchaintxstats` to height 563378 (gmaxwell)
+- #15552 Granular invalidateblock and RewindBlockIndex (MarcoFalke)
+- #14841 Move CheckBlock() call to critical section (hebasto)
+
+### P2P protocol and network code
+- #14025 Remove dead code for nVersion=10300 (MarcoFalke)
+- #12254 BIP 158: Compact Block Filters for Light Clients (jimpo)
+- #14073 blockfilter: Avoid out-of-bounds script access (jimpo)
+- #14140 Switch nPrevNodeCount to vNodesSize (pstratem)
+- #14027 Skip stale tip checking if outbound connections are off or if reindexing (gmaxwell)
+- #14532 Never bind `INADDR_ANY` by default, and warn when doing so explicitly (luke-jr)
+- #14733 Make peer timeout configurable, speed up very slow test and ensure correct code path tested (zallarak)
+- #14336 Implement poll (pstratem)
+- #15051 IsReachable is the inverse of IsLimited (DRY). Includes unit tests (mmachicao)
+- #15138 Drop IsLimited in favor of IsReachable (Empact)
+- #14605 Return of the Banman (dongcarl)
+- #14970 Add dnsseed.emzy.de to DNS seeds (Emzy)
+- #14929 Allow connections from misbehavior banned peers (gmaxwell)
+- #15345 Correct comparison of addr count (dongcarl)
+- #15201 Add missing locking annotation for vNodes. vNodes is guarded by cs_vNodes (practicalswift)
+- #14626 Select orphan transaction uniformly for eviction (sipa)
+- #15486 Ensure tried collisions resolve, and allow feeler connections to existing outbound netgroups (sdaftuar)
+
+### Wallet
+- #13962 Remove unused `dummy_tx` variable from FillPSBT (dongcarl)
+- #13967 Don't report `minversion` wallet entry as unknown (instagibbs)
+- #13988 Add checks for settxfee reasonableness (ajtowns)
+- #12559 Avoid locking `cs_main` in some wallet RPC (promag)
+- #13631 Add CMerkleTx::IsImmatureCoinBase method (Empact)
+- #14023 Remove accounts RPCs (jnewbery)
+- #13825 Kill accounts (jnewbery)
+- #10605 Add AssertLockHeld assertions in CWallet::ListCoins (ryanofsky)
+- #12490 Remove deprecated wallet rpc features from `bitcoin_server` (jnewbery)
+- #14138 Set `encrypted_batch` to nullptr after delete. Avoid double free in the case of NDEBUG (practicalswift)
+- #14168 Remove `ENABLE_WALLET` from `libbitcoin_server.a` (jnewbery)
+- #12493 Reopen CDBEnv after encryption instead of shutting down (achow101)
+- #14282 Remove `-usehd` option (jnewbery)
+- #14146 Remove trailing separators from `-walletdir` arg (PierreRochard)
+- #14291 Add ListWalletDir utility function (promag)
+- #14468 Deprecate `generate` RPC method (jnewbery)
+- #11634 Add missing `cs_wallet`/`cs_KeyStore` locks to wallet (practicalswift)
+- #14296 Remove `addwitnessaddress` (jnewbery)
+- #14451 Add BIP70 deprecation warning and allow building GUI without BIP70 support (jameshilliard)
+- #14320 Fix duplicate fileid detection (ken2812221)
+- #14561 Remove `fs::relative` call and fix listwalletdir tests (promag)
+- #14454 Add SegWit support to importmulti (MeshCollider)
+- #14410 rpcwallet: `ischange` field for `getaddressinfo` RPC (mrwhythat)
+- #14350 Add WalletLocation class (promag)
+- #14689 Require a public key to be retrieved when signing a P2PKH input (achow101)
+- #14478 Show error to user when corrupt wallet unlock fails (MeshCollider)
+- #14411 Restore ability to list incoming transactions by label (ryanofsky)
+- #14552 Detect duplicate wallet by comparing the db filename (ken2812221)
+- #14678 Remove redundant KeyOriginInfo access, already done in CreateSig (instagibbs)
+- #14477 Add ability to convert solvability info to descriptor (sipa)
+- #14380 Fix assert crash when specified change output spend size is unknown (instagibbs)
+- #14760 Log env path in `BerkeleyEnvironment::Flush` (promag)
+- #14646 Add expansion cache functions to descriptors (unused for now) (sipa)
+- #13076 Fix ScanForWalletTransactions to return an enum indicating scan result: `success` / `failure` / `user_abort` (Empact)
+- #14821 Replace CAffectedKeysVisitor with descriptor based logic (sipa)
+- #14957 Initialize `stop_block` in CWallet::ScanForWalletTransactions (Empact)
+- #14565 Overhaul `importmulti` logic (sipa)
+- #15039 Avoid leaking nLockTime fingerprint when anti-fee-sniping (MarcoFalke)
+- #14268 Introduce SafeDbt to handle Dbt with free or `memory_cleanse` raii-style (Empact)
+- #14711 Remove uses of chainActive and mapBlockIndex in wallet code (ryanofsky)
+- #15279 Clarify rescanblockchain doc (MarcoFalke)
+- #15292 Remove `boost::optional`-related false positive -Wmaybe-uninitialized warnings on GCC compiler (hebasto)
+- #13926 [Tools] bitcoin-wallet - a tool for creating and managing wallets offline (jnewbery)
+- #11911 Free BerkeleyEnvironment instances when not in use (ryanofsky)
+- #15235 Do not import private keys to wallets with private keys disabled (achow101)
+- #15263 Descriptor expansions only need pubkey entries for PKH/WPKH (sipa)
+- #15322 Add missing `cs_db` lock (promag)
+- #15297 Releases dangling files on `BerkeleyEnvironment::Close` (promag)
+- #14491 Allow descriptor imports with importmulti (MeshCollider)
+- #15365 Add lock annotation for mapAddressBook (MarcoFalke)
+- #15226 Allow creating blank (empty) wallets (alternative) (achow101)
+- #15390 [wallet-tool] Close bdb when flushing wallet (jnewbery)
+- #15334 Log absolute paths for the wallets (hebasto)
+- #14978 Factor out PSBT utilities from RPCs for use in GUI code; related refactoring (gwillen)
+- #14481 Add P2SH-P2WSH support to listunspent RPC (MeshCollider)
+- #14021 Import key origin data through descriptors in importmulti (achow101)
+- #14075 Import watch only pubkeys to the keypool if private keys are disabled (achow101)
+- #15368 Descriptor checksums (sipa)
+- #15433 Use a single wallet batch for `UpgradeKeyMetadata` (jonasschnelli)
+- #15408 Remove unused `TransactionError` constants (MarcoFalke)
+- #15583 Log and ignore errors in ListWalletDir and IsBerkeleyBtree (promag)
+- #14195 Pass privkey export DER compression flag correctly (fingera)
+- #15299 Fix assertion in `CKey::SignCompact` (promag)
+- #14437 Start to separate wallet from node (ryanofsky)
+- #15749 Fix: importmulti only imports origin info for PKH outputs (sipa)
+
+### RPC and other APIs
+- #12842 Prevent concurrent `savemempool` (promag)
+- #13987 Report `minfeefilter` value in `getpeerinfo` RPC (ajtowns)
+- #13891 Remove getinfo deprecation warning (jnewbery)
+- #13399 Add `submitheader` (MarcoFalke)
+- #12676 Show `bip125-replaceable` flag, when retrieving mempool entries (dexX7)
+- #13723 PSBT key path cleanups (sipa)
+- #14008 Preserve a format of RPC command definitions (kostyantyn)
+- #9332 Let wallet `importmulti` RPC accept labels for standard scriptPubKeys (ryanofsky)
+- #13983 Return more specific reject reason for submitblock (MarcoFalke)
+- #13152 Add getnodeaddresses RPC command (chris-belcher)
+- #14298 rest: Improve performance for JSON calls (alecalve)
+- #14297 Remove warning for removed estimatefee RPC (jnewbery)
+- #14373 Consistency fixes for RPC descriptions (ch4ot1c)
+- #14150 Add key origin support to descriptors (sipa)
+- #14518 Always throw in getblockstats if `-txindex` is required (promag)
+- #14060 ZMQ: add options to configure outbound message high water mark, aka SNDHWM (mruddy)
+- #13381 Add possibility to preserve labels on importprivkey (marcoagner)
+- #14530 Use `RPCHelpMan` to generate RPC doc strings (MarcoFalke)
+- #14720 Correctly name RPC arguments (MarcoFalke)
+- #14726 Use `RPCHelpMan` for all RPCs (MarcoFalke)
+- #14796 Pass argument descriptions to `RPCHelpMan` (MarcoFalke)
+- #14670 http: Fix HTTP server shutdown (promag)
+- #14885 Assert that named arguments are unique in `RPCHelpMan` (promag)
+- #14877 Document default values for optional arguments (MarcoFalke)
+- #14875 RPCHelpMan: Support required arguments after optional ones (MarcoFalke)
+- #14993 Fix data race (UB) in InterruptRPC() (practicalswift)
+- #14653 rpcwallet: Add missing transaction categories to RPC helptexts (andrewtoth)
+- #14981 Clarify RPC `getrawtransaction`'s time help text (benthecarman)
+- #12151 Remove `cs_main` lock from blockToJSON and blockheaderToJSON (promag)
+- #15078 Document `bytessent_per_msg` and `bytesrecv_per_msg` (MarcoFalke)
+- #15057 Correct `reconsiderblock `help text, add test (MarcoFalke)
+- #12153 Avoid permanent `cs_main` lock in `getblockheader` (promag)
+- #14982 Add `getrpcinfo` command (promag)
+- #15122 Expand help text for `importmulti` changes (jnewbery)
+- #15186 remove duplicate solvable field from `getaddressinfo` (fanquake)
+- #15209 zmq: log outbound message high water mark when reusing socket (fanquake)
+- #15177 rest: Improve tests and documention of /headers and /block (promag)
+- #14353 rest: Add blockhash call, fetch blockhash by height (jonasschnelli)
+- #15248 Compile on GCC4.8 (MarcoFalke)
+- #14987 RPCHelpMan: Pass through Result and Examples (MarcoFalke)
+- #15159 Remove lookup to UTXO set from GetTransaction (amitiuttarwar)
+- #15245 remove deprecated mentions of signrawtransaction from fundraw help (instagibbs)
+- #14667 Add `deriveaddresses` RPC util method (Sjors)
+- #15357 Don't ignore `-maxtxfee` when wallet is disabled (JBaczuk)
+- #15337 Fix for segfault if combinepsbt called with empty inputs (benthecarman)
+- #14918 RPCHelpMan: Check default values are given at compile-time (MarcoFalke)
+- #15383 mining: Omit uninitialized currentblockweight, currentblocktx (MarcoFalke)
+- #13932 Additional utility RPCs for PSBT (achow101)
+- #15401 Actually throw help when passed invalid number of params (MarcoFalke)
+- #15471 rpc/gui: Remove 'Unknown block versions being mined' warning (laanwj)
+- #15497 Consistent range arguments in scantxoutset/importmulti/deriveaddresses (sipa)
+- #15510 deriveaddresses: add range to CRPCConvertParam (Sjors)
+- #15582 Fix overflow bug in analyzepsbt fee: CAmount instead of int (sipa)
+- #13424 Consistently validate txid / blockhash length and encoding in rpc calls (Empact)
+- #15750 Remove the addresses field from the getaddressinfo return object (jnewbery)
+
+### GUI
+- #13634 Compile `boost::signals2` only once (MarcoFalke)
+- #13248 Make proxy icon from statusbar clickable (mess110)
+- #12818 TransactionView: highlight replacement tx after fee bump (Sjors)
+- #13529 Use new Qt5 connect syntax (promag)
+- #14162 Also log and print messages or questions like bitcoind (MarcoFalke)
+- #14385 Avoid system harfbuzz and bz2 (theuni)
+- #14450 Fix QCompleter popup regression (hebasto)
+- #14177 Set C locale for amountWidget (hebasto)
+- #14374 Add `Blocksdir` to Debug window (hebasto)
+- #14554 Remove unused `adjustedTime` parameter (hebasto)
+- #14228 Enable system tray icon by default if available (hebasto)
+- #14608 Remove the "Pay only required fee…" checkbox (hebasto)
+- #14521 qt, docs: Fix `bitcoin-qt -version` output formatting (hebasto)
+- #13966 When private key is disabled, only show watch-only balance (ken2812221)
+- #14828 Remove hidden columns in coin control dialog (promag)
+- #14783 Fix `boost::signals2::no_slots_error` in early calls to InitWarning (promag)
+- #14854 Cleanup SplashScreen class (hebasto)
+- #14801 Use window() instead of obsolete topLevelWidget() (hebasto)
+- #14573 Add Window menu (promag)
+- #14979 Restore < Qt5.6 compatibility for addAction (jonasschnelli)
+- #14975 Refactoring with QString::toNSString() (hebasto)
+- #15000 Fix broken notificator on GNOME (hebasto)
+- #14375 Correct misleading "overridden options" label (hebasto)
+- #15007 Notificator class refactoring (hebasto)
+- #14784 Use `WalletModel*` instead of the wallet name as map key (promag)
+- #11625 Add BitcoinApplication & RPCConsole tests (ryanofsky)
+- #14517 Fix start with the `-min` option (hebasto)
+- #13216 implements concept for different disk sizes on intro (marcoagner)
+- #15114 Replace remaining 0 with nullptr (Empact)
+- #14594 Fix minimized window bug on Linux (hebasto)
+- #14556 Fix confirmed transaction labeled "open" (#13299) (hebasto)
+- #15149 Show current wallet name in window title (promag)
+- #15136 "Peers" tab overhaul (hebasto)
+- #14250 Remove redundant stopThread() and stopExecutor() signals (hebasto)
+- #15040 Add workaround for QProgressDialog bug on macOS (hebasto)
+- #15101 Add WalletController (promag)
+- #15178 Improve "help-console" message (hebasto)
+- #15210 Fix window title update (promag)
+- #15167 Fix wallet selector size adjustment (hebasto)
+- #15208 Remove macOS launch-at-startup when compiled with > macOS 10.11, fix memory mismanagement (jonasschnelli)
+- #15163 Correct units for "-dbcache" and "-prune" (hebasto)
+- #15225 Change the receive button to respond to keypool state changing (achow101)
+- #15280 Fix shutdown order (promag)
+- #15203 Fix issue #9683 "gui, wallet: random abort (segmentation fault) (dooglus)
+- #15091 Fix model overlay header sync (jonasschnelli)
+- #15153 Add Open Wallet menu (promag)
+- #15183 Fix `m_assumed_blockchain_size` variable value (marcoagner)
+- #15063 If BIP70 is disabled, attempt to fall back to BIP21 parsing (luke-jr)
+- #15195 Add Close Wallet action (promag)
+- #15462 Fix async open wallet call order (promag)
+- #15801 Bugfix: GUI: Options: Initialise prune setting range before loading current value, and remove upper bound limit (luke-jr)
+
+### Build system
+- #13955 gitian: Bump descriptors for (0.)18 (fanquake)
+- #13899 Enable -Wredundant-decls where available. Remove redundant redeclarations (practicalswift)
+- #13665 Add RISC-V support to gitian (ken2812221)
+- #14062 Generate MSVC project files via python script (ken2812221)
+- #14037 Add README.md to linux release tarballs (hebasto)
+- #14183 Remove unused Qt 4 dependencies (ken2812221)
+- #14127 Avoid getifaddrs when unavailable (greenaddress)
+- #14184 Scripts and tools: increased timeout downloading (cisba)
+- #14204 Move `interfaces/*` to `libbitcoin_server` (laanwj)
+- #14208 Actually remove `ENABLE_WALLET` (jnewbery)
+- #14212 Remove libssl from LDADD unless GUI (MarcoFalke)
+- #13578 Upgrade zeromq to 4.2.5 and avoid deprecated zeromq API functions (mruddy)
+- #14281 lcov: filter /usr/lib/ from coverage reports (MarcoFalke)
+- #14325 gitian: Use versioned unsigned tarballs instead of generically named ones (achow101)
+- #14253 During 'make clean', remove some files that are currently missed (murrayn)
+- #14455 Unbreak `make clean` (jamesob)
+- #14495 Warn (don't fail!) on spelling errors (practicalswift)
+- #14496 Pin to specific versions of Python packages we install from PyPI in Travis (practicalswift)
+- #14568 Fix Qt link order for Windows build (ken2812221)
+- #14252 Run functional tests and benchmarks under the undefined behaviour sanitizer (UBSan) (practicalswift)
+- #14612 Include full version number in released file names (achow101)
+- #14840 Remove duplicate libconsensus linking in test make (AmirAbrams)
+- #14564 Adjust configure so that only BIP70 is disabled when protobuf is missing instead of the GUI (jameshilliard)
+- #14883 Add `--retry 5` to curl opts in `install_db4.sh` (qubenix)
+- #14701 Add `CLIENT_VERSION_BUILD` to CFBundleGetInfoString (fanquake)
+- #14849 Qt 5.9.7 (fanquake)
+- #15020 Add names to Travis jobs (gkrizek)
+- #15047 Allow to configure --with-sanitizers=fuzzer (MarcoFalke)
+- #15154 Configure: bitcoin-tx doesn't need libevent, so don't pull it in (luke-jr)
+- #15175 Drop macports support (Empact)
+- #15308 Restore compatibility with older boost (Empact)
+- #15407 msvc: Fix silent merge conflict between #13926 and #14372 part II (ken2812221)
+- #15388 Makefile.am: add rule for src/bitcoin-wallet (Sjors)
+- #15393 Bump minimum Qt version to 5.5.1 (Sjors)
+- #15285 Prefer Python 3.4 even if newer versions are present on the system (Sjors)
+- #15398 msvc: Add rapidcheck property tests (ken2812221)
+- #15431 msvc: scripted-diff: Remove NDEBUG pre-define in project file (ken2812221)
+- #15549 gitian: Improve error handling (laanwj)
+- #15548 use full version string in setup.exe (MarcoFalke)
+- #11526 Visual Studio build configuration for Bitcoin Core (sipsorcery)
+- #15110 build\_msvc: Fix the build problem in `libbitcoin_server` (Mr-Leshiy)
+- #14372 msvc: build secp256k1 and leveldb locally (ken2812221)
+- #15325 msvc: Fix silent merge conflict between #13926 and #14372 (ken2812221)
+- #15391 Add compile time verification of assumptions we're currently making implicitly/tacitly (practicalswift)
+- #15503 msvc: Use a single file to specify the include path (ken2812221)
+- #13765 contrib: Add gitian build support for github pull request (ken2812221)
+- #15809 gitignore: plist and dat (jamesob)
+
+### Tests and QA
+- #15405 appveyor: Clean cache when build configuration changes (Sjors)
+- #13953 Fix deprecation in bitcoin-util-test.py (isghe)
+- #13963 Replace usage of tostring() with tobytes() (dongcarl)
+- #13964 ci: Add appveyor ci (ken2812221)
+- #13997 appveyor: fetch the latest port data (ken2812221)
+- #13707 Add usage note to check-rpc-mappings.py (masonicboom)
+- #14036 travis: Run unit tests --with-sanitizers=undefined (MarcoFalke)
+- #13861 Add testing of `value_ret` for SelectCoinsBnB (Empact)
+- #13863 travis: Move script sections to files in `.travis/` subject to shellcheck (scravy)
+- #14081 travis: Fix missing differentiation between unit and functional tests (scravy)
+- #14042 travis: Add cxxflags=-wno-psabi at arm job (ken2812221)
+- #14051 Make `combine_logs.py` handle multi-line logs (jnewbery)
+- #14093 Fix accidental trunction from int to bool (practicalswift)
+- #14108 Add missing locking annotations and locks (`g_cs_orphans`) (practicalswift)
+- #14088 Don't assert(…) with side effects (practicalswift)
+- #14086 appveyor: Use clcache to speed up build (ken2812221)
+- #13954 Warn (don't fail!) on spelling errors. Fix typos reported by codespell (practicalswift)
+- #12775 Integration of property based testing into Bitcoin Core (Christewart)
+- #14119 Read reject reasons from debug log, not P2P messages (MarcoFalke)
+- #14189 Fix silent merge conflict in `wallet_importmulti` (MarcoFalke)
+- #13419 Speed up `knapsack_solver_test` by not recreating wallet 100 times (lucash-dev)
+- #14199 Remove redundant BIP174 test from `rpc_psbt.json` (araspitzu)
+- #14179 Fixups to "Run all tests even if wallet is not compiled" (MarcoFalke)
+- #14225 Reorder tests and move most of extended tests up to normal tests (ken2812221)
+- #14236 `generate` --> `generatetoaddress` change to allow tests run without wallet (sanket1729)
+- #14287 Use MakeUnique to construct objects owned by `unique_ptrs` (practicalswift)
+- #14007 Run functional test on Windows and enable it on Appveyor (ken2812221)
+- #14275 Write the notification message to different files to avoid race condition in `feature_notifications.py` (ken2812221)
+- #14306 appveyor: Move AppVeyor YAML to dot-file-style YAML (MitchellCash)
+- #14305 Enforce critical class instance attributes in functional tests, fix segwit test specificity (JustinTArthur)
+- #12246 Bugfix: Only run bitcoin-tx tests when bitcoin-tx is enabled (luke-jr)
+- #14316 Exclude all tests with difference parameters in `--exclude` list (ken2812221)
+- #14381 Add missing call to `skip_if_no_cli()` (practicalswift)
+- #14389 travis: Set codespell version to avoid breakage (MarcoFalke)
+- #14398 Don't access out of bounds array index: array[sizeof(array)] (Empact)
+- #14419 Remove `rpc_zmq.py` (jnewbery)
+- #14241 appveyor: Script improvement (ken2812221)
+- #14413 Allow closed RPC handler in `assert_start_raises_init_error` (ken2812221)
+- #14324 Run more tests with wallet disabled (MarcoFalke)
+- #13649 Allow arguments to be forwarded to flake8 in lint-python.sh (jamesob)
+- #14465 Stop node before removing the notification file (ken2812221)
+- #14460 Improve 'CAmount' tests (hebasto)
+- #14456 forward timeouts properly in `send_blocks_and_test` (jamesob)
+- #14527 Revert "Make qt wallet test compatible with qt4" (MarcoFalke)
+- #14504 Show the progress of functional tests (isghe)
+- #14559 appveyor: Enable multiwallet tests (ken2812221)
+- #13515 travis: Enable qt for all jobs (ken2812221)
+- #14571 Test that nodes respond to `getdata` with `notfound` (MarcoFalke)
+- #14569 Print dots by default in functional tests (ken2812221)
+- #14631 Move deterministic address import to `setup_nodes` (jnewbery)
+- #14630 test: Remove travis specific code (MarcoFalke)
+- #14528 travis: Compile once on xenial (MarcoFalke)
+- #14092 Dry run `bench_bitcoin` as part `make check` to allow for quick identification of assertion/sanitizer failures in benchmarking code (practicalswift)
+- #14664 `example_test.py`: fixup coinbase height argument, derive number clearly (instagibbs)
+- #14522 Add invalid P2P message tests (jamesob)
+- #14619 Fix value display name in `test_runner` help text (merland)
+- #14672 Send fewer spam messages in `p2p_invalid_messages` (jamesob)
+- #14673 travis: Fail the ubsan travis build in case of newly introduced ubsan errors (practicalswift)
+- #14665 appveyor: Script improvement part II (ken2812221)
+- #14365 Add Python dead code linter (vulture) to Travis (practicalswift)
+- #14693 `test_node`: `get_mem_rss` fixups (MarcoFalke)
+- #14714 util.h: explicitly include required QString header (1Il1)
+- #14705 travis: Avoid timeout on verify-commits check (MarcoFalke)
+- #14770 travis: Do not specify sudo in `.travis` (scravy)
+- #14719 Check specific reject reasons in `feature_block` (MarcoFalke)
+- #14771 Add `BOOST_REQUIRE` to getters returning optional (MarcoFalke)
+- #14777 Add regtest for JSON-RPC batch calls (domob1812)
+- #14764 travis: Run thread sanitizer on unit tests (MarcoFalke)
+- #14400 Add Benchmark to test input de-duplication worst case (JeremyRubin)
+- #14812 Fix `p2p_invalid_messages` on macOS (jamesob)
+- #14813 Add `wallet_encryption` error tests (MarcoFalke)
+- #14820 Fix `descriptor_tests` not checking ToString output of public descriptors (ryanofsky)
+- #14794 Add AddressSanitizer (ASan) Travis build (practicalswift)
+- #14819 Bugfix: `test/functional/mempool_accept`: Ensure oversize transaction is actually oversize (luke-jr)
+- #14822 bench: Destroy wallet txs instead of leaking their memory (MarcoFalke)
+- #14683 Better `combine_logs.py` behavior (jamesob)
+- #14231 travis: Save cache even when build or test fail (ken2812221)
+- #14816 Add CScriptNum decode python implementation in functional suite (instagibbs)
+- #14861 Modify `rpc_bind` to conform to #14532 behaviour (dongcarl)
+- #14864 Run scripted-diff in subshell (dongcarl)
+- #14795 Allow `test_runner` command line to receive parameters for each test (marcoagner)
+- #14788 Possible fix the permission error when the tests open the cookie file (ken2812221)
+- #14857 `wallet_keypool_topup.py`: Test for all keypool address types (instagibbs)
+- #14886 Refactor importmulti tests (jnewbery)
+- #14908 Removed implicit CTransaction constructor calls from tests and benchmarks (lucash-dev)
+- #14903 Handle ImportError explicitly, improve comparisons against None (daniel-s-ingram)
+- #14884 travis: Enforce python 3.4 support through linter (Sjors)
+- #14940 Add test for truncated pushdata script (MarcoFalke)
+- #14926 consensus: Check that final transactions are valid (MarcoFalke)
+- #14937 travis: Fix travis would always be green even if it fail (ken2812221)
+- #14953 Make `g_insecure_rand_ctx` `thread_local` (MarcoFalke)
+- #14931 mempool: Verify prioritization is dumped correctly (MarcoFalke)
+- #14935 Test for expected return values when calling functions returning a success code (practicalswift)
+- #14969 Fix `cuckoocache_tests` TSAN failure introduced in 14935 (practicalswift)
+- #14964 Fix race in `mempool_accept` (MarcoFalke)
+- #14829 travis: Enable functional tests in the threadsanitizer (tsan) build job (practicalswift)
+- #14985 Remove `thread_local` from `test_bitcoin` (MarcoFalke)
+- #15005 Bump timeout to run tests in travis thread sanitizer (MarcoFalke)
+- #15013 Avoid race in `p2p_timeouts` (MarcoFalke)
+- #14960 lint/format-strings: Correctly exclude escaped percent symbols (luke-jr)
+- #14930 pruning: Check that verifychain can be called when pruned (MarcoFalke)
+- #15022 Upgrade Travis OS to Xenial (gkrizek)
+- #14738 Fix running `wallet_listtransactions.py` individually through `test_runner.py` (kristapsk)
+- #15026 Rename `rpc_timewait` to `rpc_timeout` (MarcoFalke)
+- #15069 Fix `rpc_net.py` `pong` race condition (Empact)
+- #14790 Allow running `rpc_bind.py` --nonloopback test without IPv6 (kristapsk)
+- #14457 add invalid tx templates for use in functional tests (jamesob)
+- #14855 Correct ineffectual WithOrVersion from `transactions_tests` (Empact)
+- #15099 Use `std::vector` API for construction of test data (domob1812)
+- #15102 Run `invalid_txs.InputMissing` test in `feature_block` (MarcoFalke)
+- #15059 Add basic test for BIP34 (MarcoFalke)
+- #15108 Tidy up `wallet_importmulti.py` (amitiuttarwar)
+- #15164 Ignore shellcheck warning SC2236 (promag)
+- #15170 refactor/lint: Add ignored shellcheck suggestions to an array (koalaman)
+- #14958 Remove race between connecting and shutdown on separate connections (promag)
+- #15166 Pin shellcheck version (practicalswift)
+- #15196 Update all `subprocess.check_output` functions to be Python 3.4 compatible (gkrizek)
+- #15043 Build fuzz targets into seperate executables (MarcoFalke)
+- #15276 travis: Compile once on trusty (MarcoFalke)
+- #15246 Add tests for invalid message headers (MarcoFalke)
+- #15301 When testing with --usecli, unify RPC arg to cli arg conversion and handle dicts and lists (achow101)
+- #15247 Use wallet to retrieve raw transactions (MarcoFalke)
+- #15303 travis: Remove unused `functional_tests_config` (MarcoFalke)
+- #15330 Fix race in `p2p_invalid_messages` (MarcoFalke)
+- #15324 Make bloom tests deterministic (MarcoFalke)
+- #15328 travis: Revert "run extended tests once daily" (MarcoFalke)
+- #15327 Make test `updatecoins_simulation_test` deterministic (practicalswift)
+- #14519 add utility to easily profile node performance with perf (jamesob)
+- #15349 travis: Only exit early if compilation took longer than 30 min (MarcoFalke)
+- #15350 Drop RPC connection if --usecli (promag)
+- #15370 test: Remove unused --force option (MarcoFalke)
+- #14543 minor `p2p_sendheaders` fix of height in coinbase (instagibbs)
+- #13787 Test for Windows encoding issue (ken2812221)
+- #15378 Added missing tests for RPC wallet errors (benthecarman)
+- #15238 remove some magic mining constants in functional tests (instagibbs)
+- #15411 travis: Combine --disable-bip70 into existing job (MarcoFalke)
+- #15295 fuzz: Add `test/fuzz/test_runner.py` and run it in travis (MarcoFalke)
+- #15413 Add missing `cs_main` locks required when accessing pcoinsdbview, pcoinsTip or pblocktree (practicalswift)
+- #15399 fuzz: Script validation flags (MarcoFalke)
+- #15410 txindex: interrupt threadGroup before calling destructor (MarcoFalke)
+- #15397 Remove manual byte editing in `wallet_tx_clone` func test (instagibbs)
+- #15415 functional: allow custom cwd, use tmpdir as default (Sjors)
+- #15404 Remove `-txindex` to start nodes (amitiuttarwar)
+- #15439 remove `byte.hex()` to keep compatibility (AkioNak)
+- #15419 Always refresh cache to be out of ibd (MarcoFalke)
+- #15507 Bump timeout on tests that timeout on windows (MarcoFalke)
+- #15506 appveyor: fix cache issue and reduce dependencies build time (ken2812221)
+- #15485 add `rpc_misc.py`, mv test getmemoryinfo, add test mallocinfo (adamjonas)
+- #15321 Add `cs_main` lock annotations for mapBlockIndex (MarcoFalke)
+- #14128 lint: Make sure we read the command line inputs using UTF-8 decoding in python (ken2812221)
+- #14115 lint: Make all linters work under the default macos dev environment (build-osx.md) (practicalswift)
+- #15219 lint: Enable python linters via an array (Empact)
+
+### Platform support
+- #13866 utils: Use `_wfopen` and `_wfreopen` on windows (ken2812221)
+- #13886 utils: Run commands using UTF-8 string on windows (ken2812221)
+- #14192 utils: Convert `fs::filesystem_error` messages from local multibyte to UTF-8 on windows (ken2812221)
+- #13877 utils: Make fs::path::string() always return UTF-8 string on windows (ken2812221)
+- #13883 utils: Convert windows args to UTF-8 string (ken2812221)
+- #13878 utils: Add fstream wrapper to allow to pass unicode filename on windows (ken2812221)
+- #14426 utils: Fix broken windows filelock (ken2812221)
+- #14686 Fix windows build error if `--disable-bip70` (ken2812221)
+- #14922 windows: Set `_WIN32_WINNT` to 0x0601 (Windows 7) (ken2812221)
+- #13888 Call unicode API on Windows (ken2812221)
+- #15468 Use `fsbridge::ifstream` to fix Windows path issue (ken2812221)
+- #13734 Drop `boost::scoped_array` and use `wchar_t` API explicitly on Windows (ken2812221)
+- #13884 Enable bdb unicode support for Windows (ken2812221)
+
+### Miscellaneous
+- #13935 contrib: Adjust output to current test format (AkioNak)
+- #14097 validation: Log FormatStateMessage on ConnectBlock error in ConnectTip (MarcoFalke)
+- #13724 contrib: Support ARM and RISC-V symbol check (ken2812221)
+- #13159 Don't close old debug log file handle prematurely when trying to re-open (on SIGHUP) (practicalswift)
+- #14186 bitcoin-cli: don't translate command line options (HashUnlimited)
+- #14057 logging: Only log `using config file path_to_bitcoin.conf` message on startup if conf file exists (leishman)
+- #14164 Update univalue subtree (MarcoFalke)
+- #14272 init: Remove deprecated args from hidden args (MarcoFalke)
+- #14494 Error if # is used in rpcpassword in conf (MeshCollider)
+- #14742 Properly generate salt in rpcauth.py (dongcarl)
+- #14708 Warn unrecognised sections in the config file (AkioNak)
+- #14756 Improve rpcauth.py by using argparse and getpass modules (promag)
+- #14785 scripts: Fix detection of copyright holders (cornelius)
+- #14831 scripts: Use `#!/usr/bin/env bash` instead of `#!/bin/bash` (vim88)
+- #14869 Scripts: Add trusted key for samuel dobson (laanwj)
+- #14809 Tools: improve verify-commits.py script (jlopp)
+- #14624 Some simple improvements to the RNG code (sipa)
+- #14947 scripts: Remove python 2 import workarounds (practicalswift)
+- #15087 Error if rpcpassword contains hash in conf sections (MeshCollider)
+- #14433 Add checksum in gitian build scripts for ossl (TheCharlatan)
+- #15165 contrib: Allow use of github api authentication in github-merge (laanwj)
+- #14409 utils and libraries: Make 'blocksdir' always net specific (hebasto)
+- #14839 threads: Fix unitialized members in `sched_param` (fanquake)
+- #14955 Switch all RNG code to the built-in PRNG (sipa)
+- #15258 Scripts and tools: Fix `devtools/copyright_header.py` to always honor exclusions (Empact)
+- #12255 Update bitcoin.service to conform to init.md (dongcarl)
+- #15266 memory: Construct globals on first use (MarcoFalke)
+- #15347 Fix build after pr 15266 merged (hebasto)
+- #15351 Update linearize-hashes.py (OverlordQ)
+- #15358 util: Add setuphelpoptions() (MarcoFalke)
+- #15216 Scripts and tools: Replace script name with a special parameter (hebasto)
+- #15250 Use RdSeed when available, and reduce RdRand load (sipa)
+- #15278 Improve PID file error handling (hebasto)
+- #15270 Pull leveldb subtree (MarcoFalke)
+- #15456 Enable PID file creation on WIN (riordant)
+- #12783 macOS: disable AppNap during sync (krab)
+- #13910 Log progress while verifying blocks at level 4 (domob1812)
+- #15124 Fail AppInitMain if either disk space check fails (Empact)
+- #15117 Fix invalid memory write in case of failing mmap(…) in PosixLockedPageAllocator::AllocateLocked (practicalswift)
+- #14357 streams: Fix broken `streams_vector_reader` test. Remove unused `seek(size_t)`
+- #11640 Make `LOCK`, `LOCK2`, `TRY_LOCK` work with CWaitableCriticalSection (ryanofsky)
+- #14074 Use `std::unordered_set` instead of `set` in blockfilter interface (jimpo)
+- #15275 Add gitian PGP key for hebasto (hebasto)
+
+### Documentation
+- #14120 Notes about control port and read access to cookie (JBaczuk)
+- #14135 correct GetDifficulty doc after #13288 (fanquake)
+- #14013 Add new regtest ports in man following #10825 ports reattributions (ariard)
+- #14149 Remove misleading checkpoints comment in CMainParams (MarcoFalke)
+- #14153 Add disable-wallet section to OSX build instructions, update line in Unix instructions (bitstein)
+- #13662 Explain when reindex-chainstate can be used instead of reindex (Sjors)
+- #14207 `-help-debug` implies `-help` (laanwj)
+- #14213 Fix reference to lint-locale-dependence.sh (hebasto)
+- #14206 Document `-checklevel` levels (laanwj)
+- #14217 Add GitHub PR template (MarcoFalke)
+- #14331 doxygen: Fix member comments (MarcoFalke)
+- #14264 Split depends installation instructions per arch (MarcoFalke)
+- #14393 Add missing apt-get install (poiuty)
+- #14428 Fix macOS files description in qt/README.md (hebasto)
+- #14390 release process: RPC documentation (karel-3d)
+- #14472 getblocktemplate: use SegWit in example (Sjors)
+- #14497 Add doc/bitcoin-conf.md (hebasto)
+- #14526 Document lint tests (fanquake)
+- #14511 Remove explicit storage requirement from README.md (merland)
+- #14600 Clarify commit message guidelines (merland)
+- #14617 FreeBSD: Document Python 3 requirement for 'gmake check' (murrayn)
+- #14592 Add external interface consistency guarantees (MarcoFalke)
+- #14625 Make clear function argument case in dev notes (dongcarl)
+- #14515 Update OpenBSD build guide for 6.4 (fanquake)
+- #14436 Add comment explaining recentRejects-DoS behavior (jamesob)
+- #14684 conf: Remove deprecated options from docs, Other cleanup (MarcoFalke)
+- #14731 Improve scripted-diff developer docs (dongcarl)
+- #14778 A few minor formatting fixes and clarifications to descriptors.md (jnewbery)
+- #14448 Clarify rpcwallet flag url change (JBaczuk)
+- #14808 Clarify RPC rawtransaction documentation (jlopp)
+- #14804 Less confusing documentation for `torpassword` (fanquake)
+- #14848 Fix broken Gmane URL in security-check.py (cyounkins-bot)
+- #14882 developer-notes.md: Point out that UniValue deviates from upstream (Sjors)
+- #14909 Update minimum required Qt (fanquake)
+- #14914 Add nice table to files.md (emilengler)
+- #14741 Indicate `-rpcauth` option password hashing alg (dongcarl)
+- #14950 Add NSIS setup/install steps to windows docs (fanquake)
+- #13930 Better explain GetAncestor check for `m_failed_blocks` in AcceptBlockHeader (Sjors)
+- #14973 Improve Windows native build instructions (murrayn)
+- #15073 Botbot.me (IRC logs) not available anymore (anduck)
+- #15038 Get more info about GUI-related issue on Linux (hebasto)
+- #14832 Add more Doxygen information to Developer Notes (ch4ot1c)
+- #15128 Fix download link in doc/README.md (merland)
+- #15127 Clarifying testing instructions (benthecarman)
+- #15132 Add FreeBSD build notes link to doc/README.md (fanquake)
+- #15173 Explain what .python-version does (Sjors)
+- #15223 Add information about security to the JSON-RPC doc (harding)
+- #15249 Update python docs to reflect that wildcard imports are disallowed (Empact)
+- #15176 Get rid of badly named `doc/README_osx.md` (merland)
+- #15272 Correct logging return type and RPC example (fanquake)
+- #15244 Gdb attaching to process during tests has non-sudo solution (instagibbs)
+- #15332 Small updates to `getrawtransaction` description (amitiuttarwar)
+- #15354 Add missing `bitcoin-wallet` tool manpages (MarcoFalke)
+- #15343 netaddress: Make IPv4 loopback comment more descriptive (dongcarl)
+- #15353 Minor textual improvements in `translation_strings_policy.md` (merland)
+- #15426 importmulti: add missing description of keypool option (harding)
+- #15425 Add missing newline to listunspent help for witnessScript (harding)
+- #15348 Add separate productivity notes document (dongcarl)
+- #15416 Update FreeBSD build guide for 12.0 (fanquake)
+- #15222 Add info about factors that affect dependency list (merland)
+- #13676 Explain that mempool memory is added to `-dbcache` (Sjors)
+- #15273 Slight tweak to the verify-commits script directions (droark)
+- #15477 Remove misleading hint in getrawtransaction (MarcoFalke)
+- #15489 Update release process for snap package (MarcoFalke)
+- #15524 doc: Remove berkeleydb PPA from linux build instructions (MarcoFalke)
+- #15559 Correct `analyzepsbt` rpc doc (fanquake)
+- #15194 Add comment describing `fDisconnect` behavior (dongcarl)
+- #15754 getrpcinfo docs (benthecarman)
+- #15763 Update bips.md for 0.18.0 (sipa)
+- #15757 List new RPCs in psbt.md and descriptors.md (sipa)
+- #15765 correct bitcoinconsensus_version in shared-libraries.md (fanquake)
+- #15792 describe onlynet option in doc/tor.md (jonatack)
+- #15802 mention creating application support bitcoin folder on OSX (JimmyMow)
+- #15799 Clarify RPC versioning (MarcoFalke)
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- 1Il1
+- 251
+- Aaron Clauson
+- Adam Jonas
+- Akio Nakamura
+- Alexander Leishman
+- Alexey Ivanov
+- Alexey Poghilenkov
+- Amir Abrams
+- Amiti Uttarwar
+- Andrew Chow
+- andrewtoth
+- Anthony Towns
+- Antoine Le Calvez
+- Antoine Riard
+- Antti Majakivi
+- araspitzu
+- Arvid Norberg
+- Ben Carman
+- Ben Woosley
+- benthecarman
+- bitcoinhodler
+- Carl Dong
+- Chakib Benziane
+- Chris Moore
+- Chris Stewart
+- chris-belcher
+- Chun Kuan Lee
+- Cornelius Schumacher
+- Cory Fields
+- Craig Younkins
+- Cristian Mircea Messel
+- Damian Mee
+- Daniel Ingram
+- Daniel Kraft
+- David A. Harding
+- DesWurstes
+- dexX7
+- Dimitri Deijs
+- Dimitris Apostolou
+- Douglas Roark
+- DrahtBot
+- Emanuele Cisbani
+- Emil Engler
+- Eric Scrivner
+- fridokus
+- Gal Buki
+- Gleb Naumenko
+- Glenn Willen
+- Graham Krizek
+- Gregory Maxwell
+- Gregory Sanders
+- gustavonalle
+- Harry Moreno
+- Hennadii Stepanov
+- Isidoro Ghezzi
+- Jack Mallers
+- James Hilliard
+- James O'Beirne
+- Jameson Lopp
+- Jeremy Rubin
+- Jesse Cohen
+- Jim Posen
+- John Newbery
+- Jon Layton
+- Jonas Schnelli
+- João Barbosa
+- Jordan Baczuk
+- Jorge Timón
+- Julian Fleischer
+- Justin Turner Arthur
+- Karel Bílek
+- Karl-Johan Alm
+- Kaz Wesley
+- ken2812221
+- Kostiantyn Stepaniuk
+- Kristaps Kaupe
+- Lawrence Nahum
+- Lenny Maiorani
+- liuyujun
+- lucash-dev
+- luciana
+- Luke Dashjr
+- marcaiaf
+- marcoagner
+- MarcoFalke
+- Martin Erlandsson
+- Marty Jones
+- Mason Simon
+- Michael Ford
+- Michael Goldstein
+- Michael Polzer
+- Mitchell Cash
+- mruddy
+- Murray Nesbitt
+- OverlordQ
+- Patrick Strateman
+- Pierre Rochard
+- Pieter Wuille
+- poiuty
+- practicalswift
+- priscoan
+- qubenix
+- riordant
+- Russell Yanofsky
+- Samuel Dobson
+- sanket1729
+- Sjors Provoost
+- Stephan Oeste
+- Steven Roose
+- Suhas Daftuar
+- TheCharlatan
+- Tim Ruffing
+- Vidar Holen
+- vim88
+- Walter
+- whythat
+- Wladimir J. van der Laan
+- Zain Iqbal Allarakhia
+
+As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/).
diff --git a/doc/release-process.md b/doc/release-process.md
index d20a3dc6b3..480b09ee11 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -1,18 +1,19 @@
Release Process
====================
-Before every release candidate:
+## Branch updates
-* Update translations (ping wumpus on IRC) see [translation_process.md](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md#synchronising-translations).
+### Before every release candidate
+* Update translations (ping wumpus on IRC) see [translation_process.md](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md#synchronising-translations).
* Update manpages, see [gen-manpages.sh](https://github.com/bitcoin/bitcoin/blob/master/contrib/devtools/README.md#gen-manpagessh).
-* Update release candidate version in `configure.ac` (`CLIENT_VERSION_RC`)
+* Update release candidate version in `configure.ac` (`CLIENT_VERSION_RC`).
-Before every minor and major release:
+### Before every major and minor release
* Update [bips.md](bips.md) to account for changes since the last release.
-* Update version in `configure.ac` (don't forget to set `CLIENT_VERSION_IS_RELEASE` to `true`) (don't forget to set `CLIENT_VERSION_RC` to `0`)
-* Write release notes (see below)
+* Update version in `configure.ac` (don't forget to set `CLIENT_VERSION_RC` to `0`).
+* Write release notes (see "Write the release notes" below).
* Update `src/chainparams.cpp` nMinimumChainWork with information from the getblockchaininfo rpc.
* Update `src/chainparams.cpp` defaultAssumeValid with information from the getblockhash rpc.
- The selected value must not be orphaned so it may be useful to set the value two blocks back from the tip.
@@ -20,13 +21,38 @@ Before every minor and major release:
- This update should be reviewed with a reindex-chainstate with assumevalid=0 to catch any defect
that causes rejection of blocks in the past history.
-Before every major release:
+### Before every major release
* Update hardcoded [seeds](/contrib/seeds/README.md), see [this pull request](https://github.com/bitcoin/bitcoin/pull/7415) for an example.
* Update [`src/chainparams.cpp`](/src/chainparams.cpp) m_assumed_blockchain_size and m_assumed_chain_state_size with the current size plus some overhead.
* Update `src/chainparams.cpp` chainTxData with statistics about the transaction count and rate. Use the output of the RPC `getchaintxstats`, see
[this pull request](https://github.com/bitcoin/bitcoin/pull/12270) for an example. Reviewers can verify the results by running `getchaintxstats <window_block_count> <window_last_block_hash>` with the `window_block_count` and `window_last_block_hash` from your output.
-* Update version of `contrib/gitian-descriptors/*.yml`: usually one'd want to do this on master after branching off the release - but be sure to at least do it before a new major release
+* On both the master branch and the new release branch:
+ - update `CLIENT_VERSION_MINOR` in [`configure.ac`](../configure.ac)
+ - update `CLIENT_VERSION_MINOR`, `PACKAGE_VERSION`, and `PACKAGE_STRING` in [`build_msvc/bitcoin_config.h`](/build_msvc/bitcoin_config.h)
+* On the new release branch in [`configure.ac`](../configure.ac) and [`build_msvc/bitcoin_config.h`](/build_msvc/bitcoin_config.h) (see [this commit](https://github.com/bitcoin/bitcoin/commit/742f7dd)):
+ - set `CLIENT_VERSION_REVISION` to `0`
+ - set `CLIENT_VERSION_IS_RELEASE` to `true`
+
+#### Before branch-off
+
+- Clear the release notes and move them to the wiki (see "Write the release notes" below).
+
+#### After branch-off (on master)
+
+- Update the version of `contrib/gitian-descriptors/*.yml`.
+
+#### After branch-off (on the major release branch)
+
+- Update the versions and the link to the release notes draft in `doc/release-notes.md`.
+
+#### Before final release
+
+- Merge the release notes from the wiki into the branch.
+- Ensure the "Needs release note" label is removed from all relevant pull requests and issues.
+
+
+## Building
### First time / New builders
@@ -40,22 +66,26 @@ Check out the source code in the following directory hierarchy.
git clone https://github.com/devrandom/gitian-builder.git
git clone https://github.com/bitcoin/bitcoin.git
-### Bitcoin maintainers/release engineers, suggestion for writing release notes
+### Write the release notes
+
+Open a draft of the release notes for collaborative editing at https://github.com/bitcoin-core/bitcoin-devwiki/wiki.
+
+For the period during which the notes are being edited on the wiki, the version on the branch should be wiped and replaced with a link to the wiki which should be used for all announcements until `-final`.
-Write release notes. git shortlog helps a lot, for example:
+Write the release notes. `git shortlog` helps a lot, for example:
- git shortlog --no-merges v(current version, e.g. 0.7.2)..v(new version, e.g. 0.8.0)
+ git shortlog --no-merges v(current version, e.g. 0.19.2)..v(new version, e.g. 0.20.0)
(or ping @wumpus on IRC, he has specific tooling to generate the list of merged pulls
-and sort them into categories based on labels)
+and sort them into categories based on labels).
Generate list of authors:
- git log --format='- %aN' v(current version, e.g. 0.16.0)..v(new version, e.g. 0.16.1) | sort -fiu
+ git log --format='- %aN' v(current version, e.g. 0.20.0)..v(new version, e.g. 0.20.1) | sort -fiu
-Tag version (or release candidate) in git
+Tag the version (or release candidate) in git:
- git tag -s v(new version, e.g. 0.8.0)
+ git tag -s v(new version, e.g. 0.20.0)
### Setup and perform Gitian builds
@@ -65,7 +95,7 @@ Setup Gitian descriptors:
pushd ./bitcoin
export SIGNER="(your Gitian key, ie bluematt, sipa, etc)"
- export VERSION=(new version, e.g. 0.8.0)
+ export VERSION=(new version, e.g. 0.20.0)
git fetch
git checkout v${VERSION}
popd
@@ -216,7 +246,6 @@ Create (and optionally verify) the signed Windows binaries:
./bin/gsign --signer "$SIGNER" --release ${VERSION}-win-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml
./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-win-signed ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml
mv build/out/bitcoin-*win64-setup.exe ../bitcoin-${VERSION}-win64-setup.exe
- mv build/out/bitcoin-*win32-setup.exe ../bitcoin-${VERSION}-win32-setup.exe
popd
Commit your signature for the signed macOS/Windows binaries:
@@ -224,7 +253,7 @@ Commit your signature for the signed macOS/Windows binaries:
pushd gitian.sigs
git add ${VERSION}-osx-signed/"${SIGNER}"
git add ${VERSION}-win-signed/"${SIGNER}"
- git commit -a
+ git commit -m "Add ${SIGNER} ${VERSION} signed binaries signatures"
git push # Assuming you can push to the gitian.sigs tree
popd
@@ -241,12 +270,11 @@ The list of files should be:
bitcoin-${VERSION}-aarch64-linux-gnu.tar.gz
bitcoin-${VERSION}-arm-linux-gnueabihf.tar.gz
bitcoin-${VERSION}-i686-pc-linux-gnu.tar.gz
+bitcoin-${VERSION}-riscv64-linux-gnu.tar.gz
bitcoin-${VERSION}-x86_64-linux-gnu.tar.gz
bitcoin-${VERSION}-osx64.tar.gz
bitcoin-${VERSION}-osx.dmg
bitcoin-${VERSION}.tar.gz
-bitcoin-${VERSION}-win32-setup.exe
-bitcoin-${VERSION}-win32.zip
bitcoin-${VERSION}-win64-setup.exe
bitcoin-${VERSION}-win64.zip
```
@@ -291,24 +319,53 @@ bitcoin.org (see below for bitcoin.org update instructions).
- After the pull request is merged, the website will automatically show the newest version within 15 minutes, as well
as update the OS download links. Ping @saivann/@harding (saivann/harding on Freenode) in case anything goes wrong
-- Announce the release:
-
- - bitcoin-dev and bitcoin-core-dev mailing list
-
- - Bitcoin Core announcements list https://bitcoincore.org/en/list/announcements/join/
+- Update other repositories and websites for new version
- bitcoincore.org blog post
+ - bitcoincore.org maintained versions update:
+ [table](https://github.com/bitcoin-core/bitcoincore.org/commits/master/_includes/posts/maintenance-table.md)
+
- bitcoincore.org RPC documentation update
- - Update title of #bitcoin on Freenode IRC
+ - Update packaging repo
- - Optionally twitter, reddit /r/Bitcoin, ... but this will usually sort out itself
+ - Notify BlueMatt so that he can start building [the PPAs](https://launchpad.net/~bitcoin/+archive/ubuntu/bitcoin)
+
+ - Create a new branch for the major release "0.xx" (used to build the snap package)
+
+ - Notify MarcoFalke so that he can start building the snap package
+
+ - https://code.launchpad.net/~bitcoin-core/bitcoin-core-snap/+git/packaging (Click "Import Now" to fetch the branch)
+ - https://code.launchpad.net/~bitcoin-core/bitcoin-core-snap/+git/packaging/+ref/0.xx (Click "Create snap package")
+ - Name it "bitcoin-core-snap-0.xx"
+ - Leave owner and series as-is
+ - Select architectures that are compiled via gitian
+ - Leave "automatically build when branch changes" unticked
+ - Tick "automatically upload to store"
+ - Put "bitcoin-core" in the registered store package name field
+ - Tick the "edge" box
+ - Put "0.xx" in the track field
+ - Click "create snap package"
+ - Click "Request builds" for every new release on this branch (after updating the snapcraft.yml in the branch to reflect the latest gitian results)
+ - Promote release on https://snapcraft.io/bitcoin-core/releases if it passes sanity checks
+
+ - This repo
+
+ - Archive the release notes for the new version to `doc/release-notes/` (branch `master` and branch of the release)
- - Notify BlueMatt so that he can start building [the PPAs](https://launchpad.net/~bitcoin/+archive/ubuntu/bitcoin)
+ - Create a [new GitHub release](https://github.com/bitcoin/bitcoin/releases/new) with a link to the archived release notes
- - Archive release notes for the new version to `doc/release-notes/` (branch `master` and branch of the release)
+ - Create a pinned meta-issue for testing the release candidate (see [this issue](https://github.com/bitcoin/bitcoin/issues/15555) for an example) and provide a link to it in the release announcements where useful
- - Create a [new GitHub release](https://github.com/bitcoin/bitcoin/releases/new) with a link to the archived release notes.
+- Announce the release:
+
+ - bitcoin-dev and bitcoin-core-dev mailing list
+
+ - Bitcoin Core announcements list https://bitcoincore.org/en/list/announcements/join/
+
+ - Update title of #bitcoin on Freenode IRC
+
+ - Optionally twitter, reddit /r/Bitcoin, ... but this will usually sort out itself
- Celebrate
diff --git a/doc/shared-libraries.md b/doc/shared-libraries.md
index dc363582cc..e960863a80 100644
--- a/doc/shared-libraries.md
+++ b/doc/shared-libraries.md
@@ -7,11 +7,11 @@ The purpose of this library is to make the verification functionality that is cr
### API
-The interface is defined in the C header `bitcoinconsensus.h` located in `src/script/bitcoinconsensus.h`.
+The interface is defined in the C header `bitcoinconsensus.h` located in `src/script/bitcoinconsensus.h`.
#### Version
-`bitcoinconsensus_version` returns an `unsigned int` with the API version *(currently at an experimental `0`)*.
+`bitcoinconsensus_version` returns an `unsigned int` with the API version *(currently `1`)*.
#### Script Validation
diff --git a/doc/tor.md b/doc/tor.md
index c46b7e9f60..cfb7f16666 100644
--- a/doc/tor.md
+++ b/doc/tor.md
@@ -16,7 +16,7 @@ outgoing connections, but more is possible.
-onion=ip:port Set the proxy server to use for Tor hidden services. You do not
need to set this if it's the same as -proxy. You can use -noonion
- to explicitly disable access to hidden service.
+ to explicitly disable access to hidden services.
-listen When using -proxy, listening is disabled by default. If you want
to run a hidden service (see next section), you'll need to enable
@@ -27,6 +27,11 @@ outgoing connections, but more is possible.
-seednode=X SOCKS5. In Tor mode, such addresses can also be exchanged with
other P2P nodes.
+ -onlynet=onion Make outgoing connections only to .onion addresses. Incoming
+ connections are not affected by this option. This option can be
+ specified multiple times to allow multiple network types, e.g.
+ ipv4, ipv6, or onion.
+
In a typical situation, this suffices to run behind a Tor proxy:
./bitcoind -proxy=127.0.0.1:9050
diff --git a/doc/translation_process.md b/doc/translation_process.md
index 19f145e9bf..b9a10b6527 100644
--- a/doc/translation_process.md
+++ b/doc/translation_process.md
@@ -8,7 +8,7 @@ Transifex is setup to monitor the GitHub repo for updates, and when code contain
Multiple language support is critical in assisting Bitcoin’s global adoption, and growth. One of Bitcoin’s greatest strengths is cross-border money transfers, any help making that easier is greatly appreciated.
-See the [Transifex Bitcoin project](https://www.transifex.com/projects/p/bitcoin/) to assist in translations. You should also join the translation mailing list for announcements - see details below.
+See the [Transifex Bitcoin project](https://www.transifex.com/bitcoin/bitcoin/) to assist in translations. You should also join the translation mailing list for announcements - see details below.
### Writing code with translations
We use automated scripts to help extract translations in both Qt, and non-Qt source files. It is rarely necessary to manually edit the files in `src/qt/locale/`. The translation source files must adhere to the following format:
@@ -43,7 +43,7 @@ git commit
### Creating a Transifex account
Visit the [Transifex Signup](https://www.transifex.com/signup/) page to create an account. Take note of your username and password, as they will be required to configure the command-line tool.
-You can find the Bitcoin translation project at [https://www.transifex.com/projects/p/bitcoin/](https://www.transifex.com/projects/p/bitcoin/).
+You can find the Bitcoin translation project at [https://www.transifex.com/bitcoin/bitcoin/](https://www.transifex.com/bitcoin/bitcoin/).
### Installing the Transifex client command-line tool
The client is used to fetch updated translations. If you are having problems, or need more details, see [https://docs.transifex.com/client/installing-the-client](https://docs.transifex.com/client/installing-the-client)
@@ -68,11 +68,21 @@ The Transifex Bitcoin project config file is included as part of the repo. It ca
To assist in updating translations, we have created a script to help.
1. `python contrib/devtools/update-translations.py`
-2. Update `src/qt/bitcoin_locale.qrc` manually or via
- `ls src/qt/locale/*ts|xargs -n1 basename|sed 's/\(bitcoin_\(.*\)\).ts/<file alias="\2">locale\/\1.qm<\/file>/'`
-3. Update `src/Makefile.qt.include` manually or via
- `ls src/qt/locale/*ts|xargs -n1 basename|sed 's/\(bitcoin_\(.*\)\).ts/ qt\/locale\/\1.ts \\/'`
-4. `git add` new translations from `src/qt/locale/`
+2. `git add` new translations from `src/qt/locale/`
+3. Update `src/qt/bitcoin_locale.qrc` manually or via
+```bash
+git ls-files src/qt/locale/*ts|xargs -n1 basename|sed 's/\(bitcoin_\(.*\)\).ts/<file alias="\2">locale\/\1.qm<\/file>/'
+```
+4. Update `src/Makefile.qt.include` manually or via
+```bash
+git ls-files src/qt/locale/*ts|xargs -n1 basename|sed 's/\(bitcoin_\(.*\)\).ts/ qt\/locale\/\1.ts \\/'
+```
+5. Update `build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj` or via
+```bash
+git ls-files src/qt/locale/*ts|xargs -n1 basename |
+ sed 's/@/%40/' |
+ sed 's/\(bitcoin_\(.*\)\).ts/ <None Include="..\\..\\src\\qt\\locale\\\1.ts">\n <DeploymentContent>true<\/DeploymentContent>\n <\/None>/'
+```
**Do not directly download translations** one by one from the Transifex website, as we do a few post-processing steps before committing the translations.
diff --git a/doc/translation_strings_policy.md b/doc/translation_strings_policy.md
index 737d11f045..634aca3559 100644
--- a/doc/translation_strings_policy.md
+++ b/doc/translation_strings_policy.md
@@ -33,25 +33,25 @@ General recommendations
Try not to burden translators with translating messages that are e.g. slight variations of other messages.
In the GUI, avoid the use of text where an icon or symbol will do.
-Make sure that placeholder texts in forms don't end up in the list of strings to be translated (use `<string notr="true">`).
+Make sure that placeholder texts in forms do not end up in the list of strings to be translated (use `<string notr="true">`).
### Make translated strings understandable
-Try to write translation strings in an understandable way, for both the user and the translator. Avoid overly technical or detailed messages
+Try to write translation strings in an understandable way, for both the user and the translator. Avoid overly technical or detailed messages.
### Do not translate internal errors
-Do not translate internal errors, or log messages, or messages that appear on the RPC interface. If an error is to be shown to the user,
-use a translatable generic message, then log the detailed message to the log. E.g. "A fatal internal error occurred, see debug.log for details".
+Do not translate internal errors, log messages, or messages that appear on the RPC interface. If an error is to be shown to the user,
+use a translatable generic message, then log the detailed message to the log. E.g., "A fatal internal error occurred, see debug.log for details".
This helps troubleshooting; if the error is the same for everyone, the likelihood is increased that it can be found using a search engine.
### Avoid fragments
-Avoid dividing up a message into fragments. Translators see every string separately, so may misunderstand the context if the messages are not self-contained.
+Avoid dividing up a message into fragments. Translators see every string separately, so they may misunderstand the context if the messages are not self-contained.
### Avoid HTML in translation strings
-There have been difficulties with use of HTML in translation strings; translators should not be able to accidentally affect the formatting of messages.
+There have been difficulties with the use of HTML in translation strings; translators should not be able to accidentally affect the formatting of messages.
This may sometimes be at conflict with the recommendation in the previous section.
### Plurals
@@ -66,7 +66,7 @@ Plurals can be complex in some languages. A quote from the gettext documentation
25-31 pliko'w
and so on
-In Qt code use tr's third argument for optional plurality. For example:
+In Qt code, use tr's third argument for optional plurality. For example:
tr("%n hour(s)","",secs/HOUR_IN_SECONDS);
tr("%n day(s)","",secs/DAY_IN_SECONDS);
@@ -82,7 +82,7 @@ This adds `<numerusform>`s to the respective `.ts` file, which can be translated
</translation>
</message>
-Where it is possible try to avoid embedding numbers into the flow of the string at all. e.g.
+Where possible, try to avoid embedding numbers into the flow of the string at all. E.g.,
WARNING: check your network connection, %d blocks received in the last %d hours (%d expected)
diff --git a/doc/travis-ci.md b/doc/travis-ci.md
deleted file mode 100644
index 38085cec35..0000000000
--- a/doc/travis-ci.md
+++ /dev/null
@@ -1,42 +0,0 @@
-Travis CI
-=========
-
-Support for using travis-ci has been added in order to automate pull-testing.
-See [travis-ci.org](https://travis-ci.org/) for more info
-
-This procedure is different than the pull-tester that came before it in a few
-ways.
-
-There is nothing to administer. This is a major feature as it means
-that builds have no local state. Because there is no ability to login to the
-builders to install packages (tools, dependencies, etc), the entire build
-procedure must instead be controlled by a declarative script `.travis.yml`.
-This script declares each build configuration, creates virtual machines as
-necessary, builds, then discards the virtual machines.
-
-A build matrix is constructed to test a wide range of configurations, rather
-than a single pass/fail. This helps to catch build failures and logic errors
-that present on platforms other than the ones the author has tested. This
-matrix is defined in the build script and can be changed at any time.
-
-All builders use the dependency-generator in the [depends dir](/depends), rather than
-using apt-get to install build dependencies. This guarantees that the tester
-is using the same versions as Gitian, so the build results are nearly identical
-to what would be found in a final release. However, this also means that builds
-will fail if new dependencies are introduced without being added to the
-dependency generator.
-
-In order to avoid rebuilding all dependencies for each build, the binaries are
-cached and re-used when possible. Changes in the dependency-generator will
-trigger cache-invalidation and rebuilds as necessary.
-
-These caches can be manually removed if necessary. This is one of the very few
-manual operations that is possible with Travis, and it can be done by the
-Bitcoin Core committer via the Travis web interface.
-
-In some cases, secure strings may be needed for hiding sensitive info such as
-private keys or URLs. The travis client may be used to create these strings:
-http://docs.travis-ci.com/user/encryption-keys/
-
-For the details of the build descriptor, see the official docs:
-http://docs.travis-ci.com/user/build-configuration/
diff --git a/share/examples/bitcoin.conf b/share/examples/bitcoin.conf
index 6062d49c40..b5475dc1c6 100644
--- a/share/examples/bitcoin.conf
+++ b/share/examples/bitcoin.conf
@@ -4,6 +4,10 @@
# Network-related settings:
+# Note that if you use testnet or regtest, particularly with the options
+# addnode, connect, port, bind, rpcport, rpcbind or wallet, you will also
+# want to read "[Sections]" further down.
+
# Run on the test network instead of the real bitcoin network.
#testnet=0
@@ -53,6 +57,9 @@
# Listening mode, enabled by default except when 'connect' is being used
#listen=1
+# Port on which to listen for connections (default: 8333, testnet: 18333, regtest: 18444)
+#port=
+
# Maximum number of inbound+outbound connections.
#maxconnections=
@@ -115,6 +122,10 @@
# Wallet options
+# Specify where to find wallet, lockfile and logs. If not present, those files will be
+# created as new.
+#wallet=</path/to/dir>
+
# Create transactions that have enough fees so they are likely to begin confirmation within n blocks (default: 6).
# This setting is over-ridden by the -paytxfee option.
#txconfirmtarget=n
@@ -142,3 +153,19 @@
# Minimize to the system tray
#minimizetotray=1
+
+# [Sections]
+# Most options apply to mainnet, testnet and regtest.
+# If you want to confine an option to just one network, you should add it in the
+# relevant section below.
+# EXCEPTIONS: The options addnode, connect, port, bind, rpcport, rpcbind and wallet
+# only apply to mainnet unless they appear in the appropriate section below.
+
+# Options only for mainnet
+[main]
+
+# Options only for testnet
+[test]
+
+# Options only for regtest
+[regtest]
diff --git a/share/setup.nsi.in b/share/setup.nsi.in
index 6542370f97..acf9e86667 100644
--- a/share/setup.nsi.in
+++ b/share/setup.nsi.in
@@ -5,7 +5,6 @@ SetCompressor /SOLID lzma
# General Symbol Definitions
!define REGKEY "SOFTWARE\$(^Name)"
-!define VERSION @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@
!define COMPANY "@PACKAGE_NAME@ project"
!define URL @PACKAGE_URL@
@@ -49,7 +48,7 @@ Var StartMenuGroup
!insertmacro MUI_LANGUAGE English
# Installer attributes
-OutFile @abs_top_srcdir@/@PACKAGE_TARNAME@-${VERSION}-win@WINDOWS_BITS@-setup.exe
+OutFile @abs_top_srcdir@/@PACKAGE_TARNAME@-@PACKAGE_VERSION@-win@WINDOWS_BITS@-setup.exe
!if "@WINDOWS_BITS@" == "64"
InstallDir $PROGRAMFILES64\Bitcoin
!else
@@ -59,12 +58,12 @@ CRCCheck on
XPStyle on
BrandingText " "
ShowInstDetails show
-VIProductVersion ${VERSION}.@CLIENT_VERSION_BUILD@
+VIProductVersion @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@.@CLIENT_VERSION_BUILD@
VIAddVersionKey ProductName "@PACKAGE_NAME@"
-VIAddVersionKey ProductVersion "${VERSION}"
+VIAddVersionKey ProductVersion "@PACKAGE_VERSION@"
VIAddVersionKey CompanyName "${COMPANY}"
VIAddVersionKey CompanyWebsite "${URL}"
-VIAddVersionKey FileVersion "${VERSION}"
+VIAddVersionKey FileVersion "@PACKAGE_VERSION@"
VIAddVersionKey FileDescription ""
VIAddVersionKey LegalCopyright ""
InstallDirRegKey HKCU "${REGKEY}" Path
@@ -81,6 +80,7 @@ Section -Main SEC0000
File @abs_top_srcdir@/release/@BITCOIN_DAEMON_NAME@@EXEEXT@
File @abs_top_srcdir@/release/@BITCOIN_CLI_NAME@@EXEEXT@
File @abs_top_srcdir@/release/@BITCOIN_TX_NAME@@EXEEXT@
+ File @abs_top_srcdir@/release/@BITCOIN_WALLET_TOOL_NAME@@EXEEXT@
SetOutPath $INSTDIR\doc
File /r /x Makefile* @abs_top_srcdir@/doc\*.*
SetOutPath $INSTDIR
@@ -98,7 +98,7 @@ Section -post SEC0001
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name).lnk" $INSTDIR\uninstall.exe
!insertmacro MUI_STARTMENU_WRITE_END
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)"
- WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "${VERSION}"
+ WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "@PACKAGE_VERSION@"
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}"
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}"
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\uninstall.exe
diff --git a/src/Makefile.am b/src/Makefile.am
index a57fcb0711..ec3d81b76f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -113,24 +113,27 @@ BITCOIN_CORE_H = \
chainparams.h \
chainparamsbase.h \
chainparamsseeds.h \
- checkpoints.h \
checkqueue.h \
clientversion.h \
coins.h \
compat.h \
+ compat/assumptions.h \
compat/byteswap.h \
compat/endian.h \
compat/sanity.h \
compressor.h \
consensus/consensus.h \
+ consensus/tx_check.h \
consensus/tx_verify.h \
core_io.h \
core_memusage.h \
cuckoocache.h \
+ flatfile.h \
fs.h \
httprpc.h \
httpserver.h \
index/base.h \
+ index/blockfilterindex.h \
index/txindex.h \
indirectmap.h \
init.h \
@@ -152,6 +155,9 @@ BITCOIN_CORE_H = \
netaddress.h \
netbase.h \
netmessagemaker.h \
+ node/coin.h \
+ node/psbt.h \
+ node/transaction.h \
noui.h \
optional.h \
outputtype.h \
@@ -159,17 +165,18 @@ BITCOIN_CORE_H = \
policy/fees.h \
policy/policy.h \
policy/rbf.h \
+ policy/settings.h \
pow.h \
protocol.h \
+ psbt.h \
random.h \
reverse_iterator.h \
reverselock.h \
rpc/blockchain.h \
rpc/client.h \
- rpc/mining.h \
rpc/protocol.h \
rpc/server.h \
- rpc/rawtransaction.h \
+ rpc/rawtransaction_util.h \
rpc/register.h \
rpc/util.h \
scheduler.h \
@@ -194,11 +201,18 @@ BITCOIN_CORE_H = \
txmempool.h \
ui_interface.h \
undo.h \
+ util/bip32.h \
util/bytevectorhash.h \
+ util/error.h \
+ util/fees.h \
util/system.h \
util/memory.h \
util/moneystr.h \
+ util/rbf.h \
+ util/threadnames.h \
util/time.h \
+ util/url.h \
+ util/validation.h \
validation.h \
validationinterface.h \
versionbits.h \
@@ -209,6 +223,8 @@ BITCOIN_CORE_H = \
wallet/db.h \
wallet/feebumper.h \
wallet/fees.h \
+ wallet/load.h \
+ wallet/psbtwallet.h \
wallet/rpcwallet.h \
wallet/wallet.h \
wallet/walletdb.h \
@@ -230,36 +246,39 @@ obj/build.h: FORCE
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
# server: shared between bitcoind and bitcoin-qt
+# Contains code accessing mempool and chain state that is meant to be separated
+# from wallet and gui code (see node/README.md). Shared code should go in
+# libbitcoin_common or libbitcoin_util libraries, instead.
libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrdb.cpp \
addrman.cpp \
banman.cpp \
- bloom.cpp \
blockencodings.cpp \
blockfilter.cpp \
chain.cpp \
- checkpoints.cpp \
consensus/tx_verify.cpp \
+ flatfile.cpp \
httprpc.cpp \
httpserver.cpp \
index/base.cpp \
+ index/blockfilterindex.cpp \
index/txindex.cpp \
interfaces/chain.cpp \
- interfaces/handler.cpp \
interfaces/node.cpp \
init.cpp \
dbwrapper.cpp \
- merkleblock.cpp \
miner.cpp \
net.cpp \
net_processing.cpp \
+ node/coin.cpp \
+ node/psbt.cpp \
+ node/transaction.cpp \
noui.cpp \
- outputtype.cpp \
policy/fees.cpp \
- policy/policy.cpp \
policy/rbf.cpp \
+ policy/settings.cpp \
pow.cpp \
rest.cpp \
rpc/blockchain.cpp \
@@ -268,7 +287,6 @@ libbitcoin_server_a_SOURCES = \
rpc/net.cpp \
rpc/rawtransaction.cpp \
rpc/server.cpp \
- rpc/util.cpp \
script/sigcache.cpp \
shutdown.cpp \
timedata.cpp \
@@ -281,6 +299,9 @@ libbitcoin_server_a_SOURCES = \
versionbits.cpp \
$(BITCOIN_CORE_H)
+if ENABLE_WALLET
+libbitcoin_server_a_SOURCES += wallet/init.cpp
+endif
if !ENABLE_WALLET
libbitcoin_server_a_SOURCES += dummywallet.cpp
endif
@@ -307,7 +328,8 @@ libbitcoin_wallet_a_SOURCES = \
wallet/db.cpp \
wallet/feebumper.cpp \
wallet/fees.cpp \
- wallet/init.cpp \
+ wallet/load.cpp \
+ wallet/psbtwallet.cpp \
wallet/rpcdump.cpp \
wallet/rpcwallet.cpp \
wallet/wallet.cpp \
@@ -331,10 +353,14 @@ crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/chacha20.h \
crypto/chacha20.cpp \
crypto/common.h \
+ crypto/hkdf_sha256_32.cpp \
+ crypto/hkdf_sha256_32.h \
crypto/hmac_sha256.cpp \
crypto/hmac_sha256.h \
crypto/hmac_sha512.cpp \
crypto/hmac_sha512.h \
+ crypto/poly1305.h \
+ crypto/poly1305.cpp \
crypto/ripemd160.cpp \
crypto/ripemd160.h \
crypto/sha1.cpp \
@@ -378,6 +404,7 @@ libbitcoin_consensus_a_SOURCES = \
consensus/merkle.cpp \
consensus/merkle.h \
consensus/params.h \
+ consensus/tx_check.cpp \
consensus/validation.h \
hash.cpp \
hash.h \
@@ -410,6 +437,7 @@ libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_common_a_SOURCES = \
base58.cpp \
bech32.cpp \
+ bloom.cpp \
chainparams.cpp \
coins.cpp \
compressor.cpp \
@@ -418,10 +446,16 @@ libbitcoin_common_a_SOURCES = \
key.cpp \
key_io.cpp \
keystore.cpp \
+ merkleblock.cpp \
netaddress.cpp \
netbase.cpp \
+ outputtype.cpp \
policy/feerate.cpp \
+ policy/policy.cpp \
protocol.cpp \
+ psbt.cpp \
+ rpc/rawtransaction_util.cpp \
+ rpc/util.cpp \
scheduler.cpp \
script/descriptor.cpp \
script/ismine.cpp \
@@ -444,17 +478,25 @@ libbitcoin_util_a_SOURCES = \
compat/glibcxx_sanity.cpp \
compat/strnlen.cpp \
fs.cpp \
+ interfaces/handler.cpp \
logging.cpp \
random.cpp \
rpc/protocol.cpp \
support/cleanse.cpp \
sync.cpp \
threadinterrupt.cpp \
+ util/bip32.cpp \
util/bytevectorhash.cpp \
+ util/error.cpp \
+ util/fees.cpp \
util/system.cpp \
util/moneystr.cpp \
+ util/rbf.cpp \
+ util/threadnames.cpp \
util/strencodings.cpp \
util/time.cpp \
+ util/url.cpp \
+ util/validation.cpp \
$(BITCOIN_CORE_H)
if GLIBC_BACK_COMPAT
@@ -482,6 +524,8 @@ if TARGET_WINDOWS
bitcoind_SOURCES += bitcoind-res.rc
endif
+# Libraries below may be listed more than once to resolve circular dependencies (see
+# https://eli.thegreenplace.net/2013/07/09/library-order-in-static-linking#circular-dependency)
bitcoind_LDADD = \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_WALLET) \
@@ -552,17 +596,18 @@ endif
bitcoin_wallet_LDADD = \
$(LIBBITCOIN_WALLET_TOOL) \
$(LIBBITCOIN_WALLET) \
- $(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
+ $(LIBBITCOIN_ZMQ) \
$(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) \
$(LIBMEMENV) \
- $(LIBSECP256K1)
+ $(LIBSECP256K1) \
+ $(LIBUNIVALUE)
-bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS)
+bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(ZMQ_LIBS)
#
# bitcoinconsensus library #
@@ -629,13 +674,13 @@ clean-local:
check-symbols: $(bin_PROGRAMS)
if GLIBC_BACK_COMPAT
@echo "Checking glibc back compat..."
- $(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(top_srcdir)/contrib/devtools/symbol-check.py < $(bin_PROGRAMS)
+ $(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py < $(bin_PROGRAMS)
endif
check-security: $(bin_PROGRAMS)
if HARDEN
@echo "Checking binary security..."
- $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS)
+ $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS)
endif
if ENABLE_BIP70
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 5e787ca222..c6162b5caa 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -21,22 +21,31 @@ bench_bench_bitcoin_SOURCES = \
bench/duplicate_inputs.cpp \
bench/examples.cpp \
bench/rollingbloom.cpp \
+ bench/chacha20.cpp \
bench/crypto_hash.cpp \
bench/ccoins_caching.cpp \
bench/gcs_filter.cpp \
bench/merkle_root.cpp \
bench/mempool_eviction.cpp \
+ bench/rpc_mempool.cpp \
+ bench/util_time.cpp \
bench/verify_script.cpp \
bench/base58.cpp \
bench/bech32.cpp \
bench/lockedpool.cpp \
- bench/prevector.cpp
+ bench/poly1305.cpp \
+ bench/prevector.cpp \
+ test/setup_common.h \
+ test/setup_common.cpp \
+ test/util.h \
+ test/util.cpp
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)
bench_bench_bitcoin_LDADD = \
+ $(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \
@@ -47,7 +56,9 @@ bench_bench_bitcoin_LDADD = \
$(LIBLEVELDB_SSE42) \
$(LIBMEMENV) \
$(LIBSECP256K1) \
- $(LIBUNIVALUE)
+ $(LIBUNIVALUE) \
+ $(EVENT_PTHREADS_LIBS) \
+ $(EVENT_LIBS)
if ENABLE_ZMQ
bench_bench_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
@@ -55,9 +66,10 @@ endif
if ENABLE_WALLET
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
+bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp
endif
-bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS)
+bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS)
bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)
diff --git a/src/Makefile.leveldb.include b/src/Makefile.leveldb.include
index 833f3d2a10..bd08bcb4ed 100644
--- a/src/Makefile.leveldb.include
+++ b/src/Makefile.leveldb.include
@@ -24,7 +24,7 @@ LEVELDB_CPPFLAGS_INT += -DLEVELDB_ATOMIC_PRESENT
LEVELDB_CPPFLAGS_INT += -D__STDC_LIMIT_MACROS
if TARGET_WINDOWS
-LEVELDB_CPPFLAGS_INT += -DLEVELDB_PLATFORM_WINDOWS -DWINVER=0x0500 -D__USE_MINGW_ANSI_STDIO=1
+LEVELDB_CPPFLAGS_INT += -DLEVELDB_PLATFORM_WINDOWS -D__USE_MINGW_ANSI_STDIO=1
else
LEVELDB_CPPFLAGS_INT += -DLEVELDB_PLATFORM_POSIX
endif
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index ba6523d7c2..c4c08487f3 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -140,6 +140,7 @@ QT_MOC_CPP = \
qt/moc_overviewpage.cpp \
qt/moc_peertablemodel.cpp \
qt/moc_paymentserver.cpp \
+ qt/moc_qrimagewidget.cpp \
qt/moc_qvalidatedlineedit.cpp \
qt/moc_qvaluecombobox.cpp \
qt/moc_receivecoinsdialog.cpp \
@@ -220,6 +221,7 @@ BITCOIN_QT_H = \
qt/paymentserver.h \
qt/peertablemodel.h \
qt/platformstyle.h \
+ qt/qrimagewidget.h \
qt/qvalidatedlineedit.h \
qt/qvaluecombobox.h \
qt/receivecoinsdialog.h \
@@ -340,6 +342,7 @@ BITCOIN_QT_WALLET_CPP = \
qt/openuridialog.cpp \
qt/overviewpage.cpp \
qt/paymentserver.cpp \
+ qt/qrimagewidget.cpp \
qt/receivecoinsdialog.cpp \
qt/receiverequestdialog.cpp \
qt/recentrequeststablemodel.cpp \
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 61977b50cd..4acfff809e 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -33,10 +33,10 @@ TEST_QT_H = \
qt/test/wallettests.h
TEST_BITCOIN_CPP = \
- test/test_bitcoin.cpp
+ test/setup_common.cpp
TEST_BITCOIN_H = \
- test/test_bitcoin.h
+ test/setup_common.h
qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \
$(QT_INCLUDES) $(QT_TEST_INCLUDES) $(PROTOBUF_CFLAGS)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 12112375ea..c9c029818e 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -2,7 +2,6 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-bin_PROGRAMS += test/test_bitcoin
FUZZ_TARGETS = \
test/fuzz/address_deserialize \
@@ -21,6 +20,7 @@ FUZZ_TARGETS = \
test/fuzz/inv_deserialize \
test/fuzz/messageheader_deserialize \
test/fuzz/netaddr_deserialize \
+ test/fuzz/script_flags \
test/fuzz/service_deserialize \
test/fuzz/transaction_deserialize \
test/fuzz/txoutcompressor_deserialize \
@@ -28,6 +28,8 @@ FUZZ_TARGETS = \
if ENABLE_FUZZ
noinst_PROGRAMS += $(FUZZ_TARGETS:=)
+else
+bin_PROGRAMS += test/test_bitcoin
endif
TEST_SRCDIR = test
@@ -49,14 +51,32 @@ RAW_TEST_FILES =
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
BITCOIN_TEST_SUITE = \
- test/test_bitcoin_main.cpp \
- test/test_bitcoin.h \
- test/test_bitcoin.cpp
+ test/main.cpp \
+ test/setup_common.h \
+ test/setup_common.cpp
FUZZ_SUITE = \
+ test/setup_common.h \
+ test/setup_common.cpp \
test/fuzz/fuzz.cpp \
test/fuzz/fuzz.h
+FUZZ_SUITE_LD_COMMON = \
+ $(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_COMMON) \
+ $(LIBBITCOIN_UTIL) \
+ $(LIBBITCOIN_CONSENSUS) \
+ $(LIBBITCOIN_CRYPTO) \
+ $(LIBUNIVALUE) \
+ $(LIBLEVELDB) \
+ $(LIBLEVELDB_SSE42) \
+ $(BOOST_LIBS) \
+ $(LIBMEMENV) \
+ $(LIBSECP256K1) \
+ $(EVENT_LIBS) \
+ $(CRYPTO_LIBS) \
+ $(EVENT_PTHREADS_LIBS)
+
# test_bitcoin binary #
BITCOIN_TESTS =\
test/arith_uint256_tests.cpp \
@@ -72,15 +92,18 @@ BITCOIN_TESTS =\
test/blockchain_tests.cpp \
test/blockencodings_tests.cpp \
test/blockfilter_tests.cpp \
+ test/blockfilter_index_tests.cpp \
test/bloom_tests.cpp \
test/bswap_tests.cpp \
test/checkqueue_tests.cpp \
test/coins_tests.cpp \
+ test/compilerbug_tests.cpp \
test/compress_tests.cpp \
test/crypto_tests.cpp \
test/cuckoocache_tests.cpp \
test/denialofservice_tests.cpp \
test/descriptor_tests.cpp \
+ test/flatfile_tests.cpp \
test/fs_tests.cpp \
test/getarg_tests.cpp \
test/hash_tests.cpp \
@@ -88,7 +111,7 @@ BITCOIN_TESTS =\
test/key_tests.cpp \
test/limitedmap_tests.cpp \
test/dbwrapper_tests.cpp \
- test/main_tests.cpp \
+ test/validation_tests.cpp \
test/mempool_tests.cpp \
test/merkle_tests.cpp \
test/merkleblock_tests.cpp \
@@ -116,6 +139,7 @@ BITCOIN_TESTS =\
test/skiplist_tests.cpp \
test/streams_tests.cpp \
test/sync_tests.cpp \
+ test/util_threadnames_tests.cpp \
test/timedata_tests.cpp \
test/torcontrol_tests.cpp \
test/transaction_tests.cpp \
@@ -167,349 +191,135 @@ test_test_bitcoin_LDADD += $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDC
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
if ENABLE_ZMQ
-test_test_bitcoin_LDADD += $(ZMQ_LIBS)
+test_test_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
if ENABLE_FUZZ
-test_fuzz_block_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_block_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_block_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_DESERIALIZE=1
test_fuzz_block_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_block_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_block_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_block_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_transaction_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_transaction_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_transaction_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTRANSACTION_DESERIALIZE=1
test_fuzz_transaction_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_transaction_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_transaction_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blocklocator_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_blocklocator_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_blocklocator_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKLOCATOR_DESERIALIZE=1
test_fuzz_blocklocator_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blocklocator_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_blocklocator_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_blocklocator_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_blocklocator_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blockmerkleroot_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_blockmerkleroot_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_blockmerkleroot_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKMERKLEROOT=1
test_fuzz_blockmerkleroot_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blockmerkleroot_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_blockmerkleroot_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_blockmerkleroot_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_blockmerkleroot_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_addrman_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_addrman_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_addrman_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDRMAN_DESERIALIZE=1
test_fuzz_addrman_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_addrman_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_addrman_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_addrman_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_addrman_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blockheader_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_blockheader_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_blockheader_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKHEADER_DESERIALIZE=1
test_fuzz_blockheader_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blockheader_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_blockheader_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_blockheader_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_blockheader_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_banentry_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_banentry_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_banentry_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBANENTRY_DESERIALIZE=1
test_fuzz_banentry_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_banentry_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_banentry_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_banentry_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_banentry_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_txundo_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_txundo_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_txundo_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTXUNDO_DESERIALIZE=1
test_fuzz_txundo_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_txundo_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_txundo_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_txundo_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_txundo_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blockundo_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_blockundo_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_blockundo_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKUNDO_DESERIALIZE=1
test_fuzz_blockundo_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blockundo_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_blockundo_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_blockundo_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_blockundo_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_coins_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_coins_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_coins_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DCOINS_DESERIALIZE=1
test_fuzz_coins_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_coins_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_coins_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_coins_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_coins_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_netaddr_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_netaddr_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_netaddr_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DNETADDR_DESERIALIZE=1
test_fuzz_netaddr_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_netaddr_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_netaddr_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_netaddr_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_netaddr_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
+
+test_fuzz_script_flags_SOURCES = $(FUZZ_SUITE) test/fuzz/script_flags.cpp
+test_fuzz_script_flags_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_flags_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_flags_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_flags_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_service_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_service_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_service_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSERVICE_DESERIALIZE=1
test_fuzz_service_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_service_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_service_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_service_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_service_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_messageheader_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_messageheader_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_messageheader_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGEHEADER_DESERIALIZE=1
test_fuzz_messageheader_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_messageheader_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_messageheader_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_messageheader_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_messageheader_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_address_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_address_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_address_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDRESS_DESERIALIZE=1
test_fuzz_address_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_address_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_address_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_address_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_address_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_inv_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_inv_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_inv_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DINV_DESERIALIZE=1
test_fuzz_inv_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_inv_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_inv_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_inv_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_inv_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_bloomfilter_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_bloomfilter_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_bloomfilter_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOOMFILTER_DESERIALIZE=1
test_fuzz_bloomfilter_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_bloomfilter_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_bloomfilter_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_bloomfilter_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_bloomfilter_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_diskblockindex_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_diskblockindex_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_diskblockindex_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DDISKBLOCKINDEX_DESERIALIZE=1
test_fuzz_diskblockindex_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_diskblockindex_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_diskblockindex_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_diskblockindex_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_diskblockindex_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_txoutcompressor_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_txoutcompressor_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_txoutcompressor_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTXOUTCOMPRESSOR_DESERIALIZE=1
test_fuzz_txoutcompressor_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_txoutcompressor_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_txoutcompressor_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_txoutcompressor_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_txoutcompressor_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blocktransactions_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_blocktransactions_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_blocktransactions_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKTRANSACTIONS_DESERIALIZE=1
test_fuzz_blocktransactions_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blocktransactions_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_blocktransactions_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_blocktransactions_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_blocktransactions_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blocktransactionsrequest_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp
+test_fuzz_blocktransactionsrequest_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_blocktransactionsrequest_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKTRANSACTIONSREQUEST_DESERIALIZE=1
test_fuzz_blocktransactionsrequest_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blocktransactionsrequest_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_blocktransactionsrequest_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_blocktransactionsrequest_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_blocktransactionsrequest_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
endif # ENABLE_FUZZ
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 06c342ba73..32676f8fa5 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -7,7 +7,6 @@
#include <hash.h>
#include <serialize.h>
-#include <streams.h>
int CAddrInfo::GetTriedBucket(const uint256& nKey) const
{
@@ -239,7 +238,9 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
// Will moving this address into tried evict another entry?
if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
- LogPrint(BCLog::ADDRMAN, "Collision inserting element into tried table, moving %s to m_tried_collisions=%d\n", addr.ToString(), m_tried_collisions.size());
+ // Output the entry we'd be colliding with, for debugging purposes
+ auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
+ LogPrint(BCLog::ADDRMAN, "Collision inserting element into tried table (%s), moving %s to m_tried_collisions=%d\n", colliding_entry != mapInfo.end() ? colliding_entry->second.ToString() : "", addr.ToString(), m_tried_collisions.size());
if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {
m_tried_collisions.insert(nId);
}
@@ -561,12 +562,19 @@ void CAddrMan::ResolveCollisions_()
// Give address at least 60 seconds to successfully connect
if (GetAdjustedTime() - info_old.nLastTry > 60) {
- LogPrint(BCLog::ADDRMAN, "Swapping %s for %s in tried table\n", info_new.ToString(), info_old.ToString());
+ LogPrint(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToString(), info_new.ToString());
// Replaces an existing address already in the tried table with the new address
Good_(info_new, false, GetAdjustedTime());
erase_collision = true;
}
+ } else if (GetAdjustedTime() - info_new.nLastSuccess > ADDRMAN_TEST_WINDOW) {
+ // If the collision hasn't resolved in some reasonable amount of time,
+ // just evict the old entry -- we must not be able to
+ // connect to it for some reason.
+ LogPrint(BCLog::ADDRMAN, "Unable to test; replacing %s with %s in tried table anyway\n", info_old.ToString(), info_new.ToString());
+ Good_(info_new, false, GetAdjustedTime());
+ erase_collision = true;
}
} else { // Collision is not actually a collision anymore
Good_(info_new, false, GetAdjustedTime());
diff --git a/src/addrman.h b/src/addrman.h
index 003bd059f8..e54184ce35 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -166,6 +166,9 @@ public:
//! the maximum number of tried addr collisions to store
#define ADDRMAN_SET_TRIED_COLLISION_SIZE 10
+//! the maximum time we'll spend trying to resolve a tried table collision, in seconds
+static const int64_t ADDRMAN_TEST_WINDOW = 40*60; // 40 minutes
+
/**
* Stochastical (IP) address manager
*/
diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp
index aa66d13102..be145a0e63 100644
--- a/src/arith_uint256.cpp
+++ b/src/arith_uint256.cpp
@@ -6,7 +6,6 @@
#include <arith_uint256.h>
#include <uint256.h>
-#include <util/strencodings.h>
#include <crypto/common.h>
#include <stdio.h>
diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp
index e7702ec461..0f4b52cf79 100644
--- a/src/bench/base58.cpp
+++ b/src/bench/base58.cpp
@@ -4,7 +4,6 @@
#include <bench/bench.h>
-#include <validation.h>
#include <base58.h>
#include <array>
diff --git a/src/bench/bech32.cpp b/src/bench/bech32.cpp
index 3c4b453a23..80f13eeb3b 100644
--- a/src/bench/bech32.cpp
+++ b/src/bench/bech32.cpp
@@ -4,7 +4,6 @@
#include <bench/bench.h>
-#include <validation.h>
#include <bech32.h>
#include <util/strencodings.h>
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 966b99f6c8..f2b520e893 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -1,15 +1,19 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
+#include <chainparams.h>
+#include <test/setup_common.h>
+#include <validation.h>
+
+#include <algorithm>
#include <assert.h>
-#include <iostream>
#include <iomanip>
-#include <algorithm>
-#include <regex>
+#include <iostream>
#include <numeric>
+#include <regex>
void benchmark::ConsolePrinter::header()
{
@@ -108,6 +112,14 @@ void benchmark::BenchRunner::RunAll(Printer& printer, uint64_t num_evals, double
printer.header();
for (const auto& p : benchmarks()) {
+ TestingSetup test{CBaseChainParams::REGTEST};
+ {
+ LOCK(cs_main);
+ assert(::ChainActive().Height() == 0);
+ const bool witness_enabled{IsWitnessEnabled(::ChainActive().Tip(), Params().GetConsensus())};
+ assert(witness_enabled);
+ }
+
if (!std::regex_match(p.first, baseMatch, reFilter)) {
continue;
}
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index b804a84478..8eea96d930 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -1,19 +1,14 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
-#include <crypto/sha256.h>
-#include <key.h>
-#include <util/system.h>
#include <util/strencodings.h>
-#include <validation.h>
+#include <util/system.h>
#include <memory>
-const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
-
static const int64_t DEFAULT_BENCH_EVALUATIONS = 5;
static const char* DEFAULT_BENCH_FILTER = ".*";
static const char* DEFAULT_BENCH_SCALING = "1.0";
@@ -24,7 +19,8 @@ static const int64_t DEFAULT_PLOT_HEIGHT = 768;
static void SetupBenchArgs()
{
- gArgs.AddArg("-?", "Print this help message and exit", false, OptionsCategory::OPTIONS);
+ SetupHelpOptions(gArgs);
+
gArgs.AddArg("-list", "List benchmarks without executing them. Can be combined with -scaling and -filter", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-evals=<n>", strprintf("Number of measurement evaluations to perform. (default: %u)", DEFAULT_BENCH_EVALUATIONS), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), false, OptionsCategory::OPTIONS);
@@ -33,18 +29,6 @@ static void SetupBenchArgs()
gArgs.AddArg("-plot-plotlyurl=<uri>", strprintf("URL to use for plotly.js (default: %s)", DEFAULT_PLOT_PLOTLYURL), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-plot-width=<x>", strprintf("Plot width in pixel (default: %u)", DEFAULT_PLOT_WIDTH), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-plot-height=<x>", strprintf("Plot height in pixel (default: %u)", DEFAULT_PLOT_HEIGHT), false, OptionsCategory::OPTIONS);
-
- // Hidden
- gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN);
- gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN);
-}
-
-static fs::path SetDataDir()
-{
- fs::path ret = fs::temp_directory_path() / "bench_bitcoin" / fs::unique_path();
- fs::create_directories(ret);
- gArgs.ForceSetArg("-datadir", ret.string());
- return ret;
}
int main(int argc, char** argv)
@@ -52,7 +36,7 @@ int main(int argc, char** argv)
SetupBenchArgs();
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
- fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
+ tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
return EXIT_FAILURE;
}
@@ -62,13 +46,6 @@ int main(int argc, char** argv)
return EXIT_SUCCESS;
}
- // Set the datadir after parsing the bench options
- const fs::path bench_datadir{SetDataDir()};
-
- SHA256AutoDetect();
- ECC_Start();
- SetupEnvironment();
-
int64_t evaluations = gArgs.GetArg("-evals", DEFAULT_BENCH_EVALUATIONS);
std::string regex_filter = gArgs.GetArg("-filter", DEFAULT_BENCH_FILTER);
std::string scaling_str = gArgs.GetArg("-scaling", DEFAULT_BENCH_SCALING);
@@ -76,7 +53,7 @@ int main(int argc, char** argv)
double scaling_factor;
if (!ParseDouble(scaling_str, &scaling_factor)) {
- fprintf(stderr, "Error parsing scaling factor as double: %s\n", scaling_str.c_str());
+ tfm::format(std::cerr, "Error parsing scaling factor as double: %s\n", scaling_str.c_str());
return EXIT_FAILURE;
}
@@ -91,9 +68,5 @@ int main(int argc, char** argv)
benchmark::BenchRunner::RunAll(*printer, evaluations, scaling_factor, regex_filter, is_list_only);
- fs::remove_all(bench_datadir);
-
- ECC_Stop();
-
return EXIT_SUCCESS;
}
diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp
index 8133fd9d65..fb33c09ab2 100644
--- a/src/bench/block_assemble.cpp
+++ b/src/bench/block_assemble.cpp
@@ -1,58 +1,18 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
-#include <chainparams.h>
-#include <coins.h>
-#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <crypto/sha256.h>
-#include <miner.h>
-#include <policy/policy.h>
-#include <pow.h>
-#include <scheduler.h>
-#include <txdb.h>
+#include <test/util.h>
#include <txmempool.h>
-#include <util/time.h>
#include <validation.h>
-#include <validationinterface.h>
-#include <boost/thread.hpp>
#include <list>
#include <vector>
-static std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey)
-{
- auto block = std::make_shared<CBlock>(
- BlockAssembler{Params()}
- .CreateNewBlock(coinbase_scriptPubKey)
- ->block);
-
- block->nTime = ::chainActive.Tip()->GetMedianTimePast() + 1;
- block->hashMerkleRoot = BlockMerkleRoot(*block);
-
- return block;
-}
-
-
-static CTxIn MineBlock(const CScript& coinbase_scriptPubKey)
-{
- auto block = PrepareBlock(coinbase_scriptPubKey);
-
- while (!CheckProofOfWork(block->GetHash(), block->nBits, Params().GetConsensus())) {
- ++block->nNonce;
- assert(block->nNonce);
- }
-
- bool processed{ProcessNewBlock(Params(), block, true, nullptr)};
- assert(processed);
-
- return CTxIn{block->vtx[0]->GetHash(), 0};
-}
-
-
static void AssembleBlock(benchmark::State& state)
{
const std::vector<unsigned char> op_true{OP_TRUE};
@@ -64,30 +24,6 @@ static void AssembleBlock(benchmark::State& state)
const CScript SCRIPT_PUB{CScript(OP_0) << std::vector<unsigned char>{witness_program.begin(), witness_program.end()}};
- // Switch to regtest so we can mine faster
- // Also segwit is active, so we can include witness transactions
- SelectParams(CBaseChainParams::REGTEST);
-
- InitScriptExecutionCache();
-
- boost::thread_group thread_group;
- CScheduler scheduler;
- {
- ::pblocktree.reset(new CBlockTreeDB(1 << 20, true));
- ::pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
- ::pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
-
- const CChainParams& chainparams = Params();
- thread_group.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
- GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
- LoadGenesisBlock(chainparams);
- CValidationState state;
- ActivateBestChain(state, chainparams);
- assert(::chainActive.Tip() != nullptr);
- const bool witness_enabled{IsWitnessEnabled(::chainActive.Tip(), chainparams.GetConsensus())};
- assert(witness_enabled);
- }
-
// Collect some loose transactions that spend the coinbases of our mined blocks
constexpr size_t NUM_BLOCKS{200};
std::array<CTransactionRef, NUM_BLOCKS - COINBASE_MATURITY + 1> txs;
@@ -112,11 +48,6 @@ static void AssembleBlock(benchmark::State& state)
while (state.KeepRunning()) {
PrepareBlock(SCRIPT_PUB);
}
-
- thread_group.interrupt_all();
- thread_group.join_all();
- GetMainSignals().FlushBackgroundCallbacks();
- GetMainSignals().UnregisterBackgroundSignalScheduler();
}
BENCHMARK(AssembleBlock, 700);
diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp
index 9cfd5d23ef..1041a22303 100644
--- a/src/bench/ccoins_caching.cpp
+++ b/src/bench/ccoins_caching.cpp
@@ -39,9 +39,9 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
dummyTransactions[1].vout.resize(2);
dummyTransactions[1].vout[0].nValue = 21 * COIN;
- dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
+ dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[2].GetPubKey()));
dummyTransactions[1].vout[1].nValue = 22 * COIN;
- dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
+ dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(PKHash(key[3].GetPubKey()));
AddCoins(coinsRet, CTransaction(dummyTransactions[1]), 0);
return dummyTransactions;
diff --git a/src/bench/chacha20.cpp b/src/bench/chacha20.cpp
new file mode 100644
index 0000000000..030067aca5
--- /dev/null
+++ b/src/bench/chacha20.cpp
@@ -0,0 +1,45 @@
+// Copyright (c) 2019 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 <iostream>
+
+#include <bench/bench.h>
+#include <crypto/chacha20.h>
+
+/* Number of bytes to process per iteration */
+static const uint64_t BUFFER_SIZE_TINY = 64;
+static const uint64_t BUFFER_SIZE_SMALL = 256;
+static const uint64_t BUFFER_SIZE_LARGE = 1024*1024;
+
+static void CHACHA20(benchmark::State& state, size_t buffersize)
+{
+ std::vector<uint8_t> key(32,0);
+ ChaCha20 ctx(key.data(), key.size());
+ ctx.SetIV(0);
+ ctx.Seek(0);
+ std::vector<uint8_t> in(buffersize,0);
+ std::vector<uint8_t> out(buffersize,0);
+ while (state.KeepRunning()) {
+ ctx.Crypt(in.data(), out.data(), in.size());
+ }
+}
+
+static void CHACHA20_64BYTES(benchmark::State& state)
+{
+ CHACHA20(state, BUFFER_SIZE_TINY);
+}
+
+static void CHACHA20_256BYTES(benchmark::State& state)
+{
+ CHACHA20(state, BUFFER_SIZE_SMALL);
+}
+
+static void CHACHA20_1MB(benchmark::State& state)
+{
+ CHACHA20(state, BUFFER_SIZE_LARGE);
+}
+
+BENCHMARK(CHACHA20_64BYTES, 500000);
+BENCHMARK(CHACHA20_256BYTES, 250000);
+BENCHMARK(CHACHA20_1MB, 340);
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
index 6ab542067a..000a0259bb 100644
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -4,7 +4,6 @@
#include <bench/bench.h>
#include <util/system.h>
-#include <validation.h>
#include <checkqueue.h>
#include <prevector.h>
#include <vector>
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 74641191a1..f2ab03e20e 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -29,7 +29,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<st
static void CoinSelection(benchmark::State& state)
{
auto chain = interfaces::MakeChain();
- const CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ const CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);
@@ -61,7 +61,7 @@ static void CoinSelection(benchmark::State& state)
typedef std::set<CInputCoin> CoinSet;
static auto testChain = interfaces::MakeChain();
-static const CWallet testWallet(*testChain, WalletLocation(), WalletDatabase::CreateDummy());
+static const CWallet testWallet(testChain.get(), WalletLocation(), WalletDatabase::CreateDummy());
std::vector<std::unique_ptr<CWalletTx>> wtxn;
// Copied from src/wallet/test/coinselector_tests.cpp
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index dc0b054420..fb2bab9dee 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -5,11 +5,9 @@
#include <iostream>
#include <bench/bench.h>
-#include <bloom.h>
#include <hash.h>
#include <random.h>
#include <uint256.h>
-#include <util/time.h>
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
index e0854e2c62..2440341287 100644
--- a/src/bench/duplicate_inputs.cpp
+++ b/src/bench/duplicate_inputs.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,17 +7,9 @@
#include <coins.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
-#include <miner.h>
-#include <policy/policy.h>
#include <pow.h>
-#include <scheduler.h>
-#include <txdb.h>
#include <txmempool.h>
-#include <util/time.h>
#include <validation.h>
-#include <validationinterface.h>
-
-#include <boost/thread.hpp>
#include <list>
#include <vector>
@@ -27,35 +19,14 @@ static void DuplicateInputs(benchmark::State& state)
{
const CScript SCRIPT_PUB{CScript(OP_TRUE)};
- // Switch to regtest so we can mine faster
- // Also segwit is active, so we can include witness transactions
- SelectParams(CBaseChainParams::REGTEST);
-
- InitScriptExecutionCache();
-
- boost::thread_group thread_group;
- CScheduler scheduler;
const CChainParams& chainparams = Params();
- {
- ::pblocktree.reset(new CBlockTreeDB(1 << 20, true));
- ::pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
- ::pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
-
- thread_group.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
- GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
- LoadGenesisBlock(chainparams);
- CValidationState cvstate;
- ActivateBestChain(cvstate, chainparams);
- assert(::chainActive.Tip() != nullptr);
- const bool witness_enabled{IsWitnessEnabled(::chainActive.Tip(), chainparams.GetConsensus())};
- assert(witness_enabled);
- }
CBlock block{};
CMutableTransaction coinbaseTx{};
CMutableTransaction naughtyTx{};
- CBlockIndex* pindexPrev = ::chainActive.Tip();
+ LOCK(cs_main);
+ CBlockIndex* pindexPrev = ::ChainActive().Tip();
assert(pindexPrev != nullptr);
block.nBits = GetNextWorkRequired(pindexPrev, &block, chainparams.GetConsensus());
block.nNonce = 0;
@@ -90,11 +61,6 @@ static void DuplicateInputs(benchmark::State& state)
assert(!CheckBlock(block, cvstate, chainparams.GetConsensus(), false, false));
assert(cvstate.GetRejectReason() == "bad-txns-inputs-duplicate");
}
-
- thread_group.interrupt_all();
- thread_group.join_all();
- GetMainSignals().FlushBackgroundCallbacks();
- GetMainSignals().UnregisterBackgroundSignalScheduler();
}
BENCHMARK(DuplicateInputs, 10);
diff --git a/src/bench/examples.cpp b/src/bench/examples.cpp
index e7ddd5a938..3595249559 100644
--- a/src/bench/examples.cpp
+++ b/src/bench/examples.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
-#include <validation.h>
#include <util/time.h>
// Sanity test: this should loop ten times, and
diff --git a/src/bench/poly1305.cpp b/src/bench/poly1305.cpp
new file mode 100644
index 0000000000..16342d0fbe
--- /dev/null
+++ b/src/bench/poly1305.cpp
@@ -0,0 +1,41 @@
+// Copyright (c) 2019 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 <iostream>
+
+#include <bench/bench.h>
+#include <crypto/poly1305.h>
+
+/* Number of bytes to process per iteration */
+static constexpr uint64_t BUFFER_SIZE_TINY = 64;
+static constexpr uint64_t BUFFER_SIZE_SMALL = 256;
+static constexpr uint64_t BUFFER_SIZE_LARGE = 1024*1024;
+
+static void POLY1305(benchmark::State& state, size_t buffersize)
+{
+ std::vector<unsigned char> tag(POLY1305_TAGLEN, 0);
+ std::vector<unsigned char> key(POLY1305_KEYLEN, 0);
+ std::vector<unsigned char> in(buffersize, 0);
+ while (state.KeepRunning())
+ poly1305_auth(tag.data(), in.data(), in.size(), key.data());
+}
+
+static void POLY1305_64BYTES(benchmark::State& state)
+{
+ POLY1305(state, BUFFER_SIZE_TINY);
+}
+
+static void POLY1305_256BYTES(benchmark::State& state)
+{
+ POLY1305(state, BUFFER_SIZE_SMALL);
+}
+
+static void POLY1305_1MB(benchmark::State& state)
+{
+ POLY1305(state, BUFFER_SIZE_LARGE);
+}
+
+BENCHMARK(POLY1305_64BYTES, 500000);
+BENCHMARK(POLY1305_256BYTES, 250000);
+BENCHMARK(POLY1305_1MB, 340);
diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp
index 0a99ea3184..4016530dac 100644
--- a/src/bench/rollingbloom.cpp
+++ b/src/bench/rollingbloom.cpp
@@ -28,4 +28,13 @@ static void RollingBloom(benchmark::State& state)
}
}
+static void RollingBloomReset(benchmark::State& state)
+{
+ CRollingBloomFilter filter(120000, 0.000001);
+ while (state.KeepRunning()) {
+ filter.reset();
+ }
+}
+
BENCHMARK(RollingBloom, 1500 * 1000);
+BENCHMARK(RollingBloomReset, 20000);
diff --git a/src/bench/rpc_mempool.cpp b/src/bench/rpc_mempool.cpp
new file mode 100644
index 0000000000..b35a744055
--- /dev/null
+++ b/src/bench/rpc_mempool.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2011-2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+#include <rpc/blockchain.h>
+#include <txmempool.h>
+
+#include <univalue.h>
+
+#include <list>
+#include <vector>
+
+static void AddTx(const CTransactionRef& tx, const CAmount& fee, CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
+{
+ LockPoints lp;
+ pool.addUnchecked(CTxMemPoolEntry(tx, fee, /* time */ 0, /* height */ 1, /* spendsCoinbase */ false, /* sigOpCost */ 4, lp));
+}
+
+static void RpcMempool(benchmark::State& state)
+{
+ CTxMemPool pool;
+ LOCK2(cs_main, pool.cs);
+
+ for (int i = 0; i < 1000; ++i) {
+ CMutableTransaction tx = CMutableTransaction();
+ tx.vin.resize(1);
+ tx.vin[0].scriptSig = CScript() << OP_1;
+ tx.vin[0].scriptWitness.stack.push_back({1});
+ tx.vout.resize(1);
+ tx.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
+ tx.vout[0].nValue = i;
+ const CTransactionRef tx_r{MakeTransactionRef(tx)};
+ AddTx(tx_r, /* fee */ i, pool);
+ }
+
+ while (state.KeepRunning()) {
+ (void)MempoolToJSON(pool, /*verbose*/ true);
+ }
+}
+
+BENCHMARK(RpcMempool, 40);
diff --git a/src/bench/util_time.cpp b/src/bench/util_time.cpp
new file mode 100644
index 0000000000..72d97354aa
--- /dev/null
+++ b/src/bench/util_time.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+
+#include <util/time.h>
+
+static void BenchTimeDeprecated(benchmark::State& state)
+{
+ while (state.KeepRunning()) {
+ (void)GetTime();
+ }
+}
+
+static void BenchTimeMock(benchmark::State& state)
+{
+ SetMockTime(111);
+ while (state.KeepRunning()) {
+ (void)GetTime<std::chrono::seconds>();
+ }
+ SetMockTime(0);
+}
+
+static void BenchTimeMillis(benchmark::State& state)
+{
+ while (state.KeepRunning()) {
+ (void)GetTime<std::chrono::milliseconds>();
+ }
+}
+
+static void BenchTimeMillisSys(benchmark::State& state)
+{
+ while (state.KeepRunning()) {
+ (void)GetTimeMillis();
+ }
+}
+
+BENCHMARK(BenchTimeDeprecated, 100000000);
+BENCHMARK(BenchTimeMillis, 6000000);
+BENCHMARK(BenchTimeMillisSys, 6000000);
+BENCHMARK(BenchTimeMock, 300000000);
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index 312b66e38a..4891c57b3a 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -8,7 +8,6 @@
#include <script/bitcoinconsensus.h>
#endif
#include <script/script.h>
-#include <script/sign.h>
#include <script/standard.h>
#include <streams.h>
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
new file mode 100644
index 0000000000..313b5a3ba0
--- /dev/null
+++ b/src/bench/wallet_balance.cpp
@@ -0,0 +1,52 @@
+// Copyright (c) 2012-2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+#include <interfaces/chain.h>
+#include <optional.h>
+#include <test/util.h>
+#include <validationinterface.h>
+#include <wallet/wallet.h>
+
+static void WalletBalance(benchmark::State& state, const bool set_dirty, const bool add_watchonly, const bool add_mine)
+{
+ const auto& ADDRESS_WATCHONLY = ADDRESS_BCRT1_UNSPENDABLE;
+
+ std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain();
+ CWallet wallet{chain.get(), WalletLocation(), WalletDatabase::CreateMock()};
+ {
+ bool first_run;
+ if (wallet.LoadWallet(first_run) != DBErrors::LOAD_OK) assert(false);
+ wallet.handleNotifications();
+ }
+
+
+ const Optional<std::string> address_mine{add_mine ? Optional<std::string>{getnewaddress(wallet)} : nullopt};
+ if (add_watchonly) importaddress(wallet, ADDRESS_WATCHONLY);
+
+ for (int i = 0; i < 100; ++i) {
+ generatetoaddress(address_mine.get_value_or(ADDRESS_WATCHONLY));
+ generatetoaddress(ADDRESS_WATCHONLY);
+ }
+ SyncWithValidationInterfaceQueue();
+
+ auto bal = wallet.GetBalance(); // Cache
+
+ while (state.KeepRunning()) {
+ if (set_dirty) wallet.MarkDirty();
+ bal = wallet.GetBalance();
+ if (add_mine) assert(bal.m_mine_trusted > 0);
+ if (add_watchonly) assert(bal.m_watchonly_trusted > 0);
+ }
+}
+
+static void WalletBalanceDirty(benchmark::State& state) { WalletBalance(state, /* set_dirty */ true, /* add_watchonly */ true, /* add_mine */ true); }
+static void WalletBalanceClean(benchmark::State& state) { WalletBalance(state, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ true); }
+static void WalletBalanceMine(benchmark::State& state) { WalletBalance(state, /* set_dirty */ false, /* add_watchonly */ false, /* add_mine */ true); }
+static void WalletBalanceWatch(benchmark::State& state) { WalletBalance(state, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ false); }
+
+BENCHMARK(WalletBalanceDirty, 2500);
+BENCHMARK(WalletBalanceClean, 8000);
+BENCHMARK(WalletBalanceMine, 16000);
+BENCHMARK(WalletBalanceWatch, 8000);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 7cdf61ad35..38010c461e 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -34,11 +34,12 @@ static const int CONTINUE_EXECUTION=-1;
static void SetupCliArgs()
{
+ SetupHelpOptions(gArgs);
+
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST);
- gArgs.AddArg("-?", "This help message", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-version", "Print version and exit", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
@@ -55,10 +56,6 @@ static void SetupCliArgs()
gArgs.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-stdinrpcpass", "Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS);
-
- // Hidden
- gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN);
- gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN);
}
/** libevent event log callback */
@@ -104,7 +101,7 @@ static int AppInitRPC(int argc, char* argv[])
SetupCliArgs();
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
- fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
+ tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
return EXIT_FAILURE;
}
if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
@@ -118,26 +115,26 @@ static int AppInitRPC(int argc, char* argv[])
strUsage += "\n" + gArgs.GetHelpMessage();
}
- fprintf(stdout, "%s", strUsage.c_str());
+ tfm::format(std::cout, "%s", strUsage.c_str());
if (argc < 2) {
- fprintf(stderr, "Error: too few parameters\n");
+ tfm::format(std::cerr, "Error: too few parameters\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
if (!fs::is_directory(GetDataDir(false))) {
- fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
+ tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
return EXIT_FAILURE;
}
if (!gArgs.ReadConfigFiles(error, true)) {
- fprintf(stderr, "Error reading configuration file: %s\n", error.c_str());
+ tfm::format(std::cerr, "Error reading configuration file: %s\n", error.c_str());
return EXIT_FAILURE;
}
// Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
try {
SelectBaseParams(gArgs.GetChainName());
} catch (const std::exception& e) {
- fprintf(stderr, "Error: %s\n", e.what());
+ tfm::format(std::cerr, "Error: %s\n", e.what());
return EXIT_FAILURE;
}
return CONTINUE_EXECUTION;
@@ -256,16 +253,12 @@ public:
}
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"));
+ result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
if (!batch[ID_WALLETINFO].isNull()) {
result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]);
result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]);
@@ -502,7 +495,7 @@ static int CommandLineRPC(int argc, char *argv[])
}
if (strPrint != "") {
- fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
+ tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint.c_str());
}
return nRet;
}
@@ -515,7 +508,7 @@ int main(int argc, char* argv[])
#endif
SetupEnvironment();
if (!SetupNetworking()) {
- fprintf(stderr, "Error: Initializing networking failed\n");
+ tfm::format(std::cerr, "Error: Initializing networking failed\n");
return EXIT_FAILURE;
}
event_set_log_callback(&libevent_log_cb);
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 7c0c674a00..933b34744d 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -18,6 +18,7 @@
#include <script/script.h>
#include <script/sign.h>
#include <univalue.h>
+#include <util/rbf.h>
#include <util/system.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
@@ -35,7 +36,8 @@ const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
static void SetupBitcoinTxArgs()
{
- gArgs.AddArg("-?", "This help message", false, OptionsCategory::OPTIONS);
+ SetupHelpOptions(gArgs);
+
gArgs.AddArg("-create", "Create new, empty TX.", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-json", "Select JSON output", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-txid", "Output only the hex-encoded transaction id of the resultant transaction.", false, OptionsCategory::OPTIONS);
@@ -62,14 +64,10 @@ static void SetupBitcoinTxArgs()
"This command requires JSON registers:"
"prevtxs=JSON object, "
"privatekeys=JSON object. "
- "See signrawtransaction docs for format of sighash flags, JSON objects.", false, OptionsCategory::COMMANDS);
+ "See signrawtransactionwithkey docs for format of sighash flags, JSON objects.", false, OptionsCategory::COMMANDS);
gArgs.AddArg("load=NAME:FILENAME", "Load JSON file FILENAME into register NAME", false, OptionsCategory::REGISTER_COMMANDS);
gArgs.AddArg("set=NAME:JSON-STRING", "Set register NAME to given JSON-STRING", false, OptionsCategory::REGISTER_COMMANDS);
-
- // Hidden
- gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN);
- gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN);
}
//
@@ -84,7 +82,7 @@ static int AppInitRawTx(int argc, char* argv[])
SetupBitcoinTxArgs();
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
- fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
+ tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
return EXIT_FAILURE;
}
@@ -92,7 +90,7 @@ static int AppInitRawTx(int argc, char* argv[])
try {
SelectParams(gArgs.GetChainName());
} catch (const std::exception& e) {
- fprintf(stderr, "Error: %s\n", e.what());
+ tfm::format(std::cerr, "Error: %s\n", e.what());
return EXIT_FAILURE;
}
@@ -106,10 +104,10 @@ static int AppInitRawTx(int argc, char* argv[])
"\n";
strUsage += gArgs.GetHelpMessage();
- fprintf(stdout, "%s", strUsage.c_str());
+ tfm::format(std::cout, "%s", strUsage.c_str());
if (argc < 2) {
- fprintf(stderr, "Error: too few parameters\n");
+ tfm::format(std::cerr, "Error: too few parameters\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
@@ -325,7 +323,7 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str
}
if (bScriptHash) {
// Get the ID for the script, and then construct a P2SH destination for it.
- scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
+ scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
}
// construct TxOut, append to transaction output list
@@ -399,7 +397,7 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s
"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));
+ scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
}
// construct TxOut, append to transaction output list
@@ -471,7 +469,7 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
throw std::runtime_error(strprintf(
"redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
}
- scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
+ scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
}
// construct TxOut, append to transaction output list
@@ -725,21 +723,21 @@ static void OutputTxJSON(const CTransaction& tx)
TxToUniv(tx, uint256(), entry);
std::string jsonOutput = entry.write(4);
- fprintf(stdout, "%s\n", jsonOutput.c_str());
+ tfm::format(std::cout, "%s\n", jsonOutput.c_str());
}
static void OutputTxHash(const CTransaction& tx)
{
std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
- fprintf(stdout, "%s\n", strHexHash.c_str());
+ tfm::format(std::cout, "%s\n", strHexHash.c_str());
}
static void OutputTxHex(const CTransaction& tx)
{
std::string strHex = EncodeHexTx(tx);
- fprintf(stdout, "%s\n", strHex.c_str());
+ tfm::format(std::cout, "%s\n", strHex.c_str());
}
static void OutputTx(const CTransaction& tx)
@@ -830,7 +828,7 @@ static int CommandLineRawTx(int argc, char* argv[])
}
if (strPrint != "") {
- fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
+ tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint.c_str());
}
return nRet;
}
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 293e3efa5c..cbb4ea750c 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -8,7 +8,6 @@
#include <chainparams.h>
#include <chainparamsbase.h>
-#include <consensus/consensus.h>
#include <logging.h>
#include <util/system.h>
#include <util/strencodings.h>
@@ -20,9 +19,9 @@ const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
static void SetupWalletToolArgs()
{
+ SetupHelpOptions(gArgs);
SetupChainParamsBaseOptions();
- gArgs.AddArg("-?", "This help message", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-wallet=<wallet-name>", "Specify wallet name", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-debug=<category>", "Output debugging information (default: 0).", false, OptionsCategory::DEBUG_TEST);
@@ -30,10 +29,6 @@ static void SetupWalletToolArgs()
gArgs.AddArg("info", "Get wallet info", false, OptionsCategory::COMMANDS);
gArgs.AddArg("create", "Create new wallet file", false, OptionsCategory::COMMANDS);
-
- // Hidden
- gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN);
- gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN);
}
static bool WalletAppInit(int argc, char* argv[])
@@ -41,7 +36,7 @@ static bool WalletAppInit(int argc, char* argv[])
SetupWalletToolArgs();
std::string error_message;
if (!gArgs.ParseParameters(argc, argv, error_message)) {
- fprintf(stderr, "Error parsing command line arguments: %s\n", error_message.c_str());
+ tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error_message.c_str());
return false;
}
if (argc < 2 || HelpRequested(gArgs)) {
@@ -53,15 +48,15 @@ static bool WalletAppInit(int argc, char* argv[])
" bitcoin-wallet [options] <command>\n\n" +
gArgs.GetHelpMessage();
- fprintf(stdout, "%s", usage.c_str());
+ tfm::format(std::cout, "%s", usage.c_str());
return false;
}
// check for printtoconsole, allow -debug
- g_logger->m_print_to_console = gArgs.GetBoolArg("-printtoconsole", gArgs.GetBoolArg("-debug", false));
+ LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", gArgs.GetBoolArg("-debug", false));
if (!fs::is_directory(GetDataDir(false))) {
- fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
+ tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
return false;
}
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
@@ -92,7 +87,7 @@ int main(int argc, char* argv[])
for(int i = 1; i < argc; ++i) {
if (!IsSwitchChar(argv[i][0])) {
if (!method.empty()) {
- fprintf(stderr, "Error: two methods provided (%s and %s). Only one method should be provided.\n", method.c_str(), argv[i]);
+ tfm::format(std::cerr, "Error: two methods provided (%s and %s). Only one method should be provided.\n", method.c_str(), argv[i]);
return EXIT_FAILURE;
}
method = argv[i];
@@ -100,13 +95,13 @@ int main(int argc, char* argv[])
}
if (method.empty()) {
- fprintf(stderr, "No method provided. Run `bitcoin-wallet -help` for valid methods.\n");
+ tfm::format(std::cerr, "No method provided. Run `bitcoin-wallet -help` for valid methods.\n");
return EXIT_FAILURE;
}
// A name must be provided when creating a file
if (method == "create" && !gArgs.IsArgSet("-wallet")) {
- fprintf(stderr, "Wallet name must be provided when creating a new wallet.\n");
+ tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
return EXIT_FAILURE;
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index dde75c1b12..ba6de702e0 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -12,15 +12,12 @@
#include <compat.h>
#include <fs.h>
#include <interfaces/chain.h>
-#include <rpc/server.h>
#include <init.h>
#include <noui.h>
#include <shutdown.h>
#include <util/system.h>
-#include <httpserver.h>
-#include <httprpc.h>
+#include <util/threadnames.h>
#include <util/strencodings.h>
-#include <walletinitinterface.h>
#include <stdio.h>
@@ -64,6 +61,8 @@ static bool AppInit(int argc, char* argv[])
bool fRet = false;
+ util::ThreadRename("init");
+
//
// Parameters
//
@@ -71,7 +70,7 @@ static bool AppInit(int argc, char* argv[])
SetupServerArgs();
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
- fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
+ tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
return false;
}
@@ -89,7 +88,7 @@ static bool AppInit(int argc, char* argv[])
strUsage += "\n" + gArgs.GetHelpMessage();
}
- fprintf(stdout, "%s", strUsage.c_str());
+ tfm::format(std::cout, "%s", strUsage.c_str());
return true;
}
@@ -97,25 +96,25 @@ static bool AppInit(int argc, char* argv[])
{
if (!fs::is_directory(GetDataDir(false)))
{
- fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
+ tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
return false;
}
if (!gArgs.ReadConfigFiles(error, true)) {
- fprintf(stderr, "Error reading configuration file: %s\n", error.c_str());
+ tfm::format(std::cerr, "Error reading configuration file: %s\n", error.c_str());
return false;
}
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
try {
SelectParams(gArgs.GetChainName());
} catch (const std::exception& e) {
- fprintf(stderr, "Error: %s\n", e.what());
+ tfm::format(std::cerr, "Error: %s\n", e.what());
return false;
}
// Error out when loose non-argument tokens are encountered on command line
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]);
+ tfm::format(std::cerr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
return false;
}
}
@@ -147,18 +146,18 @@ static bool AppInit(int argc, char* argv[])
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
- fprintf(stdout, "Bitcoin server starting\n");
+ tfm::format(std::cout, "Bitcoin server starting\n");
// Daemonize
if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
- fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno));
+ tfm::format(std::cerr, "Error: daemon() failed: %s\n", strerror(errno));
return false;
}
#if defined(MAC_OSX)
#pragma GCC diagnostic pop
#endif
#else
- fprintf(stderr, "Error: -daemon is not supported on this operating system\n");
+ tfm::format(std::cerr, "Error: -daemon is not supported on this operating system\n");
return false;
#endif // HAVE_DECL_DAEMON
}
diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
index 10f51931f0..f0fcf675eb 100644
--- a/src/blockencodings.cpp
+++ b/src/blockencodings.cpp
@@ -203,7 +203,7 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
// but that is expensive, and CheckBlock caches a block's
// "checked-status" (in the CBlock?). CBlock should be able to
// check its own merkle root and cache that check.
- if (state.CorruptionPossible())
+ if (state.GetReason() == ValidationInvalidReason::BLOCK_MUTATED)
return READ_STATUS_FAILED; // Possible Short ID collision
return READ_STATUS_CHECKBLOCK_FAILED;
}
diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp
index bcf24047ff..787390be31 100644
--- a/src/blockfilter.cpp
+++ b/src/blockfilter.cpp
@@ -2,6 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <mutex>
+#include <sstream>
+
#include <blockfilter.h>
#include <crypto/siphash.h>
#include <hash.h>
@@ -15,6 +18,10 @@ static constexpr int GCS_SER_TYPE = SER_NETWORK;
/// Protocol version used to serialize parameters in GCS filter encoding.
static constexpr int GCS_SER_VERSION = 0;
+static const std::map<BlockFilterType, std::string> g_filter_types = {
+ {BlockFilterType::BASIC, "basic"},
+};
+
template <typename OStream>
static void GolombRiceEncode(BitStreamWriter<OStream>& bitwriter, uint8_t P, uint64_t x)
{
@@ -197,6 +204,57 @@ bool GCSFilter::MatchAny(const ElementSet& elements) const
return MatchInternal(queries.data(), queries.size());
}
+const std::string& BlockFilterTypeName(BlockFilterType filter_type)
+{
+ static std::string unknown_retval = "";
+ auto it = g_filter_types.find(filter_type);
+ return it != g_filter_types.end() ? it->second : unknown_retval;
+}
+
+bool BlockFilterTypeByName(const std::string& name, BlockFilterType& filter_type) {
+ for (const auto& entry : g_filter_types) {
+ if (entry.second == name) {
+ filter_type = entry.first;
+ return true;
+ }
+ }
+ return false;
+}
+
+const std::vector<BlockFilterType>& AllBlockFilterTypes()
+{
+ static std::vector<BlockFilterType> types;
+
+ static std::once_flag flag;
+ std::call_once(flag, []() {
+ types.reserve(g_filter_types.size());
+ for (auto entry : g_filter_types) {
+ types.push_back(entry.first);
+ }
+ });
+
+ return types;
+}
+
+const std::string& ListBlockFilterTypes()
+{
+ static std::string type_list;
+
+ static std::once_flag flag;
+ std::call_once(flag, []() {
+ std::stringstream ret;
+ bool first = true;
+ for (auto entry : g_filter_types) {
+ if (!first) ret << ", ";
+ ret << entry.second;
+ first = false;
+ }
+ type_list = ret.str();
+ });
+
+ return type_list;
+}
+
static GCSFilter::ElementSet BasicFilterElements(const CBlock& block,
const CBlockUndo& block_undo)
{
@@ -251,6 +309,8 @@ bool BlockFilter::BuildParams(GCSFilter::Params& params) const
params.m_P = BASIC_FILTER_P;
params.m_M = BASIC_FILTER_M;
return true;
+ case BlockFilterType::INVALID:
+ return false;
}
return false;
diff --git a/src/blockfilter.h b/src/blockfilter.h
index 4d1f51dd60..914b94fec1 100644
--- a/src/blockfilter.h
+++ b/src/blockfilter.h
@@ -6,6 +6,7 @@
#define BITCOIN_BLOCKFILTER_H
#include <stdint.h>
+#include <string>
#include <unordered_set>
#include <vector>
@@ -83,11 +84,24 @@ public:
constexpr uint8_t BASIC_FILTER_P = 19;
constexpr uint32_t BASIC_FILTER_M = 784931;
-enum BlockFilterType : uint8_t
+enum class BlockFilterType : uint8_t
{
BASIC = 0,
+ INVALID = 255,
};
+/** Get the human-readable name for a filter type. Returns empty string for unknown types. */
+const std::string& BlockFilterTypeName(BlockFilterType filter_type);
+
+/** Find a filter type by its human-readable name. */
+bool BlockFilterTypeByName(const std::string& name, BlockFilterType& filter_type);
+
+/** Get a list of known filter types. */
+const std::vector<BlockFilterType>& AllBlockFilterTypes();
+
+/** Get a comma-separated list of known filter type names. */
+const std::string& ListBlockFilterTypes();
+
/**
* Complete block filter struct as defined in BIP 157. Serialization matches
* payload of "cfilter" messages.
@@ -95,7 +109,7 @@ enum BlockFilterType : uint8_t
class BlockFilter
{
private:
- BlockFilterType m_filter_type;
+ BlockFilterType m_filter_type = BlockFilterType::INVALID;
uint256 m_block_hash;
GCSFilter m_filter;
diff --git a/src/bloom.cpp b/src/bloom.cpp
index 7732cee275..a061925089 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -14,6 +14,7 @@
#include <math.h>
#include <stdlib.h>
+#include <algorithm>
#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
#define LN2 0.6931471805599453094172321214581765680755001343602552
@@ -304,7 +305,5 @@ void CRollingBloomFilter::reset()
nTweak = GetRand(std::numeric_limits<unsigned int>::max());
nEntriesThisGeneration = 0;
nGeneration = 1;
- for (std::vector<uint64_t>::iterator it = data.begin(); it != data.end(); it++) {
- *it = 0;
- }
+ std::fill(data.begin(), data.end(), 0);
}
diff --git a/src/chain.cpp b/src/chain.cpp
index d462f94ab5..5520d8149a 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -59,10 +59,11 @@ const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
return pindex;
}
-CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime) const
+CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime, int height) const
{
- std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime,
- [](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTimeMax() < time; });
+ std::pair<int64_t, int> blockparams = std::make_pair(nTime, height);
+ std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), blockparams,
+ [](CBlockIndex* pBlock, const std::pair<int64_t, int>& blockparams) -> bool { return pBlock->GetBlockTimeMax() < blockparams.first || pBlock->nHeight < blockparams.second; });
return (lower == vChain.end() ? nullptr : *lower);
}
diff --git a/src/chain.h b/src/chain.h
index 5a6f10b84f..dd9cc2a598 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -8,6 +8,7 @@
#include <arith_uint256.h>
#include <consensus/params.h>
+#include <flatfile.h>
#include <primitives/block.h>
#include <tinyformat.h>
#include <uint256.h>
@@ -90,46 +91,6 @@ public:
}
};
-struct CDiskBlockPos
-{
- int nFile;
- unsigned int nPos;
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(VARINT(nFile, VarIntMode::NONNEGATIVE_SIGNED));
- READWRITE(VARINT(nPos));
- }
-
- CDiskBlockPos() {
- SetNull();
- }
-
- CDiskBlockPos(int nFileIn, unsigned int nPosIn) {
- nFile = nFileIn;
- nPos = nPosIn;
- }
-
- friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) {
- return (a.nFile == b.nFile && a.nPos == b.nPos);
- }
-
- friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) {
- return !(a == b);
- }
-
- void SetNull() { nFile = -1; nPos = 0; }
- bool IsNull() const { return (nFile == -1); }
-
- std::string ToString() const
- {
- return strprintf("CDiskBlockPos(nFile=%i, nPos=%i)", nFile, nPos);
- }
-
-};
-
enum BlockStatus: uint32_t {
//! Unused.
BLOCK_VALID_UNKNOWN = 0,
@@ -266,8 +227,8 @@ public:
nNonce = block.nNonce;
}
- CDiskBlockPos GetBlockPos() const {
- CDiskBlockPos ret;
+ FlatFilePos GetBlockPos() const {
+ FlatFilePos ret;
if (nStatus & BLOCK_HAVE_DATA) {
ret.nFile = nFile;
ret.nPos = nDataPos;
@@ -275,8 +236,8 @@ public:
return ret;
}
- CDiskBlockPos GetUndoPos() const {
- CDiskBlockPos ret;
+ FlatFilePos GetUndoPos() const {
+ FlatFilePos ret;
if (nStatus & BLOCK_HAVE_UNDO) {
ret.nFile = nFile;
ret.nPos = nUndoPos;
@@ -504,8 +465,8 @@ public:
/** Find the last common block between this chain and a block index entry. */
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
- /** Find the earliest block with timestamp equal or greater than the given. */
- CBlockIndex* FindEarliestAtLeast(int64_t nTime) const;
+ /** Find the earliest block with timestamp equal or greater than the given time and height equal or greater than the given height. */
+ CBlockIndex* FindEarliestAtLeast(int64_t nTime, int height) const;
};
#endif // BITCOIN_CHAIN_H
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index d3972fdb89..b8e0ea23dd 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -91,10 +91,10 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1510704000; // November 15th, 2017.
// The best chain should have at least this much work.
- consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000028822fef1c230963535a90d");
+ consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000051dc8b82f450202ecb3d471");
// By default assume that the signatures in ancestors of this block are valid.
- consensus.defaultAssumeValid = uint256S("0x0000000000000000002e63058c023a9a1de233554f28c7b21380b6c9003f36a8"); //534292
+ consensus.defaultAssumeValid = uint256S("0x0000000000000000000f1c54590ee18d15ec70e68c8cd4cfbadb1b4f11697eee"); //563378
/**
* The message start string is designed to be unlikely to occur in normal data.
@@ -107,7 +107,7 @@ public:
pchMessageStart[3] = 0xd9;
nDefaultPort = 8333;
nPruneAfterHeight = 100000;
- m_assumed_blockchain_size = 200;
+ m_assumed_blockchain_size = 240;
m_assumed_chain_state_size = 3;
genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN);
@@ -162,10 +162,10 @@ public:
};
chainTxData = ChainTxData{
- // Data from rpc: getchaintxstats 4096 0000000000000000002e63058c023a9a1de233554f28c7b21380b6c9003f36a8
- /* nTime */ 1532884444,
- /* nTxCount */ 331282217,
- /* dTxRate */ 2.4
+ // Data from rpc: getchaintxstats 4096 0000000000000000000f1c54590ee18d15ec70e68c8cd4cfbadb1b4f11697eee
+ /* nTime */ 1550374134,
+ /* nTxCount */ 383732546,
+ /* dTxRate */ 3.685496590998308
};
/* disable fallback fee on mainnet */
@@ -219,7 +219,7 @@ public:
pchMessageStart[3] = 0x07;
nDefaultPort = 18333;
nPruneAfterHeight = 1000;
- m_assumed_blockchain_size = 20;
+ m_assumed_blockchain_size = 30;
m_assumed_chain_state_size = 2;
genesis = CreateGenesisBlock(1296688602, 414098458, 0x1d00ffff, 1, 50 * COIN);
diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp
deleted file mode 100644
index ad5edfeb39..0000000000
--- a/src/checkpoints.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2009-2018 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 <checkpoints.h>
-
-#include <chain.h>
-#include <chainparams.h>
-#include <reverse_iterator.h>
-#include <validation.h>
-
-#include <stdint.h>
-
-
-namespace Checkpoints {
-
- CBlockIndex* GetLastCheckpoint(const CCheckpointData& data)
- {
- const MapCheckpoints& checkpoints = data.mapCheckpoints;
-
- for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
- {
- const uint256& hash = i.second;
- CBlockIndex* pindex = LookupBlockIndex(hash);
- if (pindex) {
- return pindex;
- }
- }
- return nullptr;
- }
-
-} // namespace Checkpoints
diff --git a/src/checkpoints.h b/src/checkpoints.h
deleted file mode 100644
index a25e97e469..0000000000
--- a/src/checkpoints.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2009-2018 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_CHECKPOINTS_H
-#define BITCOIN_CHECKPOINTS_H
-
-#include <uint256.h>
-
-#include <map>
-
-class CBlockIndex;
-struct CCheckpointData;
-
-/**
- * Block-chain checkpoints are compiled-in sanity checks.
- * They are updated every release or three.
- */
-namespace Checkpoints
-{
-
-//! Returns last CBlockIndex* that is a checkpoint
-CBlockIndex* GetLastCheckpoint(const CCheckpointData& data);
-
-} //namespace Checkpoints
-
-#endif // BITCOIN_CHECKPOINTS_H
diff --git a/src/coins.h b/src/coins.h
index d39ebf9062..482e233e8c 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -294,6 +294,10 @@ public:
bool HaveInputs(const CTransaction& tx) const;
private:
+ /**
+ * @note this is marked const, but may actually append to `cacheCoins`, increasing
+ * memory usage.
+ */
CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const;
};
diff --git a/src/compat.h b/src/compat.h
index 7b164d5630..68f6eb692c 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -11,10 +11,6 @@
#endif
#ifdef WIN32
-#ifdef _WIN32_WINNT
-#undef _WIN32_WINNT
-#endif
-#define _WIN32_WINNT 0x0501
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
diff --git a/src/compat/assumptions.h b/src/compat/assumptions.h
new file mode 100644
index 0000000000..6e7b4d3ded
--- /dev/null
+++ b/src/compat/assumptions.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+// Compile-time verification of assumptions we make.
+
+#ifndef BITCOIN_COMPAT_ASSUMPTIONS_H
+#define BITCOIN_COMPAT_ASSUMPTIONS_H
+
+#include <limits>
+
+// Assumption: We assume that the macro NDEBUG is not defined.
+// Example(s): We use assert(...) extensively with the assumption of it never
+// being a noop at runtime.
+#if defined(NDEBUG)
+# error "Bitcoin cannot be compiled without assertions."
+#endif
+
+// Assumption: We assume a C++11 (ISO/IEC 14882:2011) compiler (minimum requirement).
+// Example(s): We assume the presence of C++11 features everywhere :-)
+// Note: MSVC does not report the expected __cplusplus value due to legacy
+// reasons.
+#if !defined(_MSC_VER)
+// ISO Standard C++11 [cpp.predefined]p1:
+// "The name __cplusplus is defined to the value 201103L when compiling a C++
+// translation unit."
+static_assert(__cplusplus >= 201103L, "C++11 standard assumed");
+#endif
+
+// Assumption: We assume the floating-point types to fulfill the requirements of
+// IEC 559 (IEEE 754) standard.
+// Example(s): Floating-point division by zero in ConnectBlock, CreateTransaction
+// and EstimateMedianVal.
+static_assert(std::numeric_limits<float>::is_iec559, "IEEE 754 float assumed");
+static_assert(std::numeric_limits<double>::is_iec559, "IEEE 754 double assumed");
+
+// Assumption: We assume eight bits per byte (obviously, but remember: don't
+// trust -- verify!).
+// Example(s): Everywhere :-)
+static_assert(std::numeric_limits<unsigned char>::digits == 8, "8-bit byte assumed");
+
+// Assumption: We assume floating-point widths.
+// Example(s): Type punning in serialization code (ser_{float,double}_to_uint{32,64}).
+static_assert(sizeof(float) == 4, "32-bit float assumed");
+static_assert(sizeof(double) == 8, "64-bit double assumed");
+
+// Assumption: We assume integer widths.
+// Example(s): GetSizeOfCompactSize and WriteCompactSize in the serialization
+// code.
+static_assert(sizeof(short) == 2, "16-bit short assumed");
+static_assert(sizeof(int) == 4, "32-bit int assumed");
+
+// Assumption: We assume size_t to be 32-bit or 64-bit.
+// Example(s): size_t assumed to be at least 32-bit in ecdsa_signature_parse_der_lax(...).
+// size_t assumed to be 32-bit or 64-bit in MallocUsage(...).
+static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t assumed to be 32-bit or 64-bit");
+static_assert(sizeof(size_t) == sizeof(void*), "Sizes of size_t and void* assumed to be equal");
+
+// Some important things we are NOT assuming (non-exhaustive list):
+// * We are NOT assuming a specific value for std::endian::native.
+// * We are NOT assuming a specific value for std::locale("").name().
+// * We are NOT assuming a specific value for std::numeric_limits<char>::is_signed.
+
+#endif // BITCOIN_COMPAT_ASSUMPTIONS_H
diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp
index b47d9774ca..f87612edef 100644
--- a/src/consensus/merkle.cpp
+++ b/src/consensus/merkle.cpp
@@ -4,7 +4,6 @@
#include <consensus/merkle.h>
#include <hash.h>
-#include <util/strencodings.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/tx_check.cpp b/src/consensus/tx_check.cpp
new file mode 100644
index 0000000000..23ed3ecb53
--- /dev/null
+++ b/src/consensus/tx_check.cpp
@@ -0,0 +1,57 @@
+// Copyright (c) 2017-2018 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 <consensus/tx_check.h>
+
+#include <primitives/transaction.h>
+#include <consensus/validation.h>
+
+bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs)
+{
+ // Basic checks that don't depend on any context
+ if (tx.vin.empty())
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vin-empty");
+ if (tx.vout.empty())
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vout-empty");
+ // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
+ if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-oversize");
+
+ // Check for negative or overflow output values
+ CAmount nValueOut = 0;
+ for (const auto& txout : tx.vout)
+ {
+ if (txout.nValue < 0)
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vout-negative");
+ if (txout.nValue > MAX_MONEY)
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vout-toolarge");
+ nValueOut += txout.nValue;
+ if (!MoneyRange(nValueOut))
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
+ }
+
+ // Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
+ if (fCheckDuplicateInputs) {
+ std::set<COutPoint> vInOutPoints;
+ for (const auto& txin : tx.vin)
+ {
+ if (!vInOutPoints.insert(txin.prevout).second)
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
+ }
+ }
+
+ if (tx.IsCoinBase())
+ {
+ if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-length");
+ }
+ else
+ {
+ for (const auto& txin : tx.vin)
+ if (txin.prevout.IsNull())
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-prevout-null");
+ }
+
+ return true;
+}
diff --git a/src/consensus/tx_check.h b/src/consensus/tx_check.h
new file mode 100644
index 0000000000..bcfdf36bf9
--- /dev/null
+++ b/src/consensus/tx_check.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2017-2018 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_CONSENSUS_TX_CHECK_H
+#define BITCOIN_CONSENSUS_TX_CHECK_H
+
+/**
+ * Context-independent transaction checking code that can be called outside the
+ * bitcoin server and doesn't depend on chain or mempool state. Transaction
+ * verification code that does call server functions or depend on server state
+ * belongs in tx_verify.h/cpp instead.
+ */
+
+class CTransaction;
+class CValidationState;
+
+bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true);
+
+#endif // BITCOIN_CONSENSUS_TX_CHECK_H
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
index 0a7eacfb91..4b93cae848 100644
--- a/src/consensus/tx_verify.cpp
+++ b/src/consensus/tx_verify.cpp
@@ -156,60 +156,11 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
return nSigOps;
}
-bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs)
-{
- // Basic checks that don't depend on any context
- if (tx.vin.empty())
- return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty");
- if (tx.vout.empty())
- return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
- // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
- if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
-
- // Check for negative or overflow output values
- CAmount nValueOut = 0;
- for (const auto& txout : tx.vout)
- {
- if (txout.nValue < 0)
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
- if (txout.nValue > MAX_MONEY)
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
- nValueOut += txout.nValue;
- if (!MoneyRange(nValueOut))
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
- }
-
- // Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
- if (fCheckDuplicateInputs) {
- std::set<COutPoint> vInOutPoints;
- for (const auto& txin : tx.vin)
- {
- if (!vInOutPoints.insert(txin.prevout).second)
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
- }
- }
-
- if (tx.IsCoinBase())
- {
- if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
- return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
- }
- else
- {
- for (const auto& txin : tx.vin)
- if (txin.prevout.IsNull())
- return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
- }
-
- return true;
-}
-
bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
{
// are the actual inputs available?
if (!inputs.HaveInputs(tx)) {
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", false,
+ return state.Invalid(ValidationInvalidReason::TX_MISSING_INPUTS, false, REJECT_INVALID, "bad-txns-inputs-missingorspent",
strprintf("%s: inputs missing/spent", __func__));
}
@@ -221,28 +172,27 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
// 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",
+ return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, 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");
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, 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,
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-in-belowout",
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");
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-fee-outofrange");
}
txfee = txfee_aux;
diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h
index 0519cef8c0..3519fc555d 100644
--- a/src/consensus/tx_verify.h
+++ b/src/consensus/tx_verify.h
@@ -17,9 +17,6 @@ class CValidationState;
/** Transaction validation functions */
-/** Context-independent validity checks */
-bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true);
-
namespace Consensus {
/**
* Check whether all inputs of this transaction are valid (no double spends and amounts)
diff --git a/src/consensus/validation.h b/src/consensus/validation.h
index f2e2c3585a..2e23f4b3a4 100644
--- a/src/consensus/validation.h
+++ b/src/consensus/validation.h
@@ -22,6 +22,78 @@ static const unsigned char REJECT_NONSTANDARD = 0x40;
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
static const unsigned char REJECT_CHECKPOINT = 0x43;
+/** A "reason" why something was invalid, suitable for determining whether the
+ * provider of the object should be banned/ignored/disconnected/etc.
+ * These are much more granular than the rejection codes, which may be more
+ * useful for some other use-cases.
+ */
+enum class ValidationInvalidReason {
+ // txn and blocks:
+ NONE, //!< not actually invalid
+ CONSENSUS, //!< invalid by consensus rules (excluding any below reasons)
+ /**
+ * Invalid by a change to consensus rules more recent than SegWit.
+ * Currently unused as there are no such consensus rule changes, and any download
+ * sources realistically need to support SegWit in order to provide useful data,
+ * so differentiating between always-invalid and invalid-by-pre-SegWit-soft-fork
+ * is uninteresting.
+ */
+ RECENT_CONSENSUS_CHANGE,
+ // Only blocks (or headers):
+ CACHED_INVALID, //!< this object was cached as being invalid, but we don't know why
+ BLOCK_INVALID_HEADER, //!< invalid proof of work or time too old
+ BLOCK_MUTATED, //!< the block's data didn't match the data committed to by the PoW
+ BLOCK_MISSING_PREV, //!< We don't have the previous block the checked one is built on
+ BLOCK_INVALID_PREV, //!< A block this one builds on is invalid
+ BLOCK_TIME_FUTURE, //!< block timestamp was > 2 hours in the future (or our clock is bad)
+ BLOCK_CHECKPOINT, //!< the block failed to meet one of our checkpoints
+ // Only loose txn:
+ TX_NOT_STANDARD, //!< didn't meet our local policy rules
+ TX_MISSING_INPUTS, //!< a transaction was missing some of its inputs
+ TX_PREMATURE_SPEND, //!< transaction spends a coinbase too early, or violates locktime/sequence locks
+ /**
+ * Transaction might be missing a witness, have a witness prior to SegWit
+ * activation, or witness may have been malleated (which includes
+ * non-standard witnesses).
+ */
+ TX_WITNESS_MUTATED,
+ /**
+ * Tx already in mempool or conflicts with a tx in the chain
+ * (if it conflicts with another tx in mempool, we use MEMPOOL_POLICY as it failed to reach the RBF threshold)
+ * TODO: Currently this is only used if the transaction already exists in the mempool or on chain,
+ * TODO: ATMP's fMissingInputs and a valid CValidationState being used to indicate missing inputs
+ */
+ TX_CONFLICT,
+ TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/RBF/etc limits
+};
+
+inline bool IsTransactionReason(ValidationInvalidReason r)
+{
+ return r == ValidationInvalidReason::NONE ||
+ r == ValidationInvalidReason::CONSENSUS ||
+ r == ValidationInvalidReason::RECENT_CONSENSUS_CHANGE ||
+ r == ValidationInvalidReason::TX_NOT_STANDARD ||
+ r == ValidationInvalidReason::TX_PREMATURE_SPEND ||
+ r == ValidationInvalidReason::TX_MISSING_INPUTS ||
+ r == ValidationInvalidReason::TX_WITNESS_MUTATED ||
+ r == ValidationInvalidReason::TX_CONFLICT ||
+ r == ValidationInvalidReason::TX_MEMPOOL_POLICY;
+}
+
+inline bool IsBlockReason(ValidationInvalidReason r)
+{
+ return r == ValidationInvalidReason::NONE ||
+ r == ValidationInvalidReason::CONSENSUS ||
+ r == ValidationInvalidReason::RECENT_CONSENSUS_CHANGE ||
+ r == ValidationInvalidReason::CACHED_INVALID ||
+ r == ValidationInvalidReason::BLOCK_INVALID_HEADER ||
+ r == ValidationInvalidReason::BLOCK_MUTATED ||
+ r == ValidationInvalidReason::BLOCK_MISSING_PREV ||
+ r == ValidationInvalidReason::BLOCK_INVALID_PREV ||
+ r == ValidationInvalidReason::BLOCK_TIME_FUTURE ||
+ r == ValidationInvalidReason::BLOCK_CHECKPOINT;
+}
+
/** Capture information about block/transaction validation */
class CValidationState {
private:
@@ -30,32 +102,24 @@ private:
MODE_INVALID, //!< network rule violation (DoS value may be set)
MODE_ERROR, //!< run-time error
} mode;
- int nDoS;
+ ValidationInvalidReason m_reason;
std::string strRejectReason;
unsigned int chRejectCode;
- bool corruptionPossible;
std::string strDebugMessage;
public:
- CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {}
- bool DoS(int level, bool ret = false,
- unsigned int chRejectCodeIn=0, const std::string &strRejectReasonIn="",
- bool corruptionIn=false,
- const std::string &strDebugMessageIn="") {
+ CValidationState() : mode(MODE_VALID), m_reason(ValidationInvalidReason::NONE), chRejectCode(0) {}
+ bool Invalid(ValidationInvalidReason reasonIn, bool ret = false,
+ unsigned int chRejectCodeIn=0, const std::string &strRejectReasonIn="",
+ const std::string &strDebugMessageIn="") {
+ m_reason = reasonIn;
chRejectCode = chRejectCodeIn;
strRejectReason = strRejectReasonIn;
- corruptionPossible = corruptionIn;
strDebugMessage = strDebugMessageIn;
if (mode == MODE_ERROR)
return ret;
- nDoS += level;
mode = MODE_INVALID;
return ret;
}
- bool Invalid(bool ret = false,
- unsigned int _chRejectCode=0, const std::string &_strRejectReason="",
- const std::string &_strDebugMessage="") {
- return DoS(0, ret, _chRejectCode, _strRejectReason, false, _strDebugMessage);
- }
bool Error(const std::string& strRejectReasonIn) {
if (mode == MODE_VALID)
strRejectReason = strRejectReasonIn;
@@ -71,19 +135,7 @@ public:
bool IsError() const {
return mode == MODE_ERROR;
}
- bool IsInvalid(int &nDoSOut) const {
- if (IsInvalid()) {
- nDoSOut = nDoS;
- return true;
- }
- return false;
- }
- bool CorruptionPossible() const {
- return corruptionPossible;
- }
- void SetCorruptionPossible() {
- corruptionPossible = true;
- }
+ ValidationInvalidReason GetReason() const { return m_reason; }
unsigned int GetRejectCode() const { return chRejectCode; }
std::string GetRejectReason() const { return strRejectReason; }
std::string GetDebugMessage() const { return strDebugMessage; }
diff --git a/src/core_io.h b/src/core_io.h
index 6f87161f46..19fb7b29f6 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -16,7 +16,6 @@ class CBlockHeader;
class CScript;
class CTransaction;
struct CMutableTransaction;
-struct PartiallySignedTransaction;
class uint256;
class UniValue;
@@ -37,7 +36,6 @@ bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header);
*/
bool ParseHashStr(const std::string& strHex, uint256& result);
std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
-NODISCARD bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error);
int ParseSighashString(const UniValue& sighash);
// core_write.cpp
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 3b82b2853c..a3c9cf0159 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -11,7 +11,6 @@
#include <serialize.h>
#include <streams.h>
#include <univalue.h>
-#include <util/system.h>
#include <util/strencodings.h>
#include <version.h>
@@ -176,23 +175,6 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
return true;
}
-bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
-{
- std::vector<unsigned char> tx_data = DecodeBase64(base64_tx.c_str());
- CDataStream ss_data(tx_data, SER_NETWORK, PROTOCOL_VERSION);
- try {
- ss_data >> psbt;
- if (!ss_data.empty()) {
- error = "extra data after PSBT";
- return false;
- }
- } catch (const std::exception& e) {
- error = e.what();
- return false;
- }
- return true;
-}
-
bool ParseHashStr(const std::string& strHex, uint256& result)
{
if ((strHex.size() != 64) || !IsHex(strHex))
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 765a170307..4d64446d7b 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -13,7 +13,6 @@
#include <streams.h>
#include <univalue.h>
#include <util/system.h>
-#include <util/moneystr.h>
#include <util/strencodings.h>
UniValue ValueFromAmount(const CAmount& amount)
diff --git a/src/crypto/aes.cpp b/src/crypto/aes.cpp
index 919ea593b7..b3fb927760 100644
--- a/src/crypto/aes.cpp
+++ b/src/crypto/aes.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <crypto/aes.h>
-#include <crypto/common.h>
#include <assert.h>
#include <string.h>
@@ -12,36 +11,6 @@ extern "C" {
#include <crypto/ctaes/ctaes.c>
}
-AES128Encrypt::AES128Encrypt(const unsigned char key[16])
-{
- AES128_init(&ctx, key);
-}
-
-AES128Encrypt::~AES128Encrypt()
-{
- memset(&ctx, 0, sizeof(ctx));
-}
-
-void AES128Encrypt::Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const
-{
- AES128_encrypt(&ctx, 1, ciphertext, plaintext);
-}
-
-AES128Decrypt::AES128Decrypt(const unsigned char key[16])
-{
- AES128_init(&ctx, key);
-}
-
-AES128Decrypt::~AES128Decrypt()
-{
- memset(&ctx, 0, sizeof(ctx));
-}
-
-void AES128Decrypt::Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const
-{
- AES128_decrypt(&ctx, 1, plaintext, ciphertext);
-}
-
AES256Encrypt::AES256Encrypt(const unsigned char key[32])
{
AES256_init(&ctx, key);
@@ -182,35 +151,3 @@ AES256CBCDecrypt::~AES256CBCDecrypt()
{
memset(iv, 0, sizeof(iv));
}
-
-AES128CBCEncrypt::AES128CBCEncrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn)
- : enc(key), pad(padIn)
-{
- memcpy(iv, ivIn, AES_BLOCKSIZE);
-}
-
-AES128CBCEncrypt::~AES128CBCEncrypt()
-{
- memset(iv, 0, AES_BLOCKSIZE);
-}
-
-int AES128CBCEncrypt::Encrypt(const unsigned char* data, int size, unsigned char* out) const
-{
- return CBCEncrypt(enc, iv, data, size, pad, out);
-}
-
-AES128CBCDecrypt::AES128CBCDecrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn)
- : dec(key), pad(padIn)
-{
- memcpy(iv, ivIn, AES_BLOCKSIZE);
-}
-
-AES128CBCDecrypt::~AES128CBCDecrypt()
-{
- memset(iv, 0, AES_BLOCKSIZE);
-}
-
-int AES128CBCDecrypt::Decrypt(const unsigned char* data, int size, unsigned char* out) const
-{
- return CBCDecrypt(dec, iv, data, size, pad, out);
-}
diff --git a/src/crypto/aes.h b/src/crypto/aes.h
index fdad70c593..e06c8de272 100644
--- a/src/crypto/aes.h
+++ b/src/crypto/aes.h
@@ -12,33 +12,8 @@ extern "C" {
}
static const int AES_BLOCKSIZE = 16;
-static const int AES128_KEYSIZE = 16;
static const int AES256_KEYSIZE = 32;
-/** An encryption class for AES-128. */
-class AES128Encrypt
-{
-private:
- AES128_ctx ctx;
-
-public:
- explicit AES128Encrypt(const unsigned char key[16]);
- ~AES128Encrypt();
- void Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const;
-};
-
-/** A decryption class for AES-128. */
-class AES128Decrypt
-{
-private:
- AES128_ctx ctx;
-
-public:
- explicit AES128Decrypt(const unsigned char key[16]);
- ~AES128Decrypt();
- void Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const;
-};
-
/** An encryption class for AES-256. */
class AES256Encrypt
{
@@ -89,30 +64,4 @@ private:
unsigned char iv[AES_BLOCKSIZE];
};
-class AES128CBCEncrypt
-{
-public:
- AES128CBCEncrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn);
- ~AES128CBCEncrypt();
- int Encrypt(const unsigned char* data, int size, unsigned char* out) const;
-
-private:
- const AES128Encrypt enc;
- const bool pad;
- unsigned char iv[AES_BLOCKSIZE];
-};
-
-class AES128CBCDecrypt
-{
-public:
- AES128CBCDecrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn);
- ~AES128CBCDecrypt();
- int Decrypt(const unsigned char* data, int size, unsigned char* out) const;
-
-private:
- const AES128Decrypt dec;
- const bool pad;
- unsigned char iv[AES_BLOCKSIZE];
-};
-
#endif // BITCOIN_CRYPTO_AES_H
diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp
index ac4470f04f..42a17f02ff 100644
--- a/src/crypto/chacha20.cpp
+++ b/src/crypto/chacha20.cpp
@@ -71,7 +71,7 @@ void ChaCha20::Seek(uint64_t pos)
input[13] = pos >> 32;
}
-void ChaCha20::Output(unsigned char* c, size_t bytes)
+void ChaCha20::Keystream(unsigned char* c, size_t bytes)
{
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
@@ -178,3 +178,133 @@ void ChaCha20::Output(unsigned char* c, size_t bytes)
c += 64;
}
}
+
+void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes)
+{
+ uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
+ uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
+ unsigned char *ctarget = nullptr;
+ unsigned char tmp[64];
+ unsigned int i;
+
+ if (!bytes) return;
+
+ j0 = input[0];
+ j1 = input[1];
+ j2 = input[2];
+ j3 = input[3];
+ j4 = input[4];
+ j5 = input[5];
+ j6 = input[6];
+ j7 = input[7];
+ j8 = input[8];
+ j9 = input[9];
+ j10 = input[10];
+ j11 = input[11];
+ j12 = input[12];
+ j13 = input[13];
+ j14 = input[14];
+ j15 = input[15];
+
+ for (;;) {
+ if (bytes < 64) {
+ // if m has fewer than 64 bytes available, copy m to tmp and
+ // read from tmp instead
+ for (i = 0;i < bytes;++i) tmp[i] = m[i];
+ m = tmp;
+ ctarget = c;
+ c = tmp;
+ }
+ x0 = j0;
+ x1 = j1;
+ x2 = j2;
+ x3 = j3;
+ x4 = j4;
+ x5 = j5;
+ x6 = j6;
+ x7 = j7;
+ x8 = j8;
+ x9 = j9;
+ x10 = j10;
+ x11 = j11;
+ x12 = j12;
+ x13 = j13;
+ x14 = j14;
+ x15 = j15;
+ for (i = 20;i > 0;i -= 2) {
+ QUARTERROUND( x0, x4, x8,x12)
+ QUARTERROUND( x1, x5, x9,x13)
+ QUARTERROUND( x2, x6,x10,x14)
+ QUARTERROUND( x3, x7,x11,x15)
+ QUARTERROUND( x0, x5,x10,x15)
+ QUARTERROUND( x1, x6,x11,x12)
+ QUARTERROUND( x2, x7, x8,x13)
+ QUARTERROUND( x3, x4, x9,x14)
+ }
+ x0 += j0;
+ x1 += j1;
+ x2 += j2;
+ x3 += j3;
+ x4 += j4;
+ x5 += j5;
+ x6 += j6;
+ x7 += j7;
+ x8 += j8;
+ x9 += j9;
+ x10 += j10;
+ x11 += j11;
+ x12 += j12;
+ x13 += j13;
+ x14 += j14;
+ x15 += j15;
+
+ x0 ^= ReadLE32(m + 0);
+ x1 ^= ReadLE32(m + 4);
+ x2 ^= ReadLE32(m + 8);
+ x3 ^= ReadLE32(m + 12);
+ x4 ^= ReadLE32(m + 16);
+ x5 ^= ReadLE32(m + 20);
+ x6 ^= ReadLE32(m + 24);
+ x7 ^= ReadLE32(m + 28);
+ x8 ^= ReadLE32(m + 32);
+ x9 ^= ReadLE32(m + 36);
+ x10 ^= ReadLE32(m + 40);
+ x11 ^= ReadLE32(m + 44);
+ x12 ^= ReadLE32(m + 48);
+ x13 ^= ReadLE32(m + 52);
+ x14 ^= ReadLE32(m + 56);
+ x15 ^= ReadLE32(m + 60);
+
+ ++j12;
+ if (!j12) ++j13;
+
+ WriteLE32(c + 0, x0);
+ WriteLE32(c + 4, x1);
+ WriteLE32(c + 8, x2);
+ WriteLE32(c + 12, x3);
+ WriteLE32(c + 16, x4);
+ WriteLE32(c + 20, x5);
+ WriteLE32(c + 24, x6);
+ WriteLE32(c + 28, x7);
+ WriteLE32(c + 32, x8);
+ WriteLE32(c + 36, x9);
+ WriteLE32(c + 40, x10);
+ WriteLE32(c + 44, x11);
+ WriteLE32(c + 48, x12);
+ WriteLE32(c + 52, x13);
+ WriteLE32(c + 56, x14);
+ WriteLE32(c + 60, x15);
+
+ if (bytes <= 64) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) ctarget[i] = c[i];
+ }
+ input[12] = j12;
+ input[13] = j13;
+ return;
+ }
+ bytes -= 64;
+ c += 64;
+ m += 64;
+ }
+}
diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h
index a305977bcd..5a4674f4a8 100644
--- a/src/crypto/chacha20.h
+++ b/src/crypto/chacha20.h
@@ -8,7 +8,8 @@
#include <stdint.h>
#include <stdlib.h>
-/** A PRNG class for ChaCha20. */
+/** A class for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein
+ https://cr.yp.to/chacha/chacha-20080128.pdf */
class ChaCha20
{
private:
@@ -17,10 +18,17 @@ private:
public:
ChaCha20();
ChaCha20(const unsigned char* key, size_t keylen);
- void SetKey(const unsigned char* key, size_t keylen);
- void SetIV(uint64_t iv);
- void Seek(uint64_t pos);
- void Output(unsigned char* output, size_t bytes);
+ void SetKey(const unsigned char* key, size_t keylen); //!< set key with flexible keylength; 256bit recommended */
+ void SetIV(uint64_t iv); // set the 64bit nonce
+ void Seek(uint64_t pos); // set the 64bit block counter
+
+ /** outputs the keystream of size <bytes> into <c> */
+ void Keystream(unsigned char* c, size_t bytes);
+
+ /** enciphers the message <input> of length <bytes> and write the enciphered representation into <output>
+ * Used for encryption and decryption (XOR)
+ */
+ void Crypt(const unsigned char* input, unsigned char* output, size_t bytes);
};
#endif // BITCOIN_CRYPTO_CHACHA20_H
diff --git a/src/crypto/hkdf_sha256_32.cpp b/src/crypto/hkdf_sha256_32.cpp
new file mode 100644
index 0000000000..9cea5995ec
--- /dev/null
+++ b/src/crypto/hkdf_sha256_32.cpp
@@ -0,0 +1,21 @@
+// Copyright (c) 2018 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 <crypto/hkdf_sha256_32.h>
+
+#include <assert.h>
+#include <string.h>
+
+CHKDF_HMAC_SHA256_L32::CHKDF_HMAC_SHA256_L32(const unsigned char* ikm, size_t ikmlen, const std::string& salt)
+{
+ CHMAC_SHA256((const unsigned char*)salt.c_str(), salt.size()).Write(ikm, ikmlen).Finalize(m_prk);
+}
+
+void CHKDF_HMAC_SHA256_L32::Expand32(const std::string& info, unsigned char hash[OUTPUT_SIZE])
+{
+ // expand a 32byte key (single round)
+ assert(info.size() <= 128);
+ static const unsigned char one[1] = {1};
+ CHMAC_SHA256(m_prk, 32).Write((const unsigned char*)info.data(), info.size()).Write(one, 1).Finalize(hash);
+}
diff --git a/src/crypto/hkdf_sha256_32.h b/src/crypto/hkdf_sha256_32.h
new file mode 100644
index 0000000000..fa1e42aec1
--- /dev/null
+++ b/src/crypto/hkdf_sha256_32.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2018 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_CRYPTO_HKDF_SHA256_32_H
+#define BITCOIN_CRYPTO_HKDF_SHA256_32_H
+
+#include <crypto/hmac_sha256.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/** A rfc5869 HKDF implementation with HMAC_SHA256 and fixed key output length of 32 bytes (L=32) */
+class CHKDF_HMAC_SHA256_L32
+{
+private:
+ unsigned char m_prk[32];
+ static const size_t OUTPUT_SIZE = 32;
+
+public:
+ CHKDF_HMAC_SHA256_L32(const unsigned char* ikm, size_t ikmlen, const std::string& salt);
+ void Expand32(const std::string& info, unsigned char hash[OUTPUT_SIZE]);
+};
+
+#endif // BITCOIN_CRYPTO_HKDF_SHA256_32_H
diff --git a/src/crypto/poly1305.cpp b/src/crypto/poly1305.cpp
new file mode 100644
index 0000000000..8a86c9601c
--- /dev/null
+++ b/src/crypto/poly1305.cpp
@@ -0,0 +1,141 @@
+// Copyright (c) 2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+// Based on the public domain implementation by Andrew Moon
+// poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna
+
+#include <crypto/common.h>
+#include <crypto/poly1305.h>
+
+#include <string.h>
+
+#define mul32x32_64(a,b) ((uint64_t)(a) * (b))
+
+void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]) {
+ uint32_t t0,t1,t2,t3;
+ uint32_t h0,h1,h2,h3,h4;
+ uint32_t r0,r1,r2,r3,r4;
+ uint32_t s1,s2,s3,s4;
+ uint32_t b, nb;
+ size_t j;
+ uint64_t t[5];
+ uint64_t f0,f1,f2,f3;
+ uint64_t g0,g1,g2,g3,g4;
+ uint64_t c;
+ unsigned char mp[16];
+
+ /* clamp key */
+ t0 = ReadLE32(key+0);
+ t1 = ReadLE32(key+4);
+ t2 = ReadLE32(key+8);
+ t3 = ReadLE32(key+12);
+
+ /* precompute multipliers */
+ r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6;
+ r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12;
+ r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18;
+ r3 = t2 & 0x3f03fff; t3 >>= 8;
+ r4 = t3 & 0x00fffff;
+
+ s1 = r1 * 5;
+ s2 = r2 * 5;
+ s3 = r3 * 5;
+ s4 = r4 * 5;
+
+ /* init state */
+ h0 = 0;
+ h1 = 0;
+ h2 = 0;
+ h3 = 0;
+ h4 = 0;
+
+ /* full blocks */
+ if (inlen < 16) goto poly1305_donna_atmost15bytes;
+poly1305_donna_16bytes:
+ m += 16;
+ inlen -= 16;
+
+ t0 = ReadLE32(m-16);
+ t1 = ReadLE32(m-12);
+ t2 = ReadLE32(m-8);
+ t3 = ReadLE32(m-4);
+
+ h0 += t0 & 0x3ffffff;
+ h1 += ((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff;
+ h2 += ((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff;
+ h3 += ((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff;
+ h4 += (t3 >> 8) | (1 << 24);
+
+
+poly1305_donna_mul:
+ t[0] = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
+ t[1] = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
+ t[2] = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
+ t[3] = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
+ t[4] = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
+
+ h0 = (uint32_t)t[0] & 0x3ffffff; c = (t[0] >> 26);
+ t[1] += c; h1 = (uint32_t)t[1] & 0x3ffffff; b = (uint32_t)(t[1] >> 26);
+ t[2] += b; h2 = (uint32_t)t[2] & 0x3ffffff; b = (uint32_t)(t[2] >> 26);
+ t[3] += b; h3 = (uint32_t)t[3] & 0x3ffffff; b = (uint32_t)(t[3] >> 26);
+ t[4] += b; h4 = (uint32_t)t[4] & 0x3ffffff; b = (uint32_t)(t[4] >> 26);
+ h0 += b * 5;
+
+ if (inlen >= 16) goto poly1305_donna_16bytes;
+
+ /* final bytes */
+poly1305_donna_atmost15bytes:
+ if (!inlen) goto poly1305_donna_finish;
+
+ for (j = 0; j < inlen; j++) mp[j] = m[j];
+ mp[j++] = 1;
+ for (; j < 16; j++) mp[j] = 0;
+ inlen = 0;
+
+ t0 = ReadLE32(mp+0);
+ t1 = ReadLE32(mp+4);
+ t2 = ReadLE32(mp+8);
+ t3 = ReadLE32(mp+12);
+
+ h0 += t0 & 0x3ffffff;
+ h1 += ((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff;
+ h2 += ((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff;
+ h3 += ((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff;
+ h4 += (t3 >> 8);
+
+ goto poly1305_donna_mul;
+
+poly1305_donna_finish:
+ b = h0 >> 26; h0 = h0 & 0x3ffffff;
+ h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff;
+ h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff;
+ h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff;
+ h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff;
+ h0 += b * 5; b = h0 >> 26; h0 = h0 & 0x3ffffff;
+ h1 += b;
+
+ g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff;
+ g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff;
+ g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff;
+ g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff;
+ g4 = h4 + b - (1 << 26);
+
+ b = (g4 >> 31) - 1;
+ nb = ~b;
+ h0 = (h0 & nb) | (g0 & b);
+ h1 = (h1 & nb) | (g1 & b);
+ h2 = (h2 & nb) | (g2 & b);
+ h3 = (h3 & nb) | (g3 & b);
+ h4 = (h4 & nb) | (g4 & b);
+
+ f0 = ((h0 ) | (h1 << 26)) + (uint64_t)ReadLE32(&key[16]);
+ f1 = ((h1 >> 6) | (h2 << 20)) + (uint64_t)ReadLE32(&key[20]);
+ f2 = ((h2 >> 12) | (h3 << 14)) + (uint64_t)ReadLE32(&key[24]);
+ f3 = ((h3 >> 18) | (h4 << 8)) + (uint64_t)ReadLE32(&key[28]);
+
+ WriteLE32(&out[ 0], f0); f1 += (f0 >> 32);
+ WriteLE32(&out[ 4], f1); f2 += (f1 >> 32);
+ WriteLE32(&out[ 8], f2); f3 += (f2 >> 32);
+ WriteLE32(&out[12], f3);
+}
diff --git a/src/crypto/poly1305.h b/src/crypto/poly1305.h
new file mode 100644
index 0000000000..1598b013b9
--- /dev/null
+++ b/src/crypto/poly1305.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2019 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_CRYPTO_POLY1305_H
+#define BITCOIN_CRYPTO_POLY1305_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define POLY1305_KEYLEN 32
+#define POLY1305_TAGLEN 16
+
+void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen,
+ const unsigned char key[POLY1305_KEYLEN]);
+
+#endif // BITCOIN_CRYPTO_POLY1305_H
diff --git a/src/crypto/ripemd160.cpp b/src/crypto/ripemd160.cpp
index a00331dcb7..edee06cc34 100644
--- a/src/crypto/ripemd160.cpp
+++ b/src/crypto/ripemd160.cpp
@@ -256,7 +256,7 @@ CRIPEMD160& CRIPEMD160::Write(const unsigned char* data, size_t len)
ripemd160::Transform(s, buf);
bufsize = 0;
}
- while (end >= data + 64) {
+ while (end - data >= 64) {
// Process full chunks directly from the source.
ripemd160::Transform(s, data);
bytes += 64;
diff --git a/src/crypto/sha1.cpp b/src/crypto/sha1.cpp
index 5c601c54a6..3dcdcb186e 100644
--- a/src/crypto/sha1.cpp
+++ b/src/crypto/sha1.cpp
@@ -163,7 +163,7 @@ CSHA1& CSHA1::Write(const unsigned char* data, size_t len)
sha1::Transform(s, buf);
bufsize = 0;
}
- while (end >= data + 64) {
+ while (end - data >= 64) {
// Process full chunks directly from the source.
sha1::Transform(s, data);
bytes += 64;
diff --git a/src/crypto/sha256_avx2.cpp b/src/crypto/sha256_avx2.cpp
index 068e0e5ff6..90a72516a4 100644
--- a/src/crypto/sha256_avx2.cpp
+++ b/src/crypto/sha256_avx2.cpp
@@ -3,7 +3,6 @@
#include <stdint.h>
#include <immintrin.h>
-#include <crypto/sha256.h>
#include <crypto/common.h>
namespace sha256d64_avx2 {
diff --git a/src/crypto/sha256_sse41.cpp b/src/crypto/sha256_sse41.cpp
index adca870e2d..fc79f46f7f 100644
--- a/src/crypto/sha256_sse41.cpp
+++ b/src/crypto/sha256_sse41.cpp
@@ -3,7 +3,6 @@
#include <stdint.h>
#include <immintrin.h>
-#include <crypto/sha256.h>
#include <crypto/common.h>
namespace sha256d64_sse41 {
diff --git a/src/crypto/sha512.cpp b/src/crypto/sha512.cpp
index bc64135cae..4e6aa363f7 100644
--- a/src/crypto/sha512.cpp
+++ b/src/crypto/sha512.cpp
@@ -168,7 +168,7 @@ CSHA512& CSHA512::Write(const unsigned char* data, size_t len)
sha512::Transform(s, buf);
bufsize = 0;
}
- while (end >= data + 128) {
+ while (end - data >= 128) {
// Process full chunks directly from the source.
sha512::Transform(s, data);
data += 128;
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 58d8cc2c9d..34896f7ab2 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -115,7 +115,7 @@ static leveldb::Options GetOptions(size_t nCacheSize)
}
CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
- : m_name(fs::basename(path))
+ : m_name{path.stem().string()}
{
penv = nullptr;
readoptions.verify_checksums = true;
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 9211a7596b..eeec6dec25 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -8,6 +8,10 @@
class CWallet;
+namespace interfaces {
+class Chain;
+}
+
class DummyWalletInit : public WalletInitInterface {
public:
@@ -19,11 +23,33 @@ public:
void DummyWalletInit::AddWalletOptions() const
{
- std::vector<std::string> opts = {"-addresstype", "-changetype", "-disablewallet", "-discardfee=<amt>", "-fallbackfee=<amt>",
- "-keypool=<n>", "-mintxfee=<amt>", "-paytxfee=<amt>", "-rescan", "-salvagewallet", "-spendzeroconfchange", "-txconfirmtarget=<n>",
- "-upgradewallet", "-wallet=<path>", "-walletbroadcast", "-walletdir=<dir>", "-walletnotify=<cmd>", "-walletrbf", "-zapwallettxes=<mode>",
- "-dblogsize=<n>", "-flushwallet", "-privdb", "-walletrejectlongchains"};
- gArgs.AddHiddenArgs(opts);
+ gArgs.AddHiddenArgs({
+ "-addresstype",
+ "-avoidpartialspends",
+ "-changetype",
+ "-disablewallet",
+ "-discardfee=<amt>",
+ "-fallbackfee=<amt>",
+ "-keypool=<n>",
+ "-maxtxfee=<amt>",
+ "-mintxfee=<amt>",
+ "-paytxfee=<amt>",
+ "-rescan",
+ "-salvagewallet",
+ "-spendzeroconfchange",
+ "-txconfirmtarget=<n>",
+ "-upgradewallet",
+ "-wallet=<path>",
+ "-walletbroadcast",
+ "-walletdir=<dir>",
+ "-walletnotify=<cmd>",
+ "-walletrbf",
+ "-zapwallettxes=<mode>",
+ "-dblogsize=<n>",
+ "-flushwallet",
+ "-privdb",
+ "-walletrejectlongchains",
+ });
}
const WalletInitInterface& g_wallet_init_interface = DummyWalletInit();
@@ -43,6 +69,11 @@ std::vector<std::shared_ptr<CWallet>> GetWallets()
throw std::logic_error("Wallet function called in non-wallet build.");
}
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning)
+{
+ throw std::logic_error("Wallet function called in non-wallet build.");
+}
+
namespace interfaces {
class Wallet;
diff --git a/src/flatfile.cpp b/src/flatfile.cpp
new file mode 100644
index 0000000000..8a8f7b681c
--- /dev/null
+++ b/src/flatfile.cpp
@@ -0,0 +1,98 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 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 <stdexcept>
+
+#include <flatfile.h>
+#include <logging.h>
+#include <tinyformat.h>
+#include <util/system.h>
+
+FlatFileSeq::FlatFileSeq(fs::path dir, const char* prefix, size_t chunk_size) :
+ m_dir(std::move(dir)),
+ m_prefix(prefix),
+ m_chunk_size(chunk_size)
+{
+ if (chunk_size == 0) {
+ throw std::invalid_argument("chunk_size must be positive");
+ }
+}
+
+std::string FlatFilePos::ToString() const
+{
+ return strprintf("FlatFilePos(nFile=%i, nPos=%i)", nFile, nPos);
+}
+
+fs::path FlatFileSeq::FileName(const FlatFilePos& pos) const
+{
+ return m_dir / strprintf("%s%05u.dat", m_prefix, pos.nFile);
+}
+
+FILE* FlatFileSeq::Open(const FlatFilePos& pos, bool read_only)
+{
+ if (pos.IsNull()) {
+ return nullptr;
+ }
+ fs::path path = FileName(pos);
+ fs::create_directories(path.parent_path());
+ FILE* file = fsbridge::fopen(path, read_only ? "rb": "rb+");
+ if (!file && !read_only)
+ file = fsbridge::fopen(path, "wb+");
+ if (!file) {
+ LogPrintf("Unable to open file %s\n", path.string());
+ return nullptr;
+ }
+ if (pos.nPos && fseek(file, pos.nPos, SEEK_SET)) {
+ LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string());
+ fclose(file);
+ return nullptr;
+ }
+ return file;
+}
+
+size_t FlatFileSeq::Allocate(const FlatFilePos& pos, size_t add_size, bool& out_of_space)
+{
+ out_of_space = false;
+
+ unsigned int n_old_chunks = (pos.nPos + m_chunk_size - 1) / m_chunk_size;
+ unsigned int n_new_chunks = (pos.nPos + add_size + m_chunk_size - 1) / m_chunk_size;
+ if (n_new_chunks > n_old_chunks) {
+ size_t old_size = pos.nPos;
+ size_t new_size = n_new_chunks * m_chunk_size;
+ size_t inc_size = new_size - old_size;
+
+ if (CheckDiskSpace(m_dir, inc_size)) {
+ FILE *file = Open(pos);
+ if (file) {
+ LogPrintf("Pre-allocating up to position 0x%x in %s%05u.dat\n", new_size, m_prefix, pos.nFile);
+ AllocateFileRange(file, pos.nPos, inc_size);
+ fclose(file);
+ return inc_size;
+ }
+ } else {
+ out_of_space = true;
+ }
+ }
+ return 0;
+}
+
+bool FlatFileSeq::Flush(const FlatFilePos& pos, bool finalize)
+{
+ FILE* file = Open(FlatFilePos(pos.nFile, 0)); // Avoid fseek to nPos
+ if (!file) {
+ return error("%s: failed to open file %d", __func__, pos.nFile);
+ }
+ if (finalize && !TruncateFile(file, pos.nPos)) {
+ fclose(file);
+ return error("%s: failed to truncate file %d", __func__, pos.nFile);
+ }
+ if (!FileCommit(file)) {
+ fclose(file);
+ return error("%s: failed to commit file %d", __func__, pos.nFile);
+ }
+
+ fclose(file);
+ return true;
+}
diff --git a/src/flatfile.h b/src/flatfile.h
new file mode 100644
index 0000000000..374ceff411
--- /dev/null
+++ b/src/flatfile.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 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_FLATFILE_H
+#define BITCOIN_FLATFILE_H
+
+#include <string>
+
+#include <fs.h>
+#include <serialize.h>
+
+struct FlatFilePos
+{
+ int nFile;
+ unsigned int nPos;
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ READWRITE(VARINT(nFile, VarIntMode::NONNEGATIVE_SIGNED));
+ READWRITE(VARINT(nPos));
+ }
+
+ FlatFilePos() : nFile(-1), nPos(0) {}
+
+ FlatFilePos(int nFileIn, unsigned int nPosIn) :
+ nFile(nFileIn),
+ nPos(nPosIn)
+ {}
+
+ friend bool operator==(const FlatFilePos &a, const FlatFilePos &b) {
+ return (a.nFile == b.nFile && a.nPos == b.nPos);
+ }
+
+ friend bool operator!=(const FlatFilePos &a, const FlatFilePos &b) {
+ return !(a == b);
+ }
+
+ void SetNull() { nFile = -1; nPos = 0; }
+ bool IsNull() const { return (nFile == -1); }
+
+ std::string ToString() const;
+};
+
+/**
+ * FlatFileSeq represents a sequence of numbered files storing raw data. This class facilitates
+ * access to and efficient management of these files.
+ */
+class FlatFileSeq
+{
+private:
+ const fs::path m_dir;
+ const char* const m_prefix;
+ const size_t m_chunk_size;
+
+public:
+ /**
+ * Constructor
+ *
+ * @param dir The base directory that all files live in.
+ * @param prefix A short prefix given to all file names.
+ * @param chunk_size Disk space is pre-allocated in multiples of this amount.
+ */
+ FlatFileSeq(fs::path dir, const char* prefix, size_t chunk_size);
+
+ /** Get the name of the file at the given position. */
+ fs::path FileName(const FlatFilePos& pos) const;
+
+ /** Open a handle to the file at the given position. */
+ FILE* Open(const FlatFilePos& pos, bool read_only = false);
+
+ /**
+ * Allocate additional space in a file after the given starting position. The amount allocated
+ * will be the minimum multiple of the sequence chunk size greater than add_size.
+ *
+ * @param[in] pos The starting position that bytes will be allocated after.
+ * @param[in] add_size The minimum number of bytes to be allocated.
+ * @param[out] out_of_space Whether the allocation failed due to insufficient disk space.
+ * @return The number of bytes successfully allocated.
+ */
+ size_t Allocate(const FlatFilePos& pos, size_t add_size, bool& out_of_space);
+
+ /**
+ * Commit a file to disk, and optionally truncate off extra pre-allocated bytes if final.
+ *
+ * @param[in] pos The first unwritten position in the file to be flushed.
+ * @param[in] finalize True if no more data will be written to this file.
+ * @return true on success, false on failure.
+ */
+ bool Flush(const FlatFilePos& pos, bool finalize = false);
+};
+
+#endif // BITCOIN_FLATFILE_H
diff --git a/src/fs.cpp b/src/fs.cpp
index 3c8f4c0247..7b422b8d70 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -3,7 +3,9 @@
#ifndef WIN32
#include <fcntl.h>
#else
+#ifndef NOMINMAX
#define NOMINMAX
+#endif
#include <codecvt>
#include <windows.h>
#endif
@@ -160,6 +162,7 @@ static std::string openmodeToStr(std::ios_base::openmode mode)
void ifstream::open(const fs::path& p, std::ios_base::openmode mode)
{
close();
+ mode |= std::ios_base::in;
m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
if (m_file == nullptr) {
return;
@@ -183,6 +186,7 @@ void ifstream::close()
void ofstream::open(const fs::path& p, std::ios_base::openmode mode)
{
close();
+ mode |= std::ios_base::out;
m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
if (m_file == nullptr) {
return;
diff --git a/src/fs.h b/src/fs.h
index 8af81f173b..c713297d6e 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -11,6 +11,7 @@
#include <ext/stdio_filebuf.h>
#endif
+#define BOOST_FILESYSTEM_NO_DEPRECATED
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index fcf760a4c6..c7a119440b 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -9,7 +9,6 @@
#include <key_io.h>
#include <rpc/protocol.h>
#include <rpc/server.h>
-#include <random.h>
#include <sync.h>
#include <util/system.h>
#include <util/strencodings.h>
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index ca60ea43a7..d17667223b 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -6,6 +6,7 @@
#include <chainparamsbase.h>
#include <compat.h>
+#include <util/threadnames.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <netbase.h>
@@ -17,7 +18,7 @@
#include <memory>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
+#include <string>
#include <sys/types.h>
#include <sys/stat.h>
@@ -138,15 +139,15 @@ struct HTTPPathHandler
//! libevent event loop
static struct event_base* eventBase = nullptr;
//! HTTP server
-struct evhttp* eventHTTP = nullptr;
+static struct evhttp* eventHTTP = nullptr;
//! List of subnets to allow RPC connections from
static std::vector<CSubNet> rpc_allow_subnets;
//! Work queue for handling longer requests off the event loop thread
static WorkQueue<HTTPClosure>* workQueue = nullptr;
//! Handlers for (sub)paths
-std::vector<HTTPPathHandler> pathHandlers;
+static std::vector<HTTPPathHandler> pathHandlers;
//! Bound listening sockets
-std::vector<evhttp_bound_socket *> boundSockets;
+static std::vector<evhttp_bound_socket *> boundSockets;
/** Check if a network address is allowed to access the HTTP server */
static bool ClientAllowed(const CNetAddr& netaddr)
@@ -284,7 +285,7 @@ static void http_reject_request_cb(struct evhttp_request* req, void*)
/** Event dispatcher thread */
static bool ThreadHTTP(struct event_base* base)
{
- RenameThread("bitcoin-http");
+ util::ThreadRename("http");
LogPrint(BCLog::HTTP, "Entering http event loop\n");
event_base_dispatch(base);
// Event loop will be interrupted by InterruptHTTPServer()
@@ -335,9 +336,9 @@ static bool HTTPBindAddresses(struct evhttp* http)
}
/** Simple wrapper to set thread name and run work queue */
-static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue)
+static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
{
- RenameThread("bitcoin-httpworker");
+ util::ThreadRename(strprintf("httpworker.%i", worker_num));
queue->Run();
}
@@ -364,8 +365,8 @@ bool InitHTTPServer()
// Update libevent's log handling. Returns false if our version of
// libevent doesn't support debug logging, in which case we should
// clear the BCLog::LIBEVENT flag.
- if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) {
- g_logger->DisableCategory(BCLog::LIBEVENT);
+ if (!UpdateHTTPServerLogging(LogInstance().WillLogCategory(BCLog::LIBEVENT))) {
+ LogInstance().DisableCategory(BCLog::LIBEVENT);
}
#ifdef WIN32
@@ -419,7 +420,7 @@ bool UpdateHTTPServerLogging(bool enable) {
#endif
}
-std::thread threadHTTP;
+static std::thread threadHTTP;
static std::vector<std::thread> g_thread_http_workers;
void StartHTTPServer()
@@ -430,7 +431,7 @@ void StartHTTPServer()
threadHTTP = std::thread(ThreadHTTP, eventBase);
for (int i = 0; i < rpcThreads; i++) {
- g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue);
+ g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue, i);
}
}
@@ -655,15 +656,3 @@ void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
pathHandlers.erase(i);
}
}
-
-std::string urlDecode(const std::string &urlEncoded) {
- std::string res;
- if (!urlEncoded.empty()) {
- char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr);
- if (decoded) {
- res = std::string(decoded);
- free(decoded);
- }
- }
- return res;
-}
diff --git a/src/httpserver.h b/src/httpserver.h
index 63f96734f8..7943f0094b 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -148,6 +148,4 @@ private:
struct event* ev;
};
-std::string urlDecode(const std::string &urlEncoded);
-
#endif // BITCOIN_HTTPSERVER_H
diff --git a/src/index/base.cpp b/src/index/base.cpp
index f6f59572ce..bcc8e2ce7c 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -41,9 +41,9 @@ bool BaseIndex::DB::ReadBestBlock(CBlockLocator& locator) const
return success;
}
-bool BaseIndex::DB::WriteBestBlock(const CBlockLocator& locator)
+void BaseIndex::DB::WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator)
{
- return Write(DB_BEST_BLOCK, locator);
+ batch.Write(DB_BEST_BLOCK, locator);
}
BaseIndex::~BaseIndex()
@@ -63,9 +63,9 @@ bool BaseIndex::Init()
if (locator.IsNull()) {
m_best_block_index = nullptr;
} else {
- m_best_block_index = FindForkInGlobalIndex(chainActive, locator);
+ m_best_block_index = FindForkInGlobalIndex(::ChainActive(), locator);
}
- m_synced = m_best_block_index.load() == chainActive.Tip();
+ m_synced = m_best_block_index.load() == ::ChainActive().Tip();
return true;
}
@@ -74,15 +74,15 @@ static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev) EXCLUSIV
AssertLockHeld(cs_main);
if (!pindex_prev) {
- return chainActive.Genesis();
+ return ::ChainActive().Genesis();
}
- const CBlockIndex* pindex = chainActive.Next(pindex_prev);
+ const CBlockIndex* pindex = ::ChainActive().Next(pindex_prev);
if (pindex) {
return pindex;
}
- return chainActive.Next(chainActive.FindFork(pindex_prev));
+ return ::ChainActive().Next(::ChainActive().FindFork(pindex_prev));
}
void BaseIndex::ThreadSync()
@@ -95,7 +95,11 @@ void BaseIndex::ThreadSync()
int64_t last_locator_write_time = 0;
while (true) {
if (m_interrupt) {
- WriteBestBlock(pindex);
+ m_best_block_index = pindex;
+ // No need to handle errors in Commit. If it fails, the error will be already be
+ // logged. The best way to recover is to continue, as index cannot be corrupted by
+ // a missed commit to disk for an advanced index state.
+ Commit();
return;
}
@@ -103,11 +107,17 @@ void BaseIndex::ThreadSync()
LOCK(cs_main);
const CBlockIndex* pindex_next = NextSyncBlock(pindex);
if (!pindex_next) {
- WriteBestBlock(pindex);
m_best_block_index = pindex;
m_synced = true;
+ // No need to handle errors in Commit. See rationale above.
+ Commit();
break;
}
+ if (pindex_next->pprev != pindex && !Rewind(pindex, pindex_next->pprev)) {
+ FatalError("%s: Failed to rewind index %s to a previous chain tip",
+ __func__, GetName());
+ return;
+ }
pindex = pindex_next;
}
@@ -119,8 +129,10 @@ void BaseIndex::ThreadSync()
}
if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL < current_time) {
- WriteBestBlock(pindex);
+ m_best_block_index = pindex;
last_locator_write_time = current_time;
+ // No need to handle errors in Commit. See rationale above.
+ Commit();
}
CBlock block;
@@ -144,12 +156,35 @@ void BaseIndex::ThreadSync()
}
}
-bool BaseIndex::WriteBestBlock(const CBlockIndex* block_index)
+bool BaseIndex::Commit()
+{
+ CDBBatch batch(GetDB());
+ if (!CommitInternal(batch) || !GetDB().WriteBatch(batch)) {
+ return error("%s: Failed to commit latest %s state", __func__, GetName());
+ }
+ return true;
+}
+
+bool BaseIndex::CommitInternal(CDBBatch& batch)
{
LOCK(cs_main);
- if (!GetDB().WriteBestBlock(chainActive.GetLocator(block_index))) {
- return error("%s: Failed to write locator to disk", __func__);
+ GetDB().WriteBestBlock(batch, ::ChainActive().GetLocator(m_best_block_index));
+ return true;
+}
+
+bool BaseIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip)
+{
+ assert(current_tip == m_best_block_index);
+ assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
+
+ // In the case of a reorg, ensure persisted block locator is not stale.
+ m_best_block_index = new_tip;
+ if (!Commit()) {
+ // If commit fails, revert the best block index to avoid corruption.
+ m_best_block_index = current_tip;
+ return false;
}
+
return true;
}
@@ -180,6 +215,11 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const
best_block_index->GetBlockHash().ToString());
return;
}
+ if (best_block_index != pindex->pprev && !Rewind(best_block_index, pindex->pprev)) {
+ FatalError("%s: Failed to rewind index %s to a previous chain tip",
+ __func__, GetName());
+ return;
+ }
}
if (WriteBlock(*block, pindex)) {
@@ -224,9 +264,10 @@ void BaseIndex::ChainStateFlushed(const CBlockLocator& locator)
return;
}
- if (!GetDB().WriteBestBlock(locator)) {
- error("%s: Failed to write locator to disk", __func__);
- }
+ // No need to handle errors in Commit. If it fails, the error will be already be logged. The
+ // best way to recover is to continue, as index cannot be corrupted by a missed commit to disk
+ // for an advanced index state.
+ Commit();
}
bool BaseIndex::BlockUntilSyncedToCurrentChain()
@@ -239,9 +280,9 @@ bool BaseIndex::BlockUntilSyncedToCurrentChain()
{
// Skip the queue-draining stuff if we know we're caught up with
- // chainActive.Tip().
+ // ::ChainActive().Tip().
LOCK(cs_main);
- const CBlockIndex* chain_tip = chainActive.Tip();
+ const CBlockIndex* chain_tip = ::ChainActive().Tip();
const CBlockIndex* best_block_index = m_best_block_index.load();
if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) {
return true;
diff --git a/src/index/base.h b/src/index/base.h
index 04ee6e6cc2..31acbed0c1 100644
--- a/src/index/base.h
+++ b/src/index/base.h
@@ -32,7 +32,7 @@ protected:
bool ReadBestBlock(CBlockLocator& locator) const;
/// Write block locator of the chain that the txindex is in sync with.
- bool WriteBestBlock(const CBlockLocator& locator);
+ void WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator);
};
private:
@@ -54,8 +54,15 @@ private:
/// over and the sync thread exits.
void ThreadSync();
- /// Write the current chain block locator to the DB.
- bool WriteBestBlock(const CBlockIndex* block_index);
+ /// Write the current index state (eg. chain block locator and subclass-specific items) to disk.
+ ///
+ /// Recommendations for error handling:
+ /// If called on a successor of the previous committed best block in the index, the index can
+ /// continue processing without risk of corruption, though the index state will need to catch up
+ /// from further behind on reboot. If the new state is not a successor of the previous state (due
+ /// to a chain reorganization), the index must halt until Commit succeeds or else it could end up
+ /// getting corrupted.
+ bool Commit();
protected:
void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex,
@@ -69,6 +76,14 @@ protected:
/// Write update index entries for a newly connected block.
virtual bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) { return true; }
+ /// Virtual method called internally by Commit that can be overridden to atomically
+ /// commit more index state.
+ virtual bool CommitInternal(CDBBatch& batch);
+
+ /// Rewind index to an earlier chain tip during a chain reorg. The tip must
+ /// be an ancestor of the current best block.
+ virtual bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip);
+
virtual DB& GetDB() const = 0;
/// Get the name of the index for display in logs.
diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp
new file mode 100644
index 0000000000..20f33baf2c
--- /dev/null
+++ b/src/index/blockfilterindex.cpp
@@ -0,0 +1,467 @@
+// Copyright (c) 2018 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 <map>
+
+#include <dbwrapper.h>
+#include <index/blockfilterindex.h>
+#include <util/system.h>
+#include <validation.h>
+
+/* The index database stores three items for each block: the disk location of the encoded filter,
+ * its dSHA256 hash, and the header. Those belonging to blocks on the active chain are indexed by
+ * height, and those belonging to blocks that have been reorganized out of the active chain are
+ * indexed by block hash. This ensures that filter data for any block that becomes part of the
+ * active chain can always be retrieved, alleviating timing concerns.
+ *
+ * The filters themselves are stored in flat files and referenced by the LevelDB entries. This
+ * minimizes the amount of data written to LevelDB and keeps the database values constant size. The
+ * disk location of the next block filter to be written (represented as a FlatFilePos) is stored
+ * under the DB_FILTER_POS key.
+ *
+ * Keys for the height index have the type [DB_BLOCK_HEIGHT, uint32 (BE)]. The height is represented
+ * as big-endian so that sequential reads of filters by height are fast.
+ * Keys for the hash index have the type [DB_BLOCK_HASH, uint256].
+ */
+constexpr char DB_BLOCK_HASH = 's';
+constexpr char DB_BLOCK_HEIGHT = 't';
+constexpr char DB_FILTER_POS = 'P';
+
+constexpr unsigned int MAX_FLTR_FILE_SIZE = 0x1000000; // 16 MiB
+/** The pre-allocation chunk size for fltr?????.dat files */
+constexpr unsigned int FLTR_FILE_CHUNK_SIZE = 0x100000; // 1 MiB
+
+namespace {
+
+struct DBVal {
+ uint256 hash;
+ uint256 header;
+ FlatFilePos pos;
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ READWRITE(hash);
+ READWRITE(header);
+ READWRITE(pos);
+ }
+};
+
+struct DBHeightKey {
+ int height;
+
+ DBHeightKey() : height(0) {}
+ DBHeightKey(int height_in) : height(height_in) {}
+
+ template<typename Stream>
+ void Serialize(Stream& s) const
+ {
+ ser_writedata8(s, DB_BLOCK_HEIGHT);
+ ser_writedata32be(s, height);
+ }
+
+ template<typename Stream>
+ void Unserialize(Stream& s)
+ {
+ char prefix = ser_readdata8(s);
+ if (prefix != DB_BLOCK_HEIGHT) {
+ throw std::ios_base::failure("Invalid format for block filter index DB height key");
+ }
+ height = ser_readdata32be(s);
+ }
+};
+
+struct DBHashKey {
+ uint256 hash;
+
+ DBHashKey(const uint256& hash_in) : hash(hash_in) {}
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ char prefix = DB_BLOCK_HASH;
+ READWRITE(prefix);
+ if (prefix != DB_BLOCK_HASH) {
+ throw std::ios_base::failure("Invalid format for block filter index DB hash key");
+ }
+
+ READWRITE(hash);
+ }
+};
+
+}; // namespace
+
+static std::map<BlockFilterType, BlockFilterIndex> g_filter_indexes;
+
+BlockFilterIndex::BlockFilterIndex(BlockFilterType filter_type,
+ size_t n_cache_size, bool f_memory, bool f_wipe)
+ : m_filter_type(filter_type)
+{
+ const std::string& filter_name = BlockFilterTypeName(filter_type);
+ if (filter_name.empty()) throw std::invalid_argument("unknown filter_type");
+
+ fs::path path = GetDataDir() / "indexes" / "blockfilter" / filter_name;
+ fs::create_directories(path);
+
+ m_name = filter_name + " block filter index";
+ m_db = MakeUnique<BaseIndex::DB>(path / "db", n_cache_size, f_memory, f_wipe);
+ m_filter_fileseq = MakeUnique<FlatFileSeq>(std::move(path), "fltr", FLTR_FILE_CHUNK_SIZE);
+}
+
+bool BlockFilterIndex::Init()
+{
+ if (!m_db->Read(DB_FILTER_POS, m_next_filter_pos)) {
+ // Check that the cause of the read failure is that the key does not exist. Any other errors
+ // indicate database corruption or a disk failure, and starting the index would cause
+ // further corruption.
+ if (m_db->Exists(DB_FILTER_POS)) {
+ return error("%s: Cannot read current %s state; index may be corrupted",
+ __func__, GetName());
+ }
+
+ // If the DB_FILTER_POS is not set, then initialize to the first location.
+ m_next_filter_pos.nFile = 0;
+ m_next_filter_pos.nPos = 0;
+ }
+ return BaseIndex::Init();
+}
+
+bool BlockFilterIndex::CommitInternal(CDBBatch& batch)
+{
+ const FlatFilePos& pos = m_next_filter_pos;
+
+ // Flush current filter file to disk.
+ CAutoFile file(m_filter_fileseq->Open(pos), SER_DISK, CLIENT_VERSION);
+ if (file.IsNull()) {
+ return error("%s: Failed to open filter file %d", __func__, pos.nFile);
+ }
+ if (!FileCommit(file.Get())) {
+ return error("%s: Failed to commit filter file %d", __func__, pos.nFile);
+ }
+
+ batch.Write(DB_FILTER_POS, pos);
+ return BaseIndex::CommitInternal(batch);
+}
+
+bool BlockFilterIndex::ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& filter) const
+{
+ CAutoFile filein(m_filter_fileseq->Open(pos, true), SER_DISK, CLIENT_VERSION);
+ if (filein.IsNull()) {
+ return false;
+ }
+
+ uint256 block_hash;
+ std::vector<unsigned char> encoded_filter;
+ try {
+ filein >> block_hash >> encoded_filter;
+ filter = BlockFilter(GetFilterType(), block_hash, std::move(encoded_filter));
+ }
+ catch (const std::exception& e) {
+ return error("%s: Failed to deserialize block filter from disk: %s", __func__, e.what());
+ }
+
+ return true;
+}
+
+size_t BlockFilterIndex::WriteFilterToDisk(FlatFilePos& pos, const BlockFilter& filter)
+{
+ assert(filter.GetFilterType() == GetFilterType());
+
+ size_t data_size =
+ GetSerializeSize(filter.GetBlockHash(), CLIENT_VERSION) +
+ GetSerializeSize(filter.GetEncodedFilter(), CLIENT_VERSION);
+
+ // If writing the filter would overflow the file, flush and move to the next one.
+ if (pos.nPos + data_size > MAX_FLTR_FILE_SIZE) {
+ CAutoFile last_file(m_filter_fileseq->Open(pos), SER_DISK, CLIENT_VERSION);
+ if (last_file.IsNull()) {
+ LogPrintf("%s: Failed to open filter file %d\n", __func__, pos.nFile);
+ return 0;
+ }
+ if (!TruncateFile(last_file.Get(), pos.nPos)) {
+ LogPrintf("%s: Failed to truncate filter file %d\n", __func__, pos.nFile);
+ return 0;
+ }
+ if (!FileCommit(last_file.Get())) {
+ LogPrintf("%s: Failed to commit filter file %d\n", __func__, pos.nFile);
+ return 0;
+ }
+
+ pos.nFile++;
+ pos.nPos = 0;
+ }
+
+ // Pre-allocate sufficient space for filter data.
+ bool out_of_space;
+ m_filter_fileseq->Allocate(pos, data_size, out_of_space);
+ if (out_of_space) {
+ LogPrintf("%s: out of disk space\n", __func__);
+ return 0;
+ }
+
+ CAutoFile fileout(m_filter_fileseq->Open(pos), SER_DISK, CLIENT_VERSION);
+ if (fileout.IsNull()) {
+ LogPrintf("%s: Failed to open filter file %d\n", __func__, pos.nFile);
+ return 0;
+ }
+
+ fileout << filter.GetBlockHash() << filter.GetEncodedFilter();
+ return data_size;
+}
+
+bool BlockFilterIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
+{
+ CBlockUndo block_undo;
+ uint256 prev_header;
+
+ if (pindex->nHeight > 0) {
+ if (!UndoReadFromDisk(block_undo, pindex)) {
+ return false;
+ }
+
+ std::pair<uint256, DBVal> read_out;
+ if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) {
+ return false;
+ }
+
+ uint256 expected_block_hash = pindex->pprev->GetBlockHash();
+ if (read_out.first != expected_block_hash) {
+ return error("%s: previous block header belongs to unexpected block %s; expected %s",
+ __func__, read_out.first.ToString(), expected_block_hash.ToString());
+ }
+
+ prev_header = read_out.second.header;
+ }
+
+ BlockFilter filter(m_filter_type, block, block_undo);
+
+ size_t bytes_written = WriteFilterToDisk(m_next_filter_pos, filter);
+ if (bytes_written == 0) return false;
+
+ std::pair<uint256, DBVal> value;
+ value.first = pindex->GetBlockHash();
+ value.second.hash = filter.GetHash();
+ value.second.header = filter.ComputeHeader(prev_header);
+ value.second.pos = m_next_filter_pos;
+
+ if (!m_db->Write(DBHeightKey(pindex->nHeight), value)) {
+ return false;
+ }
+
+ m_next_filter_pos.nPos += bytes_written;
+ return true;
+}
+
+static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
+ const std::string& index_name,
+ int start_height, int stop_height)
+{
+ DBHeightKey key(start_height);
+ db_it.Seek(key);
+
+ for (int height = start_height; height <= stop_height; ++height) {
+ if (!db_it.GetKey(key) || key.height != height) {
+ return error("%s: unexpected key in %s: expected (%c, %d)",
+ __func__, index_name, DB_BLOCK_HEIGHT, height);
+ }
+
+ std::pair<uint256, DBVal> value;
+ if (!db_it.GetValue(value)) {
+ return error("%s: unable to read value in %s at key (%c, %d)",
+ __func__, index_name, DB_BLOCK_HEIGHT, height);
+ }
+
+ batch.Write(DBHashKey(value.first), std::move(value.second));
+
+ db_it.Next();
+ }
+ return true;
+}
+
+bool BlockFilterIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip)
+{
+ assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
+
+ CDBBatch batch(*m_db);
+ std::unique_ptr<CDBIterator> db_it(m_db->NewIterator());
+
+ // During a reorg, we need to copy all filters for blocks that are getting disconnected from the
+ // height index to the hash index so we can still find them when the height index entries are
+ // overwritten.
+ if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, new_tip->nHeight, current_tip->nHeight)) {
+ return false;
+ }
+
+ // The latest filter position gets written in Commit by the call to the BaseIndex::Rewind.
+ // But since this creates new references to the filter, the position should get updated here
+ // atomically as well in case Commit fails.
+ batch.Write(DB_FILTER_POS, m_next_filter_pos);
+ if (!m_db->WriteBatch(batch)) return false;
+
+ return BaseIndex::Rewind(current_tip, new_tip);
+}
+
+static bool LookupOne(const CDBWrapper& db, const CBlockIndex* block_index, DBVal& result)
+{
+ // First check if the result is stored under the height index and the value there matches the
+ // block hash. This should be the case if the block is on the active chain.
+ std::pair<uint256, DBVal> read_out;
+ if (!db.Read(DBHeightKey(block_index->nHeight), read_out)) {
+ return false;
+ }
+ if (read_out.first == block_index->GetBlockHash()) {
+ result = std::move(read_out.second);
+ return true;
+ }
+
+ // If value at the height index corresponds to an different block, the result will be stored in
+ // the hash index.
+ return db.Read(DBHashKey(block_index->GetBlockHash()), result);
+}
+
+static bool LookupRange(CDBWrapper& db, const std::string& index_name, int start_height,
+ const CBlockIndex* stop_index, std::vector<DBVal>& results)
+{
+ if (start_height < 0) {
+ return error("%s: start height (%d) is negative", __func__, start_height);
+ }
+ if (start_height > stop_index->nHeight) {
+ return error("%s: start height (%d) is greater than stop height (%d)",
+ __func__, start_height, stop_index->nHeight);
+ }
+
+ size_t results_size = static_cast<size_t>(stop_index->nHeight - start_height + 1);
+ std::vector<std::pair<uint256, DBVal>> values(results_size);
+
+ DBHeightKey key(start_height);
+ std::unique_ptr<CDBIterator> db_it(db.NewIterator());
+ db_it->Seek(DBHeightKey(start_height));
+ for (int height = start_height; height <= stop_index->nHeight; ++height) {
+ if (!db_it->Valid() || !db_it->GetKey(key) || key.height != height) {
+ return false;
+ }
+
+ size_t i = static_cast<size_t>(height - start_height);
+ if (!db_it->GetValue(values[i])) {
+ return error("%s: unable to read value in %s at key (%c, %d)",
+ __func__, index_name, DB_BLOCK_HEIGHT, height);
+ }
+
+ db_it->Next();
+ }
+
+ results.resize(results_size);
+
+ // Iterate backwards through block indexes collecting results in order to access the block hash
+ // of each entry in case we need to look it up in the hash index.
+ for (const CBlockIndex* block_index = stop_index;
+ block_index && block_index->nHeight >= start_height;
+ block_index = block_index->pprev) {
+ uint256 block_hash = block_index->GetBlockHash();
+
+ size_t i = static_cast<size_t>(block_index->nHeight - start_height);
+ if (block_hash == values[i].first) {
+ results[i] = std::move(values[i].second);
+ continue;
+ }
+
+ if (!db.Read(DBHashKey(block_hash), results[i])) {
+ return error("%s: unable to read value in %s at key (%c, %s)",
+ __func__, index_name, DB_BLOCK_HASH, block_hash.ToString());
+ }
+ }
+
+ return true;
+}
+
+bool BlockFilterIndex::LookupFilter(const CBlockIndex* block_index, BlockFilter& filter_out) const
+{
+ DBVal entry;
+ if (!LookupOne(*m_db, block_index, entry)) {
+ return false;
+ }
+
+ return ReadFilterFromDisk(entry.pos, filter_out);
+}
+
+bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out) const
+{
+ DBVal entry;
+ if (!LookupOne(*m_db, block_index, entry)) {
+ return false;
+ }
+
+ header_out = entry.header;
+ return true;
+}
+
+bool BlockFilterIndex::LookupFilterRange(int start_height, const CBlockIndex* stop_index,
+ std::vector<BlockFilter>& filters_out) const
+{
+ std::vector<DBVal> entries;
+ if (!LookupRange(*m_db, m_name, start_height, stop_index, entries)) {
+ return false;
+ }
+
+ filters_out.resize(entries.size());
+ auto filter_pos_it = filters_out.begin();
+ for (const auto& entry : entries) {
+ if (!ReadFilterFromDisk(entry.pos, *filter_pos_it)) {
+ return false;
+ }
+ ++filter_pos_it;
+ }
+
+ return true;
+}
+
+bool BlockFilterIndex::LookupFilterHashRange(int start_height, const CBlockIndex* stop_index,
+ std::vector<uint256>& hashes_out) const
+
+{
+ std::vector<DBVal> entries;
+ if (!LookupRange(*m_db, m_name, start_height, stop_index, entries)) {
+ return false;
+ }
+
+ hashes_out.clear();
+ hashes_out.reserve(entries.size());
+ for (const auto& entry : entries) {
+ hashes_out.push_back(entry.hash);
+ }
+ return true;
+}
+
+BlockFilterIndex* GetBlockFilterIndex(BlockFilterType filter_type)
+{
+ auto it = g_filter_indexes.find(filter_type);
+ return it != g_filter_indexes.end() ? &it->second : nullptr;
+}
+
+void ForEachBlockFilterIndex(std::function<void (BlockFilterIndex&)> fn)
+{
+ for (auto& entry : g_filter_indexes) fn(entry.second);
+}
+
+bool InitBlockFilterIndex(BlockFilterType filter_type,
+ size_t n_cache_size, bool f_memory, bool f_wipe)
+{
+ auto result = g_filter_indexes.emplace(std::piecewise_construct,
+ std::forward_as_tuple(filter_type),
+ std::forward_as_tuple(filter_type,
+ n_cache_size, f_memory, f_wipe));
+ return result.second;
+}
+
+bool DestroyBlockFilterIndex(BlockFilterType filter_type)
+{
+ return g_filter_indexes.erase(filter_type);
+}
+
+void DestroyAllBlockFilterIndexes()
+{
+ g_filter_indexes.clear();
+}
diff --git a/src/index/blockfilterindex.h b/src/index/blockfilterindex.h
new file mode 100644
index 0000000000..436d52515f
--- /dev/null
+++ b/src/index/blockfilterindex.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2018 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_INDEX_BLOCKFILTERINDEX_H
+#define BITCOIN_INDEX_BLOCKFILTERINDEX_H
+
+#include <blockfilter.h>
+#include <chain.h>
+#include <flatfile.h>
+#include <index/base.h>
+
+/**
+ * BlockFilterIndex is used to store and retrieve block filters, hashes, and headers for a range of
+ * blocks by height. An index is constructed for each supported filter type with its own database
+ * (ie. filter data for different types are stored in separate databases).
+ *
+ * This index is used to serve BIP 157 net requests.
+ */
+class BlockFilterIndex final : public BaseIndex
+{
+private:
+ BlockFilterType m_filter_type;
+ std::string m_name;
+ std::unique_ptr<BaseIndex::DB> m_db;
+
+ FlatFilePos m_next_filter_pos;
+ std::unique_ptr<FlatFileSeq> m_filter_fileseq;
+
+ bool ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& filter) const;
+ size_t WriteFilterToDisk(FlatFilePos& pos, const BlockFilter& filter);
+
+protected:
+ bool Init() override;
+
+ bool CommitInternal(CDBBatch& batch) override;
+
+ bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override;
+
+ bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) override;
+
+ BaseIndex::DB& GetDB() const override { return *m_db; }
+
+ const char* GetName() const override { return m_name.c_str(); }
+
+public:
+ /** Constructs the index, which becomes available to be queried. */
+ explicit BlockFilterIndex(BlockFilterType filter_type,
+ size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
+
+ BlockFilterType GetFilterType() const { return m_filter_type; }
+
+ /** Get a single filter by block. */
+ bool LookupFilter(const CBlockIndex* block_index, BlockFilter& filter_out) const;
+
+ /** Get a single filter header by block. */
+ bool LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out) const;
+
+ /** Get a range of filters between two heights on a chain. */
+ bool LookupFilterRange(int start_height, const CBlockIndex* stop_index,
+ std::vector<BlockFilter>& filters_out) const;
+
+ /** Get a range of filter hashes between two heights on a chain. */
+ bool LookupFilterHashRange(int start_height, const CBlockIndex* stop_index,
+ std::vector<uint256>& hashes_out) const;
+};
+
+/**
+ * Get a block filter index by type. Returns nullptr if index has not been initialized or was
+ * already destroyed.
+ */
+BlockFilterIndex* GetBlockFilterIndex(BlockFilterType filter_type);
+
+/** Iterate over all running block filter indexes, invoking fn on each. */
+void ForEachBlockFilterIndex(std::function<void (BlockFilterIndex&)> fn);
+
+/**
+ * Initialize a block filter index for the given type if one does not already exist. Returns true if
+ * a new index is created and false if one has already been initialized.
+ */
+bool InitBlockFilterIndex(BlockFilterType filter_type,
+ size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
+
+/**
+ * Destroy the block filter index with the given type. Returns false if no such index exists. This
+ * just releases the allocated memory and closes the database connection, it does not delete the
+ * index data.
+ */
+bool DestroyBlockFilterIndex(BlockFilterType filter_type);
+
+/** Destroy all open block filter indexes. */
+void DestroyAllBlockFilterIndexes();
+
+#endif // BITCOIN_INDEX_BLOCKFILTERINDEX_H
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index 10bc8419dd..929b85bfb5 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -16,7 +16,7 @@ constexpr char DB_TXINDEX_BLOCK = 'T';
std::unique_ptr<TxIndex> g_txindex;
-struct CDiskTxPos : public CDiskBlockPos
+struct CDiskTxPos : public FlatFilePos
{
unsigned int nTxOffset; // after header
@@ -24,11 +24,11 @@ struct CDiskTxPos : public CDiskBlockPos
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITEAS(CDiskBlockPos, *this);
+ READWRITEAS(FlatFilePos, *this);
READWRITE(VARINT(nTxOffset));
}
- CDiskTxPos(const CDiskBlockPos &blockIn, unsigned int nTxOffsetIn) : CDiskBlockPos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) {
+ CDiskTxPos(const FlatFilePos &blockIn, unsigned int nTxOffsetIn) : FlatFilePos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) {
}
CDiskTxPos() {
@@ -36,7 +36,7 @@ struct CDiskTxPos : public CDiskBlockPos
}
void SetNull() {
- CDiskBlockPos::SetNull();
+ FlatFilePos::SetNull();
nTxOffset = 0;
}
};
@@ -236,7 +236,7 @@ bool TxIndex::Init()
// Attempt to migrate txindex from the old database to the new one. Even if
// chain_tip is null, the node could be reindexing and we still want to
// delete txindex records in the old database.
- if (!m_db->MigrateData(*pblocktree, chainActive.GetLocator())) {
+ if (!m_db->MigrateData(*pblocktree, ::ChainActive().GetLocator())) {
return false;
}
diff --git a/src/init.cpp b/src/init.cpp
index 019b2e469e..2a10f28a84 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -12,14 +12,15 @@
#include <addrman.h>
#include <amount.h>
#include <banman.h>
+#include <blockfilter.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 <index/blockfilterindex.h>
#include <interfaces/chain.h>
#include <index/txindex.h>
#include <key.h>
@@ -31,6 +32,7 @@
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <rpc/server.h>
#include <rpc/register.h>
#include <rpc/blockchain.h>
@@ -39,6 +41,7 @@
#include <script/sigcache.h>
#include <scheduler.h>
#include <shutdown.h>
+#include <util/threadnames.h>
#include <timedata.h>
#include <txdb.h>
#include <txmempool.h>
@@ -46,13 +49,15 @@
#include <ui_interface.h>
#include <util/system.h>
#include <util/moneystr.h>
+#include <util/validation.h>
#include <validationinterface.h>
-#include <warnings.h>
#include <walletinitinterface.h>
#include <stdint.h>
#include <stdio.h>
#ifndef WIN32
+#include <attributes.h>
+#include <cerrno>
#include <signal.h>
#include <sys/stat.h>
#endif
@@ -61,7 +66,6 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/thread.hpp>
-#include <openssl/crypto.h>
#if ENABLE_ZMQ
#include <zmq/zmqabstractnotifier.h>
@@ -69,7 +73,7 @@
#include <zmq/zmqrpc.h>
#endif
-bool fFeeEstimatesInitialized = false;
+static bool fFeeEstimatesInitialized = false;
static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
@@ -92,6 +96,31 @@ std::unique_ptr<BanMan> g_banman;
static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
+/**
+ * The PID file facilities.
+ */
+static const char* BITCOIN_PID_FILENAME = "bitcoind.pid";
+
+static fs::path GetPidFile()
+{
+ return AbsPathForConfigVal(fs::path(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME)));
+}
+
+NODISCARD static bool CreatePidFile()
+{
+ fsbridge::ofstream file{GetPidFile()};
+ if (file) {
+#ifdef WIN32
+ tfm::format(file, "%d\n", GetCurrentProcessId());
+#else
+ tfm::format(file, "%d\n", getpid());
+#endif
+ return true;
+ } else {
+ return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile().string(), std::strerror(errno)));
+ }
+}
+
//////////////////////////////////////////////////////////////////////////////
//
// Shutdown
@@ -161,6 +190,7 @@ void Interrupt()
if (g_txindex) {
g_txindex->Interrupt();
}
+ ForEachBlockFilterIndex([](BlockFilterIndex& index) { index.Interrupt(); });
}
void Shutdown(InitInterfaces& interfaces)
@@ -175,7 +205,7 @@ void Shutdown(InitInterfaces& interfaces)
/// for example if the data directory was found to be locked.
/// Be sure that anything that writes files or flushes caches only does this if the respective
/// module was initialized.
- RenameThread("bitcoin-shutoff");
+ util::ThreadRename("shutoff");
mempool.AddTransactionsUpdated(1);
StopHTTPRPC();
@@ -192,6 +222,7 @@ void Shutdown(InitInterfaces& interfaces)
if (peerLogic) UnregisterValidationInterface(peerLogic.get());
if (g_connman) g_connman->Stop();
if (g_txindex) g_txindex->Stop();
+ ForEachBlockFilterIndex([](BlockFilterIndex& index) { index.Stop(); });
StopTorControl();
@@ -206,9 +237,10 @@ void Shutdown(InitInterfaces& interfaces)
g_connman.reset();
g_banman.reset();
g_txindex.reset();
+ DestroyAllBlockFilterIndexes();
- if (g_is_mempool_loaded && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
- DumpMempool();
+ if (::mempool.IsLoaded() && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ DumpMempool(::mempool);
}
if (fFeeEstimatesInitialized)
@@ -225,7 +257,7 @@ void Shutdown(InitInterfaces& interfaces)
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
if (pcoinsTip != nullptr) {
- FlushStateToDisk();
+ ::ChainstateActive().ForceFlushStateToDisk();
}
// After there are no more peers/RPC left to give us new data which may generate
@@ -241,7 +273,7 @@ void Shutdown(InitInterfaces& interfaces)
{
LOCK(cs_main);
if (pcoinsTip != nullptr) {
- FlushStateToDisk();
+ ::ChainstateActive().ForceFlushStateToDisk();
}
pcoinsTip.reset();
pcoinscatcher.reset();
@@ -260,13 +292,13 @@ void Shutdown(InitInterfaces& interfaces)
}
#endif
-#ifndef WIN32
try {
- fs::remove(GetPidFile());
+ if (!fs::remove(GetPidFile())) {
+ LogPrintf("%s: Unable to remove PID file: File does not exist\n", __func__);
+ }
} catch (const fs::filesystem_error& e) {
- LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what());
+ LogPrintf("%s: Unable to remove PID file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
}
-#endif
interfaces.chain_clients.clear();
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
@@ -289,7 +321,7 @@ static void HandleSIGTERM(int)
static void HandleSIGHUP(int)
{
- g_logger->m_reopen_file = true;
+ LogInstance().m_reopen_file = true;
}
#else
static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType)
@@ -311,14 +343,15 @@ static void registerSignalHandler(int signal, void(*handler)(int))
}
#endif
+static boost::signals2::connection rpc_notify_block_change_connection;
static void OnRPCStarted()
{
- uiInterface.NotifyBlockTip_connect(&RPCNotifyBlockChange);
+ rpc_notify_block_change_connection = uiInterface.NotifyBlockTip_connect(&RPCNotifyBlockChange);
}
static void OnRPCStopped()
{
- uiInterface.NotifyBlockTip_disconnect(&RPCNotifyBlockChange);
+ rpc_notify_block_change_connection.disconnect();
RPCNotifyBlockChange(false, nullptr);
g_best_block_cv.notify_all();
LogPrint(BCLog::RPC, "RPC stopped.\n");
@@ -326,6 +359,9 @@ static void OnRPCStopped()
void SetupServerArgs()
{
+ SetupHelpOptions(gArgs);
+ gArgs.AddArg("-help-debug", "Print help message with debugging options and exit", false, OptionsCategory::DEBUG_TEST); // server-only for now
+
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST);
@@ -334,25 +370,22 @@ void SetupServerArgs()
const auto regtestChainParams = CreateChainParams(CBaseChainParams::REGTEST);
// Hidden Options
- std::vector<std::string> hidden_args = {"-h", "-help",
+ std::vector<std::string> hidden_args = {
"-dbcrashratio", "-forcecompactdb",
// GUI args. These will be overwritten by SetupUIArgs for the GUI
"-allowselfsignedrootcertificates", "-choosedatadir", "-lang=<lang>", "-min", "-resetguisettings", "-rootcertificates=<file>", "-splash", "-uiplatform"};
- // Set all of the args and their help
- // When adding new options to the categories, please keep and ensure alphabetical ordering.
- gArgs.AddArg("-?", "Print this help message and exit", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-version", "Print version and exit", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-alertnotify=<cmd>", "Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-assumevalid=<hex>", strprintf("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: %s, testnet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-blocksdir=<dir>", "Specify blocks directory (default: <datadir>/blocks)", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksdir=<dir>", "Specify directory to hold blocks subdirectory for *.dat files (default: <datadir>)", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-blocksonly", strprintf("Whether to operate in a blocks only mode (default: %u)", DEFAULT_BLOCKSONLY), true, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Transactions from the wallet or RPC are not affected. (default: %u)", DEFAULT_BLOCKSONLY), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS);
- gArgs.AddArg("-dbcache=<n>", strprintf("Set database cache size in MiB (%d to %d, default: %d)", nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), true, OptionsCategory::OPTIONS);
gArgs.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", false, OptionsCategory::OPTIONS);
@@ -364,11 +397,7 @@ void SetupServerArgs()
gArgs.AddArg("-par=<n>", strprintf("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)",
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), false, OptionsCategory::OPTIONS);
-#ifndef WIN32
gArgs.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), false, OptionsCategory::OPTIONS);
-#else
- hidden_args.emplace_back("-pid");
-#endif
gArgs.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. "
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), false, OptionsCategory::OPTIONS);
@@ -380,6 +409,10 @@ void SetupServerArgs()
hidden_args.emplace_back("-sysperms");
#endif
gArgs.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blockfilterindex=<type>",
+ strprintf("Maintain an index of compact filters by block (default: %s, values: %s).", DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) +
+ " If <type> is not supplied or if <type> = 1, indexes for all known types are enabled.",
+ false, OptionsCategory::OPTIONS);
gArgs.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", false, OptionsCategory::CONNECTION);
gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), false, OptionsCategory::CONNECTION);
@@ -422,7 +455,7 @@ void SetupServerArgs()
#endif
gArgs.AddArg("-whitebind=<addr>", "Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6", false, OptionsCategory::CONNECTION);
gArgs.AddArg("-whitelist=<IP address or network>", "Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times."
- " Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway", false, OptionsCategory::CONNECTION);
+ " Whitelisted peers cannot be DoS banned", false, OptionsCategory::CONNECTION);
g_wallet_init_interface.AddWalletOptions();
@@ -455,7 +488,7 @@ void SetupServerArgs()
"and level 4 tries to reconnect the blocks, "
"each level includes the checks of the previous levels "
"(0-4, default: %u)", DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, ::ChainActive() and mapBlocksUnlinked occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", true, OptionsCategory::DEBUG_TEST);
@@ -470,15 +503,13 @@ void SetupServerArgs()
gArgs.AddArg("-debug=<category>", "Output debugging information (default: -nodebug, supplying <category> is optional). "
"If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + ListLogCategories() + ".", false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories."), false, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-help-debug", "Print help message with debugging options and exit", false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-maxtxfee=<amt>", strprintf("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)",
- CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set -nodebuglogfile)", false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", false, OptionsCategory::DEBUG_TEST);
@@ -488,14 +519,13 @@ void SetupServerArgs()
gArgs.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), true, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), true, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), true, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), true, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), false, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), false, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), false, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-mempoolreplacement", strprintf("Enable transaction replacement in the memory pool (default: %u)", DEFAULT_ENABLE_REPLACEMENT), false, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), false, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-whitelistforcerelay", strprintf("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)", DEFAULT_WHITELISTFORCERELAY), false, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-whitelistforcerelay", strprintf("Force relay of transactions from whitelisted peers even if the transactions were already in the mempool or violate local relay policy (default: %d)", DEFAULT_WHITELISTFORCERELAY), false, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-whitelistrelay", strprintf("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), false, OptionsCategory::NODE_RELAY);
@@ -635,7 +665,7 @@ static void CleanupBlockRevFiles()
static void ThreadImport(std::vector<fs::path> vImportFiles)
{
const CChainParams& chainparams = Params();
- RenameThread("bitcoin-loadblk");
+ util::ThreadRename("loadblk");
ScheduleBatchPriority();
{
@@ -645,8 +675,8 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
if (fReindex) {
int nFile = 0;
while (true) {
- CDiskBlockPos pos(nFile, 0);
- if (!fs::exists(GetBlockPosFilename(pos, "blk")))
+ FlatFilePos pos(nFile, 0);
+ if (!fs::exists(GetBlockPosFilename(pos)))
break; // No block files left to reindex
FILE *file = OpenBlockFile(pos, true);
if (!file)
@@ -702,9 +732,9 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
}
} // End scope of CImportingNow
if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
- LoadMempool();
+ LoadMempool(::mempool);
}
- g_is_mempool_loaded = !ShutdownRequested();
+ ::mempool.SetIsLoaded(!ShutdownRequested());
}
/** Sanity checks
@@ -805,19 +835,6 @@ void InitParameterInteraction()
if (gArgs.SoftSetBoolArg("-whitelistrelay", true))
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__);
}
-
- // Warn if network-specific options (-addnode, -connect, etc) are
- // specified in default section of config file, but not overridden
- // on the command line or in this network's section of the config file.
- std::string network = gArgs.GetChainName();
- for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) {
- InitWarning(strprintf(_("Config setting for %s only applied on %s network when in [%s] section."), arg, network, network));
- }
-
- // Warn if unrecognized section name are present in the config file.
- for (const auto& section : gArgs.GetUnrecognizedSections()) {
- InitWarning(strprintf(_("Section [%s] is not recognized."), section));
- }
}
static std::string ResolveErrMsg(const char * const optname, const std::string& strBind)
@@ -833,17 +850,18 @@ static std::string ResolveErrMsg(const char * const optname, const std::string&
*/
void InitLogging()
{
- g_logger->m_print_to_file = !gArgs.IsArgNegated("-debuglogfile");
- g_logger->m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
+ LogInstance().m_print_to_file = !gArgs.IsArgNegated("-debuglogfile");
+ LogInstance().m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
// Add newlines to the logfile to distinguish this execution from the last
// one; called before console logging is set up, so this is only sent to
// debug.log.
LogPrintf("\n\n\n\n\n");
- g_logger->m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
- g_logger->m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
- g_logger->m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
+ LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
+ LogInstance().m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
+ LogInstance().m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
+ LogInstance().m_log_threadnames = gArgs.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
@@ -863,6 +881,7 @@ int nUserMaxConnections;
int nFD;
ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED);
int64_t peer_connect_timeout;
+std::vector<BlockFilterType> g_enabled_filter_types;
} // namespace
@@ -891,16 +910,7 @@ bool AppInitBasicSetup()
#endif
#ifdef WIN32
// Enable Data Execution Prevention (DEP)
- // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008
- // A failure is non-critical and needs no further attention!
-#ifndef PROCESS_DEP_ENABLE
- // We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= 0x0601 (Windows 7),
- // which is not correct. Can be removed, when GCCs winbase.h is fixed!
-#define PROCESS_DEP_ENABLE 0x00000001
-#endif
- typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD);
- PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy");
- if (setProcDEPPol != nullptr) setProcDEPPol(PROCESS_DEP_ENABLE);
+ SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
#endif
if (!SetupNetworking())
@@ -936,14 +946,46 @@ bool AppInitParameterInteraction()
// also see: InitParameterInteraction()
+ // Warn if network-specific options (-addnode, -connect, etc) are
+ // specified in default section of config file, but not overridden
+ // on the command line or in this network's section of the config file.
+ std::string network = gArgs.GetChainName();
+ for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) {
+ return InitError(strprintf(_("Config setting for %s only applied on %s network when in [%s] section."), arg, network, network));
+ }
+
+ // Warn if unrecognized section name are present in the config file.
+ for (const auto& section : gArgs.GetUnrecognizedSections()) {
+ InitWarning(strprintf("%s:%i " + _("Section [%s] is not recognized."), section.m_file, section.m_line, section.m_name));
+ }
+
if (!fs::is_directory(GetBlocksDir())) {
return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), gArgs.GetArg("-blocksdir", "").c_str()));
}
+ // parse and validate enabled filter types
+ std::string blockfilterindex_value = gArgs.GetArg("-blockfilterindex", DEFAULT_BLOCKFILTERINDEX);
+ if (blockfilterindex_value == "" || blockfilterindex_value == "1") {
+ g_enabled_filter_types = AllBlockFilterTypes();
+ } else if (blockfilterindex_value != "0") {
+ const std::vector<std::string> names = gArgs.GetArgs("-blockfilterindex");
+ g_enabled_filter_types.reserve(names.size());
+ for (const auto& name : names) {
+ BlockFilterType filter_type;
+ if (!BlockFilterTypeByName(name, filter_type)) {
+ return InitError(strprintf(_("Unknown -blockfilterindex value %s."), name));
+ }
+ g_enabled_filter_types.push_back(filter_type);
+ }
+ }
+
// if using block pruning, then disallow txindex
if (gArgs.GetArg("-prune", 0)) {
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
+ if (!g_enabled_filter_types.empty()) {
+ return InitError(_("Prune mode is incompatible with -blockfilterindex."));
+ }
}
// -bind and -whitebind can't be set when not listening
@@ -981,7 +1023,7 @@ bool AppInitParameterInteraction()
if (std::none_of(categories.begin(), categories.end(),
[](std::string cat){return cat == "0" || cat == "none";})) {
for (const auto& cat : categories) {
- if (!g_logger->EnableCategory(cat)) {
+ if (!LogInstance().EnableCategory(cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat));
}
}
@@ -990,7 +1032,7 @@ bool AppInitParameterInteraction()
// Now remove the logging categories which were explicitly excluded
for (const std::string& cat : gArgs.GetArgs("-debugexclude")) {
- if (!g_logger->DisableCategory(cat)) {
+ if (!LogInstance().DisableCategory(cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
}
}
@@ -1132,15 +1174,6 @@ bool AppInitParameterInteraction()
nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
- fEnableReplacement = gArgs.GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT);
- if ((!fEnableReplacement) && gArgs.IsArgSet("-mempoolreplacement")) {
- // Minimal effort at forwards compatibility
- std::string strReplacementModeList = gArgs.GetArg("-mempoolreplacement", ""); // default is impossible
- std::vector<std::string> vstrReplacementModes;
- boost::split(vstrReplacementModes, strReplacementModeList, boost::is_any_of(","));
- fEnableReplacement = (std::find(vstrReplacementModes.begin(), vstrReplacementModes.end(), "fee") != vstrReplacementModes.end());
- }
-
return true;
}
@@ -1194,22 +1227,23 @@ bool AppInitMain(InitInterfaces& interfaces)
{
const CChainParams& chainparams = Params();
// ********************************************************* Step 4a: application initialization
-#ifndef WIN32
- CreatePidFile(GetPidFile(), getpid());
-#endif
- if (g_logger->m_print_to_file) {
- if (gArgs.GetBoolArg("-shrinkdebugfile", g_logger->DefaultShrinkDebugFile())) {
+ if (!CreatePidFile()) {
+ // Detailed error printed inside CreatePidFile().
+ return false;
+ }
+ if (LogInstance().m_print_to_file) {
+ if (gArgs.GetBoolArg("-shrinkdebugfile", LogInstance().DefaultShrinkDebugFile())) {
// Do this first since it both loads a bunch of debug.log into memory,
// and because this needs to happen before any other debug.log printing
- g_logger->ShrinkDebugFile();
+ LogInstance().ShrinkDebugFile();
}
- if (!g_logger->OpenDebugLog()) {
+ if (!LogInstance().OpenDebugLog()) {
return InitError(strprintf("Could not open debug log file %s",
- g_logger->m_file_path.string()));
+ LogInstance().m_file_path.string()));
}
}
- if (!g_logger->m_log_timestamps)
+ if (!LogInstance().m_log_timestamps)
LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime()));
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
LogPrintf("Using data directory %s\n", GetDataDir().string());
@@ -1243,7 +1277,7 @@ bool AppInitMain(InitInterfaces& interfaces)
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
if (nScriptCheckThreads) {
for (int i=0; i<nScriptCheckThreads-1; i++)
- threadGroup.create_thread(&ThreadScriptCheck);
+ threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
}
// Start the lightweight task scheduler thread
@@ -1380,7 +1414,7 @@ bool AppInitMain(InitInterfaces& interfaces)
// see Step 2: parameter interactions for more information about these
fListen = gArgs.GetBoolArg("-listen", DEFAULT_LISTEN);
fDiscover = gArgs.GetBoolArg("-discover", true);
- fRelayTxes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
+ g_relay_txes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
for (const std::string& strAddr : gArgs.GetArgs("-externalip")) {
CService addrLocal;
@@ -1417,6 +1451,13 @@ bool AppInitMain(InitInterfaces& interfaces)
nTotalCache -= nBlockTreeDBCache;
int64_t nTxIndexCache = std::min(nTotalCache / 8, gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0);
nTotalCache -= nTxIndexCache;
+ int64_t filter_index_cache = 0;
+ if (!g_enabled_filter_types.empty()) {
+ size_t n_indexes = g_enabled_filter_types.size();
+ int64_t max_cache = std::min(nTotalCache / 8, max_filter_index_cache << 20);
+ filter_index_cache = max_cache / n_indexes;
+ nTotalCache -= filter_index_cache * n_indexes;
+ }
int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache
nTotalCache -= nCoinDBCache;
@@ -1427,6 +1468,10 @@ bool AppInitMain(InitInterfaces& interfaces)
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
LogPrintf("* Using %.1f MiB for transaction index database\n", nTxIndexCache * (1.0 / 1024 / 1024));
}
+ for (BlockFilterType filter_type : g_enabled_filter_types) {
+ LogPrintf("* Using %.1f MiB for %s block filter index database\n",
+ filter_index_cache * (1.0 / 1024 / 1024), BlockFilterTypeName(filter_type));
+ }
LogPrintf("* Using %.1f MiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024));
LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024));
@@ -1437,11 +1482,11 @@ bool AppInitMain(InitInterfaces& interfaces)
uiInterface.InitMessage(_("Loading block index..."));
- LOCK(cs_main);
-
do {
const int64_t load_block_index_start_time = GetTimeMillis();
+ bool is_coinsview_empty;
try {
+ LOCK(cs_main);
UnloadBlockIndex();
pcoinsTip.reset();
pcoinsdbview.reset();
@@ -1465,6 +1510,7 @@ bool AppInitMain(InitInterfaces& interfaces)
// Note that it also sets fReindex based on the disk flag!
// From here on out fReindex and fReset mean something different!
if (!LoadBlockIndex(chainparams)) {
+ if (ShutdownRequested()) break;
strLoadError = _("Error loading block database");
break;
}
@@ -1513,27 +1559,34 @@ bool AppInitMain(InitInterfaces& interfaces)
// The on-disk coinsdb is now in a good state, create the cache
pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get()));
- bool is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull();
+ is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull();
if (!is_coinsview_empty) {
- // LoadChainTip sets chainActive based on pcoinsTip's best block
+ // LoadChainTip sets ::ChainActive() based on pcoinsTip's best block
if (!LoadChainTip(chainparams)) {
strLoadError = _("Error initializing block database");
break;
}
- assert(chainActive.Tip() != nullptr);
+ assert(::ChainActive().Tip() != nullptr);
}
+ } catch (const std::exception& e) {
+ LogPrintf("%s\n", e.what());
+ strLoadError = _("Error opening block database");
+ break;
+ }
- if (!fReset) {
- // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
- // It both disconnects blocks based on chainActive, and drops block data in
- // mapBlockIndex based on lack of available witness data.
- uiInterface.InitMessage(_("Rewinding blocks..."));
- if (!RewindBlockIndex(chainparams)) {
- strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain");
- break;
- }
+ if (!fReset) {
+ // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
+ // It both disconnects blocks based on ::ChainActive(), and drops block data in
+ // mapBlockIndex based on lack of available witness data.
+ uiInterface.InitMessage(_("Rewinding blocks..."));
+ if (!RewindBlockIndex(chainparams)) {
+ strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain");
+ break;
}
+ }
+ try {
+ LOCK(cs_main);
if (!is_coinsview_empty) {
uiInterface.InitMessage(_("Verifying blocks..."));
if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
@@ -1541,7 +1594,7 @@ bool AppInitMain(InitInterfaces& interfaces)
MIN_BLOCKS_TO_KEEP);
}
- CBlockIndex* tip = chainActive.Tip();
+ CBlockIndex* tip = ::ChainActive().Tip();
RPCNotifyBlockChange(true, tip);
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
strLoadError = _("The block database contains a block which appears to be from the future. "
@@ -1607,6 +1660,11 @@ bool AppInitMain(InitInterfaces& interfaces)
g_txindex->Start();
}
+ for (const auto& filter_type : g_enabled_filter_types) {
+ InitBlockFilterIndex(filter_type, filter_index_cache, false, fReindex);
+ GetBlockFilterIndex(filter_type)->Start();
+ }
+
// ********************************************************* Step 9: load wallet
for (const auto& client : interfaces.chain_clients) {
if (!client->load()) {
@@ -1623,7 +1681,7 @@ bool AppInitMain(InitInterfaces& interfaces)
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
if (!fReindex) {
uiInterface.InitMessage(_("Pruning blockstore..."));
- PruneAndFlush();
+ ::ChainstateActive().PruneAndFlush();
}
}
@@ -1638,19 +1696,20 @@ bool AppInitMain(InitInterfaces& interfaces)
// ********************************************************* Step 11: import blocks
- if (!CheckDiskSpace(/* additional_bytes */ 0, /* blocks_dir */ false)) {
+ if (!CheckDiskSpace(GetDataDir())) {
InitError(strprintf(_("Error: Disk space is low for %s"), GetDataDir()));
return false;
}
- if (!CheckDiskSpace(/* additional_bytes */ 0, /* blocks_dir */ true)) {
+ if (!CheckDiskSpace(GetBlocksDir())) {
InitError(strprintf(_("Error: Disk space is low for %s"), GetBlocksDir()));
return false;
}
// Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
// No locking, as this happens before any background thread is started.
- if (chainActive.Tip() == nullptr) {
- uiInterface.NotifyBlockTip_connect(BlockNotifyGenesisWait);
+ boost::signals2::connection block_notify_genesis_wait_connection;
+ if (::ChainActive().Tip() == nullptr) {
+ block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(BlockNotifyGenesisWait);
} else {
fHaveGenesis = true;
}
@@ -1674,7 +1733,7 @@ bool AppInitMain(InitInterfaces& interfaces)
while (!fHaveGenesis && !ShutdownRequested()) {
g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500));
}
- uiInterface.NotifyBlockTip_disconnect(BlockNotifyGenesisWait);
+ block_notify_genesis_wait_connection.disconnect();
}
if (ShutdownRequested()) {
@@ -1689,7 +1748,7 @@ bool AppInitMain(InitInterfaces& interfaces)
{
LOCK(cs_main);
LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size());
- chain_active_height = chainActive.Height();
+ chain_active_height = ::ChainActive().Height();
}
LogPrintf("nBestHeight = %d\n", chain_active_height);
diff --git a/src/interfaces/README.md b/src/interfaces/README.md
index 57d41df746..f77d172153 100644
--- a/src/interfaces/README.md
+++ b/src/interfaces/README.md
@@ -2,9 +2,9 @@
The following interfaces are defined here:
-* [`Chain`](chain.h) — used by wallet to access blockchain and mempool state. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
+* [`Chain`](chain.h) — used by wallet to access blockchain and mempool state. Added in [#14437](https://github.com/bitcoin/bitcoin/pull/14437), [#14711](https://github.com/bitcoin/bitcoin/pull/14711), [#15288](https://github.com/bitcoin/bitcoin/pull/15288), and [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
-* [`ChainClient`](chain.h) — used by node to start & stop `Chain` clients. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
+* [`ChainClient`](chain.h) — used by node to start & stop `Chain` clients. Added in [#14437](https://github.com/bitcoin/bitcoin/pull/14437).
* [`Node`](node.h) — used by GUI to start & stop bitcoin node. Added in [#10244](https://github.com/bitcoin/bitcoin/pull/10244).
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index 38888be8a3..161dd01ffe 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -6,11 +6,30 @@
#include <chain.h>
#include <chainparams.h>
+#include <interfaces/handler.h>
+#include <interfaces/wallet.h>
+#include <net.h>
+#include <node/coin.h>
+#include <policy/fees.h>
+#include <policy/policy.h>
+#include <policy/rbf.h>
+#include <policy/settings.h>
#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <protocol.h>
+#include <rpc/protocol.h>
+#include <rpc/server.h>
+#include <shutdown.h>
#include <sync.h>
+#include <threadsafety.h>
+#include <timedata.h>
+#include <txmempool.h>
+#include <ui_interface.h>
#include <uint256.h>
+#include <univalue.h>
#include <util/system.h>
#include <validation.h>
+#include <validationinterface.h>
#include <memory>
#include <utility>
@@ -18,11 +37,12 @@
namespace interfaces {
namespace {
-class LockImpl : public Chain::Lock
+class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
{
Optional<int> getHeight() override
{
- int height = ::chainActive.Height();
+ LockAssertion lock(::cs_main);
+ int height = ::ChainActive().Height();
if (height >= 0) {
return height;
}
@@ -30,8 +50,9 @@ class LockImpl : public Chain::Lock
}
Optional<int> getBlockHeight(const uint256& hash) override
{
+ LockAssertion lock(::cs_main);
CBlockIndex* block = LookupBlockIndex(hash);
- if (block && ::chainActive.Contains(block)) {
+ if (block && ::ChainActive().Contains(block)) {
return block->nHeight;
}
return nullopt;
@@ -44,54 +65,46 @@ class LockImpl : public Chain::Lock
}
uint256 getBlockHash(int height) override
{
- CBlockIndex* block = ::chainActive[height];
+ LockAssertion lock(::cs_main);
+ CBlockIndex* block = ::ChainActive()[height];
assert(block != nullptr);
return block->GetBlockHash();
}
int64_t getBlockTime(int height) override
{
- CBlockIndex* block = ::chainActive[height];
+ LockAssertion lock(::cs_main);
+ CBlockIndex* block = ::ChainActive()[height];
assert(block != nullptr);
return block->GetBlockTime();
}
int64_t getBlockMedianTimePast(int height) override
{
- CBlockIndex* block = ::chainActive[height];
+ LockAssertion lock(::cs_main);
+ CBlockIndex* block = ::ChainActive()[height];
assert(block != nullptr);
return block->GetMedianTimePast();
}
bool haveBlockOnDisk(int height) override
{
- CBlockIndex* block = ::chainActive[height];
+ LockAssertion lock(::cs_main);
+ CBlockIndex* block = ::ChainActive()[height];
return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0;
}
- Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) override
+ Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) override
{
- CBlockIndex* block = ::chainActive.FindEarliestAtLeast(time);
+ LockAssertion lock(::cs_main);
+ CBlockIndex* block = ::ChainActive().FindEarliestAtLeast(time, height);
if (block) {
if (hash) *hash = block->GetBlockHash();
return block->nHeight;
}
return nullopt;
}
- Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) override
- {
- // TODO: Could update CChain::FindEarliestAtLeast() to take a height
- // parameter and use it with std::lower_bound() to make this
- // implementation more efficient and allow combining
- // findFirstBlockWithTime and findFirstBlockWithTimeAndHeight into one
- // method.
- for (CBlockIndex* block = ::chainActive[height]; block; block = ::chainActive.Next(block)) {
- if (block->GetBlockTime() >= time) {
- return block->nHeight;
- }
- }
- return nullopt;
- }
Optional<int> findPruned(int start_height, Optional<int> stop_height) override
{
+ LockAssertion lock(::cs_main);
if (::fPruneMode) {
- CBlockIndex* block = stop_height ? ::chainActive[*stop_height] : ::chainActive.Tip();
+ CBlockIndex* block = stop_height ? ::ChainActive()[*stop_height] : ::ChainActive().Tip();
while (block && block->nHeight >= start_height) {
if ((block->nStatus & BLOCK_HAVE_DATA) == 0) {
return block->nHeight;
@@ -103,8 +116,9 @@ class LockImpl : public Chain::Lock
}
Optional<int> findFork(const uint256& hash, Optional<int>* height) override
{
+ LockAssertion lock(::cs_main);
const CBlockIndex* block = LookupBlockIndex(hash);
- const CBlockIndex* fork = block ? ::chainActive.FindFork(block) : nullptr;
+ const CBlockIndex* fork = block ? ::ChainActive().FindFork(block) : nullptr;
if (height) {
if (block) {
*height = block->nHeight;
@@ -117,26 +131,114 @@ class LockImpl : public Chain::Lock
}
return nullopt;
}
- bool isPotentialTip(const uint256& hash) override
+ CBlockLocator getTipLocator() override
{
- if (::chainActive.Tip()->GetBlockHash() == hash) return true;
- CBlockIndex* block = LookupBlockIndex(hash);
- return block && block->GetAncestor(::chainActive.Height()) == ::chainActive.Tip();
+ LockAssertion lock(::cs_main);
+ return ::ChainActive().GetLocator();
}
- CBlockLocator getLocator() override { return ::chainActive.GetLocator(); }
Optional<int> findLocatorFork(const CBlockLocator& locator) override
{
- LockAnnotation lock(::cs_main);
- if (CBlockIndex* fork = FindForkInGlobalIndex(::chainActive, locator)) {
+ LockAssertion lock(::cs_main);
+ if (CBlockIndex* fork = FindForkInGlobalIndex(::ChainActive(), locator)) {
return fork->nHeight;
}
return nullopt;
}
+ bool checkFinalTx(const CTransaction& tx) override
+ {
+ LockAssertion lock(::cs_main);
+ return CheckFinalTx(tx);
+ }
+ bool submitToMemoryPool(const CTransactionRef& tx, CAmount absurd_fee, CValidationState& state) override
+ {
+ LockAssertion lock(::cs_main);
+ return AcceptToMemoryPool(::mempool, state, tx, nullptr /* missing inputs */, nullptr /* txn replaced */,
+ false /* bypass limits */, absurd_fee);
+ }
+
+ using UniqueLock::UniqueLock;
};
-class LockingStateImpl : public LockImpl, public UniqueLock<CCriticalSection>
+class NotificationsHandlerImpl : public Handler, CValidationInterface
{
- using UniqueLock::UniqueLock;
+public:
+ explicit NotificationsHandlerImpl(Chain& chain, Chain::Notifications& notifications)
+ : m_chain(chain), m_notifications(&notifications)
+ {
+ RegisterValidationInterface(this);
+ }
+ ~NotificationsHandlerImpl() override { disconnect(); }
+ void disconnect() override
+ {
+ if (m_notifications) {
+ m_notifications = nullptr;
+ UnregisterValidationInterface(this);
+ }
+ }
+ void TransactionAddedToMempool(const CTransactionRef& tx) override
+ {
+ m_notifications->TransactionAddedToMempool(tx);
+ }
+ void TransactionRemovedFromMempool(const CTransactionRef& tx) override
+ {
+ m_notifications->TransactionRemovedFromMempool(tx);
+ }
+ void BlockConnected(const std::shared_ptr<const CBlock>& block,
+ const CBlockIndex* index,
+ const std::vector<CTransactionRef>& tx_conflicted) override
+ {
+ m_notifications->BlockConnected(*block, tx_conflicted);
+ }
+ void BlockDisconnected(const std::shared_ptr<const CBlock>& block) override
+ {
+ m_notifications->BlockDisconnected(*block);
+ }
+ void UpdatedBlockTip(const CBlockIndex* index, const CBlockIndex* fork_index, bool is_ibd) override
+ {
+ m_notifications->UpdatedBlockTip();
+ }
+ void ChainStateFlushed(const CBlockLocator& locator) override { m_notifications->ChainStateFlushed(locator); }
+ Chain& m_chain;
+ Chain::Notifications* m_notifications;
+};
+
+class RpcHandlerImpl : public Handler
+{
+public:
+ RpcHandlerImpl(const CRPCCommand& command) : m_command(command), m_wrapped_command(&command)
+ {
+ m_command.actor = [this](const JSONRPCRequest& request, UniValue& result, bool last_handler) {
+ if (!m_wrapped_command) return false;
+ try {
+ return m_wrapped_command->actor(request, result, last_handler);
+ } catch (const UniValue& e) {
+ // If this is not the last handler and a wallet not found
+ // exception was thrown, return false so the next handler can
+ // try to handle the request. Otherwise, reraise the exception.
+ if (!last_handler) {
+ const UniValue& code = e["code"];
+ if (code.isNum() && code.get_int() == RPC_WALLET_NOT_FOUND) {
+ return false;
+ }
+ }
+ throw;
+ }
+ };
+ ::tableRPC.appendCommand(m_command.name, &m_command);
+ }
+
+ void disconnect() override final
+ {
+ if (m_wrapped_command) {
+ m_wrapped_command = nullptr;
+ ::tableRPC.removeCommand(m_command.name, &m_command);
+ }
+ }
+
+ ~RpcHandlerImpl() override { disconnect(); }
+
+ CRPCCommand m_command;
+ const CRPCCommand* m_wrapped_command;
};
class ChainImpl : public Chain
@@ -144,13 +246,12 @@ class ChainImpl : public Chain
public:
std::unique_ptr<Chain::Lock> lock(bool try_lock) override
{
- auto result = MakeUnique<LockingStateImpl>(::cs_main, "cs_main", __FILE__, __LINE__, try_lock);
+ auto result = MakeUnique<LockImpl>(::cs_main, "cs_main", __FILE__, __LINE__, try_lock);
if (try_lock && result && !*result) return {};
// std::move necessary on some compilers due to conversion from
- // LockingStateImpl to Lock pointer
+ // LockImpl to Lock pointer
return std::move(result);
}
- std::unique_ptr<Chain::Lock> assumeLocked() override { return MakeUnique<LockImpl>(); }
bool findBlock(const uint256& hash, CBlock* block, int64_t* time, int64_t* time_max) override
{
CBlockIndex* index;
@@ -172,13 +273,111 @@ public:
}
return true;
}
+ void findCoins(std::map<COutPoint, Coin>& coins) override { return FindCoins(coins); }
double guessVerificationProgress(const uint256& block_hash) override
{
LOCK(cs_main);
return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash));
}
+ RBFTransactionState isRBFOptIn(const CTransaction& tx) override
+ {
+ LOCK(::mempool.cs);
+ return IsRBFOptIn(tx, ::mempool);
+ }
+ bool hasDescendantsInMempool(const uint256& txid) override
+ {
+ LOCK(::mempool.cs);
+ auto it = ::mempool.GetIter(txid);
+ return it && (*it)->GetCountWithDescendants() > 1;
+ }
+ void relayTransaction(const uint256& txid) override
+ {
+ CInv inv(MSG_TX, txid);
+ g_connman->ForEachNode([&inv](CNode* node) { node->PushInventory(inv); });
+ }
+ void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override
+ {
+ ::mempool.GetTransactionAncestry(txid, ancestors, descendants);
+ }
+ bool checkChainLimits(const CTransactionRef& tx) override
+ {
+ LockPoints lp;
+ CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp);
+ CTxMemPool::setEntries ancestors;
+ auto limit_ancestor_count = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
+ auto limit_ancestor_size = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;
+ auto limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
+ auto limit_descendant_size = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;
+ std::string unused_error_string;
+ LOCK(::mempool.cs);
+ return ::mempool.CalculateMemPoolAncestors(entry, ancestors, limit_ancestor_count, limit_ancestor_size,
+ limit_descendant_count, limit_descendant_size, unused_error_string);
+ }
+ CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override
+ {
+ return ::feeEstimator.estimateSmartFee(num_blocks, calc, conservative);
+ }
+ unsigned int estimateMaxBlocks() override
+ {
+ return ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
+ }
+ CFeeRate mempoolMinFee() override
+ {
+ return ::mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ }
+ CFeeRate relayMinFee() override { return ::minRelayTxFee; }
+ CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
+ CFeeRate relayDustFee() override { return ::dustRelayFee; }
+ bool havePruned() override
+ {
+ LOCK(cs_main);
+ return ::fHavePruned;
+ }
+ bool p2pEnabled() override { return g_connman != nullptr; }
+ bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); }
+ bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); }
+ bool shutdownRequested() override { return ShutdownRequested(); }
+ int64_t getAdjustedTime() override { return GetAdjustedTime(); }
+ void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); }
+ void initWarning(const std::string& message) override { InitWarning(message); }
+ void initError(const std::string& message) override { InitError(message); }
+ void loadWallet(std::unique_ptr<Wallet> wallet) override { ::uiInterface.LoadWallet(wallet); }
+ void showProgress(const std::string& title, int progress, bool resume_possible) override
+ {
+ ::uiInterface.ShowProgress(title, progress, resume_possible);
+ }
+ std::unique_ptr<Handler> handleNotifications(Notifications& notifications) override
+ {
+ return MakeUnique<NotificationsHandlerImpl>(*this, notifications);
+ }
+ void waitForNotificationsIfNewBlocksConnected(const uint256& old_tip) override
+ {
+ if (!old_tip.IsNull()) {
+ LOCK(::cs_main);
+ if (old_tip == ::ChainActive().Tip()->GetBlockHash()) return;
+ CBlockIndex* block = LookupBlockIndex(old_tip);
+ if (block && block->GetAncestor(::ChainActive().Height()) == ::ChainActive().Tip()) return;
+ }
+ SyncWithValidationInterfaceQueue();
+ }
+ std::unique_ptr<Handler> handleRpc(const CRPCCommand& command) override
+ {
+ return MakeUnique<RpcHandlerImpl>(command);
+ }
+ bool rpcEnableDeprecated(const std::string& method) override { return IsDeprecatedRPCEnabled(method); }
+ void rpcRunLater(const std::string& name, std::function<void()> fn, int64_t seconds) override
+ {
+ RPCRunLater(name, std::move(fn), seconds);
+ }
+ int rpcSerializationFlags() override { return RPCSerializationFlags(); }
+ void requestMempoolTransactions(Notifications& notifications) override
+ {
+ LOCK2(::cs_main, ::mempool.cs);
+ for (const CTxMemPoolEntry& entry : ::mempool.mapTx) {
+ notifications.TransactionAddedToMempool(entry.GetSharedTx());
+ }
+ }
};
-
} // namespace
std::unique_ptr<Chain> MakeChain() { return MakeUnique<ChainImpl>(); }
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 735d5b60df..e675defd47 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -1,25 +1,60 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 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_INTERFACES_CHAIN_H
#define BITCOIN_INTERFACES_CHAIN_H
-#include <optional.h>
+#include <optional.h> // For Optional and nullopt
+#include <primitives/transaction.h> // For CTransactionRef
#include <memory>
+#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
class CBlock;
+class CFeeRate;
+class CRPCCommand;
class CScheduler;
+class CValidationState;
+class Coin;
class uint256;
+enum class RBFTransactionState;
struct CBlockLocator;
+struct FeeCalculation;
namespace interfaces {
-//! Interface for giving wallet processes access to blockchain state.
+class Handler;
+class Wallet;
+
+//! Interface giving clients (wallet processes, maybe other analysis tools in
+//! the future) ability to access to the chain state, receive notifications,
+//! estimate fees, and submit transactions.
+//!
+//! TODO: Current chain methods are too low level, exposing too much of the
+//! internal workings of the bitcoin node, and not being very convenient to use.
+//! Chain methods should be cleaned up and simplified over time. Examples:
+//!
+//! * The Chain::lock() method, which lets clients delay chain tip updates
+//! should be removed when clients are able to respond to updates
+//! asynchronously
+//! (https://github.com/bitcoin/bitcoin/pull/10973#issuecomment-380101269).
+//!
+//! * The relayTransactions() and submitToMemoryPool() methods could be replaced
+//! with a higher-level broadcastTransaction method
+//! (https://github.com/bitcoin/bitcoin/pull/14978#issuecomment-459373984).
+//!
+//! * The initMessages() and loadWallet() methods which the wallet uses to send
+//! notifications to the GUI should go away when GUI and wallet can directly
+//! communicate with each other without going through the node
+//! (https://github.com/bitcoin/bitcoin/pull/15288#discussion_r253321096).
+//!
+//! * The handleRpc, registerRpcs, rpcEnableDeprecated methods and other RPC
+//! methods can go away if wallets listen for HTTP requests on their own
+//! ports instead of registering to handle requests on the node HTTP port.
class Chain
{
public:
@@ -64,55 +99,45 @@ public:
virtual bool haveBlockOnDisk(int height) = 0;
//! Return height of the first block in the chain with timestamp equal
- //! or greater than the given time, or nullopt if there is no block with
- //! a high enough timestamp. Also return the block hash as an optional
- //! output parameter (to avoid the cost of a second lookup in case this
- //! information is needed.)
- virtual Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) = 0;
-
- //! Return height of the first block in the chain with timestamp equal
//! or greater than the given time and height equal or greater than the
- //! given height, or nullopt if there is no such block.
- //!
- //! Calling this with height 0 is equivalent to calling
- //! findFirstBlockWithTime, but less efficient because it requires a
- //! linear instead of a binary search.
- virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) = 0;
+ //! given height, or nullopt if there is no block with a high enough
+ //! timestamp and height. Also return the block hash as an optional output parameter
+ //! (to avoid the cost of a second lookup in case this information is needed.)
+ virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) = 0;
//! Return height of last block in the specified range which is pruned, or
//! nullopt if no block in the range is pruned. Range is inclusive.
virtual Optional<int> findPruned(int start_height = 0, Optional<int> stop_height = nullopt) = 0;
- //! Return height of the highest block on the chain that is an ancestor
- //! of the specified block, or nullopt if no common ancestor is found.
+ //! Return height of the specified block if it is on the chain, otherwise
+ //! return the height of the highest block on chain that's an ancestor
+ //! of the specified block, or nullopt if there is no common ancestor.
//! Also return the height of the specified block as an optional output
//! parameter (to avoid the cost of a second hash lookup in case this
//! information is desired).
virtual Optional<int> findFork(const uint256& hash, Optional<int>* height) = 0;
- //! Return true if block hash points to the current chain tip, or to a
- //! possible descendant of the current chain tip that isn't currently
- //! connected.
- virtual bool isPotentialTip(const uint256& hash) = 0;
-
//! Get locator for the current chain tip.
- virtual CBlockLocator getLocator() = 0;
+ virtual CBlockLocator getTipLocator() = 0;
- //! Return height of the latest block common to locator and chain, which
- //! is guaranteed to be an ancestor of the block used to create the
- //! locator.
+ //! Return height of the highest block on chain in common with the locator,
+ //! which will either be the original block used to create the locator,
+ //! or one of its ancestors.
virtual Optional<int> findLocatorFork(const CBlockLocator& locator) = 0;
+
+ //! Check if transaction will be final given chain height current time.
+ virtual bool checkFinalTx(const CTransaction& tx) = 0;
+
+ //! Add transaction to memory pool if the transaction fee is below the
+ //! amount specified by absurd_fee. Returns false if the transaction
+ //! could not be added due to the fee or for another reason.
+ virtual bool submitToMemoryPool(const CTransactionRef& tx, CAmount absurd_fee, CValidationState& state) = 0;
};
//! Return Lock interface. Chain is locked when this is called, and
//! unlocked when the returned interface is freed.
virtual std::unique_ptr<Lock> lock(bool try_lock = false) = 0;
- //! Return Lock interface assuming chain is already locked. This
- //! method is temporary and is only used in a few places to avoid changing
- //! behavior while code is transitioned to use the Chain::Lock interface.
- virtual std::unique_ptr<Lock> assumeLocked() = 0;
-
//! Return whether node has the block and optionally return block metadata
//! or contents.
//!
@@ -124,9 +149,124 @@ public:
int64_t* time = nullptr,
int64_t* max_time = nullptr) = 0;
+ //! Look up unspent output information. Returns coins in the mempool and in
+ //! the current chain UTXO set. Iterates through all the keys in the map and
+ //! populates the values.
+ virtual void findCoins(std::map<COutPoint, Coin>& coins) = 0;
+
//! Estimate fraction of total transactions verified if blocks up to
//! the specified block hash are verified.
virtual double guessVerificationProgress(const uint256& block_hash) = 0;
+
+ //! Check if transaction is RBF opt in.
+ virtual RBFTransactionState isRBFOptIn(const CTransaction& tx) = 0;
+
+ //! Check if transaction has descendants in mempool.
+ virtual bool hasDescendantsInMempool(const uint256& txid) = 0;
+
+ //! Relay transaction.
+ virtual void relayTransaction(const uint256& txid) = 0;
+
+ //! Calculate mempool ancestor and descendant counts for the given transaction.
+ virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) = 0;
+
+ //! Check if transaction will pass the mempool's chain limits.
+ virtual bool checkChainLimits(const CTransactionRef& tx) = 0;
+
+ //! Estimate smart fee.
+ virtual CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc = nullptr) = 0;
+
+ //! Fee estimator max target.
+ virtual unsigned int estimateMaxBlocks() = 0;
+
+ //! Mempool minimum fee.
+ virtual CFeeRate mempoolMinFee() = 0;
+
+ //! Relay current minimum fee (from -minrelaytxfee and -incrementalrelayfee settings).
+ virtual CFeeRate relayMinFee() = 0;
+
+ //! Relay incremental fee setting (-incrementalrelayfee), reflecting cost of relay.
+ virtual CFeeRate relayIncrementalFee() = 0;
+
+ //! Relay dust fee setting (-dustrelayfee), reflecting lowest rate it's economical to spend.
+ virtual CFeeRate relayDustFee() = 0;
+
+ //! Check if any block has been pruned.
+ virtual bool havePruned() = 0;
+
+ //! Check if p2p enabled.
+ virtual bool p2pEnabled() = 0;
+
+ //! Check if the node is ready to broadcast transactions.
+ virtual bool isReadyToBroadcast() = 0;
+
+ //! Check if in IBD.
+ virtual bool isInitialBlockDownload() = 0;
+
+ //! Check if shutdown requested.
+ virtual bool shutdownRequested() = 0;
+
+ //! Get adjusted time.
+ virtual int64_t getAdjustedTime() = 0;
+
+ //! Send init message.
+ virtual void initMessage(const std::string& message) = 0;
+
+ //! Send init warning.
+ virtual void initWarning(const std::string& message) = 0;
+
+ //! Send init error.
+ virtual void initError(const std::string& message) = 0;
+
+ //! Send wallet load notification to the GUI.
+ virtual void loadWallet(std::unique_ptr<Wallet> wallet) = 0;
+
+ //! Send progress indicator.
+ virtual void showProgress(const std::string& title, int progress, bool resume_possible) = 0;
+
+ //! Chain notifications.
+ class Notifications
+ {
+ public:
+ virtual ~Notifications() {}
+ virtual void TransactionAddedToMempool(const CTransactionRef& tx) {}
+ virtual void TransactionRemovedFromMempool(const CTransactionRef& ptx) {}
+ virtual void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& tx_conflicted) {}
+ virtual void BlockDisconnected(const CBlock& block) {}
+ virtual void UpdatedBlockTip() {}
+ virtual void ChainStateFlushed(const CBlockLocator& locator) {}
+ };
+
+ //! Register handler for notifications.
+ virtual std::unique_ptr<Handler> handleNotifications(Notifications& notifications) = 0;
+
+ //! Wait for pending notifications to be processed unless block hash points to the current
+ //! chain tip, or to a possible descendant of the current chain tip that isn't currently
+ //! connected.
+ virtual void waitForNotificationsIfNewBlocksConnected(const uint256& old_tip) = 0;
+
+ //! Register handler for RPC. Command is not copied, so reference
+ //! needs to remain valid until Handler is disconnected.
+ virtual std::unique_ptr<Handler> handleRpc(const CRPCCommand& command) = 0;
+
+ //! Check if deprecated RPC is enabled.
+ virtual bool rpcEnableDeprecated(const std::string& method) = 0;
+
+ //! Run function after given number of seconds. Cancel any previous calls with same name.
+ virtual void rpcRunLater(const std::string& name, std::function<void()> fn, int64_t seconds) = 0;
+
+ //! Current RPC serialization flags.
+ virtual int rpcSerializationFlags() = 0;
+
+ //! Synchronously send TransactionAddedToMempool notifications about all
+ //! current mempool transactions to the specified handler and return after
+ //! the last one is sent. These notifications aren't coordinated with async
+ //! notifications sent by handleNotifications, so out of date async
+ //! notifications from handleNotifications can arrive during and after
+ //! synchronous notifications from requestMempoolTransactions. Clients need
+ //! to be prepared to handle this by ignoring notifications about unknown
+ //! removed transactions and already added new transactions.
+ virtual void requestMempoolTransactions(Notifications& notifications) = 0;
};
//! Interface to let node manage chain clients (wallets, or maybe tools for
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index c574f960e6..584d218dba 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -20,9 +20,9 @@
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <primitives/block.h>
#include <rpc/server.h>
-#include <scheduler.h>
#include <shutdown.h>
#include <sync.h>
#include <txmempool.h>
@@ -42,6 +42,7 @@ class CWallet;
fs::path GetWalletDir();
std::vector<fs::path> ListWalletDir();
std::vector<std::shared_ptr<CWallet>> GetWallets();
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning);
namespace interfaces {
@@ -67,7 +68,7 @@ public:
void initLogging() override { InitLogging(); }
void initParameterInteraction() override { InitParameterInteraction(); }
std::string getWarnings(const std::string& type) override { return GetWarnings(type); }
- uint32_t getLogCategories() override { return g_logger->GetCategoryMask(); }
+ uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
bool baseInitialize() override
{
return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() &&
@@ -176,13 +177,13 @@ public:
int getNumBlocks() override
{
LOCK(::cs_main);
- return ::chainActive.Height();
+ return ::ChainActive().Height();
}
int64_t getLastBlockTime() override
{
LOCK(::cs_main);
- if (::chainActive.Tip()) {
- return ::chainActive.Tip()->GetBlockTime();
+ if (::ChainActive().Tip()) {
+ return ::ChainActive().Tip()->GetBlockTime();
}
return Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network
}
@@ -191,11 +192,11 @@ public:
const CBlockIndex* tip;
{
LOCK(::cs_main);
- tip = ::chainActive.Tip();
+ tip = ::ChainActive().Tip();
}
return GuessVerificationProgress(Params().TxData(), tip);
}
- bool isInitialBlockDownload() override { return IsInitialBlockDownload(); }
+ bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); }
bool getReindex() override { return ::fReindex; }
bool getImporting() override { return ::fImporting; }
void setNetworkActive(bool active) override
@@ -205,7 +206,6 @@ public:
}
}
bool getNetworkActive() override { return g_connman && g_connman->GetNetworkActive(); }
- CAmount getMaxTxFee() override { return ::maxTxFee; }
CFeeRate estimateSmartFee(int num_blocks, bool conservative, int* returned_target = nullptr) override
{
FeeCalculation fee_calc;
@@ -252,6 +252,10 @@ public:
}
return wallets;
}
+ std::unique_ptr<Wallet> loadWallet(const std::string& name, std::string& error, std::string& warning) override
+ {
+ return MakeWallet(LoadWallet(*m_interfaces.chain, name, error, warning));
+ }
std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override
{
return MakeHandler(::uiInterface.InitMessage_connect(fn));
@@ -270,7 +274,7 @@ public:
}
std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override
{
- return MakeHandler(::uiInterface.LoadWallet_connect([fn](std::shared_ptr<CWallet> wallet) { fn(MakeWallet(wallet)); }));
+ return MakeHandler(::uiInterface.LoadWallet_connect([fn](std::unique_ptr<Wallet>& wallet) { fn(std::move(wallet)); }));
}
std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override
{
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 54c2d78338..1ccd2a31b7 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -159,9 +159,6 @@ public:
//! Get network active.
virtual bool getNetworkActive() = 0;
- //! Get max tx fee.
- virtual CAmount getMaxTxFee() = 0;
-
//! Estimate smart fee.
virtual CFeeRate estimateSmartFee(int num_blocks, bool conservative, int* returned_target = nullptr) = 0;
@@ -192,6 +189,11 @@ public:
//! Return interfaces for accessing wallets (if any).
virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
+ //! Attempts to load a wallet from file or directory.
+ //! The loaded wallet is also notified to handlers previously registered
+ //! with handleLoadWallet.
+ virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, std::string& error, std::string& warning) = 0;
+
//! Register handler for init messages.
using InitMessageFn = std::function<void(const std::string& message)>;
virtual std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) = 0;
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index a2cae2a7a7..240670cbe7 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -5,30 +5,23 @@
#include <interfaces/wallet.h>
#include <amount.h>
-#include <chain.h>
#include <consensus/validation.h>
-#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
-#include <net.h>
#include <policy/feerate.h>
#include <policy/fees.h>
-#include <policy/policy.h>
#include <primitives/transaction.h>
-#include <rpc/server.h>
-#include <scheduler.h>
#include <script/ismine.h>
#include <script/standard.h>
#include <support/allocators/secure.h>
#include <sync.h>
-#include <timedata.h>
#include <ui_interface.h>
#include <uint256.h>
#include <util/system.h>
-#include <validation.h>
#include <wallet/feebumper.h>
#include <wallet/fees.h>
#include <wallet/rpcwallet.h>
+#include <wallet/load.h>
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
@@ -47,8 +40,6 @@ public:
const CTransaction& get() override { return *m_tx; }
- int64_t getVirtualSize() override { return GetVirtualTransactionSize(*m_tx); }
-
bool commit(WalletValueMap value_map,
WalletOrderForm order_form,
std::string& reject_reason) override
@@ -56,7 +47,7 @@ public:
auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
CValidationState state;
- if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_key, g_connman.get(), state)) {
+ if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_key, state)) {
reject_reason = state.GetRejectReason();
return false;
}
@@ -99,17 +90,13 @@ WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, co
//! Construct wallet tx status struct.
WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx)
{
- LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
-
WalletTxStatus result;
- auto mi = ::mapBlockIndex.find(wtx.hashBlock);
- CBlockIndex* block = mi != ::mapBlockIndex.end() ? mi->second : nullptr;
- result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max());
+ result.block_height = locked_chain.getBlockHeight(wtx.hashBlock).get_value_or(std::numeric_limits<int>::max());
result.blocks_to_maturity = wtx.GetBlocksToMaturity(locked_chain);
result.depth_in_main_chain = wtx.GetDepthInMainChain(locked_chain);
result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime;
- result.is_final = CheckFinalTx(*wtx.tx);
+ result.is_final = locked_chain.checkFinalTx(*wtx.tx);
result.is_trusted = wtx.IsTrusted(locked_chain);
result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase();
@@ -135,55 +122,55 @@ WalletTxOut MakeWalletTxOut(interfaces::Chain::Lock& locked_chain,
class WalletImpl : public Wallet
{
public:
- explicit WalletImpl(const std::shared_ptr<CWallet>& wallet) : m_shared_wallet(wallet), m_wallet(*wallet.get()) {}
+ explicit WalletImpl(const std::shared_ptr<CWallet>& wallet) : m_wallet(wallet) {}
bool encryptWallet(const SecureString& wallet_passphrase) override
{
- return m_wallet.EncryptWallet(wallet_passphrase);
+ return m_wallet->EncryptWallet(wallet_passphrase);
}
- bool isCrypted() override { return m_wallet.IsCrypted(); }
- bool lock() override { return m_wallet.Lock(); }
- bool unlock(const SecureString& wallet_passphrase) override { return m_wallet.Unlock(wallet_passphrase); }
- bool isLocked() override { return m_wallet.IsLocked(); }
+ bool isCrypted() override { return m_wallet->IsCrypted(); }
+ bool lock() override { return m_wallet->Lock(); }
+ bool unlock(const SecureString& wallet_passphrase) override { return m_wallet->Unlock(wallet_passphrase); }
+ bool isLocked() override { return m_wallet->IsLocked(); }
bool changeWalletPassphrase(const SecureString& old_wallet_passphrase,
const SecureString& new_wallet_passphrase) override
{
- return m_wallet.ChangeWalletPassphrase(old_wallet_passphrase, new_wallet_passphrase);
+ return m_wallet->ChangeWalletPassphrase(old_wallet_passphrase, new_wallet_passphrase);
}
- void abortRescan() override { m_wallet.AbortRescan(); }
- bool backupWallet(const std::string& filename) override { return m_wallet.BackupWallet(filename); }
- std::string getWalletName() override { return m_wallet.GetName(); }
+ void abortRescan() override { m_wallet->AbortRescan(); }
+ bool backupWallet(const std::string& filename) override { return m_wallet->BackupWallet(filename); }
+ std::string getWalletName() override { return m_wallet->GetName(); }
bool getKeyFromPool(bool internal, CPubKey& pub_key) override
{
- return m_wallet.GetKeyFromPool(pub_key, internal);
+ return m_wallet->GetKeyFromPool(pub_key, internal);
}
- bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet.GetPubKey(address, pub_key); }
- bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet.GetKey(address, key); }
- bool isSpendable(const CTxDestination& dest) override { return IsMine(m_wallet, dest) & ISMINE_SPENDABLE; }
- bool haveWatchOnly() override { return m_wallet.HaveWatchOnly(); };
+ bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet->GetPubKey(address, pub_key); }
+ bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet->GetKey(address, key); }
+ bool isSpendable(const CTxDestination& dest) override { return IsMine(*m_wallet, dest) & ISMINE_SPENDABLE; }
+ bool haveWatchOnly() override { return m_wallet->HaveWatchOnly(); };
bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::string& purpose) override
{
- return m_wallet.SetAddressBook(dest, name, purpose);
+ return m_wallet->SetAddressBook(dest, name, purpose);
}
bool delAddressBook(const CTxDestination& dest) override
{
- return m_wallet.DelAddressBook(dest);
+ return m_wallet->DelAddressBook(dest);
}
bool getAddress(const CTxDestination& dest,
std::string* name,
isminetype* is_mine,
std::string* purpose) override
{
- LOCK(m_wallet.cs_wallet);
- auto it = m_wallet.mapAddressBook.find(dest);
- if (it == m_wallet.mapAddressBook.end()) {
+ LOCK(m_wallet->cs_wallet);
+ auto it = m_wallet->mapAddressBook.find(dest);
+ if (it == m_wallet->mapAddressBook.end()) {
return false;
}
if (name) {
*name = it->second.name;
}
if (is_mine) {
- *is_mine = IsMine(m_wallet, dest);
+ *is_mine = IsMine(*m_wallet, dest);
}
if (purpose) {
*purpose = it->second.purpose;
@@ -192,51 +179,52 @@ public:
}
std::vector<WalletAddress> getAddresses() override
{
- LOCK(m_wallet.cs_wallet);
+ LOCK(m_wallet->cs_wallet);
std::vector<WalletAddress> result;
- for (const auto& item : m_wallet.mapAddressBook) {
- result.emplace_back(item.first, IsMine(m_wallet, item.first), item.second.name, item.second.purpose);
+ for (const auto& item : m_wallet->mapAddressBook) {
+ result.emplace_back(item.first, IsMine(*m_wallet, item.first), item.second.name, item.second.purpose);
}
return result;
}
- void learnRelatedScripts(const CPubKey& key, OutputType type) override { m_wallet.LearnRelatedScripts(key, type); }
+ void learnRelatedScripts(const CPubKey& key, OutputType type) override { m_wallet->LearnRelatedScripts(key, type); }
bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override
{
- LOCK(m_wallet.cs_wallet);
- return m_wallet.AddDestData(dest, key, value);
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->AddDestData(dest, key, value);
}
bool eraseDestData(const CTxDestination& dest, const std::string& key) override
{
- LOCK(m_wallet.cs_wallet);
- return m_wallet.EraseDestData(dest, key);
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->EraseDestData(dest, key);
}
std::vector<std::string> getDestValues(const std::string& prefix) override
{
- return m_wallet.GetDestValues(prefix);
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->GetDestValues(prefix);
}
void lockCoin(const COutPoint& output) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.LockCoin(output);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->LockCoin(output);
}
void unlockCoin(const COutPoint& output) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.UnlockCoin(output);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->UnlockCoin(output);
}
bool isLockedCoin(const COutPoint& output) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.IsLockedCoin(output.hash, output.n);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->IsLockedCoin(output.hash, output.n);
}
void listLockedCoins(std::vector<COutPoint>& outputs) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.ListLockedCoins(outputs);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->ListLockedCoins(outputs);
}
std::unique_ptr<PendingWalletTx> createTransaction(const std::vector<CRecipient>& recipients,
const CCoinControl& coin_control,
@@ -245,25 +233,25 @@ public:
CAmount& fee,
std::string& fail_reason) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- auto pending = MakeUnique<PendingWalletTxImpl>(m_wallet);
- if (!m_wallet.CreateTransaction(*locked_chain, recipients, pending->m_tx, pending->m_key, fee, change_pos,
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ auto pending = MakeUnique<PendingWalletTxImpl>(*m_wallet);
+ if (!m_wallet->CreateTransaction(*locked_chain, recipients, pending->m_tx, pending->m_key, fee, change_pos,
fail_reason, coin_control, sign)) {
return {};
}
return std::move(pending);
}
- bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet.TransactionCanBeAbandoned(txid); }
+ bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet->TransactionCanBeAbandoned(txid); }
bool abandonTransaction(const uint256& txid) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.AbandonTransaction(*locked_chain, txid);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->AbandonTransaction(*locked_chain, txid);
}
bool transactionCanBeBumped(const uint256& txid) override
{
- return feebumper::TransactionCanBeBumped(&m_wallet, txid);
+ return feebumper::TransactionCanBeBumped(m_wallet.get(), txid);
}
bool createBumpTransaction(const uint256& txid,
const CCoinControl& coin_control,
@@ -273,46 +261,51 @@ public:
CAmount& new_fee,
CMutableTransaction& mtx) override
{
- return feebumper::CreateTransaction(&m_wallet, txid, coin_control, total_fee, errors, old_fee, new_fee, mtx) ==
- feebumper::Result::OK;
+ if (total_fee > 0) {
+ return feebumper::CreateTotalBumpTransaction(m_wallet.get(), txid, coin_control, total_fee, errors, old_fee, new_fee, mtx) ==
+ feebumper::Result::OK;
+ } else {
+ return feebumper::CreateRateBumpTransaction(m_wallet.get(), txid, coin_control, errors, old_fee, new_fee, mtx) ==
+ feebumper::Result::OK;
+ }
}
- bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(&m_wallet, mtx); }
+ bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(m_wallet.get(), mtx); }
bool commitBumpTransaction(const uint256& txid,
CMutableTransaction&& mtx,
std::vector<std::string>& errors,
uint256& bumped_txid) override
{
- return feebumper::CommitTransaction(&m_wallet, txid, std::move(mtx), errors, bumped_txid) ==
+ return feebumper::CommitTransaction(m_wallet.get(), txid, std::move(mtx), errors, bumped_txid) ==
feebumper::Result::OK;
}
CTransactionRef getTx(const uint256& txid) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- auto mi = m_wallet.mapWallet.find(txid);
- if (mi != m_wallet.mapWallet.end()) {
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ auto mi = m_wallet->mapWallet.find(txid);
+ if (mi != m_wallet->mapWallet.end()) {
return mi->second.tx;
}
return {};
}
WalletTx getWalletTx(const uint256& txid) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- auto mi = m_wallet.mapWallet.find(txid);
- if (mi != m_wallet.mapWallet.end()) {
- return MakeWalletTx(*locked_chain, m_wallet, mi->second);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ auto mi = m_wallet->mapWallet.find(txid);
+ if (mi != m_wallet->mapWallet.end()) {
+ return MakeWalletTx(*locked_chain, *m_wallet, mi->second);
}
return {};
}
std::vector<WalletTx> getWalletTxs() override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
std::vector<WalletTx> result;
- result.reserve(m_wallet.mapWallet.size());
- for (const auto& entry : m_wallet.mapWallet) {
- result.emplace_back(MakeWalletTx(*locked_chain, m_wallet, entry.second));
+ result.reserve(m_wallet->mapWallet.size());
+ for (const auto& entry : m_wallet->mapWallet) {
+ result.emplace_back(MakeWalletTx(*locked_chain, *m_wallet, entry.second));
}
return result;
}
@@ -321,16 +314,16 @@ public:
int& num_blocks,
int64_t& block_time) override
{
- auto locked_chain = m_wallet.chain().lock(true /* try_lock */);
+ auto locked_chain = m_wallet->chain().lock(true /* try_lock */);
if (!locked_chain) {
return false;
}
- TRY_LOCK(m_wallet.cs_wallet, locked_wallet);
+ TRY_LOCK(m_wallet->cs_wallet, locked_wallet);
if (!locked_wallet) {
return false;
}
- auto mi = m_wallet.mapWallet.find(txid);
- if (mi == m_wallet.mapWallet.end()) {
+ auto mi = m_wallet->mapWallet.find(txid);
+ if (mi == m_wallet->mapWallet.end()) {
return false;
}
if (Optional<int> height = locked_chain->getHeight()) {
@@ -349,37 +342,38 @@ public:
bool& in_mempool,
int& num_blocks) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- auto mi = m_wallet.mapWallet.find(txid);
- if (mi != m_wallet.mapWallet.end()) {
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ auto mi = m_wallet->mapWallet.find(txid);
+ if (mi != m_wallet->mapWallet.end()) {
num_blocks = locked_chain->getHeight().get_value_or(-1);
in_mempool = mi->second.InMempool();
order_form = mi->second.vOrderForm;
tx_status = MakeWalletTxStatus(*locked_chain, mi->second);
- return MakeWalletTx(*locked_chain, m_wallet, mi->second);
+ return MakeWalletTx(*locked_chain, *m_wallet, mi->second);
}
return {};
}
WalletBalances getBalances() override
{
+ const auto bal = m_wallet->GetBalance();
WalletBalances result;
- result.balance = m_wallet.GetBalance();
- result.unconfirmed_balance = m_wallet.GetUnconfirmedBalance();
- result.immature_balance = m_wallet.GetImmatureBalance();
- result.have_watch_only = m_wallet.HaveWatchOnly();
+ result.balance = bal.m_mine_trusted;
+ result.unconfirmed_balance = bal.m_mine_untrusted_pending;
+ result.immature_balance = bal.m_mine_immature;
+ result.have_watch_only = m_wallet->HaveWatchOnly();
if (result.have_watch_only) {
- result.watch_only_balance = m_wallet.GetBalance(ISMINE_WATCH_ONLY);
- result.unconfirmed_watch_only_balance = m_wallet.GetUnconfirmedWatchOnlyBalance();
- result.immature_watch_only_balance = m_wallet.GetImmatureWatchOnlyBalance();
+ result.watch_only_balance = bal.m_watchonly_trusted;
+ result.unconfirmed_watch_only_balance = bal.m_watchonly_untrusted_pending;
+ result.immature_watch_only_balance = bal.m_watchonly_immature;
}
return result;
}
bool tryGetBalances(WalletBalances& balances, int& num_blocks) override
{
- auto locked_chain = m_wallet.chain().lock(true /* try_lock */);
+ auto locked_chain = m_wallet->chain().lock(true /* try_lock */);
if (!locked_chain) return false;
- TRY_LOCK(m_wallet.cs_wallet, locked_wallet);
+ TRY_LOCK(m_wallet->cs_wallet, locked_wallet);
if (!locked_wallet) {
return false;
}
@@ -387,68 +381,68 @@ public:
num_blocks = locked_chain->getHeight().get_value_or(-1);
return true;
}
- CAmount getBalance() override { return m_wallet.GetBalance(); }
+ CAmount getBalance() override { return m_wallet->GetBalance().m_mine_trusted; }
CAmount getAvailableBalance(const CCoinControl& coin_control) override
{
- return m_wallet.GetAvailableBalance(&coin_control);
+ return m_wallet->GetAvailableBalance(&coin_control);
}
isminetype txinIsMine(const CTxIn& txin) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.IsMine(txin);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->IsMine(txin);
}
isminetype txoutIsMine(const CTxOut& txout) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.IsMine(txout);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->IsMine(txout);
}
CAmount getDebit(const CTxIn& txin, isminefilter filter) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.GetDebit(txin, filter);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->GetDebit(txin, filter);
}
CAmount getCredit(const CTxOut& txout, isminefilter filter) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.GetCredit(txout, filter);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->GetCredit(txout, filter);
}
CoinsList listCoins() override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
CoinsList result;
- for (const auto& entry : m_wallet.ListCoins(*locked_chain)) {
+ for (const auto& entry : m_wallet->ListCoins(*locked_chain)) {
auto& group = result[entry.first];
for (const auto& coin : entry.second) {
group.emplace_back(COutPoint(coin.tx->GetHash(), coin.i),
- MakeWalletTxOut(*locked_chain, m_wallet, *coin.tx, coin.i, coin.nDepth));
+ MakeWalletTxOut(*locked_chain, *m_wallet, *coin.tx, coin.i, coin.nDepth));
}
}
return result;
}
std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
std::vector<WalletTxOut> result;
result.reserve(outputs.size());
for (const auto& output : outputs) {
result.emplace_back();
- auto it = m_wallet.mapWallet.find(output.hash);
- if (it != m_wallet.mapWallet.end()) {
+ auto it = m_wallet->mapWallet.find(output.hash);
+ if (it != m_wallet->mapWallet.end()) {
int depth = it->second.GetDepthInMainChain(*locked_chain);
if (depth >= 0) {
- result.back() = MakeWalletTxOut(*locked_chain, m_wallet, it->second, output.n, depth);
+ result.back() = MakeWalletTxOut(*locked_chain, *m_wallet, it->second, output.n, depth);
}
}
}
return result;
}
- CAmount getRequiredFee(unsigned int tx_bytes) override { return GetRequiredFee(m_wallet, tx_bytes); }
+ CAmount getRequiredFee(unsigned int tx_bytes) override { return GetRequiredFee(*m_wallet, tx_bytes); }
CAmount getMinimumFee(unsigned int tx_bytes,
const CCoinControl& coin_control,
int* returned_target,
@@ -456,50 +450,55 @@ public:
{
FeeCalculation fee_calc;
CAmount result;
- result = GetMinimumFee(m_wallet, tx_bytes, coin_control, ::mempool, ::feeEstimator, &fee_calc);
+ result = GetMinimumFee(*m_wallet, tx_bytes, coin_control, &fee_calc);
if (returned_target) *returned_target = fee_calc.returnedTarget;
if (reason) *reason = fee_calc.reason;
return result;
}
- unsigned int getConfirmTarget() override { return m_wallet.m_confirm_target; }
- bool hdEnabled() override { return m_wallet.IsHDEnabled(); }
- bool IsWalletFlagSet(uint64_t flag) override { return m_wallet.IsWalletFlagSet(flag); }
- OutputType getDefaultAddressType() override { return m_wallet.m_default_address_type; }
- OutputType getDefaultChangeType() override { return m_wallet.m_default_change_type; }
+ unsigned int getConfirmTarget() override { return m_wallet->m_confirm_target; }
+ bool hdEnabled() override { return m_wallet->IsHDEnabled(); }
+ bool canGetAddresses() override { return m_wallet->CanGetAddresses(); }
+ bool IsWalletFlagSet(uint64_t flag) override { return m_wallet->IsWalletFlagSet(flag); }
+ OutputType getDefaultAddressType() override { return m_wallet->m_default_address_type; }
+ OutputType getDefaultChangeType() override { return m_wallet->m_default_change_type; }
+ CAmount getDefaultMaxTxFee() override { return m_wallet->m_default_max_tx_fee; }
+ void remove() override
+ {
+ RemoveWallet(m_wallet);
+ }
std::unique_ptr<Handler> handleUnload(UnloadFn fn) override
{
- return MakeHandler(m_wallet.NotifyUnload.connect(fn));
+ return MakeHandler(m_wallet->NotifyUnload.connect(fn));
}
std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override
{
- return MakeHandler(m_wallet.ShowProgress.connect(fn));
+ return MakeHandler(m_wallet->ShowProgress.connect(fn));
}
std::unique_ptr<Handler> handleStatusChanged(StatusChangedFn fn) override
{
- return MakeHandler(m_wallet.NotifyStatusChanged.connect([fn](CCryptoKeyStore*) { fn(); }));
+ return MakeHandler(m_wallet->NotifyStatusChanged.connect([fn](CCryptoKeyStore*) { fn(); }));
}
std::unique_ptr<Handler> handleAddressBookChanged(AddressBookChangedFn fn) override
{
- return MakeHandler(m_wallet.NotifyAddressBookChanged.connect(
+ return MakeHandler(m_wallet->NotifyAddressBookChanged.connect(
[fn](CWallet*, const CTxDestination& address, const std::string& label, bool is_mine,
const std::string& purpose, ChangeType status) { fn(address, label, is_mine, purpose, status); }));
}
std::unique_ptr<Handler> handleTransactionChanged(TransactionChangedFn fn) override
{
- return MakeHandler(m_wallet.NotifyTransactionChanged.connect(
+ return MakeHandler(m_wallet->NotifyTransactionChanged.connect(
[fn](CWallet*, const uint256& txid, ChangeType status) { fn(txid, status); }));
}
std::unique_ptr<Handler> handleWatchOnlyChanged(WatchOnlyChangedFn fn) override
{
- return MakeHandler(m_wallet.NotifyWatchonlyChanged.connect(fn));
+ return MakeHandler(m_wallet->NotifyWatchonlyChanged.connect(fn));
}
std::unique_ptr<Handler> handleCanGetAddressesChanged(CanGetAddressesChangedFn fn) override
{
- return MakeHandler(m_wallet.NotifyCanGetAddressesChanged.connect(fn));
+ return MakeHandler(m_wallet->NotifyCanGetAddressesChanged.connect(fn));
}
- std::shared_ptr<CWallet> m_shared_wallet;
- CWallet& m_wallet;
+ std::shared_ptr<CWallet> m_wallet;
};
class WalletClientImpl : public ChainClient
@@ -509,7 +508,7 @@ public:
: m_chain(chain), m_wallet_filenames(std::move(wallet_filenames))
{
}
- void registerRpcs() override { return RegisterWalletRPCCommands(::tableRPC); }
+ void registerRpcs() override { return RegisterWalletRPCCommands(m_chain, m_rpc_handlers); }
bool verify() override { return VerifyWallets(m_chain, m_wallet_filenames); }
bool load() override { return LoadWallets(m_chain, m_wallet_filenames); }
void start(CScheduler& scheduler) override { return StartWallets(scheduler); }
@@ -519,11 +518,12 @@ public:
Chain& m_chain;
std::vector<std::string> m_wallet_filenames;
+ std::vector<std::unique_ptr<Handler>> m_rpc_handlers;
};
} // namespace
-std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return MakeUnique<WalletImpl>(wallet); }
+std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return wallet ? MakeUnique<WalletImpl>(wallet) : nullptr; }
std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, std::vector<std::string> wallet_filenames)
{
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 72c64ded01..7096f54047 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -235,6 +235,9 @@ public:
// Return whether HD enabled.
virtual bool hdEnabled() = 0;
+ // Return whether the wallet is blank.
+ virtual bool canGetAddresses() = 0;
+
// check if a certain wallet flag is set.
virtual bool IsWalletFlagSet(uint64_t flag) = 0;
@@ -244,6 +247,12 @@ public:
// Get default change type.
virtual OutputType getDefaultChangeType() = 0;
+ //! Get max tx fee.
+ virtual CAmount getDefaultMaxTxFee() = 0;
+
+ // Remove wallet.
+ virtual void remove() = 0;
+
//! Register handler for unload message.
using UnloadFn = std::function<void()>;
virtual std::unique_ptr<Handler> handleUnload(UnloadFn fn) = 0;
@@ -286,9 +295,6 @@ public:
//! Get transaction data.
virtual const CTransaction& get() = 0;
- //! Get virtual transaction size.
- virtual int64_t getVirtualSize() = 0;
-
//! Send pending transaction and commit to wallet.
virtual bool commit(WalletValueMap value_map,
WalletOrderForm order_form,
diff --git a/src/key.cpp b/src/key.cpp
index 9d982fc44f..3ba21753a2 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -5,7 +5,6 @@
#include <key.h>
-#include <arith_uint256.h>
#include <crypto/common.h>
#include <crypto/hmac_sha512.h>
#include <random.h>
@@ -163,6 +162,12 @@ void CKey::MakeNewKey(bool fCompressedIn) {
fCompressed = fCompressedIn;
}
+bool CKey::Negate()
+{
+ assert(fValid);
+ return secp256k1_ec_privkey_negate(secp256k1_context_sign, keydata.data());
+}
+
CPrivKey CKey::GetPrivKey() const {
assert(fValid);
CPrivKey privkey;
diff --git a/src/key.h b/src/key.h
index 0f695c07b7..67e2cfc094 100644
--- a/src/key.h
+++ b/src/key.h
@@ -98,6 +98,9 @@ public:
//! Generate a new private key using a cryptographic PRNG.
void MakeNewKey(bool fCompressed);
+ //! Negate private key
+ bool Negate();
+
/**
* Convert the private key to a CPrivKey (serialized OpenSSL private key data).
* This is expensive.
diff --git a/src/key_io.cpp b/src/key_io.cpp
index d998089535..cd41a93549 100644
--- a/src/key_io.cpp
+++ b/src/key_io.cpp
@@ -26,14 +26,14 @@ private:
public:
explicit DestinationEncoder(const CChainParams& params) : m_params(params) {}
- std::string operator()(const CKeyID& id) const
+ std::string operator()(const PKHash& id) const
{
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
data.insert(data.end(), id.begin(), id.end());
return EncodeBase58Check(data);
}
- std::string operator()(const CScriptID& id) const
+ std::string operator()(const ScriptHash& id) const
{
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
data.insert(data.end(), id.begin(), id.end());
@@ -81,14 +81,14 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
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);
+ return PKHash(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);
+ return ScriptHash(hash);
}
}
data.clear();
@@ -142,7 +142,9 @@ CKey DecodeSecret(const std::string& str)
key.Set(data.begin() + privkey_prefix.size(), data.begin() + privkey_prefix.size() + 32, compressed);
}
}
- memory_cleanse(data.data(), data.size());
+ if (!data.empty()) {
+ memory_cleanse(data.data(), data.size());
+ }
return key;
}
diff --git a/src/keystore.cpp b/src/keystore.cpp
index 148979cf35..f6d19416ce 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -178,16 +178,17 @@ CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest)
{
// Only supports destinations which map to single public keys, i.e. P2PKH,
// P2WPKH, and P2SH-P2WPKH.
- if (auto id = boost::get<CKeyID>(&dest)) {
- return *id;
+ if (auto id = boost::get<PKHash>(&dest)) {
+ return CKeyID(*id);
}
if (auto witness_id = boost::get<WitnessV0KeyHash>(&dest)) {
return CKeyID(*witness_id);
}
- if (auto script_id = boost::get<CScriptID>(&dest)) {
+ if (auto script_hash = boost::get<ScriptHash>(&dest)) {
CScript script;
+ CScriptID script_id(*script_hash);
CTxDestination inner_dest;
- if (store.GetCScript(*script_id, script) && ExtractDestination(script, inner_dest)) {
+ if (store.GetCScript(script_id, script) && ExtractDestination(script, inner_dest)) {
if (auto inner_witness_id = boost::get<WitnessV0KeyHash>(&inner_dest)) {
return CKeyID(*inner_witness_id);
}
diff --git a/src/logging.cpp b/src/logging.cpp
index 77dc2d0939..3eda4995db 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -4,10 +4,15 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <logging.h>
+#include <util/threadnames.h>
#include <util/time.h>
+#include <mutex>
+
const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
+BCLog::Logger& LogInstance()
+{
/**
* NOTE: the logger instances is leaked on exit. This is ugly, but will be
* cleaned up by the OS/libc. Defining a logger as a global object doesn't work
@@ -17,11 +22,15 @@ const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
* access the logger. When the shutdown sequence is fully audited and tested,
* explicit destruction of these objects can be implemented by changing this
* from a raw pointer to a std::unique_ptr.
+ * Since the destructor is never called, the logger and all its members must
+ * have a trivial destructor.
*
* This method of initialization was originally introduced in
* ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c.
*/
-BCLog::Logger* const g_logger = new BCLog::Logger();
+ static BCLog::Logger* g_logger{new BCLog::Logger()};
+ return *g_logger;
+}
bool fLogIPs = DEFAULT_LOGIPS;
@@ -168,7 +177,7 @@ std::vector<CLogCategoryActive> ListActiveLogCategories()
return ret;
}
-std::string BCLog::Logger::LogTimestampStr(const std::string &str)
+std::string BCLog::Logger::LogTimestampStr(const std::string& str)
{
std::string strStamped;
@@ -190,21 +199,24 @@ std::string BCLog::Logger::LogTimestampStr(const std::string &str)
} else
strStamped = str;
- if (!str.empty() && str[str.size()-1] == '\n')
- m_started_new_line = true;
- else
- m_started_new_line = false;
-
return strStamped;
}
void BCLog::Logger::LogPrintStr(const std::string &str)
{
- std::string strTimestamped = LogTimestampStr(str);
+ std::string str_prefixed = str;
+
+ if (m_log_threadnames && m_started_new_line) {
+ str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] ");
+ }
+
+ str_prefixed = LogTimestampStr(str_prefixed);
+
+ m_started_new_line = !str.empty() && str[str.size()-1] == '\n';
if (m_print_to_console) {
// print to console
- fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
+ fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
fflush(stdout);
}
if (m_print_to_file) {
@@ -212,7 +224,7 @@ void BCLog::Logger::LogPrintStr(const std::string &str)
// buffer if we haven't opened the log yet
if (m_fileout == nullptr) {
- m_msgs_before_open.push_back(strTimestamped);
+ m_msgs_before_open.push_back(str_prefixed);
}
else
{
@@ -226,7 +238,7 @@ void BCLog::Logger::LogPrintStr(const std::string &str)
m_fileout = new_fileout;
}
}
- FileWriteStr(strTimestamped, m_fileout);
+ FileWriteStr(str_prefixed, m_fileout);
}
}
}
diff --git a/src/logging.h b/src/logging.h
index 0c8e7f5291..e399d4c307 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -19,6 +19,7 @@
static const bool DEFAULT_LOGTIMEMICROS = false;
static const bool DEFAULT_LOGIPS = false;
static const bool DEFAULT_LOGTIMESTAMPS = true;
+static const bool DEFAULT_LOGTHREADNAMES = false;
extern const char * const DEFAULT_DEBUGLOGFILE;
extern bool fLogIPs;
@@ -81,6 +82,7 @@ namespace BCLog {
bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS;
bool m_log_time_micros = DEFAULT_LOGTIMEMICROS;
+ bool m_log_threadnames = DEFAULT_LOGTHREADNAMES;
fs::path m_file_path;
std::atomic<bool> m_reopen_file{false};
@@ -108,12 +110,12 @@ namespace BCLog {
} // namespace BCLog
-extern BCLog::Logger* const g_logger;
+BCLog::Logger& LogInstance();
/** Return true if log accepts specified category */
static inline bool LogAcceptCategory(BCLog::LogFlags category)
{
- return g_logger->WillLogCategory(category);
+ return LogInstance().WillLogCategory(category);
}
/** Returns a string with the log categories. */
@@ -132,7 +134,7 @@ bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str);
template <typename... Args>
static inline void LogPrintf(const char* fmt, const Args&... args)
{
- if (g_logger->Enabled()) {
+ if (LogInstance().Enabled()) {
std::string log_msg;
try {
log_msg = tfm::format(fmt, args...);
@@ -140,7 +142,7 @@ static inline void LogPrintf(const char* fmt, const Args&... args)
/* Original format string will have newline so don't add one here */
log_msg = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + fmt;
}
- g_logger->LogPrintStr(log_msg);
+ LogInstance().LogPrintStr(log_msg);
}
}
diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp
index a54268d655..052aebbc80 100644
--- a/src/merkleblock.cpp
+++ b/src/merkleblock.cpp
@@ -7,7 +7,6 @@
#include <hash.h>
#include <consensus/consensus.h>
-#include <util/strencodings.h>
CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids)
diff --git a/src/miner.cpp b/src/miner.cpp
index ef48a86e32..015645c9c6 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -10,33 +10,23 @@
#include <chainparams.h>
#include <coins.h>
#include <consensus/consensus.h>
-#include <consensus/tx_verify.h>
#include <consensus/merkle.h>
+#include <consensus/tx_verify.h>
#include <consensus/validation.h>
-#include <hash.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/system.h>
#include <util/moneystr.h>
-#include <validationinterface.h>
+#include <util/system.h>
+#include <util/validation.h>
#include <algorithm>
#include <queue>
#include <utility>
-// Unconfirmed transactions in the memory pool often depend on other
-// transactions in the memory pool. When we select transactions from the
-// pool, we select by highest fee rate of a transaction combined with all
-// its ancestors.
-
-uint64_t nLastBlockTx = 0;
-uint64_t nLastBlockWeight = 0;
-
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
{
int64_t nOldTime = pblock->nTime;
@@ -95,6 +85,9 @@ void BlockAssembler::resetBlock()
nFees = 0;
}
+Optional<int64_t> BlockAssembler::m_last_block_num_txs{nullopt};
+Optional<int64_t> BlockAssembler::m_last_block_weight{nullopt};
+
std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
{
int64_t nTimeStart = GetTimeMicros();
@@ -113,7 +106,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
LOCK2(cs_main, mempool.cs);
- CBlockIndex* pindexPrev = chainActive.Tip();
+ CBlockIndex* pindexPrev = ::ChainActive().Tip();
assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
@@ -147,8 +140,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
int64_t nTime1 = GetTimeMicros();
- nLastBlockTx = nBlockTx;
- nLastBlockWeight = nBlockWeight;
+ m_last_block_num_txs = nBlockTx;
+ m_last_block_weight = nBlockWeight;
// Create coinbase transaction.
CMutableTransaction coinbaseTx;
diff --git a/src/miner.h b/src/miner.h
index 44c50b01ad..7c4c455072 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -1,17 +1,19 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 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_MINER_H
#define BITCOIN_MINER_H
+#include <optional.h>
#include <primitives/block.h>
#include <txmempool.h>
#include <validation.h>
-#include <stdint.h>
#include <memory>
+#include <stdint.h>
+
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
@@ -159,6 +161,9 @@ public:
/** Construct a new block template with coinbase to scriptPubKeyIn */
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
+ static Optional<int64_t> m_last_block_num_txs;
+ static Optional<int64_t> m_last_block_weight;
+
private:
// utility functions
/** Clear the block's state and prepare for assembling a new block */
diff --git a/src/net.cpp b/src/net.cpp
index be249b4466..3c6f5a05f3 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -58,17 +58,6 @@ static constexpr int DUMP_PEERS_INTERVAL = 15 * 60;
#define MSG_DONTWAIT 0
#endif
-// Fix for ancient MinGW versions, that don't have defined these in ws2tcpip.h.
-// Todo: Can be removed when our pull-tester is upgraded to a modern MinGW version.
-#ifdef WIN32
-#ifndef PROTECTION_LEVEL_UNRESTRICTED
-#define PROTECTION_LEVEL_UNRESTRICTED 10
-#endif
-#ifndef IPV6_PROTECTION_LEVEL
-#define IPV6_PROTECTION_LEVEL 23
-#endif
-#endif
-
/** Used to pass flags to the Bind() function */
enum BindFlags {
BF_NONE = 0,
@@ -90,14 +79,12 @@ static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // S
//
bool fDiscover = true;
bool fListen = true;
-bool fRelayTxes = true;
+bool g_relay_txes = !DEFAULT_BLOCKSONLY;
CCriticalSection cs_mapLocalHost;
std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost);
static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {};
std::string strSubVersion;
-limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);
-
void CConnman::AddOneShot(const std::string& strDest)
{
LOCK(cs_vOneShots);
@@ -174,8 +161,7 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices)
static int GetnScore(const CService& addr)
{
LOCK(cs_mapLocalHost);
- if (mapLocalHost.count(addr) == LOCAL_NONE)
- return 0;
+ if (mapLocalHost.count(addr) == 0) return 0;
return mapLocalHost[addr].nScore;
}
@@ -1779,9 +1765,15 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
addr = addrman.Select(fFeeler);
}
- // if we selected an invalid address, restart
- if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
+ // Require outbound connections, other than feelers, to be to distinct network groups
+ if (!fFeeler && setConnected.count(addr.GetGroup())) {
break;
+ }
+
+ // if we selected an invalid or local address, restart
+ if (!addr.IsValid() || IsLocal(addr)) {
+ break;
+ }
// If we didn't find an appropriate destination after trying 100 addresses fetched from addrman,
// stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates
@@ -2635,7 +2627,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
{
hSocket = hSocketIn;
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
- strSubVer = "";
hashContinue = uint256();
filterInventoryKnown.reset();
pfilter = MakeUnique<CBloomFilter>();
@@ -2656,40 +2647,6 @@ CNode::~CNode()
CloseSocket(hSocket);
}
-void CNode::AskFor(const CInv& inv)
-{
- if (mapAskFor.size() > MAPASKFOR_MAX_SZ || setAskFor.size() > SETASKFOR_MAX_SZ)
- return;
- // a peer may not have multiple non-responded queue positions for a single inv item
- if (!setAskFor.insert(inv.hash).second)
- return;
-
- // We're using mapAskFor as a priority queue,
- // the key is the earliest time the request can be sent
- int64_t nRequestTime;
- limitedmap<uint256, int64_t>::const_iterator it = mapAlreadyAskedFor.find(inv.hash);
- if (it != mapAlreadyAskedFor.end())
- nRequestTime = it->second;
- else
- nRequestTime = 0;
- LogPrint(BCLog::NET, "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, FormatISO8601Time(nRequestTime/1000000), id);
-
- // Make sure not to reuse time indexes to keep things in the same order
- int64_t nNow = GetTimeMicros() - 1000000;
- static int64_t nLastTime;
- ++nLastTime;
- nNow = std::max(nNow, nLastTime);
- nLastTime = nNow;
-
- // Each retry is 2 minutes after the last
- nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow);
- if (it != mapAlreadyAskedFor.end())
- mapAlreadyAskedFor.update(it, nRequestTime);
- else
- mapAlreadyAskedFor.insert(std::make_pair(inv.hash, nRequestTime));
- mapAskFor.insert(std::make_pair(nRequestTime, inv));
-}
-
bool CConnman::NodeFullyConnected(const CNode* pnode)
{
return pnode && pnode->fSuccessfullyConnected && !pnode->fDisconnect;
diff --git a/src/net.h b/src/net.h
index 3c1a107b43..37aaf1a63b 100644
--- a/src/net.h
+++ b/src/net.h
@@ -53,7 +53,7 @@ static const unsigned int MAX_LOCATOR_SZ = 101;
static const unsigned int MAX_ADDR_TO_SEND = 1000;
/** Maximum length of incoming protocol messages (no message over 4 MB is currently acceptable). */
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000;
-/** Maximum length of strSubVer in `version` message */
+/** Maximum length of the user agent string in `version` message */
static const unsigned int MAX_SUBVERSION_LENGTH = 256;
/** Maximum number of automatic outgoing nodes */
static const int MAX_OUTBOUND_CONNECTIONS = 8;
@@ -67,10 +67,6 @@ static const bool DEFAULT_UPNP = USE_UPNP;
#else
static const bool DEFAULT_UPNP = false;
#endif
-/** The maximum number of entries in mapAskFor */
-static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ;
-/** The maximum number of entries in setAskFor (larger due to getdata latency)*/
-static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ;
/** The maximum number of peer connections to maintain. */
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
/** The default for -maxuploadtarget. 0 = Unlimited */
@@ -178,7 +174,18 @@ public:
CConnman(uint64_t seed0, uint64_t seed1);
~CConnman();
bool Start(CScheduler& scheduler, const Options& options);
- void Stop();
+
+ // TODO: Remove NO_THREAD_SAFETY_ANALYSIS. Lock cs_vNodes before reading the variable vNodes.
+ //
+ // When removing NO_THREAD_SAFETY_ANALYSIS be aware of the following lock order requirements:
+ // * CheckForStaleTipAndEvictPeers locks cs_main before indirectly calling GetExtraOutboundCount
+ // which locks cs_vNodes.
+ // * ProcessMessage locks cs_main and g_cs_orphans before indirectly calling ForEachNode which
+ // locks cs_vNodes.
+ //
+ // Thus the implicit locking order requirement is: (1) cs_main, (2) g_cs_orphans, (3) cs_vNodes.
+ void Stop() NO_THREAD_SAFETY_ANALYSIS;
+
void Interrupt();
bool GetNetworkActive() const { return fNetworkActive; };
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
@@ -386,7 +393,7 @@ private:
CCriticalSection cs_vOneShots;
std::vector<std::string> vAddedNodes GUARDED_BY(cs_vAddedNodes);
CCriticalSection cs_vAddedNodes;
- std::vector<CNode*> vNodes;
+ std::vector<CNode*> vNodes GUARDED_BY(cs_vNodes);
std::list<CNode*> vNodesDisconnected;
mutable CCriticalSection cs_vNodes;
std::atomic<NodeId> nLastNodeId{0};
@@ -512,9 +519,7 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices)
extern bool fDiscover;
extern bool fListen;
-extern bool fRelayTxes;
-
-extern limitedmap<uint256, int64_t> mapAlreadyAskedFor;
+extern bool g_relay_txes;
/** Subversion as sent to the P2P network in `version` messages */
extern std::string strSubVersion;
@@ -645,12 +650,12 @@ public:
// Bind address of our side of the connection
const CAddress addrBind;
std::atomic<int> nVersion{0};
- // strSubVer is whatever byte array we read from the wire. However, this field is intended
- // to be printed out, displayed to humans in various forms and so on. So we sanitize it and
- // store the sanitized version in cleanSubVer. The original should be used when dealing with
- // the network or wire types and the cleaned string used when displayed or logged.
- std::string strSubVer GUARDED_BY(cs_SubVer), cleanSubVer GUARDED_BY(cs_SubVer);
- CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer
+ RecursiveMutex cs_SubVer;
+ /**
+ * cleanSubVer is a sanitized string of the user agent byte array we read
+ * from the wire. This cleaned string can safely be logged or displayed.
+ */
+ std::string cleanSubVer GUARDED_BY(cs_SubVer){};
bool m_prefer_evict{false}; // This peer is preferred for eviction.
bool fWhitelisted{false}; // This peer can bypass DoS banning.
bool fFeeler{false}; // If true this node is being used as a short lived feeler.
@@ -704,8 +709,6 @@ public:
// and in the order requested.
std::vector<uint256> vInventoryBlockToSend GUARDED_BY(cs_inventory);
CCriticalSection cs_inventory;
- std::set<uint256> setAskFor;
- std::multimap<int64_t, CInv> mapAskFor;
int64_t nNextInvSend{0};
// Used for headers announcements - unfiltered blocks to relay
std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory);
@@ -736,6 +739,8 @@ public:
CAmount lastSentFeeFilter{0};
int64_t nextSendTimeFeeFilter{0};
+ std::set<uint256> orphan_work_set;
+
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;
@@ -852,8 +857,6 @@ public:
vBlockHashesToAnnounce.push_back(hash);
}
- void AskFor(const CInv& inv);
-
void CloseSocketDisconnect();
void copyStats(CNodeStats &stats);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 62b7d4e966..e57706980a 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -25,10 +25,9 @@
#include <scheduler.h>
#include <tinyformat.h>
#include <txmempool.h>
-#include <ui_interface.h>
#include <util/system.h>
-#include <util/moneystr.h>
#include <util/strencodings.h>
+#include <util/validation.h>
#include <memory>
@@ -64,12 +63,30 @@ static constexpr 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 constexpr int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
+/** Maximum number of in-flight transactions from a peer */
+static constexpr int32_t MAX_PEER_TX_IN_FLIGHT = 100;
+/** Maximum number of announced transactions from a peer */
+static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ;
+/** How many microseconds to delay requesting transactions from inbound peers */
+static constexpr int64_t INBOUND_PEER_TX_DELAY = 2 * 1000000; // 2 seconds
+/** How long to wait (in microseconds) before downloading a transaction from an additional peer */
+static constexpr int64_t GETDATA_TX_INTERVAL = 60 * 1000000; // 1 minute
+/** Maximum delay (in microseconds) for transaction requests to avoid biasing some peers over others. */
+static constexpr int64_t MAX_GETDATA_RANDOM_DELAY = 2 * 1000000; // 2 seconds
+/** How long to wait (in microseconds) before expiring an in-flight getdata request to a peer */
+static constexpr int64_t TX_EXPIRY_INTERVAL = 10 * GETDATA_TX_INTERVAL;
+static_assert(INBOUND_PEER_TX_DELAY >= MAX_GETDATA_RANDOM_DELAY,
+"To preserve security, MAX_GETDATA_RANDOM_DELAY should not exceed INBOUND_PEER_DELAY");
+/** Limit to avoid sending big packets. Not used in processing incoming GETDATA for compatibility */
+static const unsigned int MAX_GETDATA_SZ = 1000;
+
struct COrphanTx {
// When modifying, adapt the copy of this definition in tests/DoS_tests.
CTransactionRef tx;
NodeId fromPeer;
int64_t nTimeExpire;
+ size_t list_pos;
};
CCriticalSection g_cs_orphans;
std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
@@ -159,8 +176,6 @@ namespace {
/** Expiration-time ordered list of (expire time, relay map entry) pairs. */
std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration GUARDED_BY(cs_main);
- std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
-
struct IteratorComparator
{
template<typename I>
@@ -171,6 +186,8 @@ namespace {
};
std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
+ std::vector<std::map<uint256, COrphanTx>::iterator> g_orphan_list GUARDED_BY(g_cs_orphans); //! For random eviction
+
static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
} // namespace
@@ -274,7 +291,79 @@ struct CNodeState {
//! Time of last new block announcement
int64_t m_last_block_announcement;
- CNodeState(CAddress addrIn, std::string addrNameIn) : address(addrIn), name(addrNameIn) {
+ /*
+ * State associated with transaction download.
+ *
+ * Tx download algorithm:
+ *
+ * When inv comes in, queue up (process_time, txid) inside the peer's
+ * CNodeState (m_tx_process_time) as long as m_tx_announced for the peer
+ * isn't too big (MAX_PEER_TX_ANNOUNCEMENTS).
+ *
+ * The process_time for a transaction is set to nNow for outbound peers,
+ * nNow + 2 seconds for inbound peers. This is the time at which we'll
+ * consider trying to request the transaction from the peer in
+ * SendMessages(). The delay for inbound peers is to allow outbound peers
+ * a chance to announce before we request from inbound peers, to prevent
+ * an adversary from using inbound connections to blind us to a
+ * transaction (InvBlock).
+ *
+ * When we call SendMessages() for a given peer,
+ * we will loop over the transactions in m_tx_process_time, looking
+ * at the transactions whose process_time <= nNow. We'll request each
+ * such transaction that we don't have already and that hasn't been
+ * requested from another peer recently, up until we hit the
+ * MAX_PEER_TX_IN_FLIGHT limit for the peer. Then we'll update
+ * g_already_asked_for for each requested txid, storing the time of the
+ * GETDATA request. We use g_already_asked_for to coordinate transaction
+ * requests amongst our peers.
+ *
+ * For transactions that we still need but we have already recently
+ * requested from some other peer, we'll reinsert (process_time, txid)
+ * back into the peer's m_tx_process_time at the point in the future at
+ * which the most recent GETDATA request would time out (ie
+ * GETDATA_TX_INTERVAL + the request time stored in g_already_asked_for).
+ * We add an additional delay for inbound peers, again to prefer
+ * attempting download from outbound peers first.
+ * We also add an extra small random delay up to 2 seconds
+ * to avoid biasing some peers over others. (e.g., due to fixed ordering
+ * of peer processing in ThreadMessageHandler).
+ *
+ * When we receive a transaction from a peer, we remove the txid from the
+ * peer's m_tx_in_flight set and from their recently announced set
+ * (m_tx_announced). We also clear g_already_asked_for for that entry, so
+ * that if somehow the transaction is not accepted but also not added to
+ * the reject filter, then we will eventually redownload from other
+ * peers.
+ */
+ struct TxDownloadState {
+ /* Track when to attempt download of announced transactions (process
+ * time in micros -> txid)
+ */
+ std::multimap<int64_t, uint256> m_tx_process_time;
+
+ //! Store all the transactions a peer has recently announced
+ std::set<uint256> m_tx_announced;
+
+ //! Store transactions which were requested by us, with timestamp
+ std::map<uint256, int64_t> m_tx_in_flight;
+
+ //! Periodically check for stuck getdata requests
+ int64_t m_check_expiry_timer{0};
+ };
+
+ TxDownloadState m_tx_download;
+
+ //! Whether this peer is an inbound connection
+ bool m_is_inbound;
+
+ //! Whether this peer is a manual connection
+ bool m_is_manual_connection;
+
+ CNodeState(CAddress addrIn, std::string addrNameIn, bool is_inbound, bool is_manual) :
+ address(addrIn), name(std::move(addrNameIn)), m_is_inbound(is_inbound),
+ m_is_manual_connection (is_manual)
+ {
fCurrentlyConnected = false;
nMisbehavior = 0;
fShouldBan = false;
@@ -301,6 +390,9 @@ struct CNodeState {
}
};
+// Keeps track of the time (in microseconds) when transactions were requested last time
+limitedmap<uint256, int64_t> g_already_asked_for GUARDED_BY(cs_main)(MAX_INV_SZ);
+
/** Map maintaining per-node state. */
static std::map<NodeId, CNodeState> mapNodeState GUARDED_BY(cs_main);
@@ -333,7 +425,7 @@ static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
CAddress addrMe = CAddress(CService(), nLocalNodeServices);
connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
- nonce, strSubVersion, nNodeStartingHeight, ::fRelayTxes));
+ nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes));
if (fLogIPs) {
LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid);
@@ -490,7 +582,7 @@ static bool TipMayBeStale(const Consensus::Params &consensusParams) EXCLUSIVE_LO
static bool CanDirectFetch(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- return chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
+ return ::ChainActive().Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
}
static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -516,7 +608,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
// 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 < nMinimumChainWork) {
+ if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < ::ChainActive().Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
// This peer has nothing interesting.
return;
}
@@ -524,7 +616,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
if (state->pindexLastCommonBlock == nullptr) {
// Bootstrap quickly by guessing a parent of our best tip is the forking point.
// Guessing wrong in either direction is not a problem.
- state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())];
+ state->pindexLastCommonBlock = ::ChainActive()[std::min(state->pindexBestKnownBlock->nHeight, ::ChainActive().Height())];
}
// If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor
@@ -566,7 +658,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
// We wouldn't download this block or its descendants from this peer.
return;
}
- if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) {
+ if (pindex->nStatus & BLOCK_HAVE_DATA || ::ChainActive().Contains(pindex)) {
if (pindex->HaveTxsDownloaded())
state->pindexLastCommonBlock = pindex;
} else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
@@ -591,6 +683,68 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
}
}
+void EraseTxRequest(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ g_already_asked_for.erase(txid);
+}
+
+int64_t GetTxRequestTime(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ auto it = g_already_asked_for.find(txid);
+ if (it != g_already_asked_for.end()) {
+ return it->second;
+ }
+ return 0;
+}
+
+void UpdateTxRequestTime(const uint256& txid, int64_t request_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ auto it = g_already_asked_for.find(txid);
+ if (it == g_already_asked_for.end()) {
+ g_already_asked_for.insert(std::make_pair(txid, request_time));
+ } else {
+ g_already_asked_for.update(it, request_time);
+ }
+}
+
+int64_t CalculateTxGetDataTime(const uint256& txid, int64_t current_time, bool use_inbound_delay) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ int64_t process_time;
+ int64_t last_request_time = GetTxRequestTime(txid);
+ // First time requesting this tx
+ if (last_request_time == 0) {
+ process_time = current_time;
+ } else {
+ // Randomize the delay to avoid biasing some peers over others (such as due to
+ // fixed ordering of peer processing in ThreadMessageHandler)
+ process_time = last_request_time + GETDATA_TX_INTERVAL + GetRand(MAX_GETDATA_RANDOM_DELAY);
+ }
+
+ // We delay processing announcements from inbound peers
+ if (use_inbound_delay) process_time += INBOUND_PEER_TX_DELAY;
+
+ return process_time;
+}
+
+void RequestTx(CNodeState* state, const uint256& txid, int64_t nNow) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ CNodeState::TxDownloadState& peer_download_state = state->m_tx_download;
+ if (peer_download_state.m_tx_announced.size() >= MAX_PEER_TX_ANNOUNCEMENTS ||
+ peer_download_state.m_tx_process_time.size() >= MAX_PEER_TX_ANNOUNCEMENTS ||
+ peer_download_state.m_tx_announced.count(txid)) {
+ // Too many queued announcements from this peer, or we already have
+ // this announcement
+ return;
+ }
+ peer_download_state.m_tx_announced.insert(txid);
+
+ // Calculate the time to try requesting this transaction. Use
+ // fPreferredDownload as a proxy for outbound peers.
+ int64_t process_time = CalculateTxGetDataTime(txid, nNow, !state->fPreferredDownload);
+
+ peer_download_state.m_tx_process_time.emplace(process_time, txid);
+}
+
} // namespace
// This function is used for testing the stale tip eviction logic, see
@@ -615,7 +769,7 @@ void PeerLogicValidation::InitializeNode(CNode *pnode) {
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)));
+ mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName), pnode->fInbound, pnode->m_manual_connection));
}
if(!pnode->fInbound)
PushNodeVersion(pnode, connman, GetTime());
@@ -707,8 +861,9 @@ bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRE
return false;
}
- auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME});
+ auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME, g_orphan_list.size()});
assert(ret.second);
+ g_orphan_list.push_back(ret.first);
for (const CTxIn& txin : tx->vin) {
mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first);
}
@@ -734,6 +889,18 @@ int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
if (itPrev->second.empty())
mapOrphanTransactionsByPrev.erase(itPrev);
}
+
+ size_t old_pos = it->second.list_pos;
+ assert(g_orphan_list[old_pos] == it);
+ if (old_pos + 1 != g_orphan_list.size()) {
+ // Unless we're deleting the last entry in g_orphan_list, move the last
+ // entry to the position we're deleting.
+ auto it_last = g_orphan_list.back();
+ g_orphan_list[old_pos] = it_last;
+ it_last->second.list_pos = old_pos;
+ }
+ g_orphan_list.pop_back();
+
mapOrphanTransactions.erase(it);
return 1;
}
@@ -784,11 +951,8 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
while (mapOrphanTransactions.size() > nMaxOrphans)
{
// Evict a random orphan:
- uint256 randomhash = rng.rand256();
- std::map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
- if (it == mapOrphanTransactions.end())
- it = mapOrphanTransactions.begin();
- EraseOrphanTx(it->first);
+ size_t randompos = rng.randrange(g_orphan_list.size());
+ EraseOrphanTx(g_orphan_list[randompos]->first);
++nEvicted;
}
return nEvicted;
@@ -817,6 +981,90 @@ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIV
LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d)%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed);
}
+/**
+ * Returns true if the given validation state result may result in a peer
+ * banning/disconnecting us. We use this to determine which unaccepted
+ * transactions from a whitelisted peer that we can safely relay.
+ */
+static bool TxRelayMayResultInDisconnect(const CValidationState& state)
+{
+ assert(IsTransactionReason(state.GetReason()));
+ return state.GetReason() == ValidationInvalidReason::CONSENSUS;
+}
+
+/**
+ * Potentially ban a node based on the contents of a CValidationState object
+ *
+ * @param[in] via_compact_block: this bool is passed in because net_processing should
+ * punish peers differently depending on whether the data was provided in a compact
+ * block message or not. If the compact block had a valid header, but contained invalid
+ * txs, the peer should not be punished. See BIP 152.
+ *
+ * @return Returns true if the peer was punished (probably disconnected)
+ *
+ * Changes here may need to be reflected in TxRelayMayResultInDisconnect().
+ */
+static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool via_compact_block, const std::string& message = "") {
+ switch (state.GetReason()) {
+ case ValidationInvalidReason::NONE:
+ break;
+ // The node is providing invalid data:
+ case ValidationInvalidReason::CONSENSUS:
+ case ValidationInvalidReason::BLOCK_MUTATED:
+ if (!via_compact_block) {
+ LOCK(cs_main);
+ Misbehaving(nodeid, 100, message);
+ return true;
+ }
+ break;
+ case ValidationInvalidReason::CACHED_INVALID:
+ {
+ LOCK(cs_main);
+ CNodeState *node_state = State(nodeid);
+ if (node_state == nullptr) {
+ break;
+ }
+
+ // Ban outbound (but not inbound) peers if on an invalid chain.
+ // Exempt HB compact block peers and manual connections.
+ if (!via_compact_block && !node_state->m_is_inbound && !node_state->m_is_manual_connection) {
+ Misbehaving(nodeid, 100, message);
+ return true;
+ }
+ break;
+ }
+ case ValidationInvalidReason::BLOCK_INVALID_HEADER:
+ case ValidationInvalidReason::BLOCK_CHECKPOINT:
+ case ValidationInvalidReason::BLOCK_INVALID_PREV:
+ {
+ LOCK(cs_main);
+ Misbehaving(nodeid, 100, message);
+ }
+ return true;
+ // Conflicting (but not necessarily invalid) data or different policy:
+ case ValidationInvalidReason::BLOCK_MISSING_PREV:
+ {
+ // TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
+ LOCK(cs_main);
+ Misbehaving(nodeid, 10, message);
+ }
+ return true;
+ case ValidationInvalidReason::RECENT_CONSENSUS_CHANGE:
+ case ValidationInvalidReason::BLOCK_TIME_FUTURE:
+ case ValidationInvalidReason::TX_NOT_STANDARD:
+ case ValidationInvalidReason::TX_MISSING_INPUTS:
+ case ValidationInvalidReason::TX_PREMATURE_SPEND:
+ case ValidationInvalidReason::TX_WITNESS_MUTATED:
+ case ValidationInvalidReason::TX_CONFLICT:
+ case ValidationInvalidReason::TX_MEMPOOL_POLICY:
+ break;
+ }
+ if (message != "") {
+ LogPrint(BCLog::NET, "peer=%d: %s\n", nodeid, message);
+ }
+ return false;
+}
+
@@ -836,7 +1084,7 @@ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIV
static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
- if (chainActive.Contains(pindex)) return true;
+ 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);
@@ -948,7 +1196,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
/**
* Update our best height and announce any block hashes which weren't previously
- * in chainActive to our peers.
+ * in ::ChainActive() to our peers.
*/
void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
const int nNewHeight = pindexNew->nHeight;
@@ -978,8 +1226,6 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
});
connman->WakeMessageHandler();
}
-
- nTimeBestReceived = GetTime();
}
/**
@@ -992,14 +1238,12 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
const uint256 hash(block.GetHash());
std::map<uint256, std::pair<NodeId, bool>>::iterator it = mapBlockSource.find(hash);
- int nDoS = 0;
- if (state.IsInvalid(nDoS)) {
+ if (state.IsInvalid()) {
// Don't send reject message with code 0 or an internal reject code.
if (it != mapBlockSource.end() && State(it->second.first) && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) {
CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash};
State(it->second.first)->rejects.push_back(reject);
- if (nDoS > 0 && it->second.second)
- Misbehaving(it->second.first, nDoS);
+ MaybePunishNode(/*nodeid=*/ it->second.first, state, /*via_compact_block=*/ !it->second.second);
}
}
// Check that:
@@ -1009,7 +1253,7 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
// the tip yet so we have no way to check this directly here. Instead we
// just check that there are currently no other blocks in flight.
else if (state.IsValid() &&
- !IsInitialBlockDownload() &&
+ !::ChainstateActive().IsInitialBlockDownload() &&
mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) {
if (it != mapBlockSource.end()) {
MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, connman);
@@ -1033,13 +1277,13 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
case MSG_WITNESS_TX:
{
assert(recentRejects);
- if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip)
+ if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip)
{
// If the chain tip has changed previously rejected transactions
// might be now valid, e.g. due to a nLockTime'd tx becoming valid,
// or a double-spend. Reset the rejects filter and give those
// txs a second chance.
- hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash();
+ hashRecentRejectsChainTip = ::ChainActive().Tip()->GetBlockHash();
recentRejects->reset();
}
@@ -1164,7 +1408,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
}
// 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 - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
+ (((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (::ChainActive().Tip()->nHeight - pindex->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());
@@ -1234,7 +1478,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
// instead we respond with the full, non-compact block.
bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- if (CanDirectFetch(consensusParams) && pindex->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) {
+ if (CanDirectFetch(consensusParams) && pindex->nHeight >= ::ChainActive().Height() - MAX_CMPCTBLOCK_DEPTH) {
if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
} else {
@@ -1254,7 +1498,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
// and we want it right after the last block so they don't
// wait for other stuff first.
std::vector<CInv> vInv;
- vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash()));
+ vInv.push_back(CInv(MSG_BLOCK, ::ChainActive().Tip()->GetBlockHash()));
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv));
pfrom->hashContinue.SetNull();
}
@@ -1315,12 +1559,19 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
if (!vNotFound.empty()) {
// Let the peer know that we didn't find what it asked for, so it doesn't
- // have to wait around forever. Currently only SPV clients actually care
- // about this message: it's needed when they are recursively walking the
- // dependencies of relevant unconfirmed transactions. SPV clients want to
- // do that because they want to know about (and store and rebroadcast and
- // risk analyze) the dependencies of transactions relevant to them, without
- // having to download the entire memory pool.
+ // have to wait around forever.
+ // SPV clients care about this message: it's needed when they are
+ // recursively walking the dependencies of relevant unconfirmed
+ // transactions. SPV clients want to do that because they want to know
+ // about (and store and rebroadcast and risk analyze) the dependencies
+ // of transactions relevant to them, without having to download the
+ // entire memory pool.
+ // Also, other nodes can use these messages to automatically request a
+ // transaction from some other peer that annnounced it, and stop
+ // waiting for us to respond.
+ // In normal operation, we often send NOTFOUND messages for parents of
+ // transactions that we relay; if a peer is missing a parent, they may
+ // assume we have them and request the parents from us.
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
}
}
@@ -1349,7 +1600,7 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
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)
+bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool via_compact_block)
{
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
size_t nCount = headers.size();
@@ -1375,7 +1626,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
// nUnconnectingHeaders gets reset back to 0.
if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
nodestate->nUnconnectingHeaders++;
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
+ 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(),
@@ -1411,48 +1662,8 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
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, "invalid header received");
- } else {
- LogPrint(BCLog::NET, "peer=%d: invalid header received\n", pfrom->GetId());
- }
- if (punish_duplicate_invalid && LookupBlockIndex(first_invalid_header.GetHash())) {
- // 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;
- }
+ if (state.IsInvalid()) {
+ MaybePunishNode(pfrom->GetId(), state, via_compact_block, "invalid header received");
return false;
}
}
@@ -1472,26 +1683,26 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
// 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) {
+ 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
+ // 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()));
+ 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) {
+ 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) {
+ 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)) {
@@ -1504,7 +1715,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
// 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)) {
+ if (!::ChainActive().Contains(pindexWalk)) {
LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
pindexLast->GetBlockHash().ToString(),
pindexLast->nHeight);
@@ -1537,7 +1748,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
}
// 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) {
+ if (::ChainstateActive().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) {
@@ -1545,7 +1756,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
// 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
+ // ::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.
@@ -1559,7 +1770,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
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) {
+ 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;
@@ -1570,6 +1781,68 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
return true;
}
+void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_set, std::list<CTransactionRef>& removed_txn) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans)
+{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(g_cs_orphans);
+ std::set<NodeId> setMisbehaving;
+ bool done = false;
+ while (!done && !orphan_work_set.empty()) {
+ const uint256 orphanHash = *orphan_work_set.begin();
+ orphan_work_set.erase(orphan_work_set.begin());
+
+ auto orphan_it = mapOrphanTransactions.find(orphanHash);
+ if (orphan_it == mapOrphanTransactions.end()) continue;
+
+ const CTransactionRef porphanTx = orphan_it->second.tx;
+ const CTransaction& orphanTx = *porphanTx;
+ NodeId fromPeer = orphan_it->second.fromPeer;
+ bool fMissingInputs2 = false;
+ // Use a new CValidationState because orphans come from different peers (and we call
+ // MaybePunishNode based on the source peer from the orphan map, not based on the peer
+ // that relayed the previous transaction).
+ CValidationState orphan_state;
+
+ if (setMisbehaving.count(fromPeer)) continue;
+ if (AcceptToMemoryPool(mempool, orphan_state, porphanTx, &fMissingInputs2, &removed_txn, 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++) {
+ auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(orphanHash, i));
+ if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
+ for (const auto& elem : it_by_prev->second) {
+ orphan_work_set.insert(elem->first);
+ }
+ }
+ }
+ EraseOrphanTx(orphanHash);
+ done = true;
+ } else if (!fMissingInputs2) {
+ if (orphan_state.IsInvalid()) {
+ // Punish peer that gave us an invalid orphan tx
+ if (MaybePunishNode(fromPeer, orphan_state, /*via_compact_block*/ false)) {
+ setMisbehaving.insert(fromPeer);
+ }
+ LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
+ }
+ // Has inputs but not accepted to mempool
+ // Probably non-standard or insufficient fee
+ LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
+ assert(IsTransactionReason(orphan_state.GetReason()));
+ if (!orphanTx.HasWitness() && orphan_state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) {
+ // Do not use rejection cache for witness transactions or
+ // witness-stripped transactions, as they can have been malleated.
+ // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
+ assert(recentRejects);
+ recentRejects->insert(orphanHash);
+ }
+ EraseOrphanTx(orphanHash);
+ done = true;
+ }
+ mempool.check(pcoinsTip.get());
+ }
+}
+
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 enable_bip61)
{
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
@@ -1639,7 +1912,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
ServiceFlags nServices;
int nVersion;
int nSendVersion;
- std::string strSubVer;
std::string cleanSubVer;
int nStartingHeight = -1;
bool fRelay = true;
@@ -1676,6 +1948,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!vRecv.empty())
vRecv >> addrFrom >> nNonce;
if (!vRecv.empty()) {
+ std::string strSubVer;
vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH);
cleanSubVer = SanitizeString(strSubVer);
}
@@ -1707,7 +1980,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->SetAddrLocal(addrMe);
{
LOCK(pfrom->cs_SubVer);
- pfrom->strSubVer = strSubVer;
pfrom->cleanSubVer = cleanSubVer;
}
pfrom->nStartingHeight = nStartingHeight;
@@ -1742,7 +2014,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!pfrom->fInbound)
{
// Advertise our address
- if (fListen && !IsInitialBlockDownload())
+ if (fListen && !::ChainstateActive().IsInitialBlockDownload())
{
CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices());
FastRandomContext insecure_rand;
@@ -1879,6 +2151,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60;
pfrom->AddAddressKnown(addr);
+ if (g_banman->IsBanned(addr)) continue; // Do not process banned addresses beyond remembering we received them
bool fReachable = IsReachable(addr);
if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
{
@@ -1936,7 +2209,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return false;
}
- bool fBlocksOnly = !fRelayTxes;
+ bool fBlocksOnly = !g_relay_txes;
// Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))
@@ -1945,6 +2218,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(cs_main);
uint32_t nFetchFlags = GetFetchFlags(pfrom);
+ int64_t nNow = GetTimeMicros();
for (CInv &inv : vInv)
{
@@ -1966,7 +2240,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());
}
}
@@ -1975,8 +2249,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->AddInventoryKnown(inv);
if (fBlocksOnly) {
LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->GetId());
- } else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload()) {
- pfrom->AskFor(inv);
+ } else if (!fAlreadyHave && !fImporting && !fReindex && !::ChainstateActive().IsInitialBlockDownload()) {
+ RequestTx(State(pfrom->GetId()), inv.hash, nNow);
}
}
}
@@ -2037,14 +2311,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(cs_main);
// Find the last block the caller has in the main chain
- const CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator);
+ const CBlockIndex* pindex = FindForkInGlobalIndex(::ChainActive(), locator);
// Send the rest of the chain
if (pindex)
- pindex = chainActive.Next(pindex);
+ pindex = ::ChainActive().Next(pindex);
int nLimit = 500;
LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->GetId());
- for (; pindex; pindex = chainActive.Next(pindex))
+ for (; pindex; pindex = ::ChainActive().Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
{
@@ -2054,7 +2328,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// If pruning, don't inv blocks unless we have on disk and are likely to still have
// for some reasonable time window (1 hour) that block relay might require.
const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / chainparams.GetConsensus().nPowTargetSpacing;
- if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= chainActive.Tip()->nHeight - nPrunedBlocksLikelyToHave))
+ if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= ::ChainActive().Tip()->nHeight - nPrunedBlocksLikelyToHave))
{
LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
break;
@@ -2096,7 +2370,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
- if (pindex->nHeight < chainActive.Height() - MAX_BLOCKTXN_DEPTH) {
+ if (pindex->nHeight < ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) {
// If an older block is requested (should never happen in practice,
// but can happen in tests) send a block response instead of a
// blocktxn response. Sending a full block response instead of a
@@ -2133,7 +2407,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
LOCK(cs_main);
- if (IsInitialBlockDownload() && !pfrom->fWhitelisted) {
+ if (::ChainstateActive().IsInitialBlockDownload() && !pfrom->fWhitelisted) {
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->GetId());
return true;
}
@@ -2156,23 +2430,23 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
else
{
// Find the last block the caller has in the main chain
- pindex = FindForkInGlobalIndex(chainActive, locator);
+ pindex = FindForkInGlobalIndex(::ChainActive(), locator);
if (pindex)
- pindex = chainActive.Next(pindex);
+ pindex = ::ChainActive().Next(pindex);
}
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
std::vector<CBlock> vHeaders;
int nLimit = MAX_HEADERS_RESULTS;
LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->GetId());
- for (; pindex; pindex = chainActive.Next(pindex))
+ for (; pindex; pindex = ::ChainActive().Next(pindex))
{
vHeaders.push_back(pindex->GetBlockHeader());
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}
- // pindex can be nullptr either if we sent chainActive.Tip() OR
- // if our peer has chainActive.Tip() (and thus we are sending an empty
+ // pindex can be nullptr either if we sent ::ChainActive().Tip() OR
+ // if our peer has ::ChainActive().Tip() (and thus we are sending an empty
// headers message). In both cases it's safe to update
// pindexBestHeaderSent to be our tip.
//
@@ -2183,7 +2457,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// without the new block. By resetting the BestHeaderSent, we ensure we
// will re-announce the new block via headers (or compact blocks again)
// in the SendMessages logic.
- nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip();
+ nodestate->pindexBestHeaderSent = pindex ? pindex : ::ChainActive().Tip();
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
return true;
}
@@ -2191,14 +2465,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (strCommand == NetMsgType::TX) {
// Stop processing the transaction early if
// We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
- if (!fRelayTxes && (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
+ if (!g_relay_txes && (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
{
LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId());
return true;
}
- std::deque<COutPoint> vWorkQueue;
- std::vector<uint256> vEraseQueue;
CTransactionRef ptx;
vRecv >> ptx;
const CTransaction& tx = *ptx;
@@ -2211,8 +2483,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
bool fMissingInputs = false;
CValidationState state;
- pfrom->setAskFor.erase(inv.hash);
- mapAlreadyAskedFor.erase(inv.hash);
+ CNodeState* nodestate = State(pfrom->GetId());
+ nodestate->m_tx_download.m_tx_announced.erase(inv.hash);
+ nodestate->m_tx_download.m_tx_in_flight.erase(inv.hash);
+ EraseTxRequest(inv.hash);
std::list<CTransactionRef> lRemovedTxn;
@@ -2221,7 +2495,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
mempool.check(pcoinsTip.get());
RelayTransaction(tx, connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
- vWorkQueue.emplace_back(inv.hash, i);
+ auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(inv.hash, i));
+ if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
+ for (const auto& elem : it_by_prev->second) {
+ pfrom->orphan_work_set.insert(elem->first);
+ }
+ }
}
pfrom->nLastTXTime = GetTime();
@@ -2232,65 +2511,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
mempool.size(), mempool.DynamicMemoryUsage() / 1000);
// Recursively process any orphan transactions that depended on this one
- std::set<NodeId> setMisbehaving;
- while (!vWorkQueue.empty()) {
- auto itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue.front());
- vWorkQueue.pop_front();
- if (itByPrev == mapOrphanTransactionsByPrev.end())
- continue;
- for (auto mi = itByPrev->second.begin();
- mi != itByPrev->second.end();
- ++mi)
- {
- const CTransactionRef& porphanTx = (*mi)->second.tx;
- const CTransaction& orphanTx = *porphanTx;
- const uint256& orphanHash = orphanTx.GetHash();
- NodeId fromPeer = (*mi)->second.fromPeer;
- bool fMissingInputs2 = false;
- // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
- // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
- // anyone relaying LegitTxX banned)
- CValidationState stateDummy;
-
-
- if (setMisbehaving.count(fromPeer))
- continue;
- if (AcceptToMemoryPool(mempool, stateDummy, 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++) {
- vWorkQueue.emplace_back(orphanHash, i);
- }
- vEraseQueue.push_back(orphanHash);
- }
- else if (!fMissingInputs2)
- {
- int nDos = 0;
- if (stateDummy.IsInvalid(nDos) && nDos > 0)
- {
- // Punish peer that gave us an invalid orphan tx
- Misbehaving(fromPeer, nDos);
- setMisbehaving.insert(fromPeer);
- LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
- }
- // Has inputs but not accepted to mempool
- // Probably non-standard or insufficient fee
- LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
- vEraseQueue.push_back(orphanHash);
- if (!orphanTx.HasWitness() && !stateDummy.CorruptionPossible()) {
- // Do not use rejection cache for witness transactions or
- // witness-stripped transactions, as they can have been malleated.
- // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
- assert(recentRejects);
- recentRejects->insert(orphanHash);
- }
- }
- mempool.check(pcoinsTip.get());
- }
- }
-
- for (const uint256& hash : vEraseQueue)
- EraseOrphanTx(hash);
+ ProcessOrphanTx(connman, pfrom->orphan_work_set, lRemovedTxn);
}
else if (fMissingInputs)
{
@@ -2303,10 +2524,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
if (!fRejectedParents) {
uint32_t nFetchFlags = GetFetchFlags(pfrom);
+ int64_t nNow = GetTimeMicros();
+
for (const CTxIn& txin : tx.vin) {
CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash);
pfrom->AddInventoryKnown(_inv);
- if (!AlreadyHave(_inv)) pfrom->AskFor(_inv);
+ if (!AlreadyHave(_inv)) RequestTx(State(pfrom->GetId()), _inv.hash, nNow);
}
AddOrphanTx(ptx, pfrom->GetId());
@@ -2323,7 +2546,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
recentRejects->insert(tx.GetHash());
}
} else {
- if (!tx.HasWitness() && !state.CorruptionPossible()) {
+ assert(IsTransactionReason(state.GetReason()));
+ if (!tx.HasWitness() && state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) {
// Do not use rejection cache for witness transactions or
// witness-stripped transactions, as they can have been malleated.
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
@@ -2342,15 +2566,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// to policy, allowing the node to function as a gateway for
// nodes hidden behind it.
//
- // Never relay transactions that we would assign a non-zero DoS
- // score for, as we expect peers to do the same with us in that
- // case.
- int nDoS = 0;
- if (!state.IsInvalid(nDoS) || nDoS == 0) {
+ // Never relay transactions that might result in being
+ // disconnected (or banned).
+ if (state.IsInvalid() && TxRelayMayResultInDisconnect(state)) {
+ LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->GetId(), FormatStateMessage(state));
+ } else {
LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->GetId());
RelayTransaction(tx, connman);
- } else {
- LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->GetId(), FormatStateMessage(state));
}
}
}
@@ -2375,8 +2597,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// peer simply for relaying a tx that our recentRejects has caught,
// regardless of false positives.
- int nDoS = 0;
- if (state.IsInvalid(nDoS))
+ if (state.IsInvalid())
{
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
pfrom->GetId(),
@@ -2385,15 +2606,19 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
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);
- }
+ MaybePunishNode(pfrom->GetId(), state, /*via_compact_block*/ false);
}
return true;
}
- if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
+ if (strCommand == NetMsgType::CMPCTBLOCK)
{
+ // Ignore cmpctblock received while importing
+ if (fImporting || fReindex) {
+ LogPrint(BCLog::NET, "Unexpected cmpctblock message received from peer %d\n", pfrom->GetId());
+ return true;
+ }
+
CBlockHeaderAndShortTxIDs cmpctblock;
vRecv >> cmpctblock;
@@ -2404,8 +2629,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
// 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()));
+ if (!::ChainstateActive().IsInitialBlockDownload())
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
return true;
}
@@ -2417,14 +2642,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
const CBlockIndex *pindex = nullptr;
CValidationState state;
if (!ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
- int nDoS;
- if (state.IsInvalid(nDoS)) {
- if (nDoS > 0) {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), nDoS, strprintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId()));
- } else {
- LogPrint(BCLog::NET, "Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId());
- }
+ if (state.IsInvalid()) {
+ MaybePunishNode(pfrom->GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
return true;
}
}
@@ -2455,7 +2674,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// 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) {
+ if (received_new_header && pindex->nChainWork > ::ChainActive().Tip()->nChainWork) {
nodestate->m_last_block_announcement = GetTime();
}
@@ -2465,7 +2684,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
return true;
- if (pindex->nChainWork <= chainActive.Tip()->nChainWork || // We know something better
+ if (pindex->nChainWork <= ::ChainActive().Tip()->nChainWork || // We know something better
pindex->nTx != 0) { // We had this block at some point, but pruned it
if (fAlreadyInFlight) {
// We requested this block for some reason, but our mempool will probably be useless
@@ -2489,7 +2708,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// We want to be a bit conservative just to be extra careful about DoS
// possibilities in compact block processing...
- if (pindex->nHeight <= chainActive.Height() + 2) {
+ if (pindex->nHeight <= ::ChainActive().Height() + 2) {
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
(fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
@@ -2574,7 +2793,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// 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);
+ return ProcessHeadersMessage(pfrom, connman, {cmpctblock.header}, chainparams, /*via_compact_block=*/true);
}
if (fBlockReconstructed) {
@@ -2613,8 +2832,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
- if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex) // Ignore blocks received while importing
+ if (strCommand == NetMsgType::BLOCKTXN)
{
+ // Ignore blocktxn received while importing
+ if (fImporting || fReindex) {
+ LogPrint(BCLog::NET, "Unexpected blocktxn message received from peer %d\n", pfrom->GetId());
+ return true;
+ }
+
BlockTransactions resp;
vRecv >> resp;
@@ -2688,8 +2913,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
- if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing
+ if (strCommand == NetMsgType::HEADERS)
{
+ // Ignore headers received while importing
+ if (fImporting || fReindex) {
+ LogPrint(BCLog::NET, "Unexpected headers message received from peer %d\n", pfrom->GetId());
+ return true;
+ }
+
std::vector<CBlockHeader> headers;
// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
@@ -2705,16 +2936,17 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
}
- // 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);
+ return ProcessHeadersMessage(pfrom, connman, headers, chainparams, /*via_compact_block=*/false);
}
- if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
+ if (strCommand == NetMsgType::BLOCK)
{
+ // Ignore block received while importing
+ if (fImporting || fReindex) {
+ LogPrint(BCLog::NET, "Unexpected block message received from peer %d\n", pfrom->GetId());
+ return true;
+ }
+
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
vRecv >> *pblock;
@@ -2764,8 +2996,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->vAddrToSend.clear();
std::vector<CAddress> vAddr = connman->GetAddresses();
FastRandomContext insecure_rand;
- for (const CAddress &addr : vAddr)
- pfrom->PushAddress(addr, insecure_rand);
+ for (const CAddress &addr : vAddr) {
+ if (!g_banman->IsBanned(addr)) {
+ pfrom->PushAddress(addr, insecure_rand);
+ }
+ }
return true;
}
@@ -2933,8 +3168,27 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
if (strCommand == NetMsgType::NOTFOUND) {
- // We do not care about the NOTFOUND message, but logging an Unknown Command
- // message would be undesirable as we transmit it ourselves.
+ // Remove the NOTFOUND transactions from the peer
+ LOCK(cs_main);
+ CNodeState *state = State(pfrom->GetId());
+ std::vector<CInv> vInv;
+ vRecv >> vInv;
+ if (vInv.size() <= MAX_PEER_TX_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ for (CInv &inv : vInv) {
+ if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX) {
+ // If we receive a NOTFOUND message for a txid we requested, erase
+ // it from our data structures for this peer.
+ auto in_flight_it = state->m_tx_download.m_tx_in_flight.find(inv.hash);
+ if (in_flight_it == state->m_tx_download.m_tx_in_flight.end()) {
+ // Skip any further work if this is a spurious NOTFOUND
+ // message.
+ continue;
+ }
+ state->m_tx_download.m_tx_in_flight.erase(in_flight_it);
+ state->m_tx_download.m_tx_announced.erase(inv.hash);
+ }
+ }
+ }
return true;
}
@@ -2993,11 +3247,21 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
if (!pfrom->vRecvGetData.empty())
ProcessGetData(pfrom, chainparams, connman, interruptMsgProc);
+ if (!pfrom->orphan_work_set.empty()) {
+ std::list<CTransactionRef> removed_txn;
+ LOCK2(cs_main, g_cs_orphans);
+ ProcessOrphanTx(connman, pfrom->orphan_work_set, removed_txn);
+ for (const CTransactionRef& removedTx : removed_txn) {
+ AddToCompactExtraTransactions(removedTx);
+ }
+ }
+
if (pfrom->fDisconnect)
return false;
// this maintains the order of responses
if (!pfrom->vRecvGetData.empty()) return true;
+ if (!pfrom->orphan_work_set.empty()) return true;
// Don't bother if send buffer is too full to respond anyway
if (pfrom->fPauseSend)
@@ -3063,23 +3327,22 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
if (m_enable_bip61) {
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"))
- {
+ if (strstr(e.what(), "end of data")) {
// Allow exceptions from under-length message on vRecv
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"))
- {
+ } else if (strstr(e.what(), "size too large")) {
// Allow exceptions from over-long size
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()"))
- {
+ } else if (strstr(e.what(), "non-canonical ReadCompactSize()")) {
// Allow exceptions from non-canonical encoding
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else
- {
+ } else if (strstr(e.what(), "Superfluous witness record")) {
+ // Allow exceptions from illegal witness encoding
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
+ } else if (strstr(e.what(), "Unknown transaction optional data")) {
+ // Allow exceptions from unknown witness encoding
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
+ } else {
PrintExceptionContinue(&e, "ProcessMessages()");
}
}
@@ -3113,7 +3376,7 @@ void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
// 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.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;
@@ -3125,7 +3388,7 @@ void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
// 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_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
@@ -3138,7 +3401,7 @@ void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
} 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()));
+ 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
@@ -3300,7 +3563,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Address refresh broadcast
int64_t nNow = GetTimeMicros();
- if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
+ if (!::ChainstateActive().IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
AdvertiseLocal(pto);
pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}
@@ -3336,7 +3599,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Start block sync
if (pindexBestHeader == nullptr)
- pindexBestHeader = chainActive.Tip();
+ pindexBestHeader = ::ChainActive().Tip();
bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do.
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
@@ -3355,18 +3618,10 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
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()));
}
}
- // Resend wallet transactions that haven't gotten in a block yet
- // Except during reindex, importing and IBD, when old wallet
- // transactions become unconfirmed and spams other nodes.
- if (!fReindex && !fImporting && !IsInitialBlockDownload())
- {
- GetMainSignals().Broadcast(nTimeBestReceived, connman);
- }
-
//
// Try sending block announcements via headers
//
@@ -3390,11 +3645,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
bool fFoundStartingHeader = false;
// Try to find first header that our peer doesn't have, and
// then send all headers past that one. If we come across any
- // headers that aren't on chainActive, give up.
+ // headers that aren't on ::ChainActive(), give up.
for (const uint256 &hash : pto->vBlockHashesToAnnounce) {
const CBlockIndex* pindex = LookupBlockIndex(hash);
assert(pindex);
- if (chainActive[pindex->nHeight] != pindex) {
+ if (::ChainActive()[pindex->nHeight] != pindex) {
// Bail out if we reorged away from this block
fRevertToInv = true;
break;
@@ -3490,9 +3745,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Warn if we're announcing a block that is not on the main chain.
// This should be very rare and could be optimized out.
// Just log for now.
- if (chainActive[pindex->nHeight] != pindex) {
+ if (::ChainActive()[pindex->nHeight] != pindex) {
LogPrint(BCLog::NET, "Announcing block %s not on main chain (tip=%s)\n",
- hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString());
+ hashToAnnounce.ToString(), ::ChainActive().Tip()->GetBlockHash().ToString());
}
// If the peer's chain has this block, don't inv it back.
@@ -3709,7 +3964,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Message: getdata (blocks)
//
std::vector<CInv> vGetData;
- if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !::ChainstateActive().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
std::vector<const CBlockIndex*> vToDownload;
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
@@ -3731,24 +3986,63 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
//
// Message: getdata (non-blocks)
//
- while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
- {
- const CInv& inv = (*pto->mapAskFor.begin()).second;
- if (!AlreadyHave(inv))
- {
- LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), pto->GetId());
- vGetData.push_back(inv);
- if (vGetData.size() >= 1000)
- {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
- vGetData.clear();
+
+ // For robustness, expire old requests after a long timeout, so that
+ // we can resume downloading transactions from a peer even if they
+ // were unresponsive in the past.
+ // Eventually we should consider disconnecting peers, but this is
+ // conservative.
+ if (state.m_tx_download.m_check_expiry_timer <= nNow) {
+ for (auto it=state.m_tx_download.m_tx_in_flight.begin(); it != state.m_tx_download.m_tx_in_flight.end();) {
+ if (it->second <= nNow - TX_EXPIRY_INTERVAL) {
+ LogPrint(BCLog::NET, "timeout of inflight tx %s from peer=%d\n", it->first.ToString(), pto->GetId());
+ state.m_tx_download.m_tx_announced.erase(it->first);
+ state.m_tx_download.m_tx_in_flight.erase(it++);
+ } else {
+ ++it;
+ }
+ }
+ // On average, we do this check every TX_EXPIRY_INTERVAL. Randomize
+ // so that we're not doing this for all peers at the same time.
+ state.m_tx_download.m_check_expiry_timer = nNow + TX_EXPIRY_INTERVAL/2 + GetRand(TX_EXPIRY_INTERVAL);
+ }
+
+ auto& tx_process_time = state.m_tx_download.m_tx_process_time;
+ while (!tx_process_time.empty() && tx_process_time.begin()->first <= nNow && state.m_tx_download.m_tx_in_flight.size() < MAX_PEER_TX_IN_FLIGHT) {
+ const uint256 txid = tx_process_time.begin()->second;
+ // Erase this entry from tx_process_time (it may be added back for
+ // processing at a later time, see below)
+ tx_process_time.erase(tx_process_time.begin());
+ CInv inv(MSG_TX | GetFetchFlags(pto), txid);
+ if (!AlreadyHave(inv)) {
+ // If this transaction was last requested more than 1 minute ago,
+ // then request.
+ int64_t last_request_time = GetTxRequestTime(inv.hash);
+ if (last_request_time <= nNow - GETDATA_TX_INTERVAL) {
+ LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), pto->GetId());
+ vGetData.push_back(inv);
+ if (vGetData.size() >= MAX_GETDATA_SZ) {
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ vGetData.clear();
+ }
+ UpdateTxRequestTime(inv.hash, nNow);
+ state.m_tx_download.m_tx_in_flight.emplace(inv.hash, nNow);
+ } else {
+ // This transaction is in flight from someone else; queue
+ // up processing to happen after the download times out
+ // (with a slight delay for inbound peers, to prefer
+ // requests to outbound peers).
+ int64_t next_process_time = CalculateTxGetDataTime(txid, nNow, !state.fPreferredDownload);
+ tx_process_time.emplace(next_process_time, txid);
}
} else {
- //If we're not going to ask, don't expect a response.
- pto->setAskFor.erase(inv.hash);
+ // We have already seen this transaction, no need to download.
+ state.m_tx_download.m_tx_announced.erase(inv.hash);
+ state.m_tx_download.m_tx_in_flight.erase(inv.hash);
}
- pto->mapAskFor.erase(pto->mapAskFor.begin());
}
+
+
if (!vGetData.empty())
connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index a0c7f8e3c2..4fbfa2b5c8 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -14,6 +14,11 @@ static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
// 0xFD + sha256("bitcoin")[0:5]
static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 };
+/**
+ * Construct an unspecified IPv6 network address (::/128).
+ *
+ * @note This address is considered invalid by CNetAddr::IsValid()
+ */
CNetAddr::CNetAddr()
{
memset(ip, 0, sizeof(ip));
@@ -40,6 +45,20 @@ void CNetAddr::SetRaw(Network network, const uint8_t *ip_in)
}
}
+/**
+ * Try to make this a dummy address that maps the specified name into IPv6 like
+ * so: (0xFD + %sha256("bitcoin")[0:5]) + %sha256(name)[0:10]. Such dummy
+ * addresses have a prefix of fd6b:88c0:8724::/48 and are guaranteed to not be
+ * publicly routable as it falls under RFC4193's fc00::/7 subnet allocated to
+ * unique-local addresses.
+ *
+ * CAddrMan uses these fake addresses to keep track of which DNS seeds were
+ * used.
+ *
+ * @returns Whether or not the operation was successful.
+ *
+ * @see CNetAddr::IsInternal(), CNetAddr::IsRFC4193()
+ */
bool CNetAddr::SetInternal(const std::string &name)
{
if (name.empty()) {
@@ -52,6 +71,16 @@ bool CNetAddr::SetInternal(const std::string &name)
return true;
}
+/**
+ * Try to make this a dummy address that maps the specified onion address into
+ * IPv6 using OnionCat's range and encoding. Such dummy addresses have a prefix
+ * of fd87:d87e:eb43::/48 and are guaranteed to not be publicly routable as they
+ * fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
+ *
+ * @returns Whether or not the operation was successful.
+ *
+ * @see CNetAddr::IsTor(), CNetAddr::IsRFC4193()
+ */
bool CNetAddr::SetSpecial(const std::string &strName)
{
if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") {
@@ -175,6 +204,17 @@ bool CNetAddr::IsRFC4843() const
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10);
}
+bool CNetAddr::IsRFC7343() const
+{
+ return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20);
+}
+
+/**
+ * @returns Whether or not this is a dummy address that maps an onion address
+ * into IPv6.
+ *
+ * @see CNetAddr::SetSpecial(const std::string &)
+ */
bool CNetAddr::IsTor() const
{
return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0);
@@ -182,18 +222,28 @@ bool CNetAddr::IsTor() const
bool CNetAddr::IsLocal() const
{
- // IPv4 loopback
- if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0))
- return true;
+ // IPv4 loopback (127.0.0.0/8 or 0.0.0.0/8)
+ if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0))
+ return true;
- // IPv6 loopback (::1/128)
- static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
- if (memcmp(ip, pchLocal, 16) == 0)
- return true;
+ // IPv6 loopback (::1/128)
+ static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
+ if (memcmp(ip, pchLocal, 16) == 0)
+ return true;
- return false;
+ return false;
}
+/**
+ * @returns Whether or not this network address is a valid address that @a could
+ * be used to refer to an actual host.
+ *
+ * @note A valid address may or may not be publicly routable on the global
+ * internet. As in, the set of valid addresses is a superset of the set of
+ * publicly routable addresses.
+ *
+ * @see CNetAddr::IsRoutable()
+ */
bool CNetAddr::IsValid() const
{
// Cleanup 3-byte shifted addresses caused by garbage in size field
@@ -233,11 +283,25 @@ bool CNetAddr::IsValid() const
return true;
}
+/**
+ * @returns Whether or not this network address is publicly routable on the
+ * global internet.
+ *
+ * @note A routable address is always valid. As in, the set of routable addresses
+ * is a subset of the set of valid addresses.
+ *
+ * @see CNetAddr::IsValid()
+ */
bool CNetAddr::IsRoutable() const
{
- return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal() || IsInternal());
+ return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsRFC7343() || IsLocal() || IsInternal());
}
+/**
+ * @returns Whether or not this is a dummy address that maps a name into IPv6.
+ *
+ * @see CNetAddr::SetInternal(const std::string &)
+ */
bool CNetAddr::IsInternal() const
{
return memcmp(ip, g_internal_prefix, sizeof(g_internal_prefix)) == 0;
@@ -299,6 +363,16 @@ bool operator<(const CNetAddr& a, const CNetAddr& b)
return (memcmp(a.ip, b.ip, 16) < 0);
}
+/**
+ * Try to get our IPv4 address.
+ *
+ * @param[out] pipv4Addr The in_addr struct to which to copy.
+ *
+ * @returns Whether or not the operation was successful, in particular, whether
+ * or not our address was an IPv4 address.
+ *
+ * @see CNetAddr::IsIPv4()
+ */
bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
{
if (!IsIPv4())
@@ -307,6 +381,16 @@ bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
return true;
}
+/**
+ * Try to get our IPv6 address.
+ *
+ * @param[out] pipv6Addr The in6_addr struct to which to copy.
+ *
+ * @returns Whether or not the operation was successful, in particular, whether
+ * or not our address was an IPv6 address.
+ *
+ * @see CNetAddr::IsIPv6()
+ */
bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
{
if (!IsIPv6()) {
@@ -316,8 +400,16 @@ bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
return true;
}
-// get canonical identifier of an address' group
-// no two connections will be attempted to addresses with the same group
+/**
+ * Get the canonical identifier of our network group
+ *
+ * The groups are assigned in a way where it should be costly for an attacker to
+ * obtain addresses with many different group identifiers, even if it is cheap
+ * to obtain addresses with the same identifier.
+ *
+ * @note No two connections will be attempted to addresses with the same network
+ * group.
+ */
std::vector<unsigned char> CNetAddr::GetGroup() const
{
std::vector<unsigned char> vchRet;
@@ -379,12 +471,15 @@ std::vector<unsigned char> CNetAddr::GetGroup() const
nBits = 32;
vchRet.push_back(nClass);
+
+ // push our ip onto vchRet byte by byte...
while (nBits >= 8)
{
vchRet.push_back(GetByte(15 - nStartByte));
nStartByte++;
nBits -= 8;
}
+ // ...for the last byte, push nBits and for the rest of the byte push 1's
if (nBits > 0)
vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1));
@@ -526,6 +621,18 @@ bool operator<(const CService& a, const CService& b)
return static_cast<CNetAddr>(a) < static_cast<CNetAddr>(b) || (static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port < b.port);
}
+/**
+ * Obtain the IPv4/6 socket address this represents.
+ *
+ * @param[out] paddr The obtained socket address.
+ * @param[in,out] addrlen The size, in bytes, of the address structure pointed
+ * to by paddr. The value that's pointed to by this
+ * parameter might change after calling this function if
+ * the size of the corresponding address structure
+ * changed.
+ *
+ * @returns Whether or not the operation was successful.
+ */
bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
{
if (IsIPv4()) {
@@ -556,13 +663,16 @@ bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
return false;
}
+/**
+ * @returns An identifier unique to this service's address and port number.
+ */
std::vector<unsigned char> CService::GetKey() const
{
std::vector<unsigned char> vKey;
vKey.resize(18);
memcpy(vKey.data(), ip, 16);
- vKey[16] = port / 0x100;
- vKey[17] = port & 0x0FF;
+ vKey[16] = port / 0x100; // most significant byte of our port
+ vKey[17] = port & 0x0FF; // least significant byte of our port
return vKey;
}
@@ -641,6 +751,10 @@ CSubNet::CSubNet(const CNetAddr &addr):
network = addr;
}
+/**
+ * @returns True if this subnet is valid, the specified address is valid, and
+ * the specified address belongs in this subnet.
+ */
bool CSubNet::Match(const CNetAddr &addr) const
{
if (!valid || !addr.IsValid())
@@ -651,6 +765,10 @@ bool CSubNet::Match(const CNetAddr &addr) const
return true;
}
+/**
+ * @returns The number of 1-bits in the prefix of the specified subnet mask. If
+ * the specified subnet mask is not a valid one, -1.
+ */
static inline int NetmaskBits(uint8_t x)
{
switch(x) {
diff --git a/src/netaddress.h b/src/netaddress.h
index ca435d17dc..673eaf8d7b 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -48,10 +48,6 @@ class CNetAddr
void SetRaw(Network network, const uint8_t *data);
public:
- /**
- * Transform an arbitrary string into a non-routable ipv6 address.
- * Useful for mapping resolved addresses back to their source.
- */
bool SetInternal(const std::string& name);
bool SetSpecial(const std::string &strName); // for Tor addresses
@@ -67,10 +63,11 @@ class CNetAddr
bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16)
bool IsRFC4193() const; // IPv6 unique local (FC00::/7)
bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32)
- bool IsRFC4843() const; // IPv6 ORCHID (2001:10::/28)
+ bool IsRFC4843() const; // IPv6 ORCHID (deprecated) (2001:10::/28)
+ bool IsRFC7343() const; // IPv6 ORCHIDv2 (2001:20::/28)
bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
- bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96)
- bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96)
+ bool IsRFC6052() const; // IPv6 well-known prefix for IPv4-embedded address (64:FF9B::/96)
+ bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765)
bool IsTor() const;
bool IsLocal() const;
bool IsRoutable() const;
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 355e21d4e6..78b3b6ae3a 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -5,10 +5,7 @@
#include <netbase.h>
-#include <hash.h>
#include <sync.h>
-#include <uint256.h>
-#include <random.h>
#include <tinyformat.h>
#include <util/system.h>
#include <util/strencodings.h>
@@ -80,11 +77,7 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
aiHint.ai_socktype = SOCK_STREAM;
aiHint.ai_protocol = IPPROTO_TCP;
aiHint.ai_family = AF_UNSPEC;
-#ifdef WIN32
- aiHint.ai_flags = fAllowLookup ? 0 : AI_NUMERICHOST;
-#else
aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
-#endif
struct addrinfo *aiRes = nullptr;
int nErr = getaddrinfo(pszName, nullptr, &aiHint, &aiRes);
if (nErr)
diff --git a/src/node/README.md b/src/node/README.md
new file mode 100644
index 0000000000..e99a717534
--- /dev/null
+++ b/src/node/README.md
@@ -0,0 +1,22 @@
+# src/node/
+
+The [`src/node/`](./) directory contains code that needs to access node state
+(state in `CChain`, `CBlockIndex`, `CCoinsView`, `CTxMemPool`, and similar
+classes).
+
+Code in [`src/node/`](./) is meant to be segregated from code in
+[`src/wallet/`](../wallet/) and [`src/qt/`](../qt/), to ensure wallet and GUI
+code changes don't interfere with node operation, to allow wallet and GUI code
+to run in separate processes, and to perhaps eventually allow wallet and GUI
+code to be maintained in separate source repositories.
+
+As a rule of thumb, code in one of the [`src/node/`](./),
+[`src/wallet/`](../wallet/), or [`src/qt/`](../qt/) directories should avoid
+calling code in the other directories directly, and only invoke it indirectly
+through the more limited [`src/interfaces/`](../interfaces/) classes.
+
+The [`src/node/`](./) directory is a new directory introduced in
+[#14978](https://github.com/bitcoin/bitcoin/pull/14978) and at the moment is
+sparsely populated. Eventually more substantial files like
+[`src/validation.cpp`](../validation.cpp) and
+[`src/txmempool.cpp`](../txmempool.cpp) might be moved there.
diff --git a/src/node/coin.cpp b/src/node/coin.cpp
new file mode 100644
index 0000000000..bb98e63f3a
--- /dev/null
+++ b/src/node/coin.cpp
@@ -0,0 +1,22 @@
+// Copyright (c) 2019 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 <node/coin.h>
+
+#include <txmempool.h>
+#include <validation.h>
+
+void FindCoins(std::map<COutPoint, Coin>& coins)
+{
+ LOCK2(cs_main, ::mempool.cs);
+ assert(pcoinsTip);
+ CCoinsViewCache& chain_view = *::pcoinsTip;
+ CCoinsViewMemPool mempool_view(&chain_view, ::mempool);
+ for (auto& coin : coins) {
+ if (!mempool_view.GetCoin(coin.first, coin.second)) {
+ // Either the coin is not in the CCoinsViewCache or is spent. Clear it.
+ coin.second.Clear();
+ }
+ }
+}
diff --git a/src/node/coin.h b/src/node/coin.h
new file mode 100644
index 0000000000..eb95b75cfb
--- /dev/null
+++ b/src/node/coin.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2019 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_NODE_COIN_H
+#define BITCOIN_NODE_COIN_H
+
+#include <map>
+
+class COutPoint;
+class Coin;
+
+/**
+ * Look up unspent output information. Returns coins in the mempool and in the
+ * current chain UTXO set. Iterates through all the keys in the map and
+ * populates the values.
+ *
+ * @param[in,out] coins map to fill
+ */
+void FindCoins(std::map<COutPoint, Coin>& coins);
+
+#endif // BITCOIN_NODE_COIN_H
diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp
new file mode 100644
index 0000000000..12559c5a5f
--- /dev/null
+++ b/src/node/psbt.cpp
@@ -0,0 +1,134 @@
+// Copyright (c) 2009-2018 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 <coins.h>
+#include <consensus/tx_verify.h>
+#include <node/psbt.h>
+#include <policy/policy.h>
+#include <policy/settings.h>
+
+#include <numeric>
+
+PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
+{
+ // Go through each input and build status
+ PSBTAnalysis result;
+
+ bool calc_fee = true;
+ bool all_final = true;
+ bool only_missing_sigs = true;
+ bool only_missing_final = false;
+ CAmount in_amt = 0;
+
+ result.inputs.resize(psbtx.tx->vin.size());
+
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs[i];
+ PSBTInputAnalysis& input_analysis = result.inputs[i];
+
+ // Check for a UTXO
+ CTxOut utxo;
+ if (psbtx.GetInputUTXO(utxo, i)) {
+ in_amt += utxo.nValue;
+ input_analysis.has_utxo = true;
+ } else {
+ input_analysis.has_utxo = false;
+ input_analysis.is_final = false;
+ input_analysis.next = PSBTRole::UPDATER;
+ calc_fee = false;
+ }
+
+ // Check if it is final
+ if (!utxo.IsNull() && !PSBTInputSigned(input)) {
+ input_analysis.is_final = false;
+ all_final = false;
+
+ // Figure out what is missing
+ SignatureData outdata;
+ bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
+
+ // Things are missing
+ if (!complete) {
+ input_analysis.missing_pubkeys = outdata.missing_pubkeys;
+ input_analysis.missing_redeem_script = outdata.missing_redeem_script;
+ input_analysis.missing_witness_script = outdata.missing_witness_script;
+ input_analysis.missing_sigs = outdata.missing_sigs;
+
+ // If we are only missing signatures and nothing else, then next is signer
+ if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) {
+ input_analysis.next = PSBTRole::SIGNER;
+ } else {
+ only_missing_sigs = false;
+ input_analysis.next = PSBTRole::UPDATER;
+ }
+ } else {
+ only_missing_final = true;
+ input_analysis.next = PSBTRole::FINALIZER;
+ }
+ } else if (!utxo.IsNull()){
+ input_analysis.is_final = true;
+ }
+ }
+
+ if (all_final) {
+ only_missing_sigs = false;
+ result.next = PSBTRole::EXTRACTOR;
+ }
+ if (calc_fee) {
+ // Get the output amount
+ CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), CAmount(0),
+ [](CAmount a, const CTxOut& b) {
+ return a += b.nValue;
+ }
+ );
+
+ // Get the fee
+ CAmount fee = in_amt - out_amt;
+ result.fee = fee;
+
+ // Estimate the size
+ CMutableTransaction mtx(*psbtx.tx);
+ CCoinsView view_dummy;
+ CCoinsViewCache view(&view_dummy);
+ bool success = true;
+
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs[i];
+ Coin newcoin;
+
+ if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true) || !psbtx.GetInputUTXO(newcoin.out, i)) {
+ success = false;
+ break;
+ } else {
+ mtx.vin[i].scriptSig = input.final_script_sig;
+ mtx.vin[i].scriptWitness = input.final_script_witness;
+ newcoin.nHeight = 1;
+ view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true);
+ }
+ }
+
+ if (success) {
+ CTransaction ctx = CTransaction(mtx);
+ size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS));
+ result.estimated_vsize = size;
+ // Estimate fee rate
+ CFeeRate feerate(fee, size);
+ result.estimated_feerate = feerate;
+ }
+
+ if (only_missing_sigs) {
+ result.next = PSBTRole::SIGNER;
+ } else if (only_missing_final) {
+ result.next = PSBTRole::FINALIZER;
+ } else if (all_final) {
+ result.next = PSBTRole::EXTRACTOR;
+ } else {
+ result.next = PSBTRole::UPDATER;
+ }
+ } else {
+ result.next = PSBTRole::UPDATER;
+ }
+
+ return result;
+}
diff --git a/src/node/psbt.h b/src/node/psbt.h
new file mode 100644
index 0000000000..e04366a20f
--- /dev/null
+++ b/src/node/psbt.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2009-2019 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_NODE_PSBT_H
+#define BITCOIN_NODE_PSBT_H
+
+#include <psbt.h>
+
+/**
+ * Holds an analysis of one input from a PSBT
+ */
+struct PSBTInputAnalysis {
+ bool has_utxo; //!< Whether we have UTXO information for this input
+ bool is_final; //!< Whether the input has all required information including signatures
+ PSBTRole next; //!< Which of the BIP 174 roles needs to handle this input next
+
+ std::vector<CKeyID> missing_pubkeys; //!< Pubkeys whose BIP32 derivation path is missing
+ std::vector<CKeyID> missing_sigs; //!< Pubkeys whose signatures are missing
+ uint160 missing_redeem_script; //!< Hash160 of redeem script, if missing
+ uint256 missing_witness_script; //!< SHA256 of witness script, if missing
+};
+
+/**
+ * Holds the results of AnalyzePSBT (miscellaneous information about a PSBT)
+ */
+struct PSBTAnalysis {
+ Optional<size_t> estimated_vsize; //!< Estimated weight of the transaction
+ Optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction
+ Optional<CAmount> fee; //!< Amount of fee being paid by the transaction
+ std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction
+ PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next
+};
+
+/**
+ * Provides helpful miscellaneous information about where a PSBT is in the signing workflow.
+ *
+ * @param[in] psbtx the PSBT to analyze
+ * @return A PSBTAnalysis with information about the provided PSBT.
+ */
+PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx);
+
+#endif // BITCOIN_NODE_PSBT_H
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
new file mode 100644
index 0000000000..5ffb15ed3c
--- /dev/null
+++ b/src/node/transaction.cpp
@@ -0,0 +1,78 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 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 <consensus/validation.h>
+#include <net.h>
+#include <txmempool.h>
+#include <util/validation.h>
+#include <validation.h>
+#include <validationinterface.h>
+#include <node/transaction.h>
+
+#include <future>
+
+TransactionError BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, std::string& err_string, const CAmount& highfee)
+{
+ std::promise<void> promise;
+ hashTx = tx->GetHash();
+
+ { // cs_main scope
+ LOCK(cs_main);
+ CCoinsViewCache &view = *pcoinsTip;
+ bool fHaveChain = false;
+ for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
+ const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
+ fHaveChain = !existingCoin.IsSpent();
+ }
+ bool fHaveMempool = mempool.exists(hashTx);
+ if (!fHaveMempool && !fHaveChain) {
+ // push to local node and sync with wallets
+ CValidationState state;
+ bool fMissingInputs;
+ if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, highfee)) {
+ if (state.IsInvalid()) {
+ err_string = FormatStateMessage(state);
+ return TransactionError::MEMPOOL_REJECTED;
+ } else {
+ if (fMissingInputs) {
+ return TransactionError::MISSING_INPUTS;
+ }
+ err_string = FormatStateMessage(state);
+ return TransactionError::MEMPOOL_ERROR;
+ }
+ } 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) {
+ return TransactionError::ALREADY_IN_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) {
+ return TransactionError::P2P_DISABLED;
+ }
+
+ CInv inv(MSG_TX, hashTx);
+ g_connman->ForEachNode([&inv](CNode* pnode) {
+ pnode->PushInventory(inv);
+ });
+
+ return TransactionError::OK;
+}
diff --git a/src/node/transaction.h b/src/node/transaction.h
new file mode 100644
index 0000000000..51033f94e5
--- /dev/null
+++ b/src/node/transaction.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2017-2019 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_NODE_TRANSACTION_H
+#define BITCOIN_NODE_TRANSACTION_H
+
+#include <attributes.h>
+#include <primitives/transaction.h>
+#include <uint256.h>
+#include <util/error.h>
+
+/**
+ * Broadcast a transaction
+ *
+ * @param[in] tx the transaction to broadcast
+ * @param[out] &txid the txid of the transaction, if successfully broadcast
+ * @param[out] &err_string reference to std::string to fill with error string if available
+ * @param[in] highfee Reject txs with fees higher than this (if 0, accept any fee)
+ * return error
+ */
+NODISCARD TransactionError BroadcastTransaction(CTransactionRef tx, uint256& txid, std::string& err_string, const CAmount& highfee);
+
+#endif // BITCOIN_NODE_TRANSACTION_H
diff --git a/src/noui.cpp b/src/noui.cpp
index c7d8fee0ba..0c18b0e231 100644
--- a/src/noui.cpp
+++ b/src/noui.cpp
@@ -37,7 +37,7 @@ bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& ca
if (!fSecure)
LogPrintf("%s: %s\n", strCaption, message);
- fprintf(stderr, "%s: %s\n", strCaption.c_str(), message.c_str());
+ tfm::format(std::cerr, "%s: %s\n", strCaption.c_str(), message.c_str());
return false;
}
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
index 7e5690dfc5..73ffb801f2 100644
--- a/src/outputtype.cpp
+++ b/src/outputtype.cpp
@@ -45,14 +45,14 @@ const std::string& FormatOutputType(OutputType type)
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
{
switch (type) {
- case OutputType::LEGACY: return key.GetID();
+ case OutputType::LEGACY: return PKHash(key);
case OutputType::P2SH_SEGWIT:
case OutputType::BECH32: {
- if (!key.IsCompressed()) return key.GetID();
- CTxDestination witdest = WitnessV0KeyHash(key.GetID());
+ if (!key.IsCompressed()) return PKHash(key);
+ CTxDestination witdest = WitnessV0KeyHash(PKHash(key));
CScript witprog = GetScriptForDestination(witdest);
if (type == OutputType::P2SH_SEGWIT) {
- return CScriptID(witprog);
+ return ScriptHash(witprog);
} else {
return witdest;
}
@@ -63,10 +63,10 @@ CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
{
- CKeyID keyid = key.GetID();
+ PKHash keyid(key);
if (key.IsCompressed()) {
CTxDestination segwit = WitnessV0KeyHash(keyid);
- CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit));
+ CTxDestination p2sh = ScriptHash(GetScriptForDestination(segwit));
return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
} else {
return std::vector<CTxDestination>{std::move(keyid)};
@@ -80,19 +80,19 @@ CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript&
// Note that scripts over 520 bytes are not yet supported.
switch (type) {
case OutputType::LEGACY:
- return CScriptID(script);
+ return ScriptHash(script);
case OutputType::P2SH_SEGWIT:
case OutputType::BECH32: {
CTxDestination witdest = WitnessV0ScriptHash(script);
CScript witprog = GetScriptForDestination(witdest);
// Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
- if (!IsSolvable(keystore, witprog)) return CScriptID(script);
+ if (!IsSolvable(keystore, witprog)) return ScriptHash(script);
// Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
keystore.AddCScript(witprog);
if (type == OutputType::BECH32) {
return witdest;
} else {
- return CScriptID(witprog);
+ return ScriptHash(witprog);
}
}
default: assert(false);
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index c49b9fa36b..5d538606c2 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -4,7 +4,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <policy/fees.h>
-#include <policy/policy.h>
#include <clientversion.h>
#include <primitives/transaction.h>
@@ -27,40 +26,6 @@ std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) {
return horizon_string->second;
}
-std::string StringForFeeReason(FeeReason reason) {
- static const std::map<FeeReason, std::string> fee_reason_strings = {
- {FeeReason::NONE, "None"},
- {FeeReason::HALF_ESTIMATE, "Half Target 60% Threshold"},
- {FeeReason::FULL_ESTIMATE, "Target 85% Threshold"},
- {FeeReason::DOUBLE_ESTIMATE, "Double Target 95% Threshold"},
- {FeeReason::CONSERVATIVE, "Conservative Double Target longer horizon"},
- {FeeReason::MEMPOOL_MIN, "Mempool Min Fee"},
- {FeeReason::PAYTXFEE, "PayTxFee set"},
- {FeeReason::FALLBACK, "Fallback fee"},
- {FeeReason::REQUIRED, "Minimum Required Fee"},
- {FeeReason::MAXTXFEE, "MaxTxFee limit"}
- };
- auto reason_string = fee_reason_strings.find(reason);
-
- if (reason_string == fee_reason_strings.end()) return "Unknown";
-
- return reason_string->second;
-}
-
-bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
- static const std::map<std::string, FeeEstimateMode> fee_modes = {
- {"UNSET", FeeEstimateMode::UNSET},
- {"ECONOMICAL", FeeEstimateMode::ECONOMICAL},
- {"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
- };
- auto mode = fee_modes.find(mode_string);
-
- if (mode == fee_modes.end()) return false;
-
- fee_estimate_mode = mode->second;
- return true;
-}
-
/**
* We will instantiate an instance of this class to track transactions that were
* included in a block. We will lump transactions into a bucket according to their
@@ -82,7 +47,7 @@ private:
std::vector<double> txCtAvg;
// Count the total # of txs confirmed within Y blocks in each bucket
- // Track the historical moving average of theses totals over blocks
+ // Track the historical moving average of these totals over blocks
std::vector<std::vector<double>> confAvg; // confAvg[Y][X]
// Track moving avg of txs which have been evicted from the mempool
@@ -560,7 +525,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
if (txHeight != nBestSeenHeight) {
// Ignore side chains and re-orgs; assuming they are random they don't
// affect the estimate. We'll potentially double count transactions in 1-block reorgs.
- // Ignore txs if BlockPolicyEstimator is not in sync with chainActive.Tip().
+ // Ignore txs if BlockPolicyEstimator is not in sync with ::ChainActive().Tip().
// It will be synced next time a block is processed.
return;
}
diff --git a/src/policy/fees.h b/src/policy/fees.h
index c8472a12f5..6e61f76178 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -46,8 +46,6 @@ enum class FeeReason {
MAXTXFEE,
};
-std::string StringForFeeReason(FeeReason reason);
-
/* Used to determine type of fee estimation requested */
enum class FeeEstimateMode {
UNSET, //!< Use default settings based on other criteria
@@ -55,8 +53,6 @@ enum class FeeEstimateMode {
CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
};
-bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode);
-
/* Used to return detailed information about a feerate bucket */
struct EstimatorBucket
{
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index d4cc538492..51de5841ec 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -8,11 +8,7 @@
#include <policy/policy.h>
#include <consensus/validation.h>
-#include <validation.h>
#include <coins.h>
-#include <tinyformat.h>
-#include <util/system.h>
-#include <util/strencodings.h>
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
@@ -59,7 +55,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
std::vector<std::vector<unsigned char> > vSolutions;
whichType = Solver(scriptPubKey, vSolutions);
- if (whichType == TX_NONSTANDARD || whichType == TX_WITNESS_UNKNOWN) {
+ if (whichType == TX_NONSTANDARD) {
return false;
} else if (whichType == TX_MULTISIG) {
unsigned char m = vSolutions.front()[0];
@@ -77,7 +73,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
return true;
}
-bool IsStandardTx(const CTransaction& tx, std::string& reason)
+bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason)
{
if (tx.nVersion > CTransaction::MAX_STANDARD_VERSION || tx.nVersion < 1) {
reason = "version";
@@ -123,10 +119,10 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason)
if (whichType == TX_NULL_DATA)
nDataOut++;
- else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
+ else if ((whichType == TX_MULTISIG) && (!permit_bare_multisig)) {
reason = "bare-multisig";
return false;
- } else if (IsDust(txout, ::dustRelayFee)) {
+ } else if (IsDust(txout, dust_relay_fee)) {
reason = "dust";
return false;
}
@@ -239,21 +235,17 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
return true;
}
-CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE);
-CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
-unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
-
-int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost)
+int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop)
{
- return (std::max(nWeight, nSigOpCost * nBytesPerSigOp) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
+ return (std::max(nWeight, nSigOpCost * bytes_per_sigop) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
}
-int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost)
+int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost, unsigned int bytes_per_sigop)
{
- return GetVirtualTransactionSize(GetTransactionWeight(tx), nSigOpCost);
+ return GetVirtualTransactionSize(GetTransactionWeight(tx), nSigOpCost, bytes_per_sigop);
}
-int64_t GetVirtualTransactionInputSize(const CTxIn& txin, int64_t nSigOpCost)
+int64_t GetVirtualTransactionInputSize(const CTxIn& txin, int64_t nSigOpCost, unsigned int bytes_per_sigop)
{
- return GetVirtualTransactionSize(GetTransactionInputWeight(txin), nSigOpCost);
+ return GetVirtualTransactionSize(GetTransactionInputWeight(txin), nSigOpCost, bytes_per_sigop);
}
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 3d47ac1267..ebe040f0ea 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -34,6 +34,8 @@ static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
static const unsigned int DEFAULT_INCREMENTAL_RELAY_FEE = 1000;
/** Default for -bytespersigop */
static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
+/** Default for -permitbaremultisig */
+static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
/** The maximum number of witness stack items in a standard P2WSH script */
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
/** The maximum size of each witness stack item in a standard P2WSH script */
@@ -84,7 +86,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
* Check for standard transaction types
* @return True if all outputs (scriptPubKeys) use only standard transaction forms
*/
-bool IsStandardTx(const CTransaction& tx, std::string& reason);
+bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason);
/**
* Check for standard transaction types
* @param[in] mapInputs Map of previous transactions that have outputs we're spending
@@ -98,13 +100,19 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
*/
bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
-extern CFeeRate incrementalRelayFee;
-extern CFeeRate dustRelayFee;
-extern unsigned int nBytesPerSigOp;
-
/** Compute the virtual transaction size (weight reinterpreted as bytes). */
-int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);
-int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0);
-int64_t GetVirtualTransactionInputSize(const CTxIn& tx, int64_t nSigOpCost = 0);
+int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop);
+int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost, unsigned int bytes_per_sigop);
+int64_t GetVirtualTransactionInputSize(const CTxIn& tx, int64_t nSigOpCost, unsigned int bytes_per_sigop);
+
+static inline int64_t GetVirtualTransactionSize(const CTransaction& tx)
+{
+ return GetVirtualTransactionSize(tx, 0, 0);
+}
+
+static inline int64_t GetVirtualTransactionInputSize(const CTxIn& tx)
+{
+ return GetVirtualTransactionInputSize(tx, 0, 0);
+}
#endif // BITCOIN_POLICY_POLICY_H
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 0dc130d104..b4b8341d77 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -3,18 +3,9 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <policy/rbf.h>
+#include <util/rbf.h>
-bool SignalsOptInRBF(const CTransaction &tx)
-{
- for (const CTxIn &txin : tx.vin) {
- if (txin.nSequence <= MAX_BIP125_RBF_SEQUENCE) {
- return true;
- }
- }
- return false;
-}
-
-RBFTransactionState IsRBFOptIn(const CTransaction &tx, CTxMemPool &pool)
+RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
{
AssertLockHeld(pool.cs);
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index 581f489e12..0707b0044f 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -7,22 +7,16 @@
#include <txmempool.h>
-static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
-
enum class RBFTransactionState {
UNKNOWN,
REPLACEABLE_BIP125,
FINAL
};
-// Check whether the sequence numbers on this transaction are signaling
-// opt-in to replace-by-fee, according to BIP 125
-bool SignalsOptInRBF(const CTransaction &tx);
-
// Determine whether an in-mempool transaction is signaling opt-in to RBF
// according to BIP 125
// This involves checking sequence numbers of the transaction, as well
// as the sequence numbers of all in-mempool ancestors.
-RBFTransactionState IsRBFOptIn(const CTransaction &tx, CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
+RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
#endif // BITCOIN_POLICY_RBF_H
diff --git a/src/policy/settings.cpp b/src/policy/settings.cpp
new file mode 100644
index 0000000000..e8e1559407
--- /dev/null
+++ b/src/policy/settings.cpp
@@ -0,0 +1,14 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 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 <policy/settings.h>
+
+#include <policy/feerate.h>
+#include <policy/policy.h>
+
+bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
+CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE);
+CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
+unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
diff --git a/src/policy/settings.h b/src/policy/settings.h
new file mode 100644
index 0000000000..30a7189c93
--- /dev/null
+++ b/src/policy/settings.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 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_POLICY_SETTINGS_H
+#define BITCOIN_POLICY_SETTINGS_H
+
+#include <policy/policy.h>
+
+class CFeeRate;
+class CTransaction;
+
+// Policy settings which are configurable at runtime.
+extern CFeeRate incrementalRelayFee;
+extern CFeeRate dustRelayFee;
+extern unsigned int nBytesPerSigOp;
+extern bool fIsBareMultisigStd;
+
+static inline bool IsStandardTx(const CTransaction& tx, std::string& reason)
+{
+ return IsStandardTx(tx, ::fIsBareMultisigStd, ::dustRelayFee, reason);
+}
+
+static inline int64_t GetVirtualTransactionSize(int64_t weight, int64_t sigop_cost)
+{
+ return GetVirtualTransactionSize(weight, sigop_cost, ::nBytesPerSigOp);
+}
+
+static inline int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t sigop_cost)
+{
+ return GetVirtualTransactionSize(tx, sigop_cost, ::nBytesPerSigOp);
+}
+
+#endif // BITCOIN_POLICY_SETTINGS_H
diff --git a/src/prevector.h b/src/prevector.h
index 2f368a5c66..9d576321b6 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -147,14 +147,14 @@ public:
};
private:
- size_type _size;
+ size_type _size = 0;
union direct_or_indirect {
char direct[sizeof(T) * N];
struct {
size_type capacity;
char* indirect;
};
- } _union;
+ } _union = {};
T* direct_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.direct) + pos; }
const T* direct_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.direct) + pos; }
@@ -230,34 +230,34 @@ public:
fill(item_ptr(0), first, last);
}
- prevector() : _size(0), _union{{}} {}
+ prevector() {}
- explicit prevector(size_type n) : prevector() {
+ explicit prevector(size_type n) {
resize(n);
}
- explicit prevector(size_type n, const T& val) : prevector() {
+ explicit prevector(size_type n, const T& val) {
change_capacity(n);
_size += n;
fill(item_ptr(0), n, val);
}
template<typename InputIterator>
- prevector(InputIterator first, InputIterator last) : prevector() {
+ prevector(InputIterator first, InputIterator last) {
size_type n = last - first;
change_capacity(n);
_size += n;
fill(item_ptr(0), first, last);
}
- prevector(const prevector<N, T, Size, Diff>& other) : prevector() {
+ prevector(const prevector<N, T, Size, Diff>& other) {
size_type n = other.size();
change_capacity(n);
_size += n;
fill(item_ptr(0), other.begin(), other.end());
}
- prevector(prevector<N, T, Size, Diff>&& other) : prevector() {
+ prevector(prevector<N, T, Size, Diff>&& other) {
swap(other);
}
diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp
index a0c2e3f125..60c7c2d160 100644
--- a/src/primitives/block.cpp
+++ b/src/primitives/block.cpp
@@ -7,7 +7,6 @@
#include <hash.h>
#include <tinyformat.h>
-#include <util/strencodings.h>
#include <crypto/common.h>
uint256 CBlockHeader::GetHash() const
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index f6f8e31363..aad991e2f1 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -222,6 +222,10 @@ inline void UnserializeTransaction(TxType& tx, Stream& s) {
for (size_t i = 0; i < tx.vin.size(); i++) {
s >> tx.vin[i].scriptWitness.stack;
}
+ if (!tx.HasWitness()) {
+ /* It's illegal to encode witnesses when all witness stacks are empty. */
+ throw std::ios_base::failure("Superfluous witness record");
+ }
}
if (flags) {
/* Unknown flag in the serialization */
diff --git a/src/psbt.cpp b/src/psbt.cpp
new file mode 100644
index 0000000000..d765133190
--- /dev/null
+++ b/src/psbt.cpp
@@ -0,0 +1,367 @@
+// Copyright (c) 2009-2018 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 <psbt.h>
+#include <util/strencodings.h>
+
+#include <numeric>
+
+PartiallySignedTransaction::PartiallySignedTransaction(const CMutableTransaction& tx) : tx(tx)
+{
+ inputs.resize(tx.vin.size());
+ outputs.resize(tx.vout.size());
+}
+
+bool PartiallySignedTransaction::IsNull() const
+{
+ return !tx && inputs.empty() && outputs.empty() && unknown.empty();
+}
+
+bool PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
+{
+ // Prohibited to merge two PSBTs over different transactions
+ if (tx->GetHash() != psbt.tx->GetHash()) {
+ return false;
+ }
+
+ for (unsigned int i = 0; i < inputs.size(); ++i) {
+ inputs[i].Merge(psbt.inputs[i]);
+ }
+ for (unsigned int i = 0; i < outputs.size(); ++i) {
+ outputs[i].Merge(psbt.outputs[i]);
+ }
+ unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
+
+ return true;
+}
+
+bool PartiallySignedTransaction::IsSane() const
+{
+ for (PSBTInput input : inputs) {
+ if (!input.IsSane()) return false;
+ }
+ return true;
+}
+
+bool PartiallySignedTransaction::AddInput(const CTxIn& txin, PSBTInput& psbtin)
+{
+ if (std::find(tx->vin.begin(), tx->vin.end(), txin) != tx->vin.end()) {
+ return false;
+ }
+ tx->vin.push_back(txin);
+ psbtin.partial_sigs.clear();
+ psbtin.final_script_sig.clear();
+ psbtin.final_script_witness.SetNull();
+ inputs.push_back(psbtin);
+ return true;
+}
+
+bool PartiallySignedTransaction::AddOutput(const CTxOut& txout, const PSBTOutput& psbtout)
+{
+ tx->vout.push_back(txout);
+ outputs.push_back(psbtout);
+ return true;
+}
+
+bool PartiallySignedTransaction::GetInputUTXO(CTxOut& utxo, int input_index) const
+{
+ PSBTInput input = inputs[input_index];
+ int prevout_index = tx->vin[input_index].prevout.n;
+ if (input.non_witness_utxo) {
+ utxo = input.non_witness_utxo->vout[prevout_index];
+ } else if (!input.witness_utxo.IsNull()) {
+ utxo = input.witness_utxo;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool PSBTInput::IsNull() const
+{
+ return !non_witness_utxo && witness_utxo.IsNull() && partial_sigs.empty() && unknown.empty() && hd_keypaths.empty() && redeem_script.empty() && witness_script.empty();
+}
+
+void PSBTInput::FillSignatureData(SignatureData& sigdata) const
+{
+ if (!final_script_sig.empty()) {
+ sigdata.scriptSig = final_script_sig;
+ sigdata.complete = true;
+ }
+ if (!final_script_witness.IsNull()) {
+ sigdata.scriptWitness = final_script_witness;
+ sigdata.complete = true;
+ }
+ if (sigdata.complete) {
+ return;
+ }
+
+ sigdata.signatures.insert(partial_sigs.begin(), partial_sigs.end());
+ if (!redeem_script.empty()) {
+ sigdata.redeem_script = redeem_script;
+ }
+ if (!witness_script.empty()) {
+ sigdata.witness_script = witness_script;
+ }
+ for (const auto& key_pair : hd_keypaths) {
+ sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
+ }
+}
+
+void PSBTInput::FromSignatureData(const SignatureData& sigdata)
+{
+ if (sigdata.complete) {
+ partial_sigs.clear();
+ hd_keypaths.clear();
+ redeem_script.clear();
+ witness_script.clear();
+
+ if (!sigdata.scriptSig.empty()) {
+ final_script_sig = sigdata.scriptSig;
+ }
+ if (!sigdata.scriptWitness.IsNull()) {
+ final_script_witness = sigdata.scriptWitness;
+ }
+ return;
+ }
+
+ partial_sigs.insert(sigdata.signatures.begin(), sigdata.signatures.end());
+ if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
+ redeem_script = sigdata.redeem_script;
+ }
+ if (witness_script.empty() && !sigdata.witness_script.empty()) {
+ witness_script = sigdata.witness_script;
+ }
+ for (const auto& entry : sigdata.misc_pubkeys) {
+ hd_keypaths.emplace(entry.second);
+ }
+}
+
+void PSBTInput::Merge(const PSBTInput& input)
+{
+ if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo;
+ if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) {
+ witness_utxo = input.witness_utxo;
+ non_witness_utxo = nullptr; // Clear out any non-witness utxo when we set a witness one.
+ }
+
+ partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end());
+ hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end());
+ unknown.insert(input.unknown.begin(), input.unknown.end());
+
+ if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script;
+ if (witness_script.empty() && !input.witness_script.empty()) witness_script = input.witness_script;
+ if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig;
+ if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness;
+}
+
+bool PSBTInput::IsSane() const
+{
+ // Cannot have both witness and non-witness utxos
+ if (!witness_utxo.IsNull() && non_witness_utxo) return false;
+
+ // If we have a witness_script or a scriptWitness, we must also have a witness utxo
+ if (!witness_script.empty() && witness_utxo.IsNull()) return false;
+ if (!final_script_witness.IsNull() && witness_utxo.IsNull()) return false;
+
+ return true;
+}
+
+void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
+{
+ if (!redeem_script.empty()) {
+ sigdata.redeem_script = redeem_script;
+ }
+ if (!witness_script.empty()) {
+ sigdata.witness_script = witness_script;
+ }
+ for (const auto& key_pair : hd_keypaths) {
+ sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
+ }
+}
+
+void PSBTOutput::FromSignatureData(const SignatureData& sigdata)
+{
+ if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
+ redeem_script = sigdata.redeem_script;
+ }
+ if (witness_script.empty() && !sigdata.witness_script.empty()) {
+ witness_script = sigdata.witness_script;
+ }
+ for (const auto& entry : sigdata.misc_pubkeys) {
+ hd_keypaths.emplace(entry.second);
+ }
+}
+
+bool PSBTOutput::IsNull() const
+{
+ return redeem_script.empty() && witness_script.empty() && hd_keypaths.empty() && unknown.empty();
+}
+
+void PSBTOutput::Merge(const PSBTOutput& output)
+{
+ hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end());
+ unknown.insert(output.unknown.begin(), output.unknown.end());
+
+ if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script;
+ if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script;
+}
+bool PSBTInputSigned(const PSBTInput& input)
+{
+ return !input.final_script_sig.empty() || !input.final_script_witness.IsNull();
+}
+
+bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash, SignatureData* out_sigdata, bool use_dummy)
+{
+ PSBTInput& input = psbt.inputs.at(index);
+ const CMutableTransaction& tx = *psbt.tx;
+
+ if (PSBTInputSigned(input)) {
+ return true;
+ }
+
+ // Fill SignatureData with input info
+ SignatureData sigdata;
+ input.FillSignatureData(sigdata);
+
+ // Get UTXO
+ bool require_witness_sig = false;
+ CTxOut utxo;
+
+ // Verify input sanity, which checks that at most one of witness or non-witness utxos is provided.
+ if (!input.IsSane()) {
+ return false;
+ }
+
+ if (input.non_witness_utxo) {
+ // If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
+ COutPoint prevout = tx.vin[index].prevout;
+ if (input.non_witness_utxo->GetHash() != prevout.hash) {
+ return false;
+ }
+ utxo = input.non_witness_utxo->vout[prevout.n];
+ } else if (!input.witness_utxo.IsNull()) {
+ utxo = input.witness_utxo;
+ // When we're taking our information from a witness UTXO, we can't verify it is actually data from
+ // the output being spent. This is safe in case a witness signature is produced (which includes this
+ // information directly in the hash), but not for non-witness signatures. Remember that we require
+ // a witness signature in this situation.
+ require_witness_sig = true;
+ } else {
+ return false;
+ }
+
+ sigdata.witness = false;
+ bool sig_complete;
+ if (use_dummy) {
+ sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
+ } else {
+ MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
+ sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
+ }
+ // Verify that a witness signature was produced in case one was required.
+ if (require_witness_sig && !sigdata.witness) return false;
+ input.FromSignatureData(sigdata);
+
+ // If we have a witness signature, use the smaller witness UTXO.
+ if (sigdata.witness) {
+ input.witness_utxo = utxo;
+ input.non_witness_utxo = nullptr;
+ }
+
+ // Fill in the missing info
+ if (out_sigdata) {
+ out_sigdata->missing_pubkeys = sigdata.missing_pubkeys;
+ out_sigdata->missing_sigs = sigdata.missing_sigs;
+ out_sigdata->missing_redeem_script = sigdata.missing_redeem_script;
+ out_sigdata->missing_witness_script = sigdata.missing_witness_script;
+ }
+
+ return sig_complete;
+}
+
+bool FinalizePSBT(PartiallySignedTransaction& psbtx)
+{
+ // Finalize input signatures -- in case we have partial signatures that add up to a complete
+ // signature, but have not combined them yet (e.g. because the combiner that created this
+ // PartiallySignedTransaction did not understand them), this will combine them into a final
+ // script.
+ bool complete = true;
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL);
+ }
+
+ return complete;
+}
+
+bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransaction& result)
+{
+ // It's not safe to extract a PSBT that isn't finalized, and there's no easy way to check
+ // whether a PSBT is finalized without finalizing it, so we just do this.
+ if (!FinalizePSBT(psbtx)) {
+ return false;
+ }
+
+ result = *psbtx.tx;
+ for (unsigned int i = 0; i < result.vin.size(); ++i) {
+ result.vin[i].scriptSig = psbtx.inputs[i].final_script_sig;
+ result.vin[i].scriptWitness = psbtx.inputs[i].final_script_witness;
+ }
+ return true;
+}
+
+TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs)
+{
+ out = psbtxs[0]; // Copy the first one
+
+ // Merge
+ for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) {
+ if (!out.Merge(*it)) {
+ return TransactionError::PSBT_MISMATCH;
+ }
+ }
+ if (!out.IsSane()) {
+ return TransactionError::INVALID_PSBT;
+ }
+
+ return TransactionError::OK;
+}
+
+std::string PSBTRoleName(PSBTRole role) {
+ switch (role) {
+ case PSBTRole::UPDATER: return "updater";
+ case PSBTRole::SIGNER: return "signer";
+ case PSBTRole::FINALIZER: return "finalizer";
+ case PSBTRole::EXTRACTOR: return "extractor";
+ // no default case, so the compiler can warn about missing cases
+ }
+ assert(false);
+}
+
+bool DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
+{
+ bool invalid;
+ std::string tx_data = DecodeBase64(base64_tx, &invalid);
+ if (invalid) {
+ error = "invalid base64";
+ return false;
+ }
+ return DecodeRawPSBT(psbt, tx_data, error);
+}
+
+bool DecodeRawPSBT(PartiallySignedTransaction& psbt, const std::string& tx_data, std::string& error)
+{
+ CDataStream ss_data(tx_data.data(), tx_data.data() + tx_data.size(), SER_NETWORK, PROTOCOL_VERSION);
+ try {
+ ss_data >> psbt;
+ if (!ss_data.empty()) {
+ error = "extra data after PSBT";
+ return false;
+ }
+ } catch (const std::exception& e) {
+ error = e.what();
+ return false;
+ }
+ return true;
+}
diff --git a/src/psbt.h b/src/psbt.h
new file mode 100644
index 0000000000..1bc1e91a84
--- /dev/null
+++ b/src/psbt.h
@@ -0,0 +1,599 @@
+// Copyright (c) 2009-2019 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_PSBT_H
+#define BITCOIN_PSBT_H
+
+#include <attributes.h>
+#include <node/transaction.h>
+#include <optional.h>
+#include <policy/feerate.h>
+#include <primitives/transaction.h>
+#include <pubkey.h>
+#include <script/sign.h>
+
+// Magic bytes
+static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
+
+// Global types
+static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00;
+
+// Input types
+static constexpr uint8_t PSBT_IN_NON_WITNESS_UTXO = 0x00;
+static constexpr uint8_t PSBT_IN_WITNESS_UTXO = 0x01;
+static constexpr uint8_t PSBT_IN_PARTIAL_SIG = 0x02;
+static constexpr uint8_t PSBT_IN_SIGHASH = 0x03;
+static constexpr uint8_t PSBT_IN_REDEEMSCRIPT = 0x04;
+static constexpr uint8_t PSBT_IN_WITNESSSCRIPT = 0x05;
+static constexpr uint8_t PSBT_IN_BIP32_DERIVATION = 0x06;
+static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07;
+static constexpr uint8_t PSBT_IN_SCRIPTWITNESS = 0x08;
+
+// Output types
+static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;
+static constexpr uint8_t PSBT_OUT_WITNESSSCRIPT = 0x01;
+static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;
+
+// The separator is 0x00. Reading this in means that the unserializer can interpret it
+// as a 0 length key which indicates that this is the separator. The separator has no value.
+static constexpr uint8_t PSBT_SEPARATOR = 0x00;
+
+/** A structure for PSBTs which contain per-input information */
+struct PSBTInput
+{
+ CTransactionRef non_witness_utxo;
+ CTxOut witness_utxo;
+ CScript redeem_script;
+ CScript witness_script;
+ CScript final_script_sig;
+ CScriptWitness final_script_witness;
+ std::map<CPubKey, KeyOriginInfo> hd_keypaths;
+ std::map<CKeyID, SigPair> partial_sigs;
+ std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
+ int sighash_type = 0;
+
+ bool IsNull() const;
+ void FillSignatureData(SignatureData& sigdata) const;
+ void FromSignatureData(const SignatureData& sigdata);
+ void Merge(const PSBTInput& input);
+ bool IsSane() const;
+ PSBTInput() {}
+
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+ // Write the utxo
+ // If there is a non-witness utxo, then don't add the witness one.
+ if (non_witness_utxo) {
+ SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO);
+ OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
+ SerializeToVector(os, non_witness_utxo);
+ } else if (!witness_utxo.IsNull()) {
+ SerializeToVector(s, PSBT_IN_WITNESS_UTXO);
+ SerializeToVector(s, witness_utxo);
+ }
+
+ if (final_script_sig.empty() && final_script_witness.IsNull()) {
+ // Write any partial signatures
+ for (auto sig_pair : partial_sigs) {
+ SerializeToVector(s, PSBT_IN_PARTIAL_SIG, MakeSpan(sig_pair.second.first));
+ s << sig_pair.second.second;
+ }
+
+ // Write the sighash type
+ if (sighash_type > 0) {
+ SerializeToVector(s, PSBT_IN_SIGHASH);
+ SerializeToVector(s, sighash_type);
+ }
+
+ // Write the redeem script
+ if (!redeem_script.empty()) {
+ SerializeToVector(s, PSBT_IN_REDEEMSCRIPT);
+ s << redeem_script;
+ }
+
+ // Write the witness script
+ if (!witness_script.empty()) {
+ SerializeToVector(s, PSBT_IN_WITNESSSCRIPT);
+ s << witness_script;
+ }
+
+ // Write any hd keypaths
+ SerializeHDKeypaths(s, hd_keypaths, PSBT_IN_BIP32_DERIVATION);
+ }
+
+ // Write script sig
+ if (!final_script_sig.empty()) {
+ SerializeToVector(s, PSBT_IN_SCRIPTSIG);
+ s << final_script_sig;
+ }
+ // write script witness
+ if (!final_script_witness.IsNull()) {
+ SerializeToVector(s, PSBT_IN_SCRIPTWITNESS);
+ SerializeToVector(s, final_script_witness.stack);
+ }
+
+ // Write unknown things
+ for (auto& entry : unknown) {
+ s << entry.first;
+ s << entry.second;
+ }
+
+ s << PSBT_SEPARATOR;
+ }
+
+
+ template <typename Stream>
+ inline void Unserialize(Stream& s) {
+ // Read loop
+ bool found_sep = false;
+ while(!s.empty()) {
+ // Read
+ std::vector<unsigned char> key;
+ s >> key;
+
+ // the key is empty if that was actually a separator byte
+ // This is a special case for key lengths 0 as those are not allowed (except for separator)
+ if (key.empty()) {
+ found_sep = true;
+ break;
+ }
+
+ // First byte of key is the type
+ unsigned char type = key[0];
+
+ // Do stuff based on type
+ switch(type) {
+ case PSBT_IN_NON_WITNESS_UTXO:
+ {
+ if (non_witness_utxo) {
+ throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Non-witness utxo key is more than one byte type");
+ }
+ // Set the stream to unserialize with witness since this is always a valid network transaction
+ OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() & ~SERIALIZE_TRANSACTION_NO_WITNESS);
+ UnserializeFromVector(os, non_witness_utxo);
+ break;
+ }
+ case PSBT_IN_WITNESS_UTXO:
+ if (!witness_utxo.IsNull()) {
+ throw std::ios_base::failure("Duplicate Key, input witness utxo already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Witness utxo key is more than one byte type");
+ }
+ UnserializeFromVector(s, witness_utxo);
+ break;
+ case PSBT_IN_PARTIAL_SIG:
+ {
+ // Make sure that the key is the size of pubkey + 1
+ if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) {
+ throw std::ios_base::failure("Size of key was not the expected size for the type partial signature pubkey");
+ }
+ // Read in the pubkey from key
+ CPubKey pubkey(key.begin() + 1, key.end());
+ if (!pubkey.IsFullyValid()) {
+ throw std::ios_base::failure("Invalid pubkey");
+ }
+ if (partial_sigs.count(pubkey.GetID()) > 0) {
+ throw std::ios_base::failure("Duplicate Key, input partial signature for pubkey already provided");
+ }
+
+ // Read in the signature from value
+ std::vector<unsigned char> sig;
+ s >> sig;
+
+ // Add to list
+ partial_sigs.emplace(pubkey.GetID(), SigPair(pubkey, std::move(sig)));
+ break;
+ }
+ case PSBT_IN_SIGHASH:
+ if (sighash_type > 0) {
+ throw std::ios_base::failure("Duplicate Key, input sighash type already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Sighash type key is more than one byte type");
+ }
+ UnserializeFromVector(s, sighash_type);
+ break;
+ case PSBT_IN_REDEEMSCRIPT:
+ {
+ if (!redeem_script.empty()) {
+ throw std::ios_base::failure("Duplicate Key, input redeemScript already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Input redeemScript key is more than one byte type");
+ }
+ s >> redeem_script;
+ break;
+ }
+ case PSBT_IN_WITNESSSCRIPT:
+ {
+ if (!witness_script.empty()) {
+ throw std::ios_base::failure("Duplicate Key, input witnessScript already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Input witnessScript key is more than one byte type");
+ }
+ s >> witness_script;
+ break;
+ }
+ case PSBT_IN_BIP32_DERIVATION:
+ {
+ DeserializeHDKeypaths(s, key, hd_keypaths);
+ break;
+ }
+ case PSBT_IN_SCRIPTSIG:
+ {
+ if (!final_script_sig.empty()) {
+ throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Final scriptSig key is more than one byte type");
+ }
+ s >> final_script_sig;
+ break;
+ }
+ case PSBT_IN_SCRIPTWITNESS:
+ {
+ if (!final_script_witness.IsNull()) {
+ throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Final scriptWitness key is more than one byte type");
+ }
+ UnserializeFromVector(s, final_script_witness.stack);
+ break;
+ }
+ // Unknown stuff
+ default:
+ if (unknown.count(key) > 0) {
+ throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
+ }
+ // Read in the value
+ std::vector<unsigned char> val_bytes;
+ s >> val_bytes;
+ unknown.emplace(std::move(key), std::move(val_bytes));
+ break;
+ }
+ }
+
+ if (!found_sep) {
+ throw std::ios_base::failure("Separator is missing at the end of an input map");
+ }
+ }
+
+ template <typename Stream>
+ PSBTInput(deserialize_type, Stream& s) {
+ Unserialize(s);
+ }
+};
+
+/** A structure for PSBTs which contains per output information */
+struct PSBTOutput
+{
+ CScript redeem_script;
+ CScript witness_script;
+ std::map<CPubKey, KeyOriginInfo> hd_keypaths;
+ std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
+
+ bool IsNull() const;
+ void FillSignatureData(SignatureData& sigdata) const;
+ void FromSignatureData(const SignatureData& sigdata);
+ void Merge(const PSBTOutput& output);
+ bool IsSane() const;
+ PSBTOutput() {}
+
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+ // Write the redeem script
+ if (!redeem_script.empty()) {
+ SerializeToVector(s, PSBT_OUT_REDEEMSCRIPT);
+ s << redeem_script;
+ }
+
+ // Write the witness script
+ if (!witness_script.empty()) {
+ SerializeToVector(s, PSBT_OUT_WITNESSSCRIPT);
+ s << witness_script;
+ }
+
+ // Write any hd keypaths
+ SerializeHDKeypaths(s, hd_keypaths, PSBT_OUT_BIP32_DERIVATION);
+
+ // Write unknown things
+ for (auto& entry : unknown) {
+ s << entry.first;
+ s << entry.second;
+ }
+
+ s << PSBT_SEPARATOR;
+ }
+
+
+ template <typename Stream>
+ inline void Unserialize(Stream& s) {
+ // Read loop
+ bool found_sep = false;
+ while(!s.empty()) {
+ // Read
+ std::vector<unsigned char> key;
+ s >> key;
+
+ // the key is empty if that was actually a separator byte
+ // This is a special case for key lengths 0 as those are not allowed (except for separator)
+ if (key.empty()) {
+ found_sep = true;
+ break;
+ }
+
+ // First byte of key is the type
+ unsigned char type = key[0];
+
+ // Do stuff based on type
+ switch(type) {
+ case PSBT_OUT_REDEEMSCRIPT:
+ {
+ if (!redeem_script.empty()) {
+ throw std::ios_base::failure("Duplicate Key, output redeemScript already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Output redeemScript key is more than one byte type");
+ }
+ s >> redeem_script;
+ break;
+ }
+ case PSBT_OUT_WITNESSSCRIPT:
+ {
+ if (!witness_script.empty()) {
+ throw std::ios_base::failure("Duplicate Key, output witnessScript already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Output witnessScript key is more than one byte type");
+ }
+ s >> witness_script;
+ break;
+ }
+ case PSBT_OUT_BIP32_DERIVATION:
+ {
+ DeserializeHDKeypaths(s, key, hd_keypaths);
+ break;
+ }
+ // Unknown stuff
+ default: {
+ if (unknown.count(key) > 0) {
+ throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
+ }
+ // Read in the value
+ std::vector<unsigned char> val_bytes;
+ s >> val_bytes;
+ unknown.emplace(std::move(key), std::move(val_bytes));
+ break;
+ }
+ }
+ }
+
+ if (!found_sep) {
+ throw std::ios_base::failure("Separator is missing at the end of an output map");
+ }
+ }
+
+ template <typename Stream>
+ PSBTOutput(deserialize_type, Stream& s) {
+ Unserialize(s);
+ }
+};
+
+/** A version of CTransaction with the PSBT format*/
+struct PartiallySignedTransaction
+{
+ boost::optional<CMutableTransaction> tx;
+ std::vector<PSBTInput> inputs;
+ std::vector<PSBTOutput> outputs;
+ std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
+
+ bool IsNull() const;
+
+ /** Merge psbt into this. The two psbts must have the same underlying CTransaction (i.e. the
+ * same actual Bitcoin transaction.) Returns true if the merge succeeded, false otherwise. */
+ NODISCARD bool Merge(const PartiallySignedTransaction& psbt);
+ bool IsSane() const;
+ bool AddInput(const CTxIn& txin, PSBTInput& psbtin);
+ bool AddOutput(const CTxOut& txout, const PSBTOutput& psbtout);
+ PartiallySignedTransaction() {}
+ PartiallySignedTransaction(const PartiallySignedTransaction& psbt_in) : tx(psbt_in.tx), inputs(psbt_in.inputs), outputs(psbt_in.outputs), unknown(psbt_in.unknown) {}
+ explicit PartiallySignedTransaction(const CMutableTransaction& tx);
+ /**
+ * Finds the UTXO for a given input index
+ *
+ * @param[out] utxo The UTXO of the input if found
+ * @param[in] input_index Index of the input to retrieve the UTXO of
+ * @return Whether the UTXO for the specified input was found
+ */
+ bool GetInputUTXO(CTxOut& utxo, int input_index) const;
+
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+
+ // magic bytes
+ s << PSBT_MAGIC_BYTES;
+
+ // unsigned tx flag
+ SerializeToVector(s, PSBT_GLOBAL_UNSIGNED_TX);
+
+ // Write serialized tx to a stream
+ OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
+ SerializeToVector(os, *tx);
+
+ // Write the unknown things
+ for (auto& entry : unknown) {
+ s << entry.first;
+ s << entry.second;
+ }
+
+ // Separator
+ s << PSBT_SEPARATOR;
+
+ // Write inputs
+ for (const PSBTInput& input : inputs) {
+ s << input;
+ }
+ // Write outputs
+ for (const PSBTOutput& output : outputs) {
+ s << output;
+ }
+ }
+
+
+ template <typename Stream>
+ inline void Unserialize(Stream& s) {
+ // Read the magic bytes
+ uint8_t magic[5];
+ s >> magic;
+ if (!std::equal(magic, magic + 5, PSBT_MAGIC_BYTES)) {
+ throw std::ios_base::failure("Invalid PSBT magic bytes");
+ }
+
+ // Read global data
+ bool found_sep = false;
+ while(!s.empty()) {
+ // Read
+ std::vector<unsigned char> key;
+ s >> key;
+
+ // the key is empty if that was actually a separator byte
+ // This is a special case for key lengths 0 as those are not allowed (except for separator)
+ if (key.empty()) {
+ found_sep = true;
+ break;
+ }
+
+ // First byte of key is the type
+ unsigned char type = key[0];
+
+ // Do stuff based on type
+ switch(type) {
+ case PSBT_GLOBAL_UNSIGNED_TX:
+ {
+ if (tx) {
+ throw std::ios_base::failure("Duplicate Key, unsigned tx already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Global unsigned tx key is more than one byte type");
+ }
+ CMutableTransaction mtx;
+ // Set the stream to serialize with non-witness since this should always be non-witness
+ OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
+ UnserializeFromVector(os, mtx);
+ tx = std::move(mtx);
+ // Make sure that all scriptSigs and scriptWitnesses are empty
+ for (const CTxIn& txin : tx->vin) {
+ if (!txin.scriptSig.empty() || !txin.scriptWitness.IsNull()) {
+ throw std::ios_base::failure("Unsigned tx does not have empty scriptSigs and scriptWitnesses.");
+ }
+ }
+ break;
+ }
+ // Unknown stuff
+ default: {
+ if (unknown.count(key) > 0) {
+ throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
+ }
+ // Read in the value
+ std::vector<unsigned char> val_bytes;
+ s >> val_bytes;
+ unknown.emplace(std::move(key), std::move(val_bytes));
+ }
+ }
+ }
+
+ if (!found_sep) {
+ throw std::ios_base::failure("Separator is missing at the end of the global map");
+ }
+
+ // Make sure that we got an unsigned tx
+ if (!tx) {
+ throw std::ios_base::failure("No unsigned transcation was provided");
+ }
+
+ // Read input data
+ unsigned int i = 0;
+ while (!s.empty() && i < tx->vin.size()) {
+ PSBTInput input;
+ s >> input;
+ inputs.push_back(input);
+
+ // Make sure the non-witness utxo matches the outpoint
+ if (input.non_witness_utxo && input.non_witness_utxo->GetHash() != tx->vin[i].prevout.hash) {
+ throw std::ios_base::failure("Non-witness UTXO does not match outpoint hash");
+ }
+ ++i;
+ }
+ // Make sure that the number of inputs matches the number of inputs in the transaction
+ if (inputs.size() != tx->vin.size()) {
+ throw std::ios_base::failure("Inputs provided does not match the number of inputs in transaction.");
+ }
+
+ // Read output data
+ i = 0;
+ while (!s.empty() && i < tx->vout.size()) {
+ PSBTOutput output;
+ s >> output;
+ outputs.push_back(output);
+ ++i;
+ }
+ // Make sure that the number of outputs matches the number of outputs in the transaction
+ if (outputs.size() != tx->vout.size()) {
+ throw std::ios_base::failure("Outputs provided does not match the number of outputs in transaction.");
+ }
+ // Sanity check
+ if (!IsSane()) {
+ throw std::ios_base::failure("PSBT is not sane.");
+ }
+ }
+
+ template <typename Stream>
+ PartiallySignedTransaction(deserialize_type, Stream& s) {
+ Unserialize(s);
+ }
+};
+
+enum class PSBTRole {
+ UPDATER,
+ SIGNER,
+ FINALIZER,
+ EXTRACTOR
+};
+
+std::string PSBTRoleName(PSBTRole role);
+
+/** Checks whether a PSBTInput is already signed. */
+bool PSBTInputSigned(const PSBTInput& input);
+
+/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
+bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool use_dummy = false);
+
+/**
+ * Finalizes a PSBT if possible, combining partial signatures.
+ *
+ * @param[in,out] &psbtx reference to PartiallySignedTransaction to finalize
+ * return True if the PSBT is now complete, false otherwise
+ */
+bool FinalizePSBT(PartiallySignedTransaction& psbtx);
+
+/**
+ * Finalizes a PSBT if possible, and extracts it to a CMutableTransaction if it could be finalized.
+ *
+ * @param[in] &psbtx reference to PartiallySignedTransaction
+ * @param[out] result CMutableTransaction representing the complete transaction, if successful
+ * @return True if we successfully extracted the transaction, false otherwise
+ */
+bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransaction& result);
+
+/**
+ * Combines PSBTs with the same underlying transaction, resulting in a single PSBT with all partial signatures from each input.
+ *
+ * @param[out] &out the combined PSBT, if successful
+ * @param[in] psbtxs the PSBTs to combine
+ * @return error (OK if we successfully combined the transactions, other error if they were not compatible)
+ */
+NODISCARD TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs);
+
+//! Decode a base64ed PSBT into a PartiallySignedTransaction
+NODISCARD bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error);
+//! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction
+NODISCARD bool DecodeRawPSBT(PartiallySignedTransaction& decoded_psbt, const std::string& raw_psbt, std::string& error);
+
+#endif // BITCOIN_PSBT_H
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 726dafccb0..d8c39e8862 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -10,7 +10,6 @@
#include <qt/forms/ui_addressbookpage.h>
#include <qt/addresstablemodel.h>
-#include <qt/bitcoingui.h>
#include <qt/csvmodelwriter.h>
#include <qt/editaddressdialog.h>
#include <qt/guiutil.h>
@@ -107,7 +106,7 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode,
ui->newAddress->setVisible(true);
break;
case ReceivingTab:
- ui->labelExplanation->setText(tr("These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction."));
+ ui->labelExplanation->setText(tr("These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses."));
ui->deleteAddress->setVisible(false);
ui->newAddress->setVisible(false);
break;
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index c3143e948a..fa6c9c9f7a 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -7,7 +7,6 @@
#include <qt/guiutil.h>
#include <qt/walletmodel.h>
-#include <interfaces/node.h>
#include <key_io.h>
#include <wallet/wallet.h>
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index 00c446cc63..8a6b205cd8 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -5,8 +5,6 @@
#include <qt/bantablemodel.h>
#include <qt/clientmodel.h>
-#include <qt/guiconstants.h>
-#include <qt/guiutil.h>
#include <interfaces/node.h>
#include <sync.h>
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index ca26131b95..0c232f89fe 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -30,13 +30,10 @@
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <noui.h>
-#include <rpc/server.h>
+#include <util/threadnames.h>
#include <ui_interface.h>
#include <uint256.h>
#include <util/system.h>
-#include <warnings.h>
-
-#include <walletinitinterface.h>
#include <memory>
#include <stdint.h>
@@ -55,9 +52,6 @@
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
-#if QT_VERSION < 0x050400
-Q_IMPORT_PLUGIN(AccessibleFactory)
-#endif
#if defined(QT_QPA_PLATFORM_XCB)
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
#elif defined(QT_QPA_PLATFORM_WINDOWS)
@@ -152,6 +146,7 @@ void BitcoinCore::initialize()
try
{
qDebug() << __func__ << ": Running initialization in thread";
+ util::ThreadRename("qt-init");
bool rv = m_node.appInitMain();
Q_EMIT initializeResult(rv);
} catch (const std::exception& e) {
@@ -218,6 +213,8 @@ BitcoinApplication::~BitcoinApplication()
#ifdef ENABLE_WALLET
delete paymentServer;
paymentServer = nullptr;
+ delete m_wallet_controller;
+ m_wallet_controller = nullptr;
#endif
delete optionsModel;
optionsModel = nullptr;
@@ -307,18 +304,20 @@ void BitcoinApplication::requestShutdown()
qDebug() << __func__ << ": Requesting shutdown";
startThread();
window->hide();
+ // Must disconnect node signals otherwise current thread can deadlock since
+ // no event loop is running.
+ window->unsubscribeFromCoreSignals();
+ // Request node shutdown, which can interrupt long operations, like
+ // rescanning a wallet.
+ m_node.startShutdown();
+ // Unsetting the client model can cause the current thread to wait for node
+ // to complete an operation, like wait for a RPC execution to complate.
window->setClientModel(nullptr);
pollShutdownTimer->stop();
-#ifdef ENABLE_WALLET
- delete m_wallet_controller;
- m_wallet_controller = nullptr;
-#endif
delete clientModel;
clientModel = nullptr;
- m_node.startShutdown();
-
// Request shutdown from core thread
Q_EMIT requestedShutdown();
}
@@ -422,6 +421,7 @@ int GuiMain(int argc, char* argv[])
std::tie(argc, argv) = winArgs.get();
#endif
SetupEnvironment();
+ util::ThreadRename("main");
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
@@ -452,7 +452,7 @@ int GuiMain(int argc, char* argv[])
// IMPORTANT if it is no longer a typedef use the normal variant above
qRegisterMetaType< CAmount >("CAmount");
qRegisterMetaType< std::function<void()> >("std::function<void()>");
-
+ qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
node->setupServerArgs();
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index ba7e8c7daf..1444dddeb1 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -334,6 +334,13 @@ void BitcoinGUI::createActions()
openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("Open &URI..."), this);
openAction->setStatusTip(tr("Open a bitcoin: URI or payment request"));
+ m_open_wallet_action = new QAction(tr("Open Wallet"), this);
+ m_open_wallet_action->setEnabled(false);
+ m_open_wallet_action->setStatusTip(tr("Open a wallet"));
+
+ m_close_wallet_action = new QAction(tr("Close Wallet..."), this);
+ m_close_wallet_action->setStatusTip(tr("Close wallet"));
+
showHelpMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/info"), tr("&Command-line options"), this);
showHelpMessageAction->setMenuRole(QAction::NoRole);
showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Bitcoin command-line options").arg(tr(PACKAGE_NAME)));
@@ -361,6 +368,55 @@ void BitcoinGUI::createActions()
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses);
connect(openAction, &QAction::triggered, this, &BitcoinGUI::openClicked);
+ connect(m_open_wallet_action->menu(), &QMenu::aboutToShow, [this] {
+ m_open_wallet_action->menu()->clear();
+ std::vector<std::string> available_wallets = m_wallet_controller->getWalletsAvailableToOpen();
+ std::vector<std::string> wallets = m_node.listWalletDir();
+ for (const auto& path : wallets) {
+ QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
+ QAction* action = m_open_wallet_action->menu()->addAction(name);
+
+ if (std::find(available_wallets.begin(), available_wallets.end(), path) == available_wallets.end()) {
+ // This wallet is already loaded
+ action->setEnabled(false);
+ continue;
+ }
+
+ connect(action, &QAction::triggered, [this, name, path] {
+ OpenWalletActivity* activity = m_wallet_controller->openWallet(path);
+
+ QProgressDialog* dialog = new QProgressDialog(this);
+ dialog->setLabelText(tr("Opening Wallet <b>%1</b>...").arg(name.toHtmlEscaped()));
+ dialog->setRange(0, 0);
+ dialog->setCancelButton(nullptr);
+ dialog->setWindowModality(Qt::ApplicationModal);
+ dialog->show();
+
+ connect(activity, &OpenWalletActivity::message, this, [this] (QMessageBox::Icon icon, QString text) {
+ QMessageBox box;
+ box.setIcon(icon);
+ box.setText(tr("Open Wallet Failed"));
+ box.setInformativeText(text);
+ box.setStandardButtons(QMessageBox::Ok);
+ box.setDefaultButton(QMessageBox::Ok);
+ connect(this, &QObject::destroyed, &box, &QDialog::accept);
+ box.exec();
+ });
+ connect(activity, &OpenWalletActivity::opened, this, &BitcoinGUI::setCurrentWallet);
+ connect(activity, &OpenWalletActivity::finished, activity, &QObject::deleteLater);
+ connect(activity, &OpenWalletActivity::finished, dialog, &QObject::deleteLater);
+ bool invoked = QMetaObject::invokeMethod(activity, "open");
+ assert(invoked);
+ });
+ }
+ if (wallets.empty()) {
+ QAction* action = m_open_wallet_action->menu()->addAction(tr("No wallets available"));
+ action->setEnabled(false);
+ }
+ });
+ connect(m_close_wallet_action, &QAction::triggered, [this] {
+ m_wallet_controller->closeWallet(walletFrame->currentWalletModel(), this);
+ });
}
#endif // ENABLE_WALLET
@@ -382,6 +438,9 @@ void BitcoinGUI::createMenuBar()
QMenu *file = appMenuBar->addMenu(tr("&File"));
if(walletFrame)
{
+ file->addAction(m_open_wallet_action);
+ file->addAction(m_close_wallet_action);
+ file->addSeparator();
file->addAction(openAction);
file->addAction(backupWalletAction);
file->addAction(signMessageAction);
@@ -574,6 +633,9 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller)
m_wallet_controller = wallet_controller;
+ m_open_wallet_action->setEnabled(true);
+ m_open_wallet_action->setMenu(new QMenu(this));
+
connect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet);
connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet);
@@ -656,6 +718,7 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled)
usedSendingAddressesAction->setEnabled(enabled);
usedReceivingAddressesAction->setEnabled(enabled);
openAction->setEnabled(enabled);
+ m_close_wallet_action->setEnabled(enabled);
}
void BitcoinGUI::createTrayIcon()
@@ -1281,6 +1344,7 @@ void BitcoinGUI::showProgress(const QString &title, int nProgress)
if (progressDialog) {
progressDialog->close();
progressDialog->deleteLater();
+ progressDialog = nullptr;
}
} else if (progressDialog) {
progressDialog->setValue(nProgress);
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index f1b76a6b64..b58ccbb455 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -95,6 +95,9 @@ public:
*/
bool hasTrayIcon() const { return trayIcon; }
+ /** Disconnect core signals from GUI client */
+ void unsubscribeFromCoreSignals();
+
protected:
void changeEvent(QEvent *e);
void closeEvent(QCloseEvent *event);
@@ -144,6 +147,8 @@ private:
QAction* openRPCConsoleAction = nullptr;
QAction* openAction = nullptr;
QAction* showHelpMessageAction = nullptr;
+ QAction* m_open_wallet_action{nullptr};
+ QAction* m_close_wallet_action{nullptr};
QAction* m_wallet_selector_label_action = nullptr;
QAction* m_wallet_selector_action = nullptr;
@@ -184,8 +189,6 @@ private:
/** Connect core signals to GUI client */
void subscribeToCoreSignals();
- /** Disconnect core signals from GUI client */
- void unsubscribeFromCoreSignals();
/** Update UI with latest network info from model. */
void updateNetworkState();
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index de60b30dfe..dc997e96cc 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -30,8 +30,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Distributed under the MIT software license, see the accompanying file %s or "
"%s"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Error loading %s: You can't enable HD on an already existing non-HD wallet"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error reading %s! All keys read correctly, but transaction data or address "
"book entries might be missing or incorrect."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -40,12 +38,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -"
"fallbackfee."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Group outputs by address, selecting all or none, instead of selecting on a "
-"per-output basis. Privacy is improved as an address is only used once "
-"(unless someone sends to it after spending from it), but may result in "
-"slightly higher fees as suboptimal coin selection may result due to the "
-"added limitation (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay "
"fee of %s to prevent stuck transactions)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -91,12 +83,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Unable to rewind the database to a pre-fork state. You will need to "
"redownload the blockchain"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Unsupported argument -socks found. Setting SOCKS version isn't possible "
-"anymore, only SOCKS5 proxies are supported."),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/"
-"or -whitelistforcerelay."),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"Warning: Private keys detected in wallet {%s} with disabled private keys"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Warning: The network does not appear to fully agree! Some miners appear to "
@@ -122,18 +108,17 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Cannot downgrade wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -%s address: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write to data directory '%s'; check permissions."),
QT_TRANSLATE_NOOP("bitcoin-core", "Change index out of range"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Config setting for %s only applied on %s network when in [%s] section."),
QT_TRANSLATE_NOOP("bitcoin-core", "Copyright (C) %i-%i"),
QT_TRANSLATE_NOOP("bitcoin-core", "Corrupted block database detected"),
QT_TRANSLATE_NOOP("bitcoin-core", "Do you want to rebuild the block database now?"),
QT_TRANSLATE_NOOP("bitcoin-core", "Done loading"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Error creating %s: You can't create non-HD wallets with this version."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing wallet database environment %s!"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Private keys can only be disabled during creation"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Wallet corrupted"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Wallet requires newer version of %s"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: You can't disable HD on an already existing HD wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet %s. Duplicate -wallet filename specified."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error opening block database"),
@@ -141,6 +126,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error reading from database, shutting down.")
QT_TRANSLATE_NOOP("bitcoin-core", "Error upgrading chainstate database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: A fatal internal error occurred, see debug.log for details"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low for %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to rescan the wallet during initialization"),
@@ -170,6 +156,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Reducing -maxconnections from %d to %d, becau
QT_TRANSLATE_NOOP("bitcoin-core", "Replaying blocks..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Rewinding blocks..."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Section [%s] is not recognized."),
QT_TRANSLATE_NOOP("bitcoin-core", "Signing transaction failed"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specified -walletdir \"%s\" does not exist"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specified -walletdir \"%s\" is a relative path"),
@@ -177,6 +164,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Specified -walletdir \"%s\" is not a director
QT_TRANSLATE_NOOP("bitcoin-core", "Specified blocks directory \"%s\" does not exist."),
QT_TRANSLATE_NOOP("bitcoin-core", "Starting network threads..."),
QT_TRANSLATE_NOOP("bitcoin-core", "The source code is available from %s."),
+QT_TRANSLATE_NOOP("bitcoin-core", "The specified config file %s does not exist\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "The transaction amount is too small to pay the fee"),
QT_TRANSLATE_NOOP("bitcoin-core", "The wallet will avoid paying less than the minimum relay fee."),
QT_TRANSLATE_NOOP("bitcoin-core", "This is experimental software."),
@@ -195,9 +183,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate initial keys"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate keys"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to start HTTP server. See debug log for details."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -benchmark ignored, use -debug=bench."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -debugnet ignored, use -debug=net."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -tor found, use -onion."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported logging category %s=%s."),
QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading UTXO database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading txindex database"),
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index 8aa1fdba4d..b27f8a744f 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -4,8 +4,6 @@
#include <qt/bitcoinunits.h>
-#include <primitives/transaction.h>
-
#include <QStringList>
BitcoinUnits::BitcoinUnits(QObject *parent):
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 30d9e977b8..ce950150df 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -9,19 +9,12 @@
#include <qt/guiutil.h>
#include <qt/peertablemodel.h>
-#include <chain.h>
-#include <chainparams.h>
-#include <checkpoints.h>
#include <clientversion.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
-#include <validation.h>
#include <net.h>
#include <netbase.h>
-#include <txmempool.h>
-#include <ui_interface.h>
#include <util/system.h>
-#include <warnings.h>
#include <stdint.h>
@@ -237,8 +230,8 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int heig
clientmodel->cachedBestHeaderHeight = height;
clientmodel->cachedBestHeaderTime = blockTime;
}
- // if we are in-sync, update the UI regardless of last update time
- if (!initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) {
+ // if we are in-sync or if we notify a header update, update the UI regardless of last update time
+ if (fHeader || !initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) {
//pass an async signal to the UI thread
QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
Q_ARG(int, height),
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 0d9f1adcd2..6b9f79aaf8 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -10,12 +10,10 @@
#include <qt/forms/ui_coincontroldialog.h>
#include <qt/addresstablemodel.h>
-#include <base58.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>
@@ -23,8 +21,6 @@
#include <key_io.h>
#include <policy/fees.h>
#include <policy/policy.h>
-#include <validation.h> // For mempool
-#include <wallet/fees.h>
#include <wallet/wallet.h>
#include <QApplication>
@@ -471,8 +467,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
else if(ExtractDestination(out.txout.scriptPubKey, address))
{
CPubKey pubkey;
- CKeyID *keyid = boost::get<CKeyID>(&address);
- if (keyid && model->wallet().getPubKey(*keyid, pubkey))
+ PKHash *pkhash = boost::get<PKHash>(&address);
+ if (pkhash && model->wallet().getPubKey(CKeyID(*pkhash), pubkey))
{
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
}
diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui
index bd7f3c5f56..5ce469ee96 100644
--- a/src/qt/forms/coincontroldialog.ui
+++ b/src/qt/forms/coincontroldialog.ui
@@ -467,12 +467,6 @@
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index f0b976001e..6e52c5e477 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -636,6 +636,9 @@
<property name="placeholderText">
<string/>
</property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
</widget>
</item>
</layout>
@@ -1480,6 +1483,19 @@
</property>
</widget>
</item>
+ <item row="18" column="0">
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
</widget>
diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 2f916d0b44..0d280f2993 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -108,7 +108,7 @@
</size>
</property>
<property name="text">
- <string>&amp;Request payment</string>
+ <string>&amp;Create new receiving address</string>
</property>
<property name="icon">
<iconset resource="../bitcoin.qrc">
@@ -189,7 +189,7 @@
</widget>
</item>
<item>
- <widget class="QCheckBox" name="useBech32">
+ <widget class="QCheckBox" name="useLegacyAddress">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
@@ -206,10 +206,10 @@
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
- <string>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</string>
+ <string>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When checked, an address compatible with older wallets will be created instead.</string>
</property>
<property name="text">
- <string>Generate native segwit (Bech32) address</string>
+ <string>Generate legacy address</string>
</property>
</widget>
</item>
@@ -360,7 +360,7 @@
<tabstops>
<tabstop>reqLabel</tabstop>
<tabstop>reqAmount</tabstop>
- <tabstop>useBech32</tabstop>
+ <tabstop>useLegacyAddress</tabstop>
<tabstop>reqMessage</tabstop>
<tabstop>receiveButton</tabstop>
<tabstop>clearButton</tabstop>
diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui
index dbe966b241..9f896ee3b1 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>qt/receiverequestdialog.h</header>
+ <header>qt/qrimagewidget.h</header>
</customwidget>
</customwidgets>
<resources/>
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h
index 736ff13a4a..d8f5594983 100644
--- a/src/qt/guiconstants.h
+++ b/src/qt/guiconstants.h
@@ -37,12 +37,6 @@ static const bool DEFAULT_SPLASHSCREEN = true;
*/
static const int TOOLTIP_WRAP_THRESHOLD = 80;
-/* Maximum allowed URI length */
-static const int MAX_URI_LENGTH = 255;
-
-/* QRCodeDialog -- size of exported QR Code image */
-#define QR_IMAGE_SIZE 300
-
/* Number of frames in spinner animation */
#define SPINNER_FRAMES 36
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 71e987c8f4..70e52c9f1d 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -21,10 +21,6 @@
#include <util/system.h>
#ifdef WIN32
-#ifdef _WIN32_WINNT
-#undef _WIN32_WINNT
-#endif
-#define _WIN32_WINNT 0x0501
#ifdef _WIN32_IE
#undef _WIN32_IE
#endif
@@ -64,6 +60,7 @@
#include <objc/objc-runtime.h>
#include <CoreServices/CoreServices.h>
+#include <QProcess>
#endif
namespace GUIUtil {
@@ -179,7 +176,9 @@ bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
QString formatBitcoinURI(const SendCoinsRecipient &info)
{
- QString ret = QString("bitcoin:%1").arg(info.address);
+ bool bech_32 = info.address.startsWith(QString::fromStdString(Params().Bech32HRP() + "1"));
+
+ QString ret = QString("bitcoin:%1").arg(bech_32 ? info.address.toUpper() : info.address);
int paramCount = 0;
if (info.amount)
@@ -248,6 +247,11 @@ QList<QModelIndex> getEntryData(QAbstractItemView *view, int column)
return view->selectionModel()->selectedRows(column);
}
+QString getDefaultDataDirectory()
+{
+ return boostPathToQString(GetDefaultDataDir());
+}
+
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
const QString &filter,
QString *selectedSuffixOut)
@@ -396,7 +400,15 @@ bool openBitcoinConf()
configFile.close();
/* Open bitcoin.conf with the associated application */
- return QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig)));
+ bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig)));
+#ifdef Q_OS_MAC
+ // Workaround for macOS-specific behavior; see #15409.
+ if (!res) {
+ res = QProcess::startDetached("/usr/bin/open", QStringList{"-t", boostPathToQString(pathConfig)});
+ }
+#endif
+
+ return res;
}
ToolTipToRichTextFilter::ToolTipToRichTextFilter(int _size_threshold, QObject *parent) :
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index cbec73a882..bea4a83494 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -79,6 +79,11 @@ namespace GUIUtil
void setClipboard(const QString& str);
+ /**
+ * Determine default data directory for operating system.
+ */
+ QString getDefaultDataDirectory();
+
/** Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix
when no suffix is provided by the user.
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 499af9fa07..c595361934 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -168,7 +168,7 @@ QString Intro::getDataDirectory()
void Intro::setDataDirectory(const QString &dataDir)
{
ui->dataDirectory->setText(dataDir);
- if(dataDir == getDefaultDataDirectory())
+ if(dataDir == GUIUtil::getDefaultDataDirectory())
{
ui->dataDirDefault->setChecked(true);
ui->dataDirectory->setEnabled(false);
@@ -180,11 +180,6 @@ void Intro::setDataDirectory(const QString &dataDir)
}
}
-QString Intro::getDefaultDataDirectory()
-{
- return GUIUtil::boostPathToQString(GetDefaultDataDir());
-}
-
bool Intro::pickDataDirectory(interfaces::Node& node)
{
QSettings settings;
@@ -193,7 +188,7 @@ bool Intro::pickDataDirectory(interfaces::Node& node)
if(!gArgs.GetArg("-datadir", "").empty())
return true;
/* 1) Default data directory for operating system */
- QString dataDir = getDefaultDataDirectory();
+ QString dataDir = GUIUtil::getDefaultDataDirectory();
/* 2) Allow QSettings to override default dir */
dataDir = settings.value("strDataDir", dataDir).toString();
@@ -239,7 +234,7 @@ bool Intro::pickDataDirectory(interfaces::Node& node)
* override -datadir in the bitcoin.conf file in the default data directory
* (to be consistent with bitcoind behavior)
*/
- if(dataDir != getDefaultDataDirectory()) {
+ if(dataDir != GUIUtil::getDefaultDataDirectory()) {
node.softSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
}
return true;
@@ -293,7 +288,7 @@ void Intro::on_ellipsisButton_clicked()
void Intro::on_dataDirDefault_clicked()
{
- setDataDirectory(getDefaultDataDirectory());
+ setDataDirectory(GUIUtil::getDefaultDataDirectory());
}
void Intro::on_dataDirCustom_clicked()
diff --git a/src/qt/intro.h b/src/qt/intro.h
index b537c94f7d..c3b26808d4 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -48,11 +48,6 @@ public:
*/
static bool pickDataDirectory(interfaces::Node& node);
- /**
- * Determine default data directory for operating system.
- */
- static QString getDefaultDataDirectory();
-
Q_SIGNALS:
void requestCheck();
diff --git a/src/qt/locale/bitcoin_af.ts b/src/qt/locale/bitcoin_af.ts
index bff2db1d6a..c7e7fdd56a 100644
--- a/src/qt/locale/bitcoin_af.ts
+++ b/src/qt/locale/bitcoin_af.ts
@@ -1205,6 +1205,10 @@
</context>
<context>
<name>QRImageWidget</name>
+ <message>
+ <source>&amp;Save Image...</source>
+ <translation>&amp;Stoor beeld</translation>
+ </message>
</context>
<context>
<name>RPCConsole</name>
@@ -1364,6 +1368,10 @@
<translation>&amp;Boodslap:</translation>
</message>
<message>
+ <source>Clear all fields of the form.</source>
+ <translation>Vee alle velde op die vorm skoon</translation>
+ </message>
+ <message>
<source>Clear</source>
<translation>Skoonmaak</translation>
</message>
@@ -1399,10 +1407,18 @@
<translation>QR Kode</translation>
</message>
<message>
+ <source>Copy &amp;URI</source>
+ <translation>Kopieer &amp;URI</translation>
+ </message>
+ <message>
<source>Copy &amp;Address</source>
<translation>Kopieer &amp;Address</translation>
</message>
<message>
+ <source>&amp;Save Image...</source>
+ <translation>&amp;Stoor beeld</translation>
+ </message>
+ <message>
<source>Request payment to %1</source>
<translation>Versoek betaling van %1</translation>
</message>
@@ -1434,7 +1450,15 @@
<source>Wallet</source>
<translation>Beursie</translation>
</message>
- </context>
+ <message>
+ <source>Resulting URI too long, try to reduce the text for label / message.</source>
+ <translation>Gevolglike URI te lank, probeer teks verkort vir etiket/boodskap</translation>
+ </message>
+ <message>
+ <source>Error encoding URI into QR Code.</source>
+ <translation>Fout met enkodering van URI na QR kode</translation>
+ </message>
+</context>
<context>
<name>RecentRequestsTableModel</name>
<message>
@@ -1521,6 +1545,20 @@
<translation>Kies...</translation>
</message>
<message>
+ <source>Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until you have validated the complete chain.</source>
+ <translation>Die verstekfooi kan veroorsaak dat 'n transaksie gestuur word wat
+etlike ure of dae (of nooit) sal neem om te bevestig. Oorweeg om
+'n fooi met die hand te kies, of wag tot jy die hele ketting bevestig het.</translation>
+ </message>
+ <message>
+ <source>Warning: Fee estimation is currently not possible.</source>
+ <translation>Waarskuwing: fooiskatting is tans onbeskikbaar</translation>
+ </message>
+ <message>
+ <source>collapse fee-settings</source>
+ <translation>Vou fooi-nistellings in</translation>
+ </message>
+ <message>
<source>per kilobyte</source>
<translation>per kilogreep</translation>
</message>
@@ -1529,6 +1567,13 @@
<translation>Versteek</translation>
</message>
<message>
+ <source>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
+ <translation>Om die minimumfooi te betaal is in die haak, mits daar minder transaksievolume
+as ruimte in die blok is. Wees bewus dat de gevolg kan wees dat 'n transaksie nooit
+bevestig nie indien daar meer aanvraag vir bitcoin transaksies is as wat die netwerk kan
+verwerk.</translation>
+ </message>
+ <message>
<source>Recommended:</source>
<translation>Aanbeveel:</translation>
</message>
@@ -1537,14 +1582,40 @@
<translation>Aangepaste:</translation>
</message>
<message>
+ <source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
+ <translation>(Slimfooi nog nie opgestel nie. Dit neem gewoonlik 'n paar blokke...)</translation>
+ </message>
+ <message>
+ <source>Send to multiple recipients at once</source>
+ <translation>Stuur aan veelvuldige ontvangers tegelyk</translation>
+ </message>
+ <message>
<source>Add &amp;Recipient</source>
<translation>Voeg by &amp;Ontvanger</translation>
</message>
<message>
+ <source>Clear all fields of the form.</source>
+ <translation>Vee alle velde op die vorm skoon</translation>
+ </message>
+ <message>
<source>Dust:</source>
<translation>Stof:</translation>
</message>
<message>
+ <source>Confirmation time target:</source>
+ <translation>Bevestigingstyd teiken:</translation>
+ </message>
+ <message>
+ <source>Enable Replace-By-Fee</source>
+ <translation>Bemontlik vervang-deur-fooi</translation>
+ </message>
+ <message>
+ <source>With Replace-By-Fee (BIP-125) you can increase a transaction's fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</source>
+ <translation>Met Vervang-Met-Fooi (BIP-125) kan jy 'n transaskiefooi verhoog nadat dit gestuur is.
+Daarsonder mag 'n hoër fooi dalk aanbeveel word om te kompenseer vir 'n verhoogde
+transaksievertragingsrisiko.</translation>
+ </message>
+ <message>
<source>Clear &amp;All</source>
<translation>Maak skoon &amp;Alles</translation>
</message>
@@ -1553,6 +1624,10 @@
<translation>Balans:</translation>
</message>
<message>
+ <source>Confirm the send action</source>
+ <translation>Bevestig stuuraksie</translation>
+ </message>
+ <message>
<source>S&amp;end</source>
<translation>S&amp;tuur</translation>
</message>
@@ -1593,14 +1668,80 @@
<translation>%1 tot %2</translation>
</message>
<message>
+ <source>Are you sure you want to send?</source>
+ <translation>Is u seker u wil verstuur?</translation>
+ </message>
+ <message>
+ <source>or</source>
+ <translation>of</translation>
+ </message>
+ <message>
+ <source>You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
+ <translation>U kan die fooi later verhoog (sein Vervang-met-Fooi, BIP-125)</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>Transaksie fooi</translation>
</message>
<message>
+ <source>Not signalling Replace-By-Fee, BIP-125.</source>
+ <translation>Sein nie Vervang-Met-Fooi nie, BIP-25</translation>
+ </message>
+ <message>
+ <source>Confirm send coins</source>
+ <translation>Bevestig versending van munte</translation>
+ </message>
+ <message>
+ <source>The recipient address is not valid. Please recheck.</source>
+ <translation>Die ontvangeradres is ongeldig. Kyk asseblief weer mooi.</translation>
+ </message>
+ <message>
+ <source>The amount to pay must be larger than 0.</source>
+ <translation>Bedrag moet groter as nul wees</translation>
+ </message>
+ <message>
+ <source>The amount exceeds your balance.</source>
+ <translation>Die bedrag oorskry jou saldo</translation>
+ </message>
+ <message>
+ <source>The total exceeds your balance when the %1 transaction fee is included.</source>
+ <translation>Die somtotaal oorskry jou saldo as die %1 transaksiefooi ingereken word</translation>
+ </message>
+ <message>
+ <source>Duplicate address found: addresses should only be used once each.</source>
+ <translation>Duplikaatadres: adresse behoort slegs eenkeer gebruik te word</translation>
+ </message>
+ <message>
+ <source>Transaction creation failed!</source>
+ <translation>Transaksieopstelling het gefaal</translation>
+ </message>
+ <message>
+ <source>The transaction was rejected with the following reason: %1</source>
+ <translation>Die transaksie is afgekeur met die volgende rede: %1</translation>
+ </message>
+ <message>
+ <source>A fee higher than %1 is considered an absurdly high fee.</source>
+ <translation>'n Fooi hoër as %1 word as buitensporig beskou</translation>
+ </message>
+ <message>
<source>Payment request expired.</source>
<translation>Betalings versoek verstryk.</translation>
</message>
<message>
+ <source>Pay only the required fee of %1</source>
+ <translation>Betaal slegs die verlangde fooi van %1</translation>
+ </message>
+ <message>
+ <source>Warning: Invalid Bitcoin address</source>
+ <translation>Waarskuwing: Ongeldige Bitcoinadres</translation>
+ </message>
+ <message>
+ <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source>
+ <translation>Die adres wat u gekies het vir verandering is nie deel van hierdie
+beursie nie. Enige of alle fondse mag dalk daarheen gestuur word.
+Is u seker?</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(geen etiket)</translation>
</message>
@@ -1616,14 +1757,57 @@
<translation>&amp;Etiket:</translation>
</message>
<message>
+ <source>Choose previously used address</source>
+ <translation>Kies voorhen gebruikte adres</translation>
+ </message>
+ <message>
+ <source>This is a normal payment.</source>
+ <translation>Hierdie is 'n gewone betaling</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address to send the payment to</source>
+ <translation>Die Bitcoinadres waarheen die betaling gestuur word</translation>
+ </message>
+ <message>
<source>Alt+A</source>
<translation>Alt+A</translation>
</message>
<message>
+ <source>Paste address from clipboard</source>
+ <translation>Plak adres van aanknipbord af</translation>
+ </message>
+ <message>
+ <source>Remove this entry</source>
+ <translation>Verwyder hierdie inskrywing</translation>
+ </message>
+ <message>
+ <source>The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</source>
+ <translation>De fooi word afgetrek van die gestuurde bedrag.
+Die ontvanger sal minder ontvang as wat u in die
+bedrag opgee. As daar meer as een ontvanger is,
+word die fooi eweredig verdeel.</translation>
+ </message>
+ <message>
+ <source>S&amp;ubtract fee from amount</source>
+ <translation>Bedrag &amp;Sonder fooi </translation>
+ </message>
+ <message>
+ <source>Use available balance</source>
+ <translation>Gebruik beskikbare saldo</translation>
+ </message>
+ <message>
<source>Message:</source>
<translation>Boodskap:</translation>
</message>
<message>
+ <source>This is an unauthenticated payment request.</source>
+ <translation>Hierdie is 'n ongemagtigde uitbetalingsversoek</translation>
+ </message>
+ <message>
+ <source>This is an authenticated payment request.</source>
+ <translation>Hierdie is 'n gemagtigde uitbetalingsversoek</translation>
+ </message>
+ <message>
<source>Pay To:</source>
<translation>Betaal Vir:</translation>
</message>
@@ -1645,10 +1829,18 @@
<translation>&amp;Teken Boodskap</translation>
</message>
<message>
+ <source>Choose previously used address</source>
+ <translation>Kies voorhen gebruikte adres</translation>
+ </message>
+ <message>
<source>Alt+A</source>
<translation>Alt+A</translation>
</message>
<message>
+ <source>Paste address from clipboard</source>
+ <translation>Plak adres van aanknipbord af</translation>
+ </message>
+ <message>
<source>Signature</source>
<translation>Handtekening</translation>
</message>
@@ -1978,4 +2170,4 @@
<translation>Fout</translation>
</message>
</context>
-</TS> \ No newline at end of file
+</TS>
diff --git a/src/qt/locale/bitcoin_af_ZA.ts b/src/qt/locale/bitcoin_af_ZA.ts
index b3e9acca9d..4f31d4cd62 100644
--- a/src/qt/locale/bitcoin_af_ZA.ts
+++ b/src/qt/locale/bitcoin_af_ZA.ts
@@ -227,6 +227,10 @@
<context>
<name>BanTableModel</name>
<message>
+ <source>IP/Netmask</source>
+ <translation>IP/Netmasker</translation>
+ </message>
+ <message>
<source>Banned Until</source>
<translation>Verban Tot</translation>
</message>
@@ -330,6 +334,14 @@
<translation>Klik om netwerk aktiwiteit weer aan te skakel.</translation>
</message>
<message>
+ <source>Syncing Headers (%1%)...</source>
+ <translation>Sinkroniseer tans Hoofde (%1%)...</translation>
+ </message>
+ <message>
+ <source>Reindexing blocks on disk...</source>
+ <translation>Herindekseer blokke op skyf...</translation>
+ </message>
+ <message>
<source>Send coins to a Bitcoin address</source>
<translation>Stuur muntstukke na 'n Bitcoin adres</translation>
</message>
@@ -346,6 +358,10 @@
<translation>&amp;Ontfoutvenster</translation>
</message>
<message>
+ <source>Open debugging and diagnostic console</source>
+ <translation>Open ontfouting en diagnostiese konsole</translation>
+ </message>
+ <message>
<source>&amp;Verify message...</source>
<translation>&amp;Verifieer boodskap...</translation>
</message>
@@ -414,10 +430,22 @@
<translation>Wys die lys van gebruikte ontvangsadresse en etikette</translation>
</message>
<message>
+ <source>Open a bitcoin: URI or payment request</source>
+ <translation>Open 'n bitcoin: URI of betalingsversoek</translation>
+ </message>
+ <message>
<source>&amp;Command-line options</source>
<translation>&amp;Opdrag lys opsies</translation>
</message>
<message>
+ <source>Indexing blocks on disk...</source>
+ <translation>Indekseer tans blokke op skyf...</translation>
+ </message>
+ <message>
+ <source>Processing blocks on disk...</source>
+ <translation>Prosesseer tans blokke op skyf...</translation>
+ </message>
+ <message>
<source>%1 behind</source>
<translation>%1 agter</translation>
</message>
@@ -426,6 +454,10 @@
<translation>Ontvangs van laaste blok is %1 terug.</translation>
</message>
<message>
+ <source>Transactions after this will not yet be visible.</source>
+ <translation>Opvolgende transaksies sal nog nie sigbaar wees nie.</translation>
+ </message>
+ <message>
<source>Error</source>
<translation>Fout</translation>
</message>
@@ -442,10 +474,18 @@
<translation>Op datum</translation>
</message>
<message>
+ <source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
+ <translation>Wys die %1 hulpboodskap om 'n lys met moontlike Bitcoin bevel-lyn opsies te verkry</translation>
+ </message>
+ <message>
<source>%1 client</source>
<translation>%1 klient</translation>
</message>
<message>
+ <source>Connecting to peers...</source>
+ <translation>Verbind tans aan eweknieë...</translation>
+ </message>
+ <message>
<source>Catching up...</source>
<translation>Besig om op te vang...</translation>
</message>
@@ -468,12 +508,22 @@
</translation>
</message>
<message>
+ <source>Label: %1
+</source>
+ <translation>Etiket: %1
+</translation>
+ </message>
+ <message>
<source>Address: %1
</source>
<translation>Adres: %1
</translation>
</message>
<message>
+ <source>Sent transaction</source>
+ <translation>Gestuurde transaksie</translation>
+ </message>
+ <message>
<source>Incoming transaction</source>
<translation>Inkomende transaksie</translation>
</message>
@@ -493,7 +543,11 @@
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
<translation>Beursie is &lt;b&gt;versleutel&lt;/b&gt; en is tans &lt;b&gt;gesluit&lt;/b&gt;</translation>
</message>
- </context>
+ <message>
+ <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
+ <translation>'n Noodlottige fout het voorgekom. Bitcoin kan nie langer voortgaan nie en sal afsluit.</translation>
+ </message>
+</context>
<context>
<name>CoinControlDialog</name>
<message>
@@ -521,10 +575,18 @@
<translation>Stof:</translation>
</message>
<message>
+ <source>After Fee:</source>
+ <translation>Na Fooi:</translation>
+ </message>
+ <message>
<source>Change:</source>
<translation>Verander:</translation>
</message>
<message>
+ <source>(un)select all</source>
+ <translation>(on)selekteer alles</translation>
+ </message>
+ <message>
<source>Tree mode</source>
<translation>Boom wyse</translation>
</message>
@@ -617,6 +679,14 @@
<translation>nee</translation>
</message>
<message>
+ <source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
+ <translation>Hierdie etiket verander na rooi as enige ontvanger 'n bedrag kleiner as die huidige stof drempelwaarde ontvang.</translation>
+ </message>
+ <message>
+ <source>Can vary +/- %1 satoshi(s) per input.</source>
+ <translation>Kan wissel met +/- %1 satoshi(s) per inset.</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(geen etiket)</translation>
</message>
@@ -640,6 +710,14 @@
<translation>&amp;Etiket</translation>
</message>
<message>
+ <source>The label associated with this address list entry</source>
+ <translation>Die etiket geassosieer met hierdie adreslys inskrywing</translation>
+ </message>
+ <message>
+ <source>The address associated with this address list entry. This can only be modified for sending addresses.</source>
+ <translation>Die adres geassosieer met hierdie adreslys inskrywing. Dié kan slegs gewysig word vir stuur-adresse.</translation>
+ </message>
+ <message>
<source>&amp;Address</source>
<translation>&amp;Adres</translation>
</message>
@@ -656,6 +734,10 @@
<translation>Wysig stuurende adres</translation>
</message>
<message>
+ <source>The entered address "%1" is not a valid Bitcoin address.</source>
+ <translation>Die ingeskrewe adres "%1" is nie 'n geldige Bitcoin adres nie.</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>Kon nie die beursie oopsluit nie.</translation>
</message>
@@ -679,6 +761,10 @@
<translation>Lêer bestaan reeds. Voeg %1 indien u van plan is om n nuwe lêer hier te skep.</translation>
</message>
<message>
+ <source>Path already exists, and is not a directory.</source>
+ <translation>Pad bestaan reeds en is nie 'n lêergids nie.</translation>
+ </message>
+ <message>
<source>Cannot create data directory here.</source>
<translation>Kan nie data gids hier skep nie.</translation>
</message>
@@ -971,6 +1057,10 @@
<translation>Fooi:</translation>
</message>
<message>
+ <source>After Fee:</source>
+ <translation>Na Fooi:</translation>
+ </message>
+ <message>
<source>Change:</source>
<translation>Verander:</translation>
</message>
diff --git a/src/qt/locale/bitcoin_am.ts b/src/qt/locale/bitcoin_am.ts
new file mode 100644
index 0000000000..46942066e2
--- /dev/null
+++ b/src/qt/locale/bitcoin_am.ts
@@ -0,0 +1,451 @@
+<TS language="am" version="2.1">
+<context>
+ <name>AddressBookPage</name>
+ <message>
+ <source>Right-click to edit address or label</source>
+ <translation>አድራሻ ወይም መለያ ስም ለመቀየር ቀኙን ጠቅ ያድርጉ</translation>
+ </message>
+ <message>
+ <source>Create a new address</source>
+ <translation>አዲስ አድራሻ ፍጠር</translation>
+ </message>
+ <message>
+ <source>&amp;New</source>
+ <translation>&amp;አዲስ</translation>
+ </message>
+ <message>
+ <source>Copy the currently selected address to the system clipboard</source>
+ <translation>አሁን የተመረጠውን አድራሻ ወደ ሲስተሙ ቅንጥብ ሰሌዳ ቅዳ</translation>
+ </message>
+ <message>
+ <source>&amp;Copy</source>
+ <translation>&amp;ቅዳ</translation>
+ </message>
+ <message>
+ <source>C&amp;lose</source>
+ <translation>ዝጋ</translation>
+ </message>
+ <message>
+ <source>Delete the currently selected address from the list</source>
+ <translation>አሁን የተመረጠውን አድራሻ ከዝርዝሩ ውስጥ ሰርዝ</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>በአሁኑ ማውጫ ውስጥ ያለውን መረጃ ወደ አንድ ፋይል ላክ</translation>
+ </message>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp;ላክ</translation>
+ </message>
+ <message>
+ <source>&amp;Delete</source>
+ <translation>&amp;ሰርዝ</translation>
+ </message>
+ <message>
+ <source>Choose the address to send coins to</source>
+ <translation>ገንዘብ/ኮይኖች የሚልኩለትን አድራሻ ይምረጡ</translation>
+ </message>
+ <message>
+ <source>Choose the address to receive coins with</source>
+ <translation>ገንዘብ/ኮይኖች የሚቀበሉበትን አድራሻ ይምረጡ</translation>
+ </message>
+ <message>
+ <source>C&amp;hoose</source>
+ <translation>ምረጥ</translation>
+ </message>
+ <message>
+ <source>Sending addresses</source>
+ <translation>የመላኪያ አድራሻዎች</translation>
+ </message>
+ <message>
+ <source>Receiving addresses</source>
+ <translation>የመቀበያ አድራሻዎች</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
+ <translation>እነኚ የቢትኮይን ክፍያ የመላኪያ አድራሻዎችዎ ናቸው:: ገንዘብ/ኮይኖች ከመላክዎ በፊት መጠኑን እና የመቀበያ አድራሻውን ሁልጊዜ ያረጋግጡ::</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
+ <translation>እነኚ የቢትኮይን ክፍያ የመቀበያ አድራሻዎችዎ ናቸው:: ለእያንዳንዱ ግብይት አዲስ የመቀበያ አድራሻ እንዲጠቀሙ ይመከራል:: </translation>
+ </message>
+ <message>
+ <source>&amp;Copy Address</source>
+ <translation>&amp;አድራሻ ቅዳ</translation>
+ </message>
+ <message>
+ <source>Copy &amp;Label</source>
+ <translation>ቅዳ &amp;መለያ ስም</translation>
+ </message>
+ <message>
+ <source>&amp;Edit</source>
+ <translation> &amp;ቀይር</translation>
+ </message>
+ <message>
+ <source>Export Address List</source>
+ <translation>የአድራሻ ዝርዝር ላክ</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>ኮማ ሴፓሬትድ ፋይል (*.csv)</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>ወደ ውጪ መላክ አልተሳካም</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the address list to %1. Please try again.</source>
+ <translation>የአድራሻ ዝርዝሩን ወደ %1 ለማስቀመጥ ሲሞከር ስህተት አጋጥሟል:: እባክዎ መልሰው ይሞክሩ::</translation>
+ </message>
+</context>
+<context>
+ <name>AddressTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>መለያ ስም</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>አድራሻ</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(መለያ ስም የለም)</translation>
+ </message>
+</context>
+<context>
+ <name>AskPassphraseDialog</name>
+ <message>
+ <source>Passphrase Dialog</source>
+ <translation>የይለፍ-ሐረግ ንግግር</translation>
+ </message>
+ <message>
+ <source>Enter passphrase</source>
+ <translation>የይለፍ-ሐረግዎን ያስገቡ</translation>
+ </message>
+ <message>
+ <source>New passphrase</source>
+ <translation>አዲስ የይለፍ-ሐረግ</translation>
+ </message>
+ <message>
+ <source>Repeat new passphrase</source>
+ <translation>አዲስ የይለፍ-ሐረጉን ይድገሙት</translation>
+ </message>
+ <message>
+ <source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
+ <translation>አዲስ የይለፍ ሐረግዎን ወደ ቢትኮይን ቦርሳዎ ያስገቡ:: &lt;br/&gt;እባክዎ ለየይለፍ ሐረግዎ&lt;b&gt;አስር ወይም ከዚያ በላይ የዘፈቀደ ዓይነተ-ፊደላት&lt;/b&gt;, ወይም&lt;b&gt;ስምንት ወይም ከዚያ በላይ ቃላት&lt;/b&gt; ይጠቀሙ ::</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>የቢትኮይን ቦርሳውን አመስጥር</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>ይህ ክንዋኔ የቢትኮይን ቦርሳዎን ለመክፈት የቦርሳዎ ይለፍ-ሐረግ ያስፈልገዋል::</translation>
+ </message>
+ <message>
+ <source>Unlock wallet</source>
+ <translation>የቢትኮይን ቦርሳውን ክፈት</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>ይህ ክንዋኔ የቢትኮይን ቦርሳዎን ለመፍታት የቦርሳዎ ይለፍ-ሐረግ ያስፈልገዋል::</translation>
+ </message>
+ <message>
+ <source>Decrypt wallet</source>
+ <translation>የቢትኮይን ቦርሳውን ፍታ</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>ይለፍ-ሐረግ ለውጥ</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase to the wallet.</source>
+ <translation>የድሮውን የይለፍ-ሐረግ እና አዲሱን የይለፍ-ሐረግ ወደ ቢትኮይን ቦርሳዎ ያስገቡ::</translation>
+ </message>
+ <message>
+ <source>Confirm wallet encryption</source>
+ <translation>የቢትኮይን ቦርሳዎን ማመስጠር ያረጋግጡ</translation>
+ </message>
+ <message>
+ <source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
+ <translation>ማስጠንቀቂያ: የቢትኮይን ቦርሳዎን አመስጥረው የይለፍ-ሐረግዎን ካጡት&lt;b&gt;ቢትኮይኖቾን በሙሉ ያጣሉ&lt;/b&gt;!</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to encrypt your wallet?</source>
+ <translation>እርግጠኛ ነዎት ቦርሳዎን ማመስጠር ይፈልጋሉ?</translation>
+ </message>
+ <message>
+ <source>Wallet encrypted</source>
+ <translation>ቦርሳዎ ምስጢር ተደርጓል</translation>
+ </message>
+ <message>
+ <source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>%1 የማመስጠር ሂደቱን ለመጨረስ አሁን ይዘጋል:: ያስታውሱ፣ ኮምፒተርዎ በተንኮል አዘል ሶፍትዌር ከተበከለ ቦርሳዎን ማመስጠር ቢትኮይኖቾን ሙሉበሙሉ እንዳይሰረቁ ሊከላከል አይችልም::</translation>
+ </message>
+ <message>
+ <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
+ <translation>አስፈላጊ: ከ ቦርሳ ፋይልዎ ያከናወኗቸው ቀደም ያሉ ምትኬዎች በአዲስ በተፈጠረ የማመስጠሪያ ፋይል ውስጥ መተካት አለባቸው. ለደህንነት ሲባል, አዲሱን የተመሰጠ የቦርሳ ፋይል መጠቀም ሲጀመሩ ወዲያውኑ ቀደም ሲል ያልተመሰጠሩ የቦርሳ ፋይል ቅጂዎች ዋጋ ቢስ ይሆናሉ::</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed</source>
+ <translation>የቦርሳ ማመስጠር አልተሳካም</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <translation>የቦርሳ ማመስጠር በውስጣዊ ስህተት ምክንያት አልተሳካም:: ቦርሳዎ አልተመሰጠረም::</translation>
+ </message>
+ <message>
+ <source>The supplied passphrases do not match.</source>
+ <translation>የተሰጡት የይለፍ-ሐረግዎች አይዛመዱም::</translation>
+ </message>
+ <message>
+ <source>Wallet unlock failed</source>
+ <translation>ቦርሳ መክፈት አልተሳካም</translation>
+ </message>
+ <message>
+ <source>The passphrase entered for the wallet decryption was incorrect.</source>
+ <translation>ቦርሳ ለመፍታት ያስገቡት የይለፍ-ሐረግ ትክክል አልነበረም::</translation>
+ </message>
+ <message>
+ <source>Wallet decryption failed</source>
+ <translation>ቦርሳ መፍታት አልተሳካም </translation>
+ </message>
+ <message>
+ <source>Wallet passphrase was successfully changed.</source>
+ <translation>የቦርሳ የይለፍ-ሐረግ በተሳካ ሁኔታ ተቀይሯል.</translation>
+ </message>
+ <message>
+ <source>Warning: The Caps Lock key is on!</source>
+ <translation>ማስጠንቀቂያ: የ "Caps Lock" ቁልፍ በርቷል!</translation>
+ </message>
+</context>
+<context>
+ <name>BanTableModel</name>
+ <message>
+ <source>IP/Netmask</source>
+ <translation>አይፒ/ኔትማስክ IP/Netmask</translation>
+ </message>
+ <message>
+ <source>Banned Until</source>
+ <translation>ታግደዋል እስከ</translation>
+ </message>
+</context>
+<context>
+ <name>BitcoinGUI</name>
+ <message>
+ <source>Sign &amp;message...</source>
+ <translation>ምልክትና መልእክት...</translation>
+ </message>
+ <message>
+ <source>Synchronizing with network...</source>
+ <translation>ከኔትወርክ ጋራ በማመሳሰል ላይ ነው...</translation>
+ </message>
+ <message>
+ <source>&amp;Overview</source>
+ <translation>&amp;አጠቃላይ እይታ</translation>
+ </message>
+ <message>
+ <source>Node</source>
+ <translation>እትር</translation>
+ </message>
+ <message>
+ <source>Show general overview of wallet</source>
+ <translation>የቦርሳ አጠቃላይ እይታ ኣሳይ</translation>
+ </message>
+ <message>
+ <source>&amp;Transactions</source>
+ <translation>&amp;ግብይቶች</translation>
+ </message>
+ <message>
+ <source>Browse transaction history</source>
+ <translation>የግብይት ታሪክ ያስሱ</translation>
+ </message>
+ <message>
+ <source>E&amp;xit</source>
+ <translation>ውጣ</translation>
+ </message>
+ <message>
+ <source>Quit application</source>
+ <translation>አፕሊኬሽኑን አቁም</translation>
+ </message>
+ <message>
+ <source>&amp;About %1</source>
+ <translation>&amp;ስለ %1</translation>
+ </message>
+ <message>
+ <source>Show information about %1</source>
+ <translation>ስለ %1 መረጃ አሳይ</translation>
+ </message>
+ <message>
+ <source>About &amp;Qt</source>
+ <translation>ስለ &amp;Qt</translation>
+ </message>
+ <message>
+ <source>Show information about Qt</source>
+ <translation>ስለ Qt መረጃ አሳይ</translation>
+ </message>
+ <message>
+ <source>&amp;Options...</source>
+ <translation>&amp;አማራጮች...</translation>
+ </message>
+ </context>
+<context>
+ <name>CoinControlDialog</name>
+ <message>
+ <source>(no label)</source>
+ <translation>(መለያ ስም የለም)</translation>
+ </message>
+ </context>
+<context>
+ <name>EditAddressDialog</name>
+ </context>
+<context>
+ <name>FreespaceChecker</name>
+ </context>
+<context>
+ <name>HelpMessageDialog</name>
+ </context>
+<context>
+ <name>Intro</name>
+ </context>
+<context>
+ <name>ModalOverlay</name>
+ </context>
+<context>
+ <name>OpenURIDialog</name>
+ </context>
+<context>
+ <name>OptionsDialog</name>
+ </context>
+<context>
+ <name>OverviewPage</name>
+ </context>
+<context>
+ <name>PaymentServer</name>
+ </context>
+<context>
+ <name>PeerTableModel</name>
+ </context>
+<context>
+ <name>QObject</name>
+ </context>
+<context>
+ <name>QObject::QObject</name>
+ </context>
+<context>
+ <name>QRImageWidget</name>
+ </context>
+<context>
+ <name>RPCConsole</name>
+ </context>
+<context>
+ <name>ReceiveCoinsDialog</name>
+ </context>
+<context>
+ <name>ReceiveRequestDialog</name>
+ <message>
+ <source>Address</source>
+ <translation>አድራሻ</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>መለያ ስም</translation>
+ </message>
+ </context>
+<context>
+ <name>RecentRequestsTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>መለያ ስም</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(መለያ ስም የለም)</translation>
+ </message>
+ </context>
+<context>
+ <name>SendCoinsDialog</name>
+ <message>
+ <source>(no label)</source>
+ <translation>(መለያ ስም የለም)</translation>
+ </message>
+</context>
+<context>
+ <name>SendCoinsEntry</name>
+ </context>
+<context>
+ <name>SendConfirmationDialog</name>
+ </context>
+<context>
+ <name>ShutdownWindow</name>
+ </context>
+<context>
+ <name>SignVerifyMessageDialog</name>
+ </context>
+<context>
+ <name>SplashScreen</name>
+ </context>
+<context>
+ <name>TrafficGraphWidget</name>
+ </context>
+<context>
+ <name>TransactionDesc</name>
+ </context>
+<context>
+ <name>TransactionDescDialog</name>
+ </context>
+<context>
+ <name>TransactionTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>መለያ ስም</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(መለያ ስም የለም)</translation>
+ </message>
+ </context>
+<context>
+ <name>TransactionView</name>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>ኮማ ሴፓሬትድ ፋይል (*.csv)</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>መለያ ስም</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>አድራሻ</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>ወደ ውጪ መላክ አልተሳካም</translation>
+ </message>
+ </context>
+<context>
+ <name>UnitDisplayStatusBarControl</name>
+ </context>
+<context>
+ <name>WalletFrame</name>
+ </context>
+<context>
+ <name>WalletModel</name>
+ </context>
+<context>
+ <name>WalletView</name>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp;ላክ</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>በአሁኑ ማውጫ ውስጥ ያለውን መረጃ ወደ አንድ ፋይል ላክ</translation>
+ </message>
+ </context>
+<context>
+ <name>bitcoin-core</name>
+ </context>
+</TS> \ No newline at end of file
diff --git a/src/qt/locale/bitcoin_ar.ts b/src/qt/locale/bitcoin_ar.ts
index 481e02c60e..d176505ac3 100644
--- a/src/qt/locale/bitcoin_ar.ts
+++ b/src/qt/locale/bitcoin_ar.ts
@@ -2936,6 +2936,11 @@
<translation>يرجى المساهمة إذا وجدت %s مفيداً. تفضل بزيارة %s لمزيد من المعلومات حول البرنامج.</translation>
</message>
<message>
+ <source>%s corrupt, salvage failed</source>
+ <translation>
+%s تالف, فشل الانقاذ.</translation>
+ </message>
+ <message>
<source>Change index out of range</source>
<translation>فهرس الفكة خارج النطاق</translation>
</message>
@@ -2960,6 +2965,18 @@
<translation>خطأ في تحميل %s</translation>
</message>
<message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>خطأ في تحميل %s: لا يمكن تعطيل المفاتيح الخاصة إلا أثناء الإنشاء.</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Wallet corrupted</source>
+ <translation>خطأ في التحميل %s: المحفظة تالفة.</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Wallet requires newer version of %s</source>
+ <translation>خطا تحميل %s: المحفظة تتطلب النسخة الجديدة من %s.</translation>
+ </message>
+ <message>
<source>Error loading block database</source>
<translation>خطأ في تحميل قاعدة بيانات الكتل</translation>
</message>
@@ -2976,6 +2993,10 @@
<translation>فشل في الاستماع على أي منفذ. استخدام الاستماع = 0 إذا كنت تريد هذا.</translation>
</message>
<message>
+ <source>Failed to rescan the wallet during initialization</source>
+ <translation>فشل في اعادة مسح المحفظة خلال عملية التهيئة.</translation>
+ </message>
+ <message>
<source>Importing...</source>
<translation>استيراد...</translation>
</message>
@@ -3020,6 +3041,10 @@
<translation>شفرة المصدر متاحة من %s.</translation>
</message>
<message>
+ <source>Unable to generate keys</source>
+ <translation> غير قادر على توليد مفاتيح.</translation>
+ </message>
+ <message>
<source>Unsupported argument -tor found, use -onion.</source>
<translation>تم العثور على وسيطة غير مدعومة -tor ، استخدم -onion.</translation>
</message>
@@ -3128,6 +3153,10 @@
<translation>يجب ألا تكون قيمة المعاملة سلبية</translation>
</message>
<message>
+ <source>Transaction has too long of a mempool chain</source>
+ <translation>المعاملات طويلة جداً على حجم سلسلة الذاكرة </translation>
+ </message>
+ <message>
<source>Transaction must have at least one recipient</source>
<translation>يجب أن تحتوي المعاملة على مستلم واحد على الأقل</translation>
</message>
@@ -3136,6 +3165,10 @@
<translation>اموال غير كافية</translation>
</message>
<message>
+ <source>Cannot write to data directory '%s'; check permissions.</source>
+ <translation>لايمكن الكتابة على دليل البيانات '%s'؛ تحقق من السماحيات.</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>تحميل مؤشر الكتلة</translation>
</message>
diff --git a/src/qt/locale/bitcoin_bg.ts b/src/qt/locale/bitcoin_bg.ts
index be7fed59a1..c9d33ed6bb 100644
--- a/src/qt/locale/bitcoin_bg.ts
+++ b/src/qt/locale/bitcoin_bg.ts
@@ -136,6 +136,10 @@
<translation>Въведете новата парола повторно</translation>
</message>
<message>
+ <source>Show password</source>
+ <translation>Покажи парола</translation>
+ </message>
+ <message>
<source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
<translation>Въведете новата парола към портфейла.&lt;br/&gt;Моля ползвайте парола съставена от &lt;b&gt;десет или повече произволни символа&lt;/b&gt;, или &lt;b&gt;осем или повече думи&lt;/b&gt;.</translation>
</message>
@@ -318,6 +322,14 @@
<translation>Отвори &amp;URI...</translation>
</message>
<message>
+ <source>Wallet:</source>
+ <translation>Портфейл</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>Портфейл по подразбиране</translation>
+ </message>
+ <message>
<source>Click to disable network activity.</source>
<translation>Натиснете за деактивиране на мрежата</translation>
</message>
@@ -330,6 +342,10 @@
<translation>Натиснете за повторно активиране на мрежата.</translation>
</message>
<message>
+ <source>Syncing Headers (%1%)...</source>
+ <translation>Синхронизиране на хедъри (%1%)</translation>
+ </message>
+ <message>
<source>Reindexing blocks on disk...</source>
<translation>Повторно индексиране на блоковете на диска...</translation>
</message>
@@ -876,6 +892,10 @@
<translation>&amp;Мрежа</translation>
</message>
<message>
+ <source>GB</source>
+ <translation>ГБ</translation>
+ </message>
+ <message>
<source>W&amp;allet</source>
<translation>По&amp;ртфейл</translation>
</message>
@@ -900,6 +920,14 @@
<translation>Отваряне на входящия порт чрез &amp;UPnP</translation>
</message>
<message>
+ <source>Accept connections from outside.</source>
+ <translation>Позволи външни връзки</translation>
+ </message>
+ <message>
+ <source>Allow incomin&amp;g connections</source>
+ <translation>Позволи входящи връзки</translation>
+ </message>
+ <message>
<source>Connect to the Bitcoin network through a SOCKS5 proxy.</source>
<translation>Свързване с Биткойн мрежата чрез SOCKS5 прокси.</translation>
</message>
@@ -920,6 +948,10 @@
<translation>Порт на прокси сървъра (пр. 9050)</translation>
</message>
<message>
+ <source>Tor</source>
+ <translation>Тор</translation>
+ </message>
+ <message>
<source>&amp;Window</source>
<translation>&amp;Прозорец</translation>
</message>
@@ -980,6 +1012,10 @@
<translation>Изисква се рестартиране на клиента за активиране на извършените промени.</translation>
</message>
<message>
+ <source>Client will be shut down. Do you want to proceed?</source>
+ <translation>Клиентът ще бъде изключен. Искате ли да продължите?</translation>
+ </message>
+ <message>
<source>Error</source>
<translation>Грешка</translation>
</message>
@@ -1349,6 +1385,10 @@
<translation>Изчисти конзолата</translation>
</message>
<message>
+ <source>default wallet</source>
+ <translation>Портфейл по подразбиране</translation>
+ </message>
+ <message>
<source>via %1</source>
<translation>посредством %1</translation>
</message>
diff --git a/src/qt/locale/bitcoin_cs.ts b/src/qt/locale/bitcoin_cs.ts
index 260d79b766..a08cc960da 100644
--- a/src/qt/locale/bitcoin_cs.ts
+++ b/src/qt/locale/bitcoin_cs.ts
@@ -354,6 +354,10 @@
<translation>Vytvářím nový index bloků na disku...</translation>
</message>
<message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>Proxy je &lt;b&gt;zapnutá&lt;/b&gt;: %1</translation>
+ </message>
+ <message>
<source>Send coins to a Bitcoin address</source>
<translation>Pošli mince na bitcoinovou adresu</translation>
</message>
@@ -522,6 +526,12 @@
</translation>
</message>
<message>
+ <source>Wallet: %1
+</source>
+ <translation>Peněženka: %1
+</translation>
+ </message>
+ <message>
<source>Type: %1
</source>
<translation>Typ: %1
@@ -758,6 +768,14 @@
<translation>Zadaná adresa „%1“ není platná bitcoinová adresa.</translation>
</message>
<message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Adresa "%1" již existuje jako přijímací adresa s označením "%2" a proto nemůže být přidána jako odesílací adresa.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>Zadaná adresa „%1“ už v adresáři je s označením "%2".</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>Nemohu odemknout peněženku.</translation>
</message>
@@ -1036,6 +1054,22 @@
<translation>&amp;Síť</translation>
</message>
<message>
+ <source>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</source>
+ <translation>Zakáže některé pokročilé funkce, ale všechny bloky budou stále plně otevřené. Obnovení tohoto nastavení vyžaduje opětovné převzetí celého blockchainu. Skutečné využít disku může být o něco vyšší.</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Redukovat prostor pro &amp;bloky na</translation>
+ </message>
+ <message>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Obnovení tohoto nastavení vyžaduje opětovné převzetí celého blockchainu.</translation>
+ </message>
+ <message>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
<translation>(0 = automaticky, &lt;0 = nechat daný počet jader volný, výchozí: 0)</translation>
</message>
@@ -1503,10 +1537,18 @@
<context>
<name>QObject::QObject</name>
<message>
+ <source>Error parsing command line arguments: %1.</source>
+ <translation>Chyba při zpracování argumentů příkazového řádku: %1.</translation>
+ </message>
+ <message>
<source>Error: Specified data directory "%1" does not exist.</source>
<translation>Chyba: Zadaný adresář pro data „%1“ neexistuje.</translation>
</message>
<message>
+ <source>Error: Cannot parse configuration file: %1.</source>
+ <translation>Chyba: Konfigurační soubor se nedá zpracovat: %1.</translation>
+ </message>
+ <message>
<source>Error: %1</source>
<translation>Chyba: %1</translation>
</message>
@@ -1597,6 +1639,14 @@
<translation>Obsazenost paměti</translation>
</message>
<message>
+ <source>Wallet: </source>
+ <translation>Peněženka:</translation>
+ </message>
+ <message>
+ <source>(none)</source>
+ <translation>(žádné)</translation>
+ </message>
+ <message>
<source>&amp;Reset</source>
<translation>&amp;Vynulovat</translation>
</message>
@@ -1777,6 +1827,14 @@
<translation>V historii se pohybuješ šipkami nahoru a dolů a pomocí %1 čistíš obrazovku.</translation>
</message>
<message>
+ <source>Type %1 for an overview of available commands.</source>
+ <translation>Napiš %1 pro přehled dostupných příkazů.</translation>
+ </message>
+ <message>
+ <source>For more information on using this console type %1.</source>
+ <translation>Pro více informací jak používat tuto konzoli napište %1.</translation>
+ </message>
+ <message>
<source>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.</source>
<translation>UPOZORNĚNÍ: Podvodníci jsou aktivní a říkají uživatelům, aby sem zadávali příkazy, kterými jim pak ale vykradou jejich peněženky. Nepoužívej tuhle konzoli, pokud úplně neznáš důsledky jednotlivých příkazů.</translation>
</message>
@@ -1785,6 +1843,14 @@
<translation>Síť je vypnutá</translation>
</message>
<message>
+ <source>Executing command without any wallet</source>
+ <translation>Spouštění příkazu bez jakékoliv peněženky</translation>
+ </message>
+ <message>
+ <source>Executing command using "%1" wallet</source>
+ <translation>Příkaz se vykonává s použitím peněženky "%1"</translation>
+ </message>
+ <message>
<source>(node id: %1)</source>
<translation>(id uzlu: %1)</translation>
</message>
@@ -1856,6 +1922,14 @@
<translation>Vyčistit</translation>
</message>
<message>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <translation>Nativní segwit adresy (Bech32 nebo BIP-173) snižují Vaše budoucí transakční poplatky a nabízejí lepší ochranu před překlepy, avšak staré peněženky je nepodporují. Pokud je toto pole nezaškrtnuté, bude vytvořena adresa kompatibilní se staršími peněženkami.</translation>
+ </message>
+ <message>
+ <source>Generate native segwit (Bech32) address</source>
+ <translation>Generovat nativní segwit adresu (Bench32)</translation>
+ </message>
+ <message>
<source>Requested payments history</source>
<translation>Historie vyžádaných plateb</translation>
</message>
@@ -2061,6 +2135,14 @@
<translation>sbal nastavení poplatků</translation>
</message>
<message>
+ <source>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</source>
+ <translation>Specifikujte vlastní poplatek za kB (1000 bajtů) virtuální velikosti transakce.
+
+Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 satoshi za kB" a velikost transakce 500 bajtů (polovina z 1 kB) by stál jen 50 satoshi.</translation>
+ </message>
+ <message>
<source>per kilobyte</source>
<translation>za kilobajt</translation>
</message>
@@ -2113,6 +2195,10 @@
<translation>Povolit možnost dodatečně transakci navýšit poplatek (tzv. „replace-by-fee“)</translation>
</message>
<message>
+ <source>With Replace-By-Fee (BIP-125) you can increase a transaction's fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</source>
+ <translation>S dodatečným navýšením poplatku (BIP-125, tzv. „Replace-By-Fee“) můžete zvýšit poplatek i po odeslání. Bez dodatečného navýšení bude navrhnut vyšší transakční poplatek, tak aby kompenzoval zvýšené riziko prodlení transakce.</translation>
+ </message>
+ <message>
<source>Clear &amp;All</source>
<translation>Všechno s&amp;maž</translation>
</message>
@@ -2173,10 +2259,30 @@
<translation>nebo</translation>
</message>
<message>
+ <source>You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
+ <translation>Poplatek můžete navýšit později (vysílá se "Replace-By-Fee" - nahrazení poplatkem, BIP-125).</translation>
+ </message>
+ <message>
+ <source>from wallet %1</source>
+ <translation>z peněženky %1</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction.</source>
+ <translation>Prosím, zkontrolujte vaši transakci.</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>Transakční poplatek</translation>
</message>
<message>
+ <source>Not signalling Replace-By-Fee, BIP-125.</source>
+ <translation>Nevysílá se "Replace-By-Fee" - nahrazení poplatkem, BIP-125.</translation>
+ </message>
+ <message>
+ <source>Total Amount</source>
+ <translation>Celková částka</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>Potvrď odeslání mincí</translation>
</message>
@@ -2296,6 +2402,10 @@
<translation>Od&amp;ečíst poplatek od částky</translation>
</message>
<message>
+ <source>Use available balance</source>
+ <translation>Použít dostupný zůstatek</translation>
+ </message>
+ <message>
<source>Message:</source>
<translation>Zpráva:</translation>
</message>
@@ -2626,6 +2736,10 @@
<translation>Celková velikost transakce</translation>
</message>
<message>
+ <source>Transaction virtual size</source>
+ <translation>Virtuální velikost transakce</translation>
+ </message>
+ <message>
<source>Output index</source>
<translation>Pořadí výstupu</translation>
</message>
@@ -3030,7 +3144,11 @@
<source>The wallet data was successfully saved to %1.</source>
<translation>Data z peněženky byla v pořádku uložena do %1.</translation>
</message>
- </context>
+ <message>
+ <source>Cancel</source>
+ <translation>Zrušit</translation>
+ </message>
+</context>
<context>
<name>bitcoin-core</name>
<message>
@@ -3082,6 +3200,10 @@
<translation>Nastala chyba při čtení souboru %s! Všechny klíče se přečetly správně, ale data o transakcích nebo záznamy v adresáři mohou chybět či být nesprávné.</translation>
</message>
<message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>Seskupení výstupů podle adresy, při výběru všech nebo žádných, namísto výběru báze za výstup. Zvýšena ochrana soukromí, protože je adresa použita pouze jednou (pokud nikdo na tuto adresu již nic po minutě nepošle). Může být spojeno s vyššími poplatky, jelikož neoptimalizovaný výběr mincí může vyústit v přidané omezení (předvolené: %u)</translation>
+ </message>
+ <message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation>Zkontroluj, že máš v počítači správně nastavený datum a čas! Pokud jsou nastaveny špatně, %s nebude fungovat správně.</translation>
</message>
@@ -3166,6 +3288,10 @@
<translation>Chyba při načítání %s</translation>
</message>
<message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>Chyba při načítání %s: Soukromé klíče můžou být zakázané jen v průběhu vytváření.</translation>
+ </message>
+ <message>
<source>Error loading %s: Wallet corrupted</source>
<translation>Chyba při načítání %s: peněženka je poškozená</translation>
</message>
@@ -3190,6 +3316,10 @@
<translation>Nepodařilo se naslouchat na žádném portu. Použij -listen=0, pokud to byl tvůj záměr.</translation>
</message>
<message>
+ <source>Failed to rescan the wallet during initialization</source>
+ <translation>Během inicializace se nepodařilo proskenovat peněženku</translation>
+ </message>
+ <message>
<source>Importing...</source>
<translation>Importuji...</translation>
</message>
@@ -3214,6 +3344,14 @@
<translation>Neplatná částka pro -fallbackfee=&lt;částka&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Zadaný adresář bloků "%s" neexistuje.</translation>
+ </message>
+ <message>
+ <source>Upgrading txindex database</source>
+ <translation>Aktualizuje se txindex databáze</translation>
+ </message>
+ <message>
<source>Loading P2P addresses...</source>
<translation>Načítám P2P adresy…</translation>
</message>
@@ -3254,6 +3392,10 @@
<translation>Nedaří se mi připojit na %s na tomhle počítači. %s už pravděpodobně jednou běží.</translation>
</message>
<message>
+ <source>Unable to generate keys</source>
+ <translation>Nepodařilo se vygenerovat klíče</translation>
+ </message>
+ <message>
<source>Unsupported argument -benchmark ignored, use -debug=bench.</source>
<translation>Nepodporovaný argument -benchmark se ignoruje, použij -debug=bench.</translation>
</message>
@@ -3346,6 +3488,18 @@
<translation>Nepodařilo se podepsat transakci</translation>
</message>
<message>
+ <source>Specified -walletdir "%s" does not exist</source>
+ <translation>Uvedená -walletdir "%s" neexistuje</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is a relative path</source>
+ <translation>Uvedená -walletdir "%s" je relatívna cesta</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is not a directory</source>
+ <translation>Uvedená -walletdir "%s" není složkou</translation>
+ </message>
+ <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>Částka v transakci je příliš malá na pokrytí poplatku</translation>
</message>
@@ -3378,6 +3532,10 @@
<translation>Kontroluji peněženku/y…</translation>
</message>
<message>
+ <source>Wallet %s resides outside wallet directory %s</source>
+ <translation>Peněženka %s se nachází mimo adresář pro peněženky %s</translation>
+ </message>
+ <message>
<source>Warning</source>
<translation>Upozornění</translation>
</message>
@@ -3474,6 +3632,22 @@
<translation>Nedostatek prostředků</translation>
</message>
<message>
+ <source>Can't generate a change-address key. Private keys are disabled for this wallet.</source>
+ <translation>Není možné vygenerovat klíč na změnu adresy. Soukromé klíče jsou pro tuto peněženku zakázané.</translation>
+ </message>
+ <message>
+ <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
+ <translation>Není možné vylepšit peněženku bez HD bez aktualizace, která podporuje dělení keypoolu. Použijte prosím -upgradewallet=169900 nebo -upgradewallet bez specifikované verze.</translation>
+ </message>
+ <message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>Odhad poplatku se nepodařil. Fallbackfee je zakázaný. Počkejte několik bloků nebo povolte -fallbackfee.</translation>
+ </message>
+ <message>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <translation>Upozornění: Byly zjištěné soukromé klíče v peněžence {%s} se zakázanými soukromými klíči.</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>Načítám index bloků...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_da.ts b/src/qt/locale/bitcoin_da.ts
index 21dc7831c3..c7a2422c8a 100644
--- a/src/qt/locale/bitcoin_da.ts
+++ b/src/qt/locale/bitcoin_da.ts
@@ -3200,6 +3200,10 @@ Note: Siden gebyret er kalkuleret på en per-byte basis, et gebyr på "100 satos
<translation>Fejl under læsning af %s! Alle nøgler blev læst korrekt, men transaktionsdata eller indgange i adressebogen kan mangle eller være ukorrekte.</translation>
</message>
<message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>Gruppér output efter adresse og vælg alle eller ingen, i stedet for at vælge på per-output-basis. Højere sikring af privatliv, da en adresse kun bruges én gang (med mindre nogen sender til en adresse efter den er brugt), men kan resultere i en anelse højere gebyrer, da ikke-optimal valg af output-adresser kan forekomme på grund af den tilføjede begrænsning (standard: %u)</translation>
+ </message>
+ <message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation>Undersøg venligst at din computers dato og klokkeslet er korrekt indstillet! Hvis der er fejl i disse, vil %s ikke fungere korrekt.</translation>
</message>
@@ -3340,6 +3344,10 @@ Note: Siden gebyret er kalkuleret på en per-byte basis, et gebyr på "100 satos
<translation>Ugyldigt beløb for -fallbackfee=&lt;beløb&gt;: “%s”</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Angivet blokmappe “%s” eksisterer ikke.</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Opgraderer txindex database</translation>
</message>
@@ -3480,12 +3488,6 @@ Note: Siden gebyret er kalkuleret på en per-byte basis, et gebyr på "100 satos
<translation>Signering af transaktion mislykkedes</translation>
</message>
<message>
- <source>Specified blocks directory "%s" does not exist.
-</source>
- <translation>Specificeret blokke mappe "%s" eksisterer ikke.
-</translation>
- </message>
- <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>Transaktionsbeløbet er for lille til at betale gebyret</translation>
</message>
diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts
index 45d09655c4..31feaf114f 100644
--- a/src/qt/locale/bitcoin_de.ts
+++ b/src/qt/locale/bitcoin_de.ts
@@ -145,7 +145,7 @@
</message>
<message>
<source>Encrypt wallet</source>
- <translation>Wallet verschlüsseln</translation>
+ <translation>Brieftasche verschlüsseln</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
@@ -161,7 +161,7 @@
</message>
<message>
<source>Decrypt wallet</source>
- <translation>Brieftasche entschlüsseln</translation>
+ <translation>Wallet entschlüsseln</translation>
</message>
<message>
<source>Change passphrase</source>
@@ -3196,6 +3196,10 @@ Hinweis: Eine Gebühr von "100 Satoshis pro kB" bei einer Größe der Transaktio
<translation>Lesen von %s fehlgeschlagen! Alle Schlüssel wurden korrekt gelesen, Transaktionsdaten bzw. Adressbucheinträge fehlen aber möglicherweise oder sind inkorrekt.</translation>
</message>
<message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>Gruppiere Outputs mit der Auswahl von allen oder keinen Adressen, anstatt alle pro Output auszuwählen. Die Privatsphäre wurde verbessert, da eine Adresse nur einmal verwendet wird (ausser jemand sendet Coins nachdem von der Adresse ausgegeben wurde). Durch diese hinzugefügte Limitierung kann es allerdings zu leicht erhöhten Transaktionskosten kommen, da eine suboptimale Coinselektierung bei Transaktionen stattfinden kann. (Standardeinstellung: %u)</translation>
+ </message>
+ <message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation>Bitte korrigieren Sie die Datums- und Uhrzeiteinstellungen Ihres Computers, da %s ansonsten nicht ordnungsgemäß funktionieren wird.</translation>
</message>
@@ -3337,6 +3341,10 @@ Hinweis: Eine Gebühr von "100 Satoshis pro kB" bei einer Größe der Transaktio
<translation>Ungültiger Betrag für -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Angegebener Blöcke-Ordner "%s" existiert nicht.</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Erneuern der txindex Datenbank</translation>
</message>
@@ -3485,12 +3493,6 @@ Hinweis: Eine Gebühr von "100 Satoshis pro kB" bei einer Größe der Transaktio
<translation>Angegebenes Verzeichniss "%s" ist kein Verzeichniss</translation>
</message>
<message>
- <source>Specified blocks directory "%s" does not exist.
-</source>
- <translation>Angegebenes 'blocks'-Verzeichnis "%s" existiert nicht.
-</translation>
- </message>
- <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>Der Transaktionsbetrag ist zu niedrig, um die Gebühr zu bezahlen.</translation>
</message>
diff --git a/src/qt/locale/bitcoin_el_GR.ts b/src/qt/locale/bitcoin_el_GR.ts
index 4557fd85aa..85d5c62c91 100644
--- a/src/qt/locale/bitcoin_el_GR.ts
+++ b/src/qt/locale/bitcoin_el_GR.ts
@@ -234,7 +234,11 @@
<source>IP/Netmask</source>
<translation>IP/Netmask</translation>
</message>
- </context>
+ <message>
+ <source>Banned Until</source>
+ <translation>Απαγορευμένο έως</translation>
+ </message>
+</context>
<context>
<name>BitcoinGUI</name>
<message>
@@ -326,6 +330,10 @@
<translation>Πορτοφόλι</translation>
</message>
<message>
+ <source>default wallet</source>
+ <translation>Προεπιλεγμένο πορτοφόλι</translation>
+ </message>
+ <message>
<source>Click to disable network activity.</source>
<translation>Κάντε κλικ για να απενεργοποιήσετε το δίκτυο.</translation>
</message>
@@ -338,6 +346,10 @@
<translation>Κάντε κλικ για να ενεργοποιήσετε τo δίκτυο ξανά.</translation>
</message>
<message>
+ <source>Syncing Headers (%1%)...</source>
+ <translation>Συγχρονισμός Επικεφαλίδων (%1%)...</translation>
+ </message>
+ <message>
<source>Reindexing blocks on disk...</source>
<translation>Φόρτωση ευρετηρίου μπλοκ στον σκληρό δίσκο...</translation>
</message>
@@ -490,6 +502,12 @@
</translation>
</message>
<message>
+ <source>Wallet: %1
+</source>
+ <translation>Πορτοφόλι: %1
+</translation>
+ </message>
+ <message>
<source>Type: %1
</source>
<translation>Τύπος: %1
@@ -611,6 +629,14 @@
<translation>Αντιγραφή ταυτότητας συναλλαγής</translation>
</message>
<message>
+ <source>Copy quantity</source>
+ <translation>Αντιγραφή ποσότητας</translation>
+ </message>
+ <message>
+ <source>Copy fee</source>
+ <translation>Αντιγραφή τελών</translation>
+ </message>
+ <message>
<source>yes</source>
<translation>ναι</translation>
</message>
@@ -661,7 +687,27 @@
<source>Edit sending address</source>
<translation> Επεξεργασία διεύθυνσης αποστολής</translation>
</message>
- </context>
+ <message>
+ <source>The entered address "%1" is not a valid Bitcoin address.</source>
+ <translation>Η διεύθυνση "%1" δεν είναι έγκυρη Bitcoin διεύθυνση.</translation>
+ </message>
+ <message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Η διεύθυνση "%1" υπάρχει ήδη ως διεύθυνσης λήψης με ετικέτα "%2" και γιαυτό τον λόγο δεν μπορεί να προστεθεί ως διεύθυνση αποστολής.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>Η διεύθυνση "%1" βρίσκεται ήδη στο βιβλίο διευθύνσεων με ετικέτα "%2".</translation>
+ </message>
+ <message>
+ <source>Could not unlock wallet.</source>
+ <translation>Δεν είναι δυνατό το ξεκλείδωμα του πορτοφολιού.</translation>
+ </message>
+ <message>
+ <source>New key generation failed.</source>
+ <translation>Η δημιουργία νέου κλειδιού απέτυχε.</translation>
+ </message>
+</context>
<context>
<name>FreespaceChecker</name>
<message>
@@ -696,6 +742,10 @@
<translation>(%1-bit)</translation>
</message>
<message>
+ <source>About %1</source>
+ <translation>Σχετικά %1</translation>
+ </message>
+ <message>
<source>Command-line options</source>
<translation>Επιλογές γραμμής εντολών</translation>
</message>
@@ -828,6 +878,10 @@
<translation>&amp;Δίκτυο</translation>
</message>
<message>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
<translation>(0 = αυτόματο, &lt;0 = ελεύθεροι πυρήνες)</translation>
</message>
@@ -880,6 +934,18 @@
<translation>Θύρα διαμεσολαβητή</translation>
</message>
<message>
+ <source>IPv4</source>
+ <translation>IPv4</translation>
+ </message>
+ <message>
+ <source>IPv6</source>
+ <translation>IPv6</translation>
+ </message>
+ <message>
+ <source>Tor</source>
+ <translation>Tor</translation>
+ </message>
+ <message>
<source>&amp;Window</source>
<translation>&amp;Παράθυρο</translation>
</message>
@@ -1181,6 +1247,10 @@
<translation>Τρέχον αριθμός μπλοκ</translation>
</message>
<message>
+ <source>Memory usage</source>
+ <translation>χρήση Μνήμης</translation>
+ </message>
+ <message>
<source>Received</source>
<translation>Παραλήφθησαν</translation>
</message>
@@ -1201,6 +1271,14 @@
<translation>Έκδοση</translation>
</message>
<message>
+ <source>Decrease font size</source>
+ <translation>Μείωση μεγέθους γραμματοσειράς</translation>
+ </message>
+ <message>
+ <source>Increase font size</source>
+ <translation>Αύξηση μεγέθους γραμματοσειράς</translation>
+ </message>
+ <message>
<source>Services</source>
<translation>Υπηρεσίες</translation>
</message>
@@ -1261,6 +1339,26 @@
<translation>Καθαρισμός κονσόλας</translation>
</message>
<message>
+ <source>1 &amp;hour</source>
+ <translation>1 &amp;ώρα</translation>
+ </message>
+ <message>
+ <source>1 &amp;day</source>
+ <translation>1 &amp;μέρα</translation>
+ </message>
+ <message>
+ <source>1 &amp;week</source>
+ <translation>1 &amp;εβδομάδα</translation>
+ </message>
+ <message>
+ <source>1 &amp;year</source>
+ <translation>1 &amp;χρόνος</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>Προεπιλεγμένο πορτοφόλι</translation>
+ </message>
+ <message>
<source>via %1</source>
<translation>μέσω %1</translation>
</message>
@@ -1277,6 +1375,14 @@
<translation>Εξερχόμενα</translation>
</message>
<message>
+ <source>Yes</source>
+ <translation>Ναι</translation>
+ </message>
+ <message>
+ <source>No</source>
+ <translation>Όχι</translation>
+ </message>
+ <message>
<source>Unknown</source>
<translation>Άγνωστο(α)</translation>
</message>
@@ -1328,6 +1434,10 @@
<translation>Αντιγραφή ετικέτας</translation>
</message>
<message>
+ <source>Copy message</source>
+ <translation>Αντιγραφή μηνύματος</translation>
+ </message>
+ <message>
<source>Copy amount</source>
<translation>Αντιγραφή ποσού</translation>
</message>
@@ -1351,14 +1461,26 @@
<translation>&amp;Αποθήκευση εικόνας...</translation>
</message>
<message>
+ <source>Payment information</source>
+ <translation>Πληροφορίες πληρωμής</translation>
+ </message>
+ <message>
<source>Address</source>
<translation>Διεύθυνση</translation>
</message>
<message>
+ <source>Amount</source>
+ <translation>Ποσό</translation>
+ </message>
+ <message>
<source>Label</source>
<translation>Ετικέτα</translation>
</message>
<message>
+ <source>Message</source>
+ <translation>Μήνυμα</translation>
+ </message>
+ <message>
<source>Wallet</source>
<translation>Πορτοφόλι</translation>
</message>
@@ -1374,6 +1496,10 @@
<translation>Ετικέτα</translation>
</message>
<message>
+ <source>Message</source>
+ <translation>Μήνυμα</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(χωρίς ετικέτα)</translation>
</message>
@@ -1489,10 +1615,22 @@
<translation>Αποστολή</translation>
</message>
<message>
+ <source>Copy quantity</source>
+ <translation>Αντιγραφή ποσότητας</translation>
+ </message>
+ <message>
<source>Copy amount</source>
<translation>Αντιγραφή ποσού</translation>
</message>
<message>
+ <source>Copy fee</source>
+ <translation>Αντιγραφή τελών</translation>
+ </message>
+ <message>
+ <source>or</source>
+ <translation>ή</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>Κόστος συναλλαγής</translation>
</message>
@@ -1562,7 +1700,11 @@
</context>
<context>
<name>SendConfirmationDialog</name>
- </context>
+ <message>
+ <source>Yes</source>
+ <translation>Ναι</translation>
+ </message>
+</context>
<context>
<name>ShutdownWindow</name>
<message>
@@ -1725,6 +1867,14 @@
<source>Transaction fee</source>
<translation>Κόστος συναλλαγής</translation>
</message>
+ <message>
+ <source>Message</source>
+ <translation>Μήνυμα</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Ποσό</translation>
+ </message>
</context>
<context>
<name>TransactionDescDialog</name>
@@ -1815,10 +1965,18 @@
</context>
<context>
<name>WalletModel</name>
+ <message>
+ <source>Increase:</source>
+ <translation>Αύξηση:</translation>
+ </message>
</context>
<context>
<name>WalletView</name>
- </context>
+ <message>
+ <source>Cancel</source>
+ <translation>Ακύρωση</translation>
+ </message>
+</context>
<context>
<name>bitcoin-core</name>
<message>
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index fbff2d36a0..c6ecbc3f87 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -237,7 +237,7 @@
</message>
<message>
<location line="-56"/>
- <source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <source>Your wallet is now encrypted. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -246,32 +246,33 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
+ <location line="+8"/>
<location line="+7"/>
- <location line="+42"/>
+ <location line="+43"/>
<location line="+6"/>
<source>Wallet encryption failed</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-54"/>
+ <location line="-55"/>
<source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+7"/>
- <location line="+48"/>
+ <location line="+49"/>
<source>The supplied passphrases do not match.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-37"/>
+ <location line="-38"/>
+ <location line="+6"/>
<source>Wallet unlock failed</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <location line="+11"/>
+ <location line="-5"/>
+ <location line="+12"/>
<location line="+19"/>
<source>The passphrase entered for the wallet decryption was incorrect.</source>
<translation type="unfinished"></translation>
@@ -309,27 +310,22 @@
<context>
<name>BitcoinGUI</name>
<message>
- <location filename="../bitcoingui.cpp" line="+307"/>
+ <location filename="../bitcoingui.cpp" line="+318"/>
<source>Sign &amp;message...</source>
<translation>Sign &amp;message...</translation>
</message>
<message>
- <location line="+483"/>
+ <location line="+574"/>
<source>Synchronizing with network...</source>
<translation>Synchronizing with network...</translation>
</message>
<message>
- <location line="-561"/>
+ <location line="-652"/>
<source>&amp;Overview</source>
<translation>&amp;Overview</translation>
</message>
<message>
- <location line="-140"/>
- <source>Node</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+141"/>
+ <location line="+1"/>
<source>Show general overview of wallet</source>
<translation>Show general overview of wallet</translation>
</message>
@@ -399,32 +395,17 @@
<translation>&amp;Change Passphrase...</translation>
</message>
<message>
- <location line="+12"/>
- <source>&amp;Sending addresses...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>&amp;Receiving addresses...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
+ <location line="+18"/>
<source>Open &amp;URI...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+104"/>
+ <location line="+157"/>
<source>Wallet:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+83"/>
- <source>default wallet</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+226"/>
+ <location line="+330"/>
<source>Click to disable network activity.</source>
<translation type="unfinished"></translation>
</message>
@@ -444,17 +425,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+37"/>
+ <location line="+53"/>
<source>Reindexing blocks on disk...</source>
<translation>Reindexing blocks on disk...</translation>
</message>
<message>
- <location line="+316"/>
+ <location line="+315"/>
<source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-880"/>
+ <location line="-970"/>
<source>Send coins to a Bitcoin address</source>
<translation>Send coins to a Bitcoin address</translation>
</message>
@@ -484,17 +465,12 @@
<translation>&amp;Verify message...</translation>
</message>
<message>
- <location line="+570"/>
+ <location line="+660"/>
<source>Bitcoin</source>
<translation>Bitcoin</translation>
</message>
<message>
- <location line="-792"/>
- <source>Wallet</source>
- <translation>Wallet</translation>
- </message>
- <message>
- <location line="+149"/>
+ <location line="-733"/>
<source>&amp;Send</source>
<translation>&amp;Send</translation>
</message>
@@ -529,32 +505,32 @@
<translation>Verify messages to ensure they were signed with specified Bitcoin addresses</translation>
</message>
<message>
- <location line="+58"/>
+ <location line="+61"/>
<source>&amp;File</source>
<translation>&amp;File</translation>
</message>
<message>
- <location line="+14"/>
+ <location line="+11"/>
<source>&amp;Settings</source>
<translation>&amp;Settings</translation>
</message>
<message>
- <location line="+9"/>
+ <location line="+66"/>
<source>&amp;Help</source>
<translation>&amp;Help</translation>
</message>
<message>
- <location line="+15"/>
+ <location line="+11"/>
<source>Tabs toolbar</source>
<translation>Tabs toolbar</translation>
</message>
<message>
- <location line="-158"/>
+ <location line="-211"/>
<source>Request payments (generates QR codes and bitcoin: URIs)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+70"/>
+ <location line="+71"/>
<source>Show the list of used sending addresses and labels</source>
<translation type="unfinished"></translation>
</message>
@@ -574,7 +550,7 @@
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="+410"/>
+ <location line="+484"/>
<source>%n active connection(s) to Bitcoin network</source>
<translation>
<numerusform>%n active connection to Bitcoin network</numerusform>
@@ -582,7 +558,7 @@
</translation>
</message>
<message>
- <location line="+60"/>
+ <location line="+76"/>
<source>Indexing blocks on disk...</source>
<translation type="unfinished"></translation>
</message>
@@ -600,7 +576,7 @@
</translation>
</message>
<message>
- <location line="+24"/>
+ <location line="+23"/>
<source>%1 behind</source>
<translation>%1 behind</translation>
</message>
@@ -635,22 +611,57 @@
<translation>Up to date</translation>
</message>
<message>
- <location line="-494"/>
+ <location line="-593"/>
+ <source>&amp;Sending addresses</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>&amp;Receiving addresses</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
<source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+253"/>
+ <location line="+63"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished">&amp;Window</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Minimize</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Zoom</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <source>Restore</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Main Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+228"/>
<source>%1 client</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+227"/>
+ <location line="+241"/>
<source>Connecting to peers...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+38"/>
+ <location line="+37"/>
<source>Catching up...</source>
<translation>Catching up...</translation>
</message>
@@ -711,6 +722,11 @@
<translation type="unfinished"></translation>
</message>
<message>
+ <location line="+0"/>
+ <source>Private key &lt;b&gt;disabled&lt;/b&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+19"/>
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
<translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</translation>
@@ -721,7 +737,7 @@
<translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+529"/>
+ <location filename="../bitcoin.cpp" line="+395"/>
<source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
<translation type="unfinished"></translation>
</message>
@@ -814,7 +830,7 @@
<translation type="unfinished">Confirmed</translation>
</message>
<message>
- <location filename="../coincontroldialog.cpp" line="+53"/>
+ <location filename="../coincontroldialog.cpp" line="+58"/>
<source>Copy address</source>
<translation type="unfinished"></translation>
</message>
@@ -875,7 +891,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+315"/>
+ <location line="+313"/>
<source>(%1 locked)</source>
<translation type="unfinished"></translation>
</message>
@@ -987,7 +1003,7 @@
<context>
<name>FreespaceChecker</name>
<message>
- <location filename="../intro.cpp" line="+77"/>
+ <location filename="../intro.cpp" line="+73"/>
<source>A new data directory will be created.</source>
<translation>A new data directory will be created.</translation>
</message>
@@ -1015,7 +1031,7 @@
<context>
<name>HelpMessageDialog</name>
<message>
- <location filename="../utilitydialog.cpp" line="+41"/>
+ <location filename="../utilitydialog.cpp" line="+44"/>
<source>version</source>
<translation type="unfinished">version</translation>
</message>
@@ -1079,7 +1095,7 @@
<translation>Use a custom data directory:</translation>
</message>
<message>
- <location filename="../intro.cpp" line="+20"/>
+ <location filename="../intro.cpp" line="+22"/>
<source>Bitcoin</source>
<translation type="unfinished">Bitcoin</translation>
</message>
@@ -1104,7 +1120,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+73"/>
+ <location line="+80"/>
<source>Error: Specified data directory &quot;%1&quot; cannot be created.</source>
<translation type="unfinished"></translation>
</message>
@@ -1252,12 +1268,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+16"/>
- <source>MB</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+27"/>
+ <location line="+43"/>
<source>Number of script &amp;verification threads</source>
<translation type="unfinished"></translation>
</message>
@@ -1301,12 +1312,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+31"/>
- <source>Active command-line options that override above options:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+45"/>
+ <location line="+76"/>
<source>Open the %1 configuration file from the working directory.</source>
<translation type="unfinished"></translation>
</message>
@@ -1351,7 +1357,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+68"/>
+ <location line="+28"/>
+ <source>MiB</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+40"/>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
<translation type="unfinished"></translation>
</message>
@@ -1509,7 +1520,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+182"/>
+ <location line="+41"/>
+ <source>Options set in this dialog are overridden by the command line or in the configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+141"/>
<source>&amp;OK</source>
<translation>&amp;OK</translation>
</message>
@@ -1519,17 +1535,17 @@
<translation>&amp;Cancel</translation>
</message>
<message>
- <location filename="../optionsdialog.cpp" line="+92"/>
+ <location filename="../optionsdialog.cpp" line="+96"/>
<source>default</source>
<translation>default</translation>
</message>
<message>
- <location line="+56"/>
+ <location line="+63"/>
<source>none</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+77"/>
+ <location line="+93"/>
<source>Confirm options reset</source>
<translation>Confirm options reset</translation>
</message>
@@ -1672,8 +1688,8 @@
<context>
<name>PaymentServer</name>
<message>
- <location filename="../paymentserver.cpp" line="+317"/>
- <location line="+215"/>
+ <location filename="../paymentserver.cpp" line="+226"/>
+ <location line="+338"/>
<location line="+42"/>
<location line="+110"/>
<location line="+14"/>
@@ -1682,30 +1698,42 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-398"/>
+ <location line="-521"/>
<source>Cannot start bitcoin: click-to-pay handler</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+82"/>
- <location line="+21"/>
- <location line="+13"/>
+ <location line="+62"/>
+ <location line="+9"/>
+ <location line="+16"/>
+ <location line="+5"/>
+ <location line="+12"/>
<location line="+7"/>
<source>URI handling</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-41"/>
+ <location line="-49"/>
<source>&apos;bitcoin://&apos; is not a valid URI. Use &apos;bitcoin:&apos; instead.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
+ <location line="+10"/>
+ <source>You are using a BIP70 URL which will be unsupported in the future.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+16"/>
<source>Payment request fetch URL is invalid: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+12"/>
+ <location line="+5"/>
+ <source>Cannot process payment request because BIP70 support was not compiled in.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+11"/>
<source>Invalid payment address %1</source>
<translation type="unfinished"></translation>
</message>
@@ -1715,7 +1743,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
+ <location line="+14"/>
<source>Payment request file handling</source>
<translation type="unfinished"></translation>
</message>
@@ -1725,7 +1753,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+61"/>
+ <location line="+195"/>
<location line="+9"/>
<location line="+31"/>
<location line="+10"/>
@@ -1796,7 +1824,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
+ <location line="+6"/>
<source>Payment acknowledged</source>
<translation type="unfinished"></translation>
</message>
@@ -1842,12 +1870,12 @@
<translation type="unfinished">Amount</translation>
</message>
<message>
- <location filename="../guiutil.cpp" line="+115"/>
+ <location filename="../guiutil.cpp" line="+111"/>
<source>Enter a Bitcoin address (e.g. %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+686"/>
+ <location line="+687"/>
<source>%1 d</source>
<translation type="unfinished"></translation>
</message>
@@ -1957,7 +1985,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+192"/>
+ <location filename="../bitcoin.cpp" line="+185"/>
<source>%1 didn&apos;t yet exit safely...</source>
<translation type="unfinished"></translation>
</message>
@@ -1970,12 +1998,12 @@
<context>
<name>QObject::QObject</name>
<message>
- <location filename="../bitcoin.cpp" line="-117"/>
+ <location filename="../bitcoin.cpp" line="-113"/>
<source>Error parsing command line arguments: %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+38"/>
+ <location line="+37"/>
<source>Error: Specified data directory &quot;%1&quot; does not exist.</source>
<translation type="unfinished"></translation>
</message>
@@ -2019,7 +2047,8 @@
<location filename="../forms/debugwindow.ui" line="+56"/>
<location line="+26"/>
<location line="+26"/>
- <location line="+23"/>
+ <location line="+26"/>
+ <location line="+29"/>
<location line="+26"/>
<location line="+36"/>
<location line="+23"/>
@@ -2027,7 +2056,7 @@
<location line="+23"/>
<location line="+36"/>
<location line="+23"/>
- <location line="+679"/>
+ <location line="+713"/>
<location line="+23"/>
<location line="+23"/>
<location line="+23"/>
@@ -2049,7 +2078,7 @@
<translation>N/A</translation>
</message>
<message>
- <location line="-1361"/>
+ <location line="-1427"/>
<source>Client version</source>
<translation>Client version</translation>
</message>
@@ -2079,7 +2108,22 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+26"/>
+ <location line="+10"/>
+ <source>To specify a non-default location of the data directory use the &apos;%1&apos; option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+19"/>
+ <source>Blocksdir</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>To specify a non-default location of the blocks directory use the &apos;%1&apos; option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+19"/>
<source>Startup time</source>
<translation>Startup time</translation>
</message>
@@ -2129,7 +2173,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+11"/>
<source>(none)</source>
<translation type="unfinished"></translation>
</message>
@@ -2140,35 +2184,35 @@
</message>
<message>
<location line="+80"/>
- <location line="+558"/>
+ <location line="+589"/>
<source>Received</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-478"/>
- <location line="+455"/>
+ <location line="-509"/>
+ <location line="+486"/>
<source>Sent</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-414"/>
+ <location line="-445"/>
<source>&amp;Peers</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+53"/>
+ <location line="+67"/>
<source>Banned peers</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+60"/>
- <location filename="../rpcconsole.cpp" line="+502"/>
- <location line="+760"/>
+ <location line="+65"/>
+ <location filename="../rpcconsole.cpp" line="+501"/>
+ <location line="+754"/>
<source>Select a peer to view detailed information.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+25"/>
+ <location line="+37"/>
<source>Whitelisted</source>
<translation type="unfinished"></translation>
</message>
@@ -2198,18 +2242,18 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1095"/>
- <location line="+1003"/>
+ <location line="-1161"/>
+ <location line="+1069"/>
<source>User Agent</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-700"/>
+ <location line="-734"/>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+84"/>
+ <location line="+87"/>
<source>Decrease font size</source>
<translation type="unfinished"></translation>
</message>
@@ -2219,7 +2263,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+610"/>
+ <location line="+641"/>
<source>Services</source>
<translation type="unfinished"></translation>
</message>
@@ -2269,7 +2313,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1132"/>
+ <location line="-1166"/>
<source>Last block time</source>
<translation>Last block time</translation>
</message>
@@ -2284,7 +2328,7 @@
<translation>&amp;Console</translation>
</message>
<message>
- <location line="+211"/>
+ <location line="+214"/>
<source>&amp;Network Traffic</source>
<translation type="unfinished"></translation>
</message>
@@ -2294,7 +2338,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../rpcconsole.cpp" line="-414"/>
+ <location filename="../rpcconsole.cpp" line="-411"/>
<source>In:</source>
<translation type="unfinished"></translation>
</message>
@@ -2304,17 +2348,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../forms/debugwindow.ui" line="-315"/>
+ <location filename="../forms/debugwindow.ui" line="-318"/>
<source>Debug log file</source>
<translation>Debug log file</translation>
</message>
<message>
- <location line="+152"/>
+ <location line="+155"/>
<source>Clear console</source>
<translation>Clear console</translation>
</message>
<message>
- <location filename="../rpcconsole.cpp" line="-253"/>
+ <location filename="../rpcconsole.cpp" line="-249"/>
<source>1 &amp;hour</source>
<translation type="unfinished"></translation>
</message>
@@ -2347,17 +2391,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+48"/>
+ <location line="+47"/>
<source>&amp;Unban</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+54"/>
- <source>default wallet</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+110"/>
+ <location line="+161"/>
<source>Welcome to the %1 RPC console.</source>
<translation type="unfinished"></translation>
</message>
@@ -2387,17 +2426,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+68"/>
+ <location line="+70"/>
<source>Executing command without any wallet</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="-2"/>
<source>Executing command using &quot;%1&quot; wallet</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+190"/>
+ <location line="+189"/>
<source>(node id: %1)</source>
<translation type="unfinished"></translation>
</message>
@@ -2626,7 +2665,7 @@
<context>
<name>RecentRequestsTableModel</name>
<message>
- <location filename="../recentrequeststablemodel.cpp" line="+27"/>
+ <location filename="../recentrequeststablemodel.cpp" line="+25"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -2665,7 +2704,7 @@
<name>SendCoinsDialog</name>
<message>
<location filename="../forms/sendcoinsdialog.ui" line="+14"/>
- <location filename="../sendcoinsdialog.cpp" line="+588"/>
+ <location filename="../sendcoinsdialog.cpp" line="+593"/>
<source>Send Coins</source>
<translation>Send Coins</translation>
</message>
@@ -2772,18 +2811,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+80"/>
- <location line="+13"/>
- <source>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
- <source>(read the tooltip)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+29"/>
+ <location line="+112"/>
<source>Recommended:</source>
<translation type="unfinished"></translation>
</message>
@@ -2793,12 +2821,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+52"/>
+ <location line="+49"/>
<source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+185"/>
+ <location line="+166"/>
<source>Send to multiple recipients at once</source>
<translation>Send to multiple recipients at once</translation>
</message>
@@ -2813,17 +2841,27 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-835"/>
+ <location line="-800"/>
<source>Dust:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+696"/>
+ <location line="+543"/>
+ <source>When there is less transaction volume than space in the blocks, miners as well as relaying nodes may enforce a minimum fee. Paying only this minimum fee is just fine, but be aware that this can result in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>A too low fee might result in a never confirming transaction (read the tooltip)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+131"/>
<source>Confirmation time target:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+74"/>
+ <location line="+58"/>
<source>Enable Replace-By-Fee</source>
<translation type="unfinished"></translation>
</message>
@@ -2853,7 +2891,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>S&amp;end</translation>
</message>
<message>
- <location filename="../sendcoinsdialog.cpp" line="-504"/>
+ <location filename="../sendcoinsdialog.cpp" line="-505"/>
<source>Copy quantity</source>
<translation type="unfinished"></translation>
</message>
@@ -2888,20 +2926,20 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+76"/>
+ <location line="+74"/>
<source>%1 (%2 blocks)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+131"/>
- <location line="+5"/>
+ <location line="+133"/>
<location line="+5"/>
+ <location line="+6"/>
<location line="+4"/>
<source>%1 to %2</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+7"/>
<source>Are you sure you want to send?</source>
<translation type="unfinished"></translation>
</message>
@@ -2916,12 +2954,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-57"/>
+ <location line="-61"/>
<source>from wallet %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+36"/>
+ <location line="+40"/>
<source>Please, review your transaction.</source>
<translation type="unfinished"></translation>
</message>
@@ -2946,7 +2984,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+192"/>
+ <location line="+191"/>
<source>The recipient address is not valid. Please recheck.</source>
<translation type="unfinished"></translation>
</message>
@@ -2990,13 +3028,8 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<source>Payment request expired.</source>
<translation type="unfinished"></translation>
</message>
- <message>
- <location line="+91"/>
- <source>Pay only the required fee of %1</source>
- <translation type="unfinished"></translation>
- </message>
<message numerus="yes">
- <location line="+43"/>
+ <location line="+120"/>
<source>Estimated to begin confirmation within %n block(s).</source>
<translation>
<numerusform>Estimated to begin confirmation within %n block.</numerusform>
@@ -3138,7 +3171,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../sendcoinsentry.cpp" line="+35"/>
+ <location filename="../sendcoinsentry.cpp" line="+39"/>
<source>Enter a label for this address to add it to your address book</source>
<translation type="unfinished"></translation>
</message>
@@ -3366,7 +3399,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>TransactionDesc</name>
<message numerus="yes">
- <location filename="../transactiondesc.cpp" line="+31"/>
+ <location filename="../transactiondesc.cpp" line="+35"/>
<source>Open for %n more block(s)</source>
<translation>
<numerusform>Open for %n more block</numerusform>
@@ -3414,7 +3447,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+23"/>
+ <location line="+22"/>
<source>Status</source>
<translation type="unfinished"></translation>
</message>
@@ -3473,12 +3506,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<location line="+12"/>
<location line="+54"/>
<location line="+30"/>
- <location line="+58"/>
+ <location line="+60"/>
<source>Credit</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="-152"/>
+ <location line="-154"/>
<source>matures in %n more block(s)</source>
<translation>
<numerusform>matures in %n more block</numerusform>
@@ -3493,12 +3526,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<message>
<location line="+60"/>
<location line="+26"/>
- <location line="+61"/>
+ <location line="+63"/>
<source>Debit</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-77"/>
+ <location line="-79"/>
<source>Total debit</source>
<translation type="unfinished"></translation>
</message>
@@ -3549,12 +3582,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+18"/>
+ <location line="+19"/>
<source>Merchant</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
+ <location line="+8"/>
<source>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to &quot;not accepted&quot; and it won&apos;t be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
<translation type="unfinished"></translation>
</message>
@@ -3607,7 +3640,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>TransactionTableModel</name>
<message>
- <location filename="../transactiontablemodel.cpp" line="+226"/>
+ <location filename="../transactiontablemodel.cpp" line="+227"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -3743,7 +3776,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>TransactionView</name>
<message>
- <location filename="../transactionview.cpp" line="+70"/>
+ <location filename="../transactionview.cpp" line="+71"/>
<location line="+16"/>
<source>All</source>
<translation type="unfinished"></translation>
@@ -3814,7 +3847,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+47"/>
+ <location line="+51"/>
<source>Abandon transaction</source>
<translation type="unfinished"></translation>
</message>
@@ -3864,7 +3897,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+193"/>
+ <location line="+199"/>
<source>Export Transaction History</source>
<translation type="unfinished"></translation>
</message>
@@ -3929,7 +3962,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+166"/>
+ <location line="+171"/>
<source>Range:</source>
<translation type="unfinished"></translation>
</message>
@@ -3942,7 +3975,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>UnitDisplayStatusBarControl</name>
<message>
- <location filename="../bitcoingui.cpp" line="+159"/>
+ <location filename="../bitcoingui.cpp" line="+154"/>
<source>Unit to show amounts in. Click to select another unit.</source>
<translation type="unfinished"></translation>
</message>
@@ -3958,19 +3991,19 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>WalletModel</name>
<message>
- <location filename="../walletmodel.cpp" line="+215"/>
+ <location filename="../walletmodel.cpp" line="+219"/>
<source>Send Coins</source>
<translation type="unfinished">Send Coins</translation>
</message>
<message>
- <location line="+289"/>
+ <location line="+301"/>
<location line="+39"/>
- <location line="+6"/>
+ <location line="+5"/>
<source>Fee bump error</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-45"/>
+ <location line="-44"/>
<source>Increasing transaction fee failed</source>
<translation type="unfinished"></translation>
</message>
@@ -4005,10 +4038,15 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+5"/>
<source>Could not commit transaction</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location line="+35"/>
+ <source>default wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>WalletView</name>
@@ -4023,7 +4061,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished">Export the data in the current tab to a file</translation>
</message>
<message>
- <location line="+207"/>
+ <location line="+206"/>
<source>Backup Wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -4053,7 +4091,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+54"/>
+ <location line="+44"/>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
@@ -4066,7 +4104,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+28"/>
+ <location line="+20"/>
<source>Prune configured below the minimum of %d MiB. Please use a higher number.</source>
<translation type="unfinished"></translation>
</message>
@@ -4081,22 +4119,22 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+81"/>
+ <location line="+74"/>
<source>Error: A fatal internal error occurred, see debug.log for details</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+25"/>
+ <location line="+26"/>
<source>Pruning blockstore...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+28"/>
+ <location line="+30"/>
<source>Unable to start HTTP server. See debug log for details.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-184"/>
+ <location line="-172"/>
<source>Bitcoin Core</source>
<translation type="unfinished">Bitcoin Core</translation>
</message>
@@ -4116,17 +4154,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+12"/>
+ <location line="+10"/>
<source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
- <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+9"/>
+ <location line="+11"/>
<source>Please check that your computer&apos;s date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation type="unfinished"></translation>
</message>
@@ -4161,7 +4194,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
+ <location line="+5"/>
<source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source>
<translation type="unfinished"></translation>
</message>
@@ -4197,6 +4230,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</message>
<message>
<location line="+1"/>
+ <source>Config setting for %s only applied on %s network when in [%s] section.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Copyright (C) %i-%i</source>
<translation type="unfinished"></translation>
</message>
@@ -4212,11 +4250,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</message>
<message>
<location line="+2"/>
- <source>Error creating %s: You can&apos;t create non-HD wallets with this version.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
<source>Error initializing block database</source>
<translation>Error initializing block database</translation>
</message>
@@ -4246,7 +4279,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+1"/>
<source>Error loading block database</source>
<translation>Error loading block database</translation>
</message>
@@ -4256,7 +4289,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>Error opening block database</translation>
</message>
<message>
- <location line="+5"/>
+ <location line="+6"/>
<source>Error: Disk space is low!</source>
<translation>Error: Disk space is low!</translation>
</message>
@@ -4301,17 +4334,17 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+21"/>
+ <location line="+22"/>
<source>Specified blocks directory &quot;%s&quot; does not exist.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+26"/>
+ <location line="+24"/>
<source>Upgrading txindex database</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-43"/>
+ <location line="-42"/>
<source>Loading P2P addresses...</source>
<translation type="unfinished"></translation>
</message>
@@ -4346,12 +4379,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
+ <location line="+8"/>
<source>The source code is available from %s.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+9"/>
<source>Transaction fee and change calculation failed</source>
<translation type="unfinished"></translation>
</message>
@@ -4367,21 +4400,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</message>
<message>
<location line="+3"/>
- <source>Unsupported argument -benchmark ignored, use -debug=bench.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Unsupported argument -debugnet ignored, use -debug=net.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Unsupported argument -tor found, use -onion.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
<source>Unsupported logging category %s=%s.</source>
<translation type="unfinished"></translation>
</message>
@@ -4406,12 +4424,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-171"/>
+ <location line="-158"/>
<source>Error: Listening for incoming connections failed (listen returned error %s)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
+ <location line="+5"/>
<source>Invalid amount for -maxtxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
<translation type="unfinished"></translation>
</message>
@@ -4421,17 +4439,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+44"/>
+ <location line="+38"/>
<source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
- <source>Error loading %s: You can&apos;t disable HD on an already existing HD wallet</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+4"/>
+ <location line="+25"/>
<source>Error reading from database, shutting down.</source>
<translation type="unfinished"></translation>
</message>
@@ -4441,7 +4454,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+3"/>
+ <source>Error: Disk space is low for %s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
<source>Information</source>
<translation>Information</translation>
</message>
@@ -4477,6 +4495,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</message>
<message>
<location line="+4"/>
+ <source>Section [%s] is not recognized.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Signing transaction failed</source>
<translation>Signing transaction failed</translation>
</message>
@@ -4497,6 +4520,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</message>
<message>
<location line="+4"/>
+ <source>The specified config file %s does not exist
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>The transaction amount is too small to pay the fee</source>
<translation type="unfinished"></translation>
</message>
@@ -4531,7 +4560,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+12"/>
+ <location line="+9"/>
<source>Verifying wallet(s)...</source>
<translation type="unfinished"></translation>
</message>
@@ -4556,17 +4585,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-197"/>
+ <location line="-182"/>
<source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+18"/>
- <source>Error loading %s: You can&apos;t enable HD on an already existing non-HD wallet</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+46"/>
+ <location line="+56"/>
<source>This is the transaction fee you may pay when fee estimates are not available.</source>
<translation type="unfinished"></translation>
</message>
@@ -4581,17 +4605,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
- <source>Unsupported argument -socks found. Setting SOCKS version isn&apos;t possible anymore, only SOCKS5 proxies are supported.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
- <source>Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+8"/>
+ <location line="+14"/>
<source>Warning: Unknown block versions being mined! It&apos;s possible unknown rules are in effect</source>
<translation type="unfinished"></translation>
</message>
@@ -4606,22 +4620,22 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+19"/>
+ <location line="+18"/>
<source>Error loading wallet %s. Duplicate -wallet filename specified.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+21"/>
+ <location line="+22"/>
<source>Keypool ran out, please call keypoolrefill first</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+19"/>
+ <location line="+20"/>
<source>Starting network threads...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+4"/>
<source>The wallet will avoid paying less than the minimum relay fee.</source>
<translation type="unfinished"></translation>
</message>
@@ -4656,12 +4670,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>Unknown network specified in -onlynet: &apos;%s&apos;</translation>
</message>
<message>
- <location line="-46"/>
+ <location line="-48"/>
<source>Insufficient funds</source>
<translation>Insufficient funds</translation>
</message>
<message>
- <location line="-134"/>
+ <location line="-120"/>
<source>Can&apos;t generate a change-address key. Private keys are disabled for this wallet.</source>
<translation type="unfinished"></translation>
</message>
@@ -4671,12 +4685,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+14"/>
+ <location line="+12"/>
<source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+60"/>
+ <location line="+48"/>
<source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
<translation type="unfinished"></translation>
</message>
@@ -4706,12 +4720,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>Rescanning...</translation>
</message>
<message>
- <location line="-43"/>
+ <location line="-42"/>
<source>Done loading</source>
<translation>Done loading</translation>
</message>
<message>
- <location line="+14"/>
+ <location line="+12"/>
<source>Error</source>
<translation>Error</translation>
</message>
diff --git a/src/qt/locale/bitcoin_en_GB.ts b/src/qt/locale/bitcoin_en_GB.ts
index 7da223f52e..92a08280d7 100644
--- a/src/qt/locale/bitcoin_en_GB.ts
+++ b/src/qt/locale/bitcoin_en_GB.ts
@@ -330,6 +330,10 @@
<translation>Wallet:</translation>
</message>
<message>
+ <source>default wallet</source>
+ <translation>default wallet</translation>
+ </message>
+ <message>
<source>Click to disable network activity.</source>
<translation>Click to disable network activity.</translation>
</message>
@@ -350,6 +354,10 @@
<translation>Reindexing blocks on disk...</translation>
</message>
<message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</translation>
+ </message>
+ <message>
<source>Send coins to a Bitcoin address</source>
<translation>Send coins to a Bitcoin address</translation>
</message>
@@ -760,6 +768,14 @@
<translation>The entered address "%1" is not a valid Bitcoin address.</translation>
</message>
<message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>The entered address "%1" is already in the address book with label "%2".</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>Could not unlock wallet.</translation>
</message>
@@ -834,7 +850,7 @@
</message>
<message>
<source>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source>
- <translation>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</translation>
+ <translation>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterwards to keep your disk usage low.</translation>
</message>
<message>
<source>Use the default data directory</source>
@@ -1038,10 +1054,22 @@
<translation>&amp;Network</translation>
</message>
<message>
+ <source>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</source>
+ <translation>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Prune &amp;block storage to</translation>
+ </message>
+ <message>
<source>GB</source>
<translation>GB</translation>
</message>
<message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Reverting this setting requires re-downloading the entire blockchain.</translation>
+ </message>
+ <message>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
<translation>(0 = auto, &lt;0 = leave that many cores free)</translation>
</message>
@@ -1308,6 +1336,10 @@
<translation>URI handling</translation>
</message>
<message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</translation>
+ </message>
+ <message>
<source>Payment request fetch URL is invalid: %1</source>
<translation>Payment request fetch URL is invalid: %1</translation>
</message>
@@ -1505,10 +1537,18 @@
<context>
<name>QObject::QObject</name>
<message>
+ <source>Error parsing command line arguments: %1.</source>
+ <translation>Error parsing command line arguments: %1.</translation>
+ </message>
+ <message>
<source>Error: Specified data directory "%1" does not exist.</source>
<translation>Error: Specified data directory "%1" does not exist.</translation>
</message>
<message>
+ <source>Error: Cannot parse configuration file: %1.</source>
+ <translation>Error: Cannot parse configuration file: %1.</translation>
+ </message>
+ <message>
<source>Error: %1</source>
<translation>Error: %1</translation>
</message>
@@ -1564,7 +1604,7 @@
</message>
<message>
<source>Startup time</source>
- <translation>Startup time</translation>
+ <translation>Start up time</translation>
</message>
<message>
<source>Network</source>
@@ -1775,6 +1815,10 @@
<translation>&amp;Unban</translation>
</message>
<message>
+ <source>default wallet</source>
+ <translation>default wallet</translation>
+ </message>
+ <message>
<source>Welcome to the %1 RPC console.</source>
<translation>Welcome to the %1 RPC console.</translation>
</message>
@@ -1799,6 +1843,14 @@
<translation>Network activity disabled</translation>
</message>
<message>
+ <source>Executing command without any wallet</source>
+ <translation>Executing command without any wallet</translation>
+ </message>
+ <message>
+ <source>Executing command using "%1" wallet</source>
+ <translation>Executing command using "%1" wallet</translation>
+ </message>
+ <message>
<source>(node id: %1)</source>
<translation>(node id: %1)</translation>
</message>
@@ -1870,6 +1922,14 @@
<translation>Clear</translation>
</message>
<message>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <translation>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</translation>
+ </message>
+ <message>
+ <source>Generate native segwit (Bech32) address</source>
+ <translation>Generate native segwit (Bech32) address</translation>
+ </message>
+ <message>
<source>Requested payments history</source>
<translation>Requested payments history</translation>
</message>
@@ -2075,6 +2135,14 @@
<translation>collapse fee-settings</translation>
</message>
<message>
+ <source>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</source>
+ <translation>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</translation>
+ </message>
+ <message>
<source>per kilobyte</source>
<translation>per kilobyte</translation>
</message>
@@ -2195,6 +2263,14 @@
<translation>You can increase the fee later (signals Replace-By-Fee, BIP-125).</translation>
</message>
<message>
+ <source>from wallet %1</source>
+ <translation>from wallet %1</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction.</source>
+ <translation>Please, review your transaction.</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>Transaction fee</translation>
</message>
@@ -2203,6 +2279,10 @@
<translation>Not signalling Replace-By-Fee, BIP-125.</translation>
</message>
<message>
+ <source>Total Amount</source>
+ <translation>Total Amount</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>Confirm send coins</translation>
</message>
@@ -2656,6 +2736,10 @@
<translation>Transaction total size</translation>
</message>
<message>
+ <source>Transaction virtual size</source>
+ <translation>Transaction virtual size</translation>
+ </message>
+ <message>
<source>Output index</source>
<translation>Output index</translation>
</message>
@@ -3116,6 +3200,10 @@
<translation>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</translation>
</message>
<message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</translation>
+ </message>
+ <message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</translation>
</message>
@@ -3200,6 +3288,10 @@
<translation>Error loading %s</translation>
</message>
<message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>Error loading %s: Private keys can only be disabled during creation</translation>
+ </message>
+ <message>
<source>Error loading %s: Wallet corrupted</source>
<translation>Error loading %s: Wallet corrupted</translation>
</message>
@@ -3252,6 +3344,14 @@
<translation>Invalid amount for -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Specified blocks directory "%s" does not exist.</translation>
+ </message>
+ <message>
+ <source>Upgrading txindex database</source>
+ <translation>Upgrading txindex database</translation>
+ </message>
+ <message>
<source>Loading P2P addresses...</source>
<translation>Loading P2P addresses...</translation>
</message>
@@ -3292,6 +3392,10 @@
<translation>Unable to bind to %s on this computer. %s is probably already running.</translation>
</message>
<message>
+ <source>Unable to generate keys</source>
+ <translation>Unable to generate keys</translation>
+ </message>
+ <message>
<source>Unsupported argument -benchmark ignored, use -debug=bench.</source>
<translation>Unsupported argument -benchmark ignored, use -debug=bench.</translation>
</message>
@@ -3465,7 +3569,7 @@
</message>
<message>
<source>Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.</source>
- <translation>Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.</translation>
+ <translation>Unsupported argument -socks found. Setting SOCKS version isn't possible any more, only SOCKS5 proxies are supported.</translation>
</message>
<message>
<source>Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay.</source>
@@ -3528,6 +3632,26 @@
<translation>Insufficient funds</translation>
</message>
<message>
+ <source>Can't generate a change-address key. Private keys are disabled for this wallet.</source>
+ <translation>Can't generate a change-address key. Private keys are disabled for this wallet.</translation>
+ </message>
+ <message>
+ <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
+ <translation>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</translation>
+ </message>
+ <message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</translation>
+ </message>
+ <message>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <translation>Warning: Private keys detected in wallet {%s} with disabled private keys</translation>
+ </message>
+ <message>
+ <source>Cannot write to data directory '%s'; check permissions.</source>
+ <translation>Cannot write to data directory '%s'; check permissions.</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>Loading block index...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_es.ts b/src/qt/locale/bitcoin_es.ts
index 0ae126eb33..7b07380606 100644
--- a/src/qt/locale/bitcoin_es.ts
+++ b/src/qt/locale/bitcoin_es.ts
@@ -3199,6 +3199,10 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
<translation>Error leyendo %s!. Todas las claves se han leido correctamente, pero los datos de transacciones o la libreta de direcciones pueden faltar o ser incorrectos.</translation>
</message>
<message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>Grupo de salida por dirección, seleccionando todo o nada, en lugar de seleccionar en una base para salida. La privacidad se mejora como una dirección utilizada una sola vez (a menos que alguien lo envíe después de gastarlo), a menos que alguien envíe una solicitud, pero puede resultar en tarifas ligeramente más altas ya que la selección de la moneda puede ser inferior a la óptima debido a la limitación adicional después de gastarla. (por defecto, predeterminado %u)</translation>
+ </message>
+ <message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation>Por favor, compruebe si la fecha y hora en su computadora son correctas! Si su reloj esta mal, %s no trabajara correctamente. </translation>
</message>
@@ -3339,6 +3343,10 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
<translation>Cantidad inválida para -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>El directorio de bloques «%s» especificado no existe.</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Actualización de la base de datos txindex</translation>
</message>
@@ -3491,12 +3499,6 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
<translation>El -walletdir "%s" indicado no es un directorio</translation>
</message>
<message>
- <source>Specified blocks directory "%s" does not exist.
-</source>
- <translation>El directorio de bloques especificado "%s" no existe.
-</translation>
- </message>
- <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>Cantidad de la transacción demasiado pequeña para pagar la comisión</translation>
</message>
diff --git a/src/qt/locale/bitcoin_es_VE.ts b/src/qt/locale/bitcoin_es_VE.ts
index 9c7fb2e2a0..b500c16352 100644
--- a/src/qt/locale/bitcoin_es_VE.ts
+++ b/src/qt/locale/bitcoin_es_VE.ts
@@ -50,6 +50,10 @@
<translation>Elige la dirección para recibir monedas</translation>
</message>
<message>
+ <source>C&amp;hoose</source>
+ <translation>Escoger</translation>
+ </message>
+ <message>
<source>Sending addresses</source>
<translation>Envío de direcciones</translation>
</message>
@@ -66,6 +70,10 @@
<translation>Estas son tus direcciones Bitcoin para recibir pagos. Es recomendable usar una nueva dirección de recibo para cada transacción.</translation>
</message>
<message>
+ <source>&amp;Copy Address</source>
+ <translation>Copiar dirección</translation>
+ </message>
+ <message>
<source>Copy &amp;Label</source>
<translation>Copiar &amp;Etiqueta</translation>
</message>
@@ -77,10 +85,30 @@
<source>Export Address List</source>
<translation>Exportar lista de direcciones</translation>
</message>
- </context>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>Separar los archivos con comas (*.csv)</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Error al exportar</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the address list to %1. Please try again.</source>
+ <translation>Tuvimos un problema al guardar la dirección en la lista %1. Intenta de Nuevo.</translation>
+ </message>
+</context>
<context>
<name>AddressTableModel</name>
<message>
+ <source>Label</source>
+ <translation>Nombre</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Direccion</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(sin etiqueta)</translation>
</message>
@@ -103,6 +131,40 @@
<source>Repeat new passphrase</source>
<translation>Repetir nueva frase de contraseña</translation>
</message>
+ <message>
+ <source>Show password</source>
+ <translation>Mostrar contraseña</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>Cifrar monedero</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>Esta operación necesita su frase de contraseña de la billetera para desbloquearla.
+</translation>
+ </message>
+ <message>
+ <source>Unlock wallet</source>
+ <translation>Desbloquear monedero</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>Esta operación necesita su frase de contraseña de la billetera para descifrar la billetera.
+</translation>
+ </message>
+ <message>
+ <source>Decrypt wallet</source>
+ <translation>Descifrar monedero</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>Cambiar frase secreta</translation>
+ </message>
+ <message>
+ <source>Confirm wallet encryption</source>
+ <translation>Confirmar cifrado de billetera</translation>
+ </message>
</context>
<context>
<name>BanTableModel</name>
@@ -577,6 +639,14 @@
<translation>&amp;Copiar Dirección</translation>
</message>
<message>
+ <source>Address</source>
+ <translation>Direccion</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Nombre</translation>
+ </message>
+ <message>
<source>Wallet</source>
<translation>Billetera</translation>
</message>
@@ -584,6 +654,10 @@
<context>
<name>RecentRequestsTableModel</name>
<message>
+ <source>Label</source>
+ <translation>Nombre</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(sin etiqueta)</translation>
</message>
@@ -670,12 +744,32 @@
<context>
<name>TransactionTableModel</name>
<message>
+ <source>Label</source>
+ <translation>Nombre</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(sin etiqueta)</translation>
</message>
</context>
<context>
<name>TransactionView</name>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>Separar los archivos con comas (*.csv)</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Nombre</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Direccion</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Error al exportar</translation>
+ </message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
diff --git a/src/qt/locale/bitcoin_eu_ES.ts b/src/qt/locale/bitcoin_eu_ES.ts
index 1abc55acce..e266621d16 100644
--- a/src/qt/locale/bitcoin_eu_ES.ts
+++ b/src/qt/locale/bitcoin_eu_ES.ts
@@ -3,7 +3,7 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>Klikatu eskuinean helbidea edo etiketa aldatzeko</translation>
+ <translation>Klikatu eskuinarekin helbidea edo etiketa aldatzeko</translation>
</message>
<message>
<source>Create a new address</source>
@@ -136,6 +136,14 @@
<translation>Pasahitz berria errepiikatu</translation>
</message>
<message>
+ <source>Show password</source>
+ <translation>Erakutsi pasahitza</translation>
+ </message>
+ <message>
+ <source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
+ <translation>Sartu pasaesaldi bat diru-zorrorako. Mesedez erabili ausazko hamar edo gehiago karaktere edo zortzi edo gehiago hitz.</translation>
+ </message>
+ <message>
<source>Encrypt wallet</source>
<translation>Diruzorroa enkriptatu</translation>
</message>
@@ -274,6 +282,10 @@
<translation>Fitxen tresna-barra</translation>
</message>
<message>
+ <source>Error</source>
+ <translation>Akatsa</translation>
+ </message>
+ <message>
<source>Up to date</source>
<translation>Eguneratua</translation>
</message>
@@ -368,6 +380,10 @@
</context>
<context>
<name>Intro</name>
+ <message>
+ <source>Error</source>
+ <translation>Akatsa</translation>
+ </message>
</context>
<context>
<name>ModalOverlay</name>
@@ -385,6 +401,10 @@
<source>Options</source>
<translation>Aukerak</translation>
</message>
+ <message>
+ <source>Error</source>
+ <translation>Akatsa</translation>
+ </message>
</context>
<context>
<name>OverviewPage</name>
@@ -808,6 +828,10 @@
<context>
<name>bitcoin-core</name>
<message>
+ <source>Loading wallet...</source>
+ <translation>Diru-zorroa kargatzen</translation>
+ </message>
+ <message>
<source>Rescanning...</source>
<translation>Birbilatzen...</translation>
</message>
@@ -815,5 +839,9 @@
<source>Done loading</source>
<translation>Zamaketa amaitua</translation>
</message>
- </context>
+ <message>
+ <source>Error</source>
+ <translation>Akatsa</translation>
+ </message>
+</context>
</TS> \ No newline at end of file
diff --git a/src/qt/locale/bitcoin_fa.ts b/src/qt/locale/bitcoin_fa.ts
index 1fa0d9fb44..c2f2930a08 100644
--- a/src/qt/locale/bitcoin_fa.ts
+++ b/src/qt/locale/bitcoin_fa.ts
@@ -997,6 +997,10 @@
<context>
<name>PeerTableModel</name>
<message>
+ <source>Ping</source>
+ <translation>پینگ</translation>
+ </message>
+ <message>
<source>Sent</source>
<translation>ارسال شده</translation>
</message>
@@ -1811,10 +1815,30 @@
</context>
<context>
<name>WalletModel</name>
+ <message>
+ <source>Send Coins</source>
+ <translation>فرستادن سکه ها</translation>
+ </message>
+ <message>
+ <source>Current fee:</source>
+ <translation>دستمزد فعلی</translation>
+ </message>
+ <message>
+ <source>Increase:</source>
+ <translation>افزایش</translation>
+ </message>
+ <message>
+ <source>New fee:</source>
+ <translation>تعرفه جدید</translation>
+ </message>
</context>
<context>
<name>WalletView</name>
<message>
+ <source>&amp;Export</source>
+ <translation>و صدور</translation>
+ </message>
+ <message>
<source>Export the data in the current tab to a file</source>
<translation>صدور داده‌های برگهٔ فعلی به یک پرونده</translation>
</message>
diff --git a/src/qt/locale/bitcoin_fa_IR.ts b/src/qt/locale/bitcoin_fa_IR.ts
index 7912f43582..be00b00656 100644
--- a/src/qt/locale/bitcoin_fa_IR.ts
+++ b/src/qt/locale/bitcoin_fa_IR.ts
@@ -231,6 +231,10 @@
<context>
<name>BanTableModel</name>
<message>
+ <source>IP/Netmask</source>
+ <translation>آی پی/نت ماسک</translation>
+ </message>
+ <message>
<source>Banned Until</source>
<translation>مسدودشده تا</translation>
</message>
@@ -239,7 +243,7 @@
<name>BitcoinGUI</name>
<message>
<source>Sign &amp;message...</source>
- <translation>امضا و پیام</translation>
+ <translation>ثبت &amp;پیام</translation>
</message>
<message>
<source>Synchronizing with network...</source>
@@ -318,6 +322,10 @@
<translation>دریافت آدرس ها</translation>
</message>
<message>
+ <source>Open &amp;URI...</source>
+ <translation>بازکردن آدرس...</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>کیف پول:</translation>
</message>
@@ -362,6 +370,10 @@
<translation>پنجره دیباگ</translation>
</message>
<message>
+ <source>Open debugging and diagnostic console</source>
+ <translation>باز کردن کنسول دی باگ و تشخیص</translation>
+ </message>
+ <message>
<source>&amp;Verify message...</source>
<translation>تایید پیام</translation>
</message>
@@ -433,6 +445,10 @@
<source>Open a bitcoin: URI or payment request</source>
<translation>بازکردن بیت‌کوین: آدرس یا درخواست پرداخت</translation>
</message>
+ <message>
+ <source>&amp;Command-line options</source>
+ <translation>گزینه های خط فرمان</translation>
+ </message>
<message numerus="yes">
<source>%n active connection(s) to Bitcoin network</source>
<translation><numerusform>%n ارتباط فعال به شبکه بیت‌کوین</numerusform><numerusform>%n ارتباط فعال به شبکه بیت‌کوین</numerusform></translation>
@@ -474,6 +490,10 @@
<translation>به روز</translation>
</message>
<message>
+ <source>Connecting to peers...</source>
+ <translation>در حال اتصال به همتاهای شبکه...</translation>
+ </message>
+ <message>
<source>Catching up...</source>
<translation>در حال روزآمد سازی..</translation>
</message>
@@ -505,7 +525,11 @@
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
<translation>wallet رمزگذاری شد و در حال حاضر قفل است</translation>
</message>
- </context>
+ <message>
+ <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
+ <translation>خطای بحرانی رخ داده است. بیتکوین دیگر به صورت ایمن قادر به ادامه دادن نمی‌باشد و خارج خواهد شد.</translation>
+ </message>
+</context>
<context>
<name>CoinControlDialog</name>
<message>
@@ -517,6 +541,10 @@
<translation>مقدار</translation>
</message>
<message>
+ <source>Bytes:</source>
+ <translation>بایت ها:</translation>
+ </message>
+ <message>
<source>Amount:</source>
<translation>میزان وجه:</translation>
</message>
@@ -525,6 +553,14 @@
<translation>هزینه</translation>
</message>
<message>
+ <source>Dust:</source>
+ <translation>گرد و غبار با داست:</translation>
+ </message>
+ <message>
+ <source>After Fee:</source>
+ <translation>بعد از احتساب کارمزد</translation>
+ </message>
+ <message>
<source>Change:</source>
<translation>تغییر</translation>
</message>
@@ -589,6 +625,22 @@
<translation>کپی هزینه</translation>
</message>
<message>
+ <source>Copy after fee</source>
+ <translation>کپی کردن بعد از احتساب کارمزد</translation>
+ </message>
+ <message>
+ <source>Copy bytes</source>
+ <translation>کپی کردن بایت ها</translation>
+ </message>
+ <message>
+ <source>Copy dust</source>
+ <translation>کپی کردن داست:</translation>
+ </message>
+ <message>
+ <source>Copy change</source>
+ <translation>کپی کردن تغییر</translation>
+ </message>
+ <message>
<source>yes</source>
<translation>بله</translation>
</message>
@@ -600,7 +652,11 @@
<source>(no label)</source>
<translation>(برچسب ندارد)</translation>
</message>
- </context>
+ <message>
+ <source>(change)</source>
+ <translation>(تغییر)</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -631,21 +687,41 @@
<source>The entered address "%1" is not a valid Bitcoin address.</source>
<translation>آدرس وارد شده "%1" آدرس معتبر بیت کوین نیست.</translation>
</message>
- </context>
+ <message>
+ <source>Could not unlock wallet.</source>
+ <translation>نمیتوان کیف پول را باز کرد.</translation>
+ </message>
+ <message>
+ <source>New key generation failed.</source>
+ <translation>تولید کلید جدید به خطا انجامید.</translation>
+ </message>
+</context>
<context>
<name>FreespaceChecker</name>
<message>
+ <source>A new data directory will be created.</source>
+ <translation>پوشه داده جدید ساخته خواهد شد</translation>
+ </message>
+ <message>
<source>name</source>
<translation>نام</translation>
</message>
- </context>
+ <message>
+ <source>Cannot create data directory here.</source>
+ <translation>نمیتوان در اینجا پوشه داده ساخت.</translation>
+ </message>
+</context>
<context>
<name>HelpMessageDialog</name>
<message>
<source>version</source>
<translation>نسخه</translation>
</message>
- </context>
+ <message>
+ <source>Command-line options</source>
+ <translation>گزینه های خط-فرمان </translation>
+ </message>
+</context>
<context>
<name>Intro</name>
<message>
@@ -657,6 +733,14 @@
<translation>به %1 خوش آمدید.</translation>
</message>
<message>
+ <source>Use the default data directory</source>
+ <translation>استفاده کردن از پوشه داده پیشفرض</translation>
+ </message>
+ <message>
+ <source>Use a custom data directory:</source>
+ <translation>استفاده کردن از پوشه داده مخصوص:</translation>
+ </message>
+ <message>
<source>Bitcoin</source>
<translation>بیت کوین</translation>
</message>
@@ -692,6 +776,10 @@
<translation>پیشرفت</translation>
</message>
<message>
+ <source>Progress increase per hour</source>
+ <translation>سرعت افزایش پیشرفت بر ساعت</translation>
+ </message>
+ <message>
<source>calculating...</source>
<translation>در حال محاسبه...</translation>
</message>
@@ -702,7 +790,27 @@
</context>
<context>
<name>OpenURIDialog</name>
- </context>
+ <message>
+ <source>Open URI</source>
+ <translation>باز کردن آدرس URL</translation>
+ </message>
+ <message>
+ <source>Open payment request from URI or file</source>
+ <translation>بازکردن درخواست پرداخت از آدرس URL یا فایل</translation>
+ </message>
+ <message>
+ <source>URI:</source>
+ <translation>آدرس:</translation>
+ </message>
+ <message>
+ <source>Select payment request file</source>
+ <translation>انتخاب فایل درخواست وجه (Payment Request)</translation>
+ </message>
+ <message>
+ <source>Select payment request file to open</source>
+ <translation>انتخاب فایل درحواست وجه برای باز کردن</translation>
+ </message>
+</context>
<context>
<name>OptionsDialog</name>
<message>
@@ -710,10 +818,26 @@
<translation>گزینه ها</translation>
</message>
<message>
+ <source>&amp;Main</source>
+ <translation>&amp;اصلی</translation>
+ </message>
+ <message>
+ <source>Size of &amp;database cache</source>
+ <translation>اندازه کش پایگاه داده.</translation>
+ </message>
+ <message>
<source>MB</source>
<translation>مگابایت</translation>
</message>
<message>
+ <source>Open Configuration File</source>
+ <translation>بازکردن فایل پیکربندی</translation>
+ </message>
+ <message>
+ <source>Reset all client options to default.</source>
+ <translation>ریست تمامی تنظیمات کلاینت به پیشفرض</translation>
+ </message>
+ <message>
<source>&amp;Reset Options</source>
<translation>تنظیم مجدد گزینه ها</translation>
</message>
@@ -722,14 +846,50 @@
<translation>شبکه</translation>
</message>
<message>
+ <source>GB</source>
+ <translation>گیگابایت</translation>
+ </message>
+ <message>
<source>W&amp;allet</source>
<translation>کیف پول</translation>
</message>
<message>
+ <source>Expert</source>
+ <translation>حرفه‌ای</translation>
+ </message>
+ <message>
+ <source>Accept connections from outside.</source>
+ <translation>پذیرفتن اتصال شدن از بیرون</translation>
+ </message>
+ <message>
+ <source>Allow incomin&amp;g connections</source>
+ <translation>اجازه دادن به ارتباطات ورودی</translation>
+ </message>
+ <message>
+ <source>Proxy &amp;IP:</source>
+ <translation>پراکسی و آی‌پی:</translation>
+ </message>
+ <message>
<source>&amp;Port:</source>
<translation>پورت:</translation>
</message>
<message>
+ <source>IPv4</source>
+ <translation>IPv4</translation>
+ </message>
+ <message>
+ <source>IPv6</source>
+ <translation>IPv6</translation>
+ </message>
+ <message>
+ <source>Tor</source>
+ <translation>شبکه Tor</translation>
+ </message>
+ <message>
+ <source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source>
+ <translation>اتصال به شبکه بیت کوین با استفاده از پراکسی SOCKS5 برای استفاده از سرویس مخفی تور</translation>
+ </message>
+ <message>
<source>&amp;Window</source>
<translation>پنجره</translation>
</message>
@@ -738,6 +898,18 @@
<translation>نمایش</translation>
</message>
<message>
+ <source>User Interface &amp;language:</source>
+ <translation>زبان واسط کاربری:</translation>
+ </message>
+ <message>
+ <source>&amp;Unit to show amounts in:</source>
+ <translation>واحد نمایشگر مقادیر:</translation>
+ </message>
+ <message>
+ <source>&amp;Third party transaction URLs</source>
+ <translation>URLهای تراکنش شخص ثالث</translation>
+ </message>
+ <message>
<source>&amp;OK</source>
<translation>تایید</translation>
</message>
@@ -750,10 +922,42 @@
<translation>پیش فرض</translation>
</message>
<message>
+ <source>none</source>
+ <translation>خالی</translation>
+ </message>
+ <message>
+ <source>Confirm options reset</source>
+ <translation>تایید ریست تنظیمات</translation>
+ </message>
+ <message>
+ <source>Client restart required to activate changes.</source>
+ <translation>کلاینت نیازمند ریست شدن است برای فعال کردن تغییرات</translation>
+ </message>
+ <message>
+ <source>Client will be shut down. Do you want to proceed?</source>
+ <translation>کلاینت خاموش خواهد شد.آیا میخواهید ادامه دهید؟</translation>
+ </message>
+ <message>
+ <source>Configuration options</source>
+ <translation>تنظیمات پیکربندی</translation>
+ </message>
+ <message>
<source>Error</source>
<translation>خطا</translation>
</message>
- </context>
+ <message>
+ <source>The configuration file could not be opened.</source>
+ <translation>فایل پیکربندی قادر به بازشدن نبود.</translation>
+ </message>
+ <message>
+ <source>This change would require a client restart.</source>
+ <translation>تغییرات منوط به ریست کاربر است.</translation>
+ </message>
+ <message>
+ <source>The supplied proxy address is invalid.</source>
+ <translation>آدرس پراکسی ارائه شده نامعتبر است.</translation>
+ </message>
+</context>
<context>
<name>OverviewPage</name>
<message>
@@ -765,18 +969,46 @@
<translation>اطلاعات نمایش داده شده ممکن است روزآمد نباشد. wallet شما به صورت خودکار بعد از برقراری اتصال با شبکه bitcoin به روز می شود اما این فرایند هنوز تکمیل نشده است.</translation>
</message>
<message>
+ <source>Watch-only:</source>
+ <translation>فقط قابل-مشاهده:</translation>
+ </message>
+ <message>
<source>Available:</source>
<translation>در دسترس:</translation>
</message>
<message>
+ <source>Your current spendable balance</source>
+ <translation>موجودی قابل خرج در الان</translation>
+ </message>
+ <message>
<source>Pending:</source>
<translation>در حال انتظار:</translation>
</message>
<message>
+ <source>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source>
+ <translation>تعداد تراکنشهایی که نیاز به تایید دارند و هنوز در مانده حساب جاری شما به حساب نیامده اند</translation>
+ </message>
+ <message>
+ <source>Mined balance that has not yet matured</source>
+ <translation>موجودی استخراج شده هنوز کامل نشده است</translation>
+ </message>
+ <message>
+ <source>Balances</source>
+ <translation>موجودی ها</translation>
+ </message>
+ <message>
<source>Total:</source>
<translation>کل:</translation>
</message>
<message>
+ <source>Your current total balance</source>
+ <translation>موجودی شما در همین لحظه</translation>
+ </message>
+ <message>
+ <source>Your current balance in watch-only addresses</source>
+ <translation>موجودی شما در همین لحظه در آدرس های Watch only Addresses </translation>
+ </message>
+ <message>
<source>Spendable:</source>
<translation>قابل مصرف:</translation>
</message>
@@ -784,13 +1016,65 @@
<source>Recent transactions</source>
<translation>تراکنش های اخیر</translation>
</message>
+ <message>
+ <source>Unconfirmed transactions to watch-only addresses</source>
+ <translation>تراکنش های تایید نشده به آدرس های فقط قابل مشاهده watch-only</translation>
+ </message>
+ <message>
+ <source>Mined balance in watch-only addresses that has not yet matured</source>
+ <translation>موجودی استخراج شده در آدرس های فقط قابل مشاهده هنوز کامل نشده است</translation>
+ </message>
</context>
<context>
<name>PaymentServer</name>
+ <message>
+ <source>Payment request error</source>
+ <translation>درخواست پرداخت با خطا مواجه شد</translation>
+ </message>
+ <message>
+ <source>Payment request rejected</source>
+ <translation>درخواست پرداخت رد شد</translation>
+ </message>
+ <message>
+ <source>Payment request expired.</source>
+ <translation>درخواست پرداخت منقضی شد یا تاریخ آن گذشت.</translation>
+ </message>
+ <message>
+ <source>Payment request is not initialized.</source>
+ <translation>درخواست پرداخت, با مقداردهی اولیه یکسان نبود.</translation>
+ </message>
+ <message>
+ <source>Invalid payment request.</source>
+ <translation>درخواست پرداخت نامعتبر بود.</translation>
+ </message>
+ <message>
+ <source>Network request error</source>
+ <translation>درخواست از شبکه با خطا مواجه شد</translation>
+ </message>
</context>
<context>
<name>PeerTableModel</name>
- </context>
+ <message>
+ <source>Node/Service</source>
+ <translation>گره/خدمت</translation>
+ </message>
+ <message>
+ <source>NodeId</source>
+ <translation>شناسه گره</translation>
+ </message>
+ <message>
+ <source>Ping</source>
+ <translation>پینگ</translation>
+ </message>
+ <message>
+ <source>Sent</source>
+ <translation>فرستاده شد</translation>
+ </message>
+ <message>
+ <source>Received</source>
+ <translation>دریافت شد</translation>
+ </message>
+</context>
<context>
<name>QObject</name>
<message>
@@ -798,6 +1082,14 @@
<translation>میزان</translation>
</message>
<message>
+ <source>None</source>
+ <translation>هیچ کدام</translation>
+ </message>
+ <message>
+ <source>N/A</source>
+ <translation>موجود نیست</translation>
+ </message>
+ <message>
<source>unknown</source>
<translation>ناشناس</translation>
</message>
@@ -807,18 +1099,58 @@
</context>
<context>
<name>QRImageWidget</name>
- </context>
+ <message>
+ <source>&amp;Save Image...</source>
+ <translation>&amp;ذخیره کردن Image...</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Image</source>
+ <translation>&amp;کپی کردن image</translation>
+ </message>
+ <message>
+ <source>Save QR Code</source>
+ <translation>ذحیره کردن Qr Code</translation>
+ </message>
+ <message>
+ <source>PNG Image (*.png)</source>
+ <translation>تصویر با فرمت PNG انتخاب(*.png)</translation>
+ </message>
+</context>
<context>
<name>RPCConsole</name>
<message>
+ <source>N/A</source>
+ <translation>موجود نیست</translation>
+ </message>
+ <message>
<source>Client version</source>
<translation>ویرایش کنسول RPC</translation>
</message>
<message>
+ <source>&amp;Information</source>
+ <translation>&amp;اطلاعات</translation>
+ </message>
+ <message>
+ <source>Debug window</source>
+ <translation>پنجره یا صفحه دی باگ</translation>
+ </message>
+ <message>
+ <source>General</source>
+ <translation>عمومی</translation>
+ </message>
+ <message>
+ <source>Datadir</source>
+ <translation>پوشه داده Datadir</translation>
+ </message>
+ <message>
<source>Network</source>
<translation>شبکه</translation>
</message>
<message>
+ <source>Name</source>
+ <translation>نام</translation>
+ </message>
+ <message>
<source>Number of connections</source>
<translation>تعداد اتصال</translation>
</message>
@@ -831,14 +1163,174 @@
<translation>تعداد زنجیره های حاضر</translation>
</message>
<message>
+ <source>Current number of transactions</source>
+ <translation>تعداد تراکنش ها در حال حاضر</translation>
+ </message>
+ <message>
+ <source>Memory usage</source>
+ <translation>حافظه استفاده شده</translation>
+ </message>
+ <message>
+ <source>Wallet: </source>
+ <translation>کیف پول:</translation>
+ </message>
+ <message>
+ <source>(none)</source>
+ <translation>(هیچ کدام)</translation>
+ </message>
+ <message>
+ <source>&amp;Reset</source>
+ <translation>&amp;ریست کردن</translation>
+ </message>
+ <message>
+ <source>Received</source>
+ <translation>دریافت شد</translation>
+ </message>
+ <message>
+ <source>Sent</source>
+ <translation>فرستاده شد</translation>
+ </message>
+ <message>
+ <source>&amp;Peers</source>
+ <translation>&amp;همتاها</translation>
+ </message>
+ <message>
+ <source>Banned peers</source>
+ <translation>همتاهای بن شده</translation>
+ </message>
+ <message>
+ <source>Select a peer to view detailed information.</source>
+ <translation>انتخاب همتا یا جفت برای جزییات اطلاعات</translation>
+ </message>
+ <message>
+ <source>Whitelisted</source>
+ <translation>لیست سفید شده یا لیست سالم WhiteList</translation>
+ </message>
+ <message>
+ <source>Direction</source>
+ <translation>مسیر</translation>
+ </message>
+ <message>
+ <source>Version</source>
+ <translation>نسخه</translation>
+ </message>
+ <message>
+ <source>Decrease font size</source>
+ <translation>کاهش دادن اندازه فونت</translation>
+ </message>
+ <message>
+ <source>Increase font size</source>
+ <translation>افزایش دادن اندازه فونت</translation>
+ </message>
+ <message>
+ <source>Services</source>
+ <translation>خدمات</translation>
+ </message>
+ <message>
+ <source>Connection Time</source>
+ <translation>زمان اتصال</translation>
+ </message>
+ <message>
+ <source>Last Send</source>
+ <translation>آخرین ارسال</translation>
+ </message>
+ <message>
+ <source>Last Receive</source>
+ <translation>آخرین دریافت</translation>
+ </message>
+ <message>
+ <source>Ping Time</source>
+ <translation>مدت زمان پینگ</translation>
+ </message>
+ <message>
+ <source>Ping Wait</source>
+ <translation>انتظار پینگ</translation>
+ </message>
+ <message>
+ <source>Min Ping</source>
+ <translation>حداقل پینگ</translation>
+ </message>
+ <message>
<source>Last block time</source>
<translation>زمان آخرین بلوک</translation>
</message>
<message>
+ <source>&amp;Open</source>
+ <translation>&amp;بازکردن</translation>
+ </message>
+ <message>
+ <source>&amp;Network Traffic</source>
+ <translation>&amp;شلوغی شبکه</translation>
+ </message>
+ <message>
+ <source>Totals</source>
+ <translation>جمع کل ها</translation>
+ </message>
+ <message>
+ <source>In:</source>
+ <translation>به یا داخل:</translation>
+ </message>
+ <message>
+ <source>Out:</source>
+ <translation>خارج شده:</translation>
+ </message>
+ <message>
+ <source>Clear console</source>
+ <translation>پاک کردن کنسول</translation>
+ </message>
+ <message>
+ <source>1 &amp;hour</source>
+ <translation>1 &amp;ساعت</translation>
+ </message>
+ <message>
+ <source>1 &amp;day</source>
+ <translation>1 &amp;روز</translation>
+ </message>
+ <message>
+ <source>1 &amp;week</source>
+ <translation>1 &amp;هفته</translation>
+ </message>
+ <message>
+ <source>1 &amp;year</source>
+ <translation>1 &amp;سال</translation>
+ </message>
+ <message>
+ <source>&amp;Disconnect</source>
+ <translation>&amp;قطع شدن</translation>
+ </message>
+ <message>
+ <source>Ban for</source>
+ <translation>بن یا بن شده برای</translation>
+ </message>
+ <message>
+ <source>&amp;Unban</source>
+ <translation>&amp;خارج کردن از بن</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>کیف پول پیش‌فرض</translation>
</message>
- </context>
+ <message>
+ <source>Network activity disabled</source>
+ <translation>فعالیت شبکه غیر فعال شد</translation>
+ </message>
+ <message>
+ <source>never</source>
+ <translation>هیچ وقت</translation>
+ </message>
+ <message>
+ <source>Yes</source>
+ <translation>بله</translation>
+ </message>
+ <message>
+ <source>No</source>
+ <translation>خیر</translation>
+ </message>
+ <message>
+ <source>Unknown</source>
+ <translation>ناشناس یا نامعلوم</translation>
+ </message>
+</context>
<context>
<name>ReceiveCoinsDialog</name>
<message>
@@ -854,14 +1346,46 @@
<translation>پیام:</translation>
</message>
<message>
+ <source>Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</source>
+ <translation>از این فرم استفاده کنید برای درخواست پرداخت ها.تمامی گزینه ها &lt;b&gt;اختیاری&lt;/b&gt;هستند.</translation>
+ </message>
+ <message>
+ <source>Clear all fields of the form.</source>
+ <translation>پاک کردن تمامی گزینه های این فرم</translation>
+ </message>
+ <message>
+ <source>Clear</source>
+ <translation>پاک کردن</translation>
+ </message>
+ <message>
+ <source>Requested payments history</source>
+ <translation>تاریخچه پرداخت های درخواست شده</translation>
+ </message>
+ <message>
+ <source>&amp;Request payment</source>
+ <translation>&amp;درخواست پرداخت</translation>
+ </message>
+ <message>
+ <source>Show</source>
+ <translation>نمایش</translation>
+ </message>
+ <message>
<source>Remove</source>
<translation>حذف</translation>
</message>
<message>
+ <source>Copy URI</source>
+ <translation>کپی کردن آدرس URL</translation>
+ </message>
+ <message>
<source>Copy label</source>
<translation>کپی برچسب</translation>
</message>
<message>
+ <source>Copy message</source>
+ <translation>کپی کردن پیام</translation>
+ </message>
+ <message>
<source>Copy amount</source>
<translation>کپی مقدار</translation>
</message>
@@ -869,18 +1393,46 @@
<context>
<name>ReceiveRequestDialog</name>
<message>
+ <source>QR Code</source>
+ <translation>کی یو آر کد Qr Code</translation>
+ </message>
+ <message>
+ <source>Copy &amp;URI</source>
+ <translation>کپی کردن &amp;آدرس URL</translation>
+ </message>
+ <message>
<source>Copy &amp;Address</source>
<translation>کپی آدرس</translation>
</message>
<message>
+ <source>&amp;Save Image...</source>
+ <translation>&amp;ذخیره کردن تصویر...</translation>
+ </message>
+ <message>
+ <source>Payment information</source>
+ <translation>اطلاعات پرداخت</translation>
+ </message>
+ <message>
+ <source>URI</source>
+ <translation>آدرس URL</translation>
+ </message>
+ <message>
<source>Address</source>
<translation>آدرس</translation>
</message>
<message>
+ <source>Amount</source>
+ <translation>میزان وجه:</translation>
+ </message>
+ <message>
<source>Label</source>
<translation>برچسب</translation>
</message>
<message>
+ <source>Message</source>
+ <translation>پیام</translation>
+ </message>
+ <message>
<source>Wallet</source>
<translation>کیف پول</translation>
</message>
@@ -888,14 +1440,34 @@
<context>
<name>RecentRequestsTableModel</name>
<message>
+ <source>Date</source>
+ <translation>تاریخ</translation>
+ </message>
+ <message>
<source>Label</source>
<translation>برچسب</translation>
</message>
<message>
+ <source>Message</source>
+ <translation>پیام</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(برچسب ندارد)</translation>
</message>
- </context>
+ <message>
+ <source>(no message)</source>
+ <translation>(بدون پیام)</translation>
+ </message>
+ <message>
+ <source>(no amount requested)</source>
+ <translation>(هیچ درخواست پرداخت وجود ندارد)</translation>
+ </message>
+ <message>
+ <source>Requested</source>
+ <translation>درخواست شده</translation>
+ </message>
+</context>
<context>
<name>SendCoinsDialog</name>
<message>
@@ -903,6 +1475,14 @@
<translation>سکه های ارسالی</translation>
</message>
<message>
+ <source>Coin Control Features</source>
+ <translation>قابلیت های کنترل کوین</translation>
+ </message>
+ <message>
+ <source>automatically selected</source>
+ <translation>به صورت خودکار انتخاب شده</translation>
+ </message>
+ <message>
<source>Insufficient funds!</source>
<translation>وجوه ناکافی</translation>
</message>
@@ -911,6 +1491,10 @@
<translation>مقدار</translation>
</message>
<message>
+ <source>Bytes:</source>
+ <translation>بایت ها:</translation>
+ </message>
+ <message>
<source>Amount:</source>
<translation>میزان وجه:</translation>
</message>
@@ -919,18 +1503,86 @@
<translation>هزینه</translation>
</message>
<message>
+ <source>After Fee:</source>
+ <translation>بعد از احتساب کارمزد</translation>
+ </message>
+ <message>
<source>Change:</source>
<translation>تغییر</translation>
</message>
<message>
+ <source>Custom change address</source>
+ <translation>تغییر آدرس مخصوص</translation>
+ </message>
+ <message>
+ <source>Transaction Fee:</source>
+ <translation>کارمزد تراکنش:</translation>
+ </message>
+ <message>
+ <source>Choose...</source>
+ <translation>انتخاب...</translation>
+ </message>
+ <message>
+ <source>Warning: Fee estimation is currently not possible.</source>
+ <translation>هشدار:تخمین کارمزد در حال حاضر امکان پذیر نیست.</translation>
+ </message>
+ <message>
+ <source>collapse fee-settings</source>
+ <translation>تنظیمات کاهش کارمزد</translation>
+ </message>
+ <message>
+ <source>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</source>
+ <translation>مشخص کردن هزینه کارمزد مخصوص به ازاری کیلوبایت(1,000 بایت) حجم مجازی تراکنش
+
+توجه: از آن جایی که کارمزد بر اساس هر بایت محاسبه میشود,هزینه کارمزد"100 ساتوشی بر کیلو بایت"برای تراکنش با حجم 500 بایت(نصف 1 کیلوبایت) کارمزد فقط اندازه 50 ساتوشی خواهد بود.</translation>
+ </message>
+ <message>
+ <source>per kilobyte</source>
+ <translation>به ازای هر کیلوبایت</translation>
+ </message>
+ <message>
<source>Hide</source>
<translation>پنهان کردن</translation>
</message>
<message>
+ <source>Recommended:</source>
+ <translation>پیشنهاد شده:</translation>
+ </message>
+ <message>
+ <source>Custom:</source>
+ <translation>سفارشی:</translation>
+ </message>
+ <message>
+ <source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
+ <translation>(مقداردهی کارمزد هوشمند هنوز مشخص نشده است.این کارمزد معمولا به اندازه چند بلاک طول میکشد...) </translation>
+ </message>
+ <message>
<source>Send to multiple recipients at once</source>
<translation>ارسال همزمان به گیرنده های متعدد</translation>
</message>
<message>
+ <source>Add &amp;Recipient</source>
+ <translation>اضافه کردن &amp;گیرنده</translation>
+ </message>
+ <message>
+ <source>Clear all fields of the form.</source>
+ <translation>پاک کردن تمامی گزینه های این فرم</translation>
+ </message>
+ <message>
+ <source>Dust:</source>
+ <translation>گرد و غبار یا داست:</translation>
+ </message>
+ <message>
+ <source>Confirmation time target:</source>
+ <translation>هدف زمانی تایید شدن:</translation>
+ </message>
+ <message>
+ <source>Enable Replace-By-Fee</source>
+ <translation>فعال کردن جایگذاری دوباره از کارمزد</translation>
+ </message>
+ <message>
<source>Clear &amp;All</source>
<translation>پاک کردن همه</translation>
</message>
@@ -959,6 +1611,90 @@
<translation>کپی هزینه</translation>
</message>
<message>
+ <source>Copy after fee</source>
+ <translation>کپی کردن بعد از احتساب کارمزد</translation>
+ </message>
+ <message>
+ <source>Copy bytes</source>
+ <translation>کپی کردن بایت ها</translation>
+ </message>
+ <message>
+ <source>Copy dust</source>
+ <translation>کپی کردن داست:</translation>
+ </message>
+ <message>
+ <source>Copy change</source>
+ <translation>کپی کردن تغییر</translation>
+ </message>
+ <message>
+ <source>Are you sure you want to send?</source>
+ <translation>آیا برای ارسال کردن یا فرستادن مطمئن هستید؟</translation>
+ </message>
+ <message>
+ <source>or</source>
+ <translation>یا</translation>
+ </message>
+ <message>
+ <source>You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
+ <translation>تو میتوانی بعدا هزینه کارمزد را افزایش بدی(signals Replace-By-Fee, BIP-125)</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction.</source>
+ <translation>لطفا,تراکنش خود را بازبینی کنید.</translation>
+ </message>
+ <message>
+ <source>Transaction fee</source>
+ <translation>کارمزد تراکنش</translation>
+ </message>
+ <message>
+ <source>Total Amount</source>
+ <translation>میزان کل</translation>
+ </message>
+ <message>
+ <source>Confirm send coins</source>
+ <translation>تایید کردن ارسال کوین ها</translation>
+ </message>
+ <message>
+ <source>The recipient address is not valid. Please recheck.</source>
+ <translation>آدرس گیرنده نامعتبر است.لطفا دوباره چک یا بررسی کنید.</translation>
+ </message>
+ <message>
+ <source>The amount to pay must be larger than 0.</source>
+ <translation>میزان پولی کخ پرداخت می کنید باید بزرگتر از 0 باشد.</translation>
+ </message>
+ <message>
+ <source>The amount exceeds your balance.</source>
+ <translation>این میزان پول بیشتر از موجودی شما است.</translation>
+ </message>
+ <message>
+ <source>Duplicate address found: addresses should only be used once each.</source>
+ <translation>آدرس تکراری یافت شد:آدرس ها باید فقط یک بار استفاده شوند.</translation>
+ </message>
+ <message>
+ <source>Transaction creation failed!</source>
+ <translation>ایجاد تراکنش با خطا مواجه شد!</translation>
+ </message>
+ <message>
+ <source>Payment request expired.</source>
+ <translation>درخواست پرداخت منقضی شد یا تاریخ آن گذشت.</translation>
+ </message>
+ <message>
+ <source>Warning: Invalid Bitcoin address</source>
+ <translation>هشدار: آدرس بیت کوین نامعتبر</translation>
+ </message>
+ <message>
+ <source>Warning: Unknown change address</source>
+ <translation>هشدار:تغییر آدرس نامعلوم</translation>
+ </message>
+ <message>
+ <source>Confirm custom change address</source>
+ <translation>تایید کردن تغییر آدرس سفارشی</translation>
+ </message>
+ <message>
+ <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source>
+ <translation>این آدرس که شما انتخاب کرده اید بخشی از کیف پول شما نیست.هر یا همه دارایی های شما در این کیف پول به این آدرس ارسال خواهد شد.آیا مطمئن هستید؟</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(برچسب ندارد)</translation>
</message>
@@ -978,10 +1714,38 @@
<translation>برچسب:</translation>
</message>
<message>
+ <source>Choose previously used address</source>
+ <translation>انتخاب آدرس قبلا استفاده شده</translation>
+ </message>
+ <message>
+ <source>This is a normal payment.</source>
+ <translation>این پرداحت,عادی هست.</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address to send the payment to</source>
+ <translation>آدرس بیت کوین برای ارسال پرداحت به آن</translation>
+ </message>
+ <message>
+ <source>Alt+A</source>
+ <translation>Alt+A</translation>
+ </message>
+ <message>
<source>Paste address from clipboard</source>
<translation>استفاده از آدرس کلیپ بورد</translation>
</message>
<message>
+ <source>Alt+P</source>
+ <translation>Alt+P</translation>
+ </message>
+ <message>
+ <source>Remove this entry</source>
+ <translation>پاک کردن این ورودی</translation>
+ </message>
+ <message>
+ <source>Use available balance</source>
+ <translation>استفاده از موجودی حساب</translation>
+ </message>
+ <message>
<source>Message:</source>
<translation>پیام:</translation>
</message>
@@ -996,7 +1760,11 @@
</context>
<context>
<name>SendConfirmationDialog</name>
- </context>
+ <message>
+ <source>Yes</source>
+ <translation>بله</translation>
+ </message>
+</context>
<context>
<name>ShutdownWindow</name>
<message>
@@ -1007,14 +1775,30 @@
<context>
<name>SignVerifyMessageDialog</name>
<message>
+ <source>Signatures - Sign / Verify a Message</source>
+ <translation>امضاها -ثبت/تایید پیام</translation>
+ </message>
+ <message>
<source>&amp;Sign Message</source>
- <translation>امضای پیام </translation>
+ <translation>&amp;ثبت پیام </translation>
+ </message>
+ <message>
+ <source>Choose previously used address</source>
+ <translation>انتخاب آدرس قبلا استفاده شده</translation>
+ </message>
+ <message>
+ <source>Alt+A</source>
+ <translation>Alt+A</translation>
</message>
<message>
<source>Paste address from clipboard</source>
<translation>آدرس را بر کلیپ بورد کپی کنید</translation>
</message>
<message>
+ <source>Alt+P</source>
+ <translation>Alt+P</translation>
+ </message>
+ <message>
<source>Enter the message you want to sign here</source>
<translation>پیامی که می خواهید امضا کنید را اینجا وارد کنید</translation>
</message>
@@ -1024,7 +1808,7 @@
</message>
<message>
<source>Sign &amp;Message</source>
- <translation>امضای پیام </translation>
+ <translation>ثبت &amp;پیام</translation>
</message>
<message>
<source>Clear &amp;All</source>
@@ -1039,10 +1823,30 @@
<translation>تایید پیام</translation>
</message>
<message>
+ <source>Click "Sign Message" to generate signature</source>
+ <translation>برای تولید امضا "Sign Message" و یا "ثبت پیام" را کلیک کنید</translation>
+ </message>
+ <message>
<source>The entered address is invalid.</source>
<translation>آدرس وارد شده نامعتبر است.</translation>
</message>
- </context>
+ <message>
+ <source>Message signed.</source>
+ <translation>پیام ثبت شده</translation>
+ </message>
+ <message>
+ <source>The signature did not match the message digest.</source>
+ <translation>این امضا با حرفو پیام همخوانی ندارد</translation>
+ </message>
+ <message>
+ <source>Message verification failed.</source>
+ <translation>پیام شما با خطا مواجه شد</translation>
+ </message>
+ <message>
+ <source>Message verified.</source>
+ <translation>پیام شما تایید شد</translation>
+ </message>
+</context>
<context>
<name>SplashScreen</name>
<message>
@@ -1052,7 +1856,11 @@
</context>
<context>
<name>TrafficGraphWidget</name>
- </context>
+ <message>
+ <source>KB/s</source>
+ <translation>کیلو بایت بر ثانیه</translation>
+ </message>
+</context>
<context>
<name>TransactionDesc</name>
<message>
@@ -1060,6 +1868,10 @@
<translation>وضعیت</translation>
</message>
<message>
+ <source>Date</source>
+ <translation>تاریخ</translation>
+ </message>
+ <message>
<source>Source</source>
<translation>منبع</translation>
</message>
@@ -1072,10 +1884,86 @@
<translation>از</translation>
</message>
<message>
+ <source>unknown</source>
+ <translation>ناشناس</translation>
+ </message>
+ <message>
<source>To</source>
<translation>به</translation>
</message>
- </context>
+ <message>
+ <source>own address</source>
+ <translation>آدرس خود</translation>
+ </message>
+ <message>
+ <source>watch-only</source>
+ <translation>فقط-با قابلیت دیدن</translation>
+ </message>
+ <message>
+ <source>label</source>
+ <translation>برچسب</translation>
+ </message>
+ <message>
+ <source>Credit</source>
+ <translation>اعتبار</translation>
+ </message>
+ <message>
+ <source>not accepted</source>
+ <translation>قبول نشده</translation>
+ </message>
+ <message>
+ <source>Total credit</source>
+ <translation>تمامی اعتبار</translation>
+ </message>
+ <message>
+ <source>Transaction fee</source>
+ <translation>کارمزد تراکنش</translation>
+ </message>
+ <message>
+ <source>Net amount</source>
+ <translation>میزان وجه دقیق</translation>
+ </message>
+ <message>
+ <source>Message</source>
+ <translation>پیام</translation>
+ </message>
+ <message>
+ <source>Comment</source>
+ <translation>کامنت</translation>
+ </message>
+ <message>
+ <source>Transaction ID</source>
+ <translation>شناسه تراکنش</translation>
+ </message>
+ <message>
+ <source>Transaction total size</source>
+ <translation>حجم کل تراکنش</translation>
+ </message>
+ <message>
+ <source>Debug information</source>
+ <translation>اطلاعات دی باگ Debug</translation>
+ </message>
+ <message>
+ <source>Transaction</source>
+ <translation>تراکنش</translation>
+ </message>
+ <message>
+ <source>Inputs</source>
+ <translation>ورودی ها</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>میزان وجه:</translation>
+ </message>
+ <message>
+ <source>true</source>
+ <translation>درست</translation>
+ </message>
+ <message>
+ <source>false</source>
+ <translation>نادرست</translation>
+ </message>
+</context>
<context>
<name>TransactionDescDialog</name>
<message>
@@ -1086,6 +1974,14 @@
<context>
<name>TransactionTableModel</name>
<message>
+ <source>Date</source>
+ <translation>تاریخ</translation>
+ </message>
+ <message>
+ <source>Type</source>
+ <translation>نوع</translation>
+ </message>
+ <message>
<source>Label</source>
<translation>برچسب</translation>
</message>
@@ -1094,6 +1990,14 @@
<translation>تایید نشده</translation>
</message>
<message>
+ <source>Generated but not accepted</source>
+ <translation>تولید شده ولی هنوز قبول نشده است</translation>
+ </message>
+ <message>
+ <source>Received with</source>
+ <translation>دریافت شده با</translation>
+ </message>
+ <message>
<source>Received from</source>
<translation>دریافت شده از</translation>
</message>
@@ -1102,25 +2006,101 @@
<translation>ارسال شده به</translation>
</message>
<message>
+ <source>Payment to yourself</source>
+ <translation>پرداخت به خود</translation>
+ </message>
+ <message>
<source>Mined</source>
<translation>استخراج شده</translation>
</message>
<message>
+ <source>watch-only</source>
+ <translation>فقط-با قابلیت دیدن</translation>
+ </message>
+ <message>
+ <source>(n/a)</source>
+ <translation>(موجود نیست)</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(برچسب ندارد)</translation>
</message>
- </context>
+ <message>
+ <source>Date and time that the transaction was received.</source>
+ <translation>تاریخ و زمان تراکنش دریافت شده است</translation>
+ </message>
+ <message>
+ <source>Type of transaction.</source>
+ <translation>نوع تراکنش.</translation>
+ </message>
+ <message>
+ <source>Amount removed from or added to balance.</source>
+ <translation> میزان وجه کم شده یا اضافه شده به حساب</translation>
+ </message>
+</context>
<context>
<name>TransactionView</name>
<message>
+ <source>All</source>
+ <translation>همه</translation>
+ </message>
+ <message>
+ <source>Today</source>
+ <translation>امروز</translation>
+ </message>
+ <message>
+ <source>This week</source>
+ <translation>این هفته</translation>
+ </message>
+ <message>
+ <source>This month</source>
+ <translation>این ماه</translation>
+ </message>
+ <message>
+ <source>Last month</source>
+ <translation>ماه گذشته</translation>
+ </message>
+ <message>
+ <source>This year</source>
+ <translation>امسال یا این سال</translation>
+ </message>
+ <message>
+ <source>Range...</source>
+ <translation>دامنه...</translation>
+ </message>
+ <message>
+ <source>Received with</source>
+ <translation>گرفته شده با</translation>
+ </message>
+ <message>
<source>Sent to</source>
<translation>ارسال شده به</translation>
</message>
<message>
+ <source>To yourself</source>
+ <translation>به خودت</translation>
+ </message>
+ <message>
<source>Mined</source>
<translation>استخراج شده</translation>
</message>
<message>
+ <source>Other</source>
+ <translation>بقیه</translation>
+ </message>
+ <message>
+ <source>Enter address, transaction id, or label to search</source>
+ <translation>وارد کردن آدرس,شناسه تراکنش, یا برچسب برای جست و جو</translation>
+ </message>
+ <message>
+ <source>Min amount</source>
+ <translation>حداقل میزان وجه</translation>
+ </message>
+ <message>
+ <source>Increase transaction fee</source>
+ <translation>افزایش کارمزد تراکنش</translation>
+ </message>
+ <message>
<source>Copy address</source>
<translation>کپی آدرس</translation>
</message>
@@ -1137,10 +2117,38 @@
<translation>کپی شناسه تراکنش</translation>
</message>
<message>
+ <source>Copy full transaction details</source>
+ <translation>کپی کردن تمامی اطلاعات تراکنش</translation>
+ </message>
+ <message>
+ <source>Edit label</source>
+ <translation>ویرایش کردن برچسب</translation>
+ </message>
+ <message>
+ <source>Show transaction details</source>
+ <translation>نشان دادن جرییات تراکنش</translation>
+ </message>
+ <message>
+ <source>Export Transaction History</source>
+ <translation>خارج کردن یا بالا بردن سابقه تراکنش ها</translation>
+ </message>
+ <message>
<source>Comma separated file (*.csv)</source>
<translation>فایل سی اس وی (*.csv)</translation>
</message>
<message>
+ <source>Confirmed</source>
+ <translation>تایید شده</translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>تاریخ</translation>
+ </message>
+ <message>
+ <source>Type</source>
+ <translation>نوع</translation>
+ </message>
+ <message>
<source>Label</source>
<translation>برچسب</translation>
</message>
@@ -1149,10 +2157,26 @@
<translation>آدرس</translation>
</message>
<message>
+ <source>ID</source>
+ <translation>شناسه</translation>
+ </message>
+ <message>
<source>Exporting Failed</source>
<translation>گرفتن خروجی به مشکل خورد</translation>
</message>
- </context>
+ <message>
+ <source>Exporting Successful</source>
+ <translation>خارج کردن موفقیت آمیز بود Exporting</translation>
+ </message>
+ <message>
+ <source>Range:</source>
+ <translation>دامنه:</translation>
+ </message>
+ <message>
+ <source>to</source>
+ <translation>به</translation>
+ </message>
+</context>
<context>
<name>UnitDisplayStatusBarControl</name>
</context>
@@ -1161,29 +2185,209 @@
</context>
<context>
<name>WalletModel</name>
+ <message>
+ <source>Send Coins</source>
+ <translation>ارسال کوین ها یا سکه ها</translation>
+ </message>
+ <message>
+ <source>Increasing transaction fee failed</source>
+ <translation>افزایش کارمزد تراکنش با خطا مواجه شد</translation>
+ </message>
+ <message>
+ <source>Do you want to increase the fee?</source>
+ <translation>آیا میخواهید اندازه کارمزد را افزایش دهید؟</translation>
+ </message>
+ <message>
+ <source>Current fee:</source>
+ <translation>کارمزد الان:</translation>
+ </message>
+ <message>
+ <source>Increase:</source>
+ <translation>افزایش دادن:</translation>
+ </message>
+ <message>
+ <source>New fee:</source>
+ <translation>کارمزد جدید:</translation>
+ </message>
+ <message>
+ <source>Can't sign transaction.</source>
+ <translation>نمیتوان تراکنش را ثبت کرد</translation>
+ </message>
</context>
<context>
<name>WalletView</name>
- </context>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp;صدور</translation>
+ </message>
+ <message>
+ <source>Backup Wallet</source>
+ <translation>بازیابی یا پشتیبان گیری کیف پول</translation>
+ </message>
+ <message>
+ <source>Backup Failed</source>
+ <translation>بازیابی یا پشتیبان گیری با خطا مواجه شد</translation>
+ </message>
+ <message>
+ <source>Backup Successful</source>
+ <translation>بازیابی یا پشتیبان گیری موفقیت آمیز بود.</translation>
+ </message>
+ <message>
+ <source>Cancel</source>
+ <translation>لغو</translation>
+ </message>
+</context>
<context>
<name>bitcoin-core</name>
<message>
+ <source>Bitcoin Core</source>
+ <translation>هسته بیت کوین</translation>
+ </message>
+ <message>
+ <source>Change index out of range</source>
+ <translation>تغییر دادن اندیس خارج از دامنه</translation>
+ </message>
+ <message>
+ <source>Copyright (C) %i-%i</source>
+ <translation>کپی رایت (C) %i-%i</translation>
+ </message>
+ <message>
+ <source>Do you want to rebuild the block database now?</source>
+ <translation>آیا میخواهید الان پایگاه داده بلاک را بازسازی کنید؟</translation>
+ </message>
+ <message>
+ <source>Error loading block database</source>
+ <translation>خطا در بارگذاری پایگاه داده بلاک block</translation>
+ </message>
+ <message>
+ <source>Error opening block database</source>
+ <translation>خطا در بازکردن پایگاه داده بلاک block</translation>
+ </message>
+ <message>
+ <source>Error: Disk space is low!</source>
+ <translation>حطا:فضای دیسک کم است!</translation>
+ </message>
+ <message>
+ <source>Importing...</source>
+ <translation>در حال وارد کردن...</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -%s=&lt;amount&gt;: '%s'</source>
+ <translation>میزان نامعتبر برای -%s=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Upgrading txindex database</source>
+ <translation>ارتقا دادن پایگاه داده اندیس تراکنش ها یا txindex</translation>
+ </message>
+ <message>
+ <source>Loading P2P addresses...</source>
+ <translation>در حال بارگذاری آدرس های همتا-به-همتا یا P2P</translation>
+ </message>
+ <message>
+ <source>Loading banlist...</source>
+ <translation>در حال بارگذاری لیست بن...</translation>
+ </message>
+ <message>
+ <source>Unable to generate keys</source>
+ <translation>نمیتوان کلید ها را تولید کرد</translation>
+ </message>
+ <message>
+ <source>Upgrading UTXO database</source>
+ <translation>ارتقا دادن پایگاه داده UTXO</translation>
+ </message>
+ <message>
+ <source>Verifying blocks...</source>
+ <translation>در حال تایید کردن بلاک ها...</translation>
+ </message>
+ <message>
<source>The transaction amount is too small to send after the fee has been deducted</source>
<translation>مبلغ تراکنش کمتر از آن است که پس از کسر هزینه تراکنش قابل ارسال باشد</translation>
</message>
<message>
+ <source>Error reading from database, shutting down.</source>
+ <translation>خواندن از پایگاه داده با خطا مواجه شد,در حال خاموش شدن.</translation>
+ </message>
+ <message>
<source>Information</source>
<translation>اطلاعات</translation>
</message>
<message>
+ <source>Signing transaction failed</source>
+ <translation>ثبت تراکنش با خطا مواجه شد</translation>
+ </message>
+ <message>
+ <source>The transaction amount is too small to pay the fee</source>
+ <translation>حجم تراکنش بسیار کم است برای پرداخت کارمزد</translation>
+ </message>
+ <message>
+ <source>This is experimental software.</source>
+ <translation>این یک نرم افزار تجربی است.</translation>
+ </message>
+ <message>
+ <source>Transaction amount too small</source>
+ <translation>حجم تراکنش خیلی کم است</translation>
+ </message>
+ <message>
+ <source>Transaction too large for fee policy</source>
+ <translation>حجم تراکنش خیلی زیاد است به خاطر سیاست های کارمزدی بیت کوین</translation>
+ </message>
+ <message>
+ <source>Transaction too large</source>
+ <translation>حجم تراکنش خیلی زیاد است</translation>
+ </message>
+ <message>
+ <source>Unable to generate initial keys</source>
+ <translation>نمیتوان کلید های اولیه را تولید کرد.</translation>
+ </message>
+ <message>
+ <source>Verifying wallet(s)...</source>
+ <translation>در حال تایید شدن کیف پول(ها)...</translation>
+ </message>
+ <message>
<source>Warning</source>
<translation>هشدار</translation>
</message>
<message>
+ <source>This is the transaction fee you may pay when fee estimates are not available.</source>
+ <translation>این هزینه تراکنشی است که در صورت عدم وجود هزینه تخمینی، پرداخت می کنید.</translation>
+ </message>
+ <message>
+ <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source>
+ <translation>هشدار:یک بلاک از یک نسخه ناشناس در حال ماین شدن است.در این اتفاق ممکن است قوانین ناشناسی اثرات نامعلوم بگذارند.</translation>
+ </message>
+ <message>
+ <source>%s is set very high!</source>
+ <translation>%s بسیار بزرگ انتخاب شده است.</translation>
+ </message>
+ <message>
+ <source>Keypool ran out, please call keypoolrefill first</source>
+ <translation>کلید استخر تمام شده است، لطفاً درخواست کلید استخر را پرنمایید.</translation>
+ </message>
+ <message>
+ <source>Starting network threads...</source>
+ <translation>ایجاد نخ‌های شبکه ...</translation>
+ </message>
+ <message>
+ <source>This is the minimum transaction fee you pay on every transaction.</source>
+ <translation>این کمترین فی تراکنش است که در هر تراکنش پرداخت می‌نمایید.</translation>
+ </message>
+ <message>
+ <source>Transaction amounts must not be negative</source>
+ <translation>مقدار تراکنش نمی‌تواند منفی باشد.</translation>
+ </message>
+ <message>
+ <source>Transaction must have at least one recipient</source>
+ <translation>تراکنش باید حداقل یک دریافت کننده داشته باشد</translation>
+ </message>
+ <message>
<source>Insufficient funds</source>
<translation>وجوه ناکافی</translation>
</message>
<message>
+ <source>Can't generate a change-address key. Private keys are disabled for this wallet.</source>
+ <translation>نمیتوان تغییر-آدرس کلید را تولید کرد.کلید های خصوصی در این کیف پول غیر فعال شده اند.</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>لود شدن نمایه بلاکها..</translation>
</message>
diff --git a/src/qt/locale/bitcoin_fi.ts b/src/qt/locale/bitcoin_fi.ts
index ea97d48b10..71832200df 100644
--- a/src/qt/locale/bitcoin_fi.ts
+++ b/src/qt/locale/bitcoin_fi.ts
@@ -326,6 +326,14 @@
<translation>Avaa &amp;URI...</translation>
</message>
<message>
+ <source>Wallet:</source>
+ <translation>Lompakko:</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>oletuslompakko</translation>
+ </message>
+ <message>
<source>Click to disable network activity.</source>
<translation>Paina poistaaksesi verkkoyhteysilmaisin käytöstä.</translation>
</message>
@@ -346,6 +354,10 @@
<translation>Ladataan lohkoindeksiä...</translation>
</message>
<message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>Välipalvelin on &lt;b&gt;käytössä&lt;/b&gt;: %1</translation>
+ </message>
+ <message>
<source>Send coins to a Bitcoin address</source>
<translation>Lähetä kolikoita Bitcoin-osoitteeseen</translation>
</message>
@@ -441,6 +453,10 @@
<source>&amp;Command-line options</source>
<translation>&amp;Komentorivin valinnat</translation>
</message>
+ <message numerus="yes">
+ <source>%n active connection(s) to Bitcoin network</source>
+ <translation><numerusform>%n aktiivinen yhteys Bitcoin-verkkoon</numerusform><numerusform>%n aktiivista yhteyttä Bitcoin-verkkoon</numerusform></translation>
+ </message>
<message>
<source>Indexing blocks on disk...</source>
<translation>Ladataan lohkoindeksiä...</translation>
@@ -449,6 +465,10 @@
<source>Processing blocks on disk...</source>
<translation>Käsitellään lohkoja levyllä...</translation>
</message>
+ <message numerus="yes">
+ <source>Processed %n block(s) of transaction history.</source>
+ <translation><numerusform>Käsitelty %n lohko rahansiirtohistoriasta.</numerusform><numerusform>Käsitelty %n lohkoa rahansiirtohistoriasta.</numerusform></translation>
+ </message>
<message>
<source>%1 behind</source>
<translation>%1 jäljessä</translation>
@@ -742,6 +762,14 @@
<translation>Antamasi osoite "%1" ei ole kelvollinen Bitcoin-osoite.</translation>
</message>
<message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Osoite "%1" on jo vastaanotto-osoitteena nimellä "%2", joten sitä ei voi lisätä lähetysosoitteeksi.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>Syötetty osoite "%1" on jo osoitekirjassa nimellä "%2".</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>Lompakkoa ei voitu avata.</translation>
</message>
@@ -854,7 +882,15 @@
<source>Error</source>
<translation>Virhe</translation>
</message>
- </context>
+ <message numerus="yes">
+ <source>%n GB of free space available</source>
+ <translation><numerusform>%n GB tilaa vapaana</numerusform><numerusform>%n GB tilaa vapaana</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>(of %n GB needed)</source>
+ <translation><numerusform>(tarvitaan %n GB)</numerusform><numerusform>(tarvitaan %n GB)</numerusform></translation>
+ </message>
+</context>
<context>
<name>ModalOverlay</name>
<message>
@@ -957,7 +993,7 @@
</message>
<message>
<source>Number of script &amp;verification threads</source>
- <translation>Script &amp;varmistuksen threadien määrä</translation>
+ <translation>Säikeiden määrä skriptien &amp;varmistuksessa</translation>
</message>
<message>
<source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source>
@@ -968,6 +1004,10 @@
<translation>Ilmoittaa, mikäli oletetettua SOCKS5-välityspalvelinta käytetään vertaisten tavoittamiseen tämän verkkotyypin kautta.</translation>
</message>
<message>
+ <source>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</source>
+ <translation>Käytä SOCKS&amp;5-välityspalvelinta tavoittamaan Tor-verkon piilotetut palvelut:</translation>
+ </message>
+ <message>
<source>Hide the icon from the system tray.</source>
<translation>Piilota kuvake järjestelmäpalkista.</translation>
</message>
@@ -1008,6 +1048,18 @@
<translation>&amp;Verkko</translation>
</message>
<message>
+ <source>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</source>
+ <translation>Jättää pois joitain edistyneitä ominaisuuksia, mutta silti varmistaa kaikki lohkot kokonaan. Tämän asetuksen muutto vaatii koko lohkoketjun uudelleen lataamisen. Levyn käyttöaste saattaa olla hiukan suurempaa.</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Karsi lohkovaraston kooksi</translation>
+ </message>
+ <message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Tämän asetuksen muuttaminen vaatii koko lohkoketjun uudelleenlataamista.</translation>
+ </message>
+ <message>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
<translation>(0 = auto, &lt;0 = jätä näin monta ydintä vapaaksi)</translation>
</message>
@@ -1274,6 +1326,10 @@
<translation>URI käsittely</translation>
</message>
<message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin://' ei ole kelvollinen URI. Käytä 'bitcoin:' sen sijaan.</translation>
+ </message>
+ <message>
<source>Payment request fetch URL is invalid: %1</source>
<translation>Maksupyynnön haku URL on virheellinen: %1</translation>
</message>
@@ -1447,10 +1503,18 @@
<context>
<name>QObject::QObject</name>
<message>
+ <source>Error parsing command line arguments: %1.</source>
+ <translation>Virhe käsitellessä komentorivin valintaa: %1.</translation>
+ </message>
+ <message>
<source>Error: Specified data directory "%1" does not exist.</source>
<translation>Virhe: Annettua data-hakemistoa "%1" ei ole olemassa.</translation>
</message>
<message>
+ <source>Error: Cannot parse configuration file: %1.</source>
+ <translation>Virhe: Asetustiedostoa ei voida käsitellä: %1.</translation>
+ </message>
+ <message>
<source>Error: %1</source>
<translation>Virhe: %1</translation>
</message>
@@ -1709,6 +1773,10 @@
<translation>&amp;Poista esto</translation>
</message>
<message>
+ <source>default wallet</source>
+ <translation>oletuslompakko</translation>
+ </message>
+ <message>
<source>Welcome to the %1 RPC console.</source>
<translation>Tervetuloa %1 RPC-konsoliin.</translation>
</message>
@@ -1800,6 +1868,10 @@
<translation>Tyhjennä</translation>
</message>
<message>
+ <source>Generate native segwit (Bech32) address</source>
+ <translation>Luo natiivi segwit (Bech32) -osoite</translation>
+ </message>
+ <message>
<source>Requested payments history</source>
<translation>Pyydettyjen maksujen historia</translation>
</message>
@@ -2013,6 +2085,10 @@
<translation>Piilota</translation>
</message>
<message>
+ <source>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
+ <translation>Vähimmäispalkkion maksaminen käy hyvin, kunhan verkossa on vähemmän siirtotapahtumia kuin lohkoissa on tilaa. Huomaa, että tämä saattaa johtaa ei-koskaan-vahvistuvaan siirtoon, jos verkon käsittelykyky ylittyy.</translation>
+ </message>
+ <message>
<source>(read the tooltip)</source>
<translation>(lue työkaluvinkki)</translation>
</message>
@@ -2053,6 +2129,10 @@
<translation>Käytä Replace-By-Fee:tä</translation>
</message>
<message>
+ <source>With Replace-By-Fee (BIP-125) you can increase a transaction's fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</source>
+ <translation>Replace-By-Fee:tä (BIP-125) käyttämällä voit korottaa siirtotapahtuman palkkiota sen lähettämisen jälkeen. Ilman tätä saatetaan suositella käyttämään suurempaa palkkiota kompensoimaan viiveen kasvamisen riskiä.</translation>
+ </message>
+ <message>
<source>Clear &amp;All</source>
<translation>&amp;Tyhjennnä Kaikki</translation>
</message>
@@ -2113,6 +2193,14 @@
<translation>tai</translation>
</message>
<message>
+ <source>You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
+ <translation>Voit korottaa palkkiota myöhemmin (osoittaa Replace-By-Fee:tä, BIP-125).</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction.</source>
+ <translation>Tarkistathan siirtosi.</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>Siirtokulu</translation>
</message>
@@ -2160,6 +2248,10 @@
<source>Pay only the required fee of %1</source>
<translation>Maksa vain vaadittu kulu %1 </translation>
</message>
+ <message numerus="yes">
+ <source>Estimated to begin confirmation within %n block(s).</source>
+ <translation><numerusform>Vahvistuminen alkaa arviolta %n lohkon sisällä.</numerusform><numerusform>Vahvistuminen alkaa arviolta %n lohkon sisällä.</numerusform></translation>
+ </message>
<message>
<source>Warning: Invalid Bitcoin address</source>
<translation>Varoitus: Virheellinen Bitcoin-osoite </translation>
@@ -2441,6 +2533,10 @@
</context>
<context>
<name>TransactionDesc</name>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Avoinna vielä %n lohkon ajan</numerusform><numerusform>Avoinna vielä %n lohkon ajan</numerusform></translation>
+ </message>
<message>
<source>Open until %1</source>
<translation>Avoinna %1 asti</translation>
@@ -2615,6 +2711,10 @@
<source>Label</source>
<translation>Nimike</translation>
</message>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Avoinna vielä %n lohkon ajan</numerusform><numerusform>Avoinna vielä %n lohkon ajan</numerusform></translation>
+ </message>
<message>
<source>Open until %1</source>
<translation>Avoinna %1 asti</translation>
@@ -2884,6 +2984,10 @@
<translation>Lähetä kolikoita</translation>
</message>
<message>
+ <source>Fee bump error</source>
+ <translation>Virhe nostaessa palkkiota.</translation>
+ </message>
+ <message>
<source>Increasing transaction fee failed</source>
<translation>Siirtokulun nosto epäonnistui</translation>
</message>
@@ -2950,7 +3054,11 @@
<source>The wallet data was successfully saved to %1.</source>
<translation>Lompakko tallennettiin onnistuneesti tiedostoon %1.</translation>
</message>
- </context>
+ <message>
+ <source>Cancel</source>
+ <translation>Peruuta</translation>
+ </message>
+</context>
<context>
<name>bitcoin-core</name>
<message>
@@ -3122,6 +3230,14 @@
<translation>Virheellinen määrä -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Määrättyä lohkohakemistoa "%s" ei ole olemassa.</translation>
+ </message>
+ <message>
+ <source>Upgrading txindex database</source>
+ <translation>Päivitetään txindex -tietokantaa</translation>
+ </message>
+ <message>
<source>Loading P2P addresses...</source>
<translation>Ladataan P2P-vertaisten osoitteita...</translation>
</message>
@@ -3142,6 +3258,10 @@
<translation>Karsittu tila ei ole yhteensopiva -txindex:n kanssa.</translation>
</message>
<message>
+ <source>Replaying blocks...</source>
+ <translation>Tarkastetaan lohkoja..</translation>
+ </message>
+ <message>
<source>Rewinding blocks...</source>
<translation>Varmistetaan lohkoja...</translation>
</message>
@@ -3158,6 +3278,10 @@
<translation>Kytkeytyminen kohteeseen %s ei onnistu tällä tietokoneella. %s on luultavasti jo käynnissä.</translation>
</message>
<message>
+ <source>Unable to generate keys</source>
+ <translation>Avaimia ei voitu luoda</translation>
+ </message>
+ <message>
<source>Unsupported argument -benchmark ignored, use -debug=bench.</source>
<translation>Argumenttia -benchmark ei tueta, käytä -debug=bench.</translation>
</message>
@@ -3206,6 +3330,10 @@
<translation>Virheitä tietokantaa luettaessa, ohjelma pysäytetään.</translation>
</message>
<message>
+ <source>Error upgrading chainstate database</source>
+ <translation>Virhe päivittäessä chainstate-tietokantaa</translation>
+ </message>
+ <message>
<source>Information</source>
<translation>Tietoa</translation>
</message>
@@ -3238,6 +3366,14 @@
<translation>Siirron vahvistus epäonnistui</translation>
</message>
<message>
+ <source>Specified -walletdir "%s" does not exist</source>
+ <translation>Määriteltyä lompakon hakemistoa "%s" ei ole olemassa.</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is a relative path</source>
+ <translation>Määritelty lompakkohakemisto "%s" sijaitsee suhteellisessa polussa</translation>
+ </message>
+ <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>Rahansiirron määrä on liian pieni kattaakseen maksukulun</translation>
</message>
@@ -3266,6 +3402,10 @@
<translation>Varmistetaan lompakko(ja)...</translation>
</message>
<message>
+ <source>Wallet %s resides outside wallet directory %s</source>
+ <translation>Lompakko %s sijaitsee lompakkohakemiston %s ulkopuolella.</translation>
+ </message>
+ <message>
<source>Warning</source>
<translation>Varoitus</translation>
</message>
@@ -3286,6 +3426,10 @@
<translation>Virhe ladattaessa %s: Et voi ottaa HD:ta käyttöön jo olemassa olevalle ei-HD -lompakolle.</translation>
</message>
<message>
+ <source>This is the transaction fee you may pay when fee estimates are not available.</source>
+ <translation>Tämän siirtomaksun maksat, kun siirtomaksun arviointi ei ole käytettävissä.</translation>
+ </message>
+ <message>
<source>Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.</source>
<translation>Ei tuettu argumentti -socks löytynyt. SOCKS -version asetusta ei enää tueta, ainoastaan SOCKS5 -välityspalvelimet on tuettu.</translation>
</message>
@@ -3314,6 +3458,10 @@
<translation>Käynnistetään verkkoa...</translation>
</message>
<message>
+ <source>The wallet will avoid paying less than the minimum relay fee.</source>
+ <translation>Lompakko välttää maksamasta alle vähimmäisen välityskulun.</translation>
+ </message>
+ <message>
<source>This is the minimum transaction fee you pay on every transaction.</source>
<translation>Tämä on jokaisesta siirrosta maksettava vähimmäismaksu.</translation>
</message>
@@ -3342,6 +3490,14 @@
<translation>Lompakon saldo ei riitä</translation>
</message>
<message>
+ <source>Can't generate a change-address key. Private keys are disabled for this wallet.</source>
+ <translation>Vaihtorahaosoitetteen avainta ei voitu luoda. Yksityisavaimet on poistettu käytöstä tässä lompakossa.</translation>
+ </message>
+ <message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>Siirtomaksun arviointi epäonnistui. Odota muutama lohko tai käytä -fallbackfee -valintaa..</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>Ladataan lohkoindeksiä...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_fr.ts b/src/qt/locale/bitcoin_fr.ts
index 52e7bda368..046972360e 100644
--- a/src/qt/locale/bitcoin_fr.ts
+++ b/src/qt/locale/bitcoin_fr.ts
@@ -842,7 +842,7 @@
</message>
<message>
<source>When you click OK, %1 will begin to download and process the full %4 block chain (%2GB) starting with the earliest transactions in %3 when %4 initially launched.</source>
- <translation>Lorsque vous cliquez sur OK, %1 commence à télécharger et à traiter l’intégralité de la chaîne de blocs %4 (%2 Go) en débutant avec les transactions les plus anciennes de %3, quand %4 a été lancé initialement.</translation>
+ <translation>Quand vous cliquerez sur Valider, %1 commencera à télécharger et à traiter l’intégralité de la chaîne de blocs %4 (%2 Go) en débutant avec les transactions les plus anciennes de %3, quand %4 a été lancé initialement.</translation>
</message>
<message>
<source>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source>
@@ -1195,7 +1195,7 @@
</message>
<message>
<source>&amp;OK</source>
- <translation>&amp;OK</translation>
+ <translation>&amp;Valider</translation>
</message>
<message>
<source>&amp;Cancel</source>
@@ -1254,7 +1254,7 @@
</message>
<message>
<source>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source>
- <translation>Les informations affichées peuvent être obsolètes. Votre porte-monnaie est automatiquement synchronisé avec le réseau Bitcoin lorsque la connexion s’établit, or ce processus n’est pas encore terminé.</translation>
+ <translation>Les informations affichées peuvent être obsolètes. Votre porte-monnaie se synchronise automatiquement avec le réseau Bitcoin dès qu’une connexion est établie, mais ce processus n’est pas encore achevé.</translation>
</message>
<message>
<source>Watch-only:</source>
@@ -2300,7 +2300,7 @@ Note : Les frais étant calculés par octet, des frais de « 100 satoshis par
</message>
<message>
<source>The total exceeds your balance when the %1 transaction fee is included.</source>
- <translation>Le montant dépasse votre solde lorsque les frais de transaction de %1 sont inclus.</translation>
+ <translation>Le montant dépasse votre solde quand les frais de transaction de %1 sont inclus.</translation>
</message>
<message>
<source>Duplicate address found: addresses should only be used once each.</source>
@@ -2741,7 +2741,7 @@ Note : Les frais étant calculés par octet, des frais de « 100 satoshis par
</message>
<message>
<source>Output index</source>
- <translation>Index de sorties</translation>
+ <translation>Index des sorties</translation>
</message>
<message>
<source>Merchant</source>
@@ -2749,7 +2749,7 @@ Note : Les frais étant calculés par octet, des frais de « 100 satoshis par
</message>
<message>
<source>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
- <translation>Les pièces générées doivent mûrir pendant %1 blocs avant de pouvoir être dépensées. Lorsque ce bloc a été généré, il a été diffusé sur le réseau pour être ajouté à la chaîne de blocs. Si son intégration à la chaîne échoue, son état sera modifié en « refusée » et il ne sera pas possible de le dépenser. Cela peut arriver occasionnellement si un autre nœud génère un bloc à quelques secondes du vôtre.</translation>
+ <translation>Les pièces générées doivent mûrir pendant %1 blocs avant de pouvoir être dépensées. Quand vous avez généré ce bloc, il a été diffusé sur le réseau pour être ajouté à la chaîne de blocs. Si son intégration à la chaîne échoue, son état sera modifié en « refusée » et il ne sera pas possible de le dépenser. Cela peut arriver occasionnellement si un autre nœud génère un bloc à quelques secondes du vôtre.</translation>
</message>
<message>
<source>Debug information</source>
@@ -3200,6 +3200,10 @@ Note : Les frais étant calculés par octet, des frais de « 100 satoshis par
<translation>Erreur de lecture de %s ! Toutes les clés ont été lues correctement, mais les données transactionnelles ou les entrées du carnet d’adresses sont peut-être manquantes ou incorrectes.</translation>
</message>
<message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>Grouper les sorties par adresse, les sélectionnant toutes ou n’en sélectionnant aucune, au lieu d’une sélection par sortie. La confidentialité est améliorée dans la mesure où une adresse n’est utilisée qu’une fois (à moins que quelqu’un envoie à adresse après l’avoir utilisée pour une dépense). Cela pourrait entraîner des frais légèrement plus élevés, car une sélection sous-optimale des pièces pourrait en résulter en raison de la restriction supplémentaire (par défaut : %u)</translation>
+ </message>
+ <message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation>Veuillez vérifier que l’heure et la date de votre ordinateur sont justes ! Si votre horloge n’est pas à l’heure, %s ne fonctionnera pas correctement.</translation>
</message>
@@ -3340,6 +3344,10 @@ Note : Les frais étant calculés par octet, des frais de « 100 satoshis par
<translation>Montant invalide pour -fallbackfee=&lt;amount&gt; : « %s »</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Le répertoire des blocs indiqué « %s » n’existe pas.</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Mise à niveau de la base de données txindex</translation>
</message>
@@ -3480,12 +3488,6 @@ Note : Les frais étant calculés par octet, des frais de « 100 satoshis par
<translation>Échec de signature de la transaction</translation>
</message>
<message>
- <source>Specified blocks directory "%s" does not exist.
-</source>
- <translation>Le répertoire de blocs indiqué « %s » n’existe pas.
-</translation>
- </message>
- <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>Le montant de la transaction est trop bas pour que les frais soient payés</translation>
</message>
diff --git a/src/qt/locale/bitcoin_he.ts b/src/qt/locale/bitcoin_he.ts
index cf884f0fc7..c8088da4e2 100644
--- a/src/qt/locale/bitcoin_he.ts
+++ b/src/qt/locale/bitcoin_he.ts
@@ -3,7 +3,7 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>יש ללחוץ על הכפתור הימני כדי לערוך כתובת או תווית</translation>
+ <translation>לחץ על הכפתור הימני בעכבר כדי לערוך את הכתובת או התווית</translation>
</message>
<message>
<source>Create a new address</source>
@@ -279,19 +279,19 @@
</message>
<message>
<source>&amp;About %1</source>
- <translation>על &amp;אודות %1</translation>
+ <translation>&amp;אודות %1</translation>
</message>
<message>
<source>Show information about %1</source>
- <translation>הצגת מידע על %1</translation>
+ <translation>הצג מידע על %1</translation>
</message>
<message>
<source>About &amp;Qt</source>
- <translation>על אודות Qt</translation>
+ <translation>אודות &amp;Qt</translation>
</message>
<message>
<source>Show information about Qt</source>
- <translation>הצגת מידע על Qt</translation>
+ <translation>הצג מידע על Qt</translation>
</message>
<message>
<source>&amp;Options...</source>
@@ -815,7 +815,7 @@
</message>
<message>
<source>About %1</source>
- <translation>על אודות %1</translation>
+ <translation>אודות %1</translation>
</message>
<message>
<source>Command-line options</source>
@@ -1002,6 +1002,14 @@
<translation>כתובת ה־IP של המתווך (לדוגמה IPv4: 127.0.0.1‏ / IPv6: ::1)</translation>
</message>
<message>
+ <source>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
+ <translation>מראה אם פרוקסי SOCKS5 המסופק כבררת מחדל משמש להתקשרות עם עמיתים באמצעות סוג רשת זה.</translation>
+ </message>
+ <message>
+ <source>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</source>
+ <translation>השתמשו בפרוקסי SOCKS&amp;5 נפרד כדי להתקשר עם עמיתים באמצעות שירותים חבויים ברשת Tor:</translation>
+ </message>
+ <message>
<source>Hide the icon from the system tray.</source>
<translation>הסתר את סמל מגש המערכת</translation>
</message>
@@ -1556,7 +1564,7 @@
</message>
<message>
<source>Client version</source>
- <translation>גרסת מנשק</translation>
+ <translation>גרסה</translation>
</message>
<message>
<source>&amp;Information</source>
@@ -1572,7 +1580,7 @@
</message>
<message>
<source>Using BerkeleyDB version</source>
- <translation>שימוש ב־BerkeleyDB גרסה</translation>
+ <translation>גרסת BerkeleyDB</translation>
</message>
<message>
<source>Datadir</source>
@@ -1675,6 +1683,10 @@
<translation>סוכן משתמש</translation>
</message>
<message>
+ <source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
+ <translation>פתחו את לוג ניפוי השגיאות ה%1 מתיקיית הנתונים הנוכחית. עבור קבצי לוג גדולים ייתכן זמן המתנה של מספר שניות.</translation>
+ </message>
+ <message>
<source>Decrease font size</source>
<translation>הקטן גודל גופן</translation>
</message>
@@ -2203,6 +2215,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>העתקת השינוי</translation>
</message>
<message>
+ <source>%1 (%2 blocks)</source>
+ <translation>%1 (%2 בלוקים)</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>לשלוח?</translation>
</message>
diff --git a/src/qt/locale/bitcoin_hi_IN.ts b/src/qt/locale/bitcoin_hi_IN.ts
index 51d6770b1a..cf969b67db 100644
--- a/src/qt/locale/bitcoin_hi_IN.ts
+++ b/src/qt/locale/bitcoin_hi_IN.ts
@@ -18,6 +18,14 @@
<translation>चुनिन्दा पते को सिस्टम क्लिपबोर्ड पर कापी करे !</translation>
</message>
<message>
+ <source>&amp;Copy</source>
+ <translation>&amp;कॉपी </translation>
+ </message>
+ <message>
+ <source>C&amp;lose</source>
+ <translation>सी&amp;लूज़ </translation>
+ </message>
+ <message>
<source>Delete the currently selected address from the list</source>
<translation>सूची से वर्तमान में चयनित पता हटाएं</translation>
</message>
@@ -61,13 +69,49 @@
<source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
<translation>भुगतान प्राप्त करने के लिए ये आपके बीटकोइन पते हैं प्रत्येक लेनदेन के लिए एक नया प्राप्त पता उपयोग करने की सिफारिश की जाती है।</translation>
</message>
+ <message>
+ <source>&amp;Copy Address</source>
+ <translation>&amp;पता कॉपी करें </translation>
+ </message>
+ <message>
+ <source>&amp;Edit</source>
+ <translation>&amp;संशोधित करें </translation>
+ </message>
+ <message>
+ <source>Export Address List</source>
+ <translation>निर्यात पता सूची</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>कोमा द्वारा अलग की गई फ़ाइल (* .csv)</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>निर्यात विफल रहा</translation>
+ </message>
</context>
<context>
<name>AddressTableModel</name>
- </context>
+ <message>
+ <source>Label</source>
+ <translation>परचा</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>पता </translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(कोई परचा नहीं )</translation>
+ </message>
+</context>
<context>
<name>AskPassphraseDialog</name>
<message>
+ <source>Passphrase Dialog</source>
+ <translation>पासफ़्रेज़ डायलॉग</translation>
+ </message>
+ <message>
<source>Enter passphrase</source>
<translation>पहचान शब्द/अक्षर डालिए !</translation>
</message>
@@ -79,6 +123,18 @@
<source>Repeat new passphrase</source>
<translation>दोबारा नया पहचान शब्द/अक्षर डालिए !</translation>
</message>
+ <message>
+ <source>Show password</source>
+ <translation>पासवर्ड दिखाए</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>वॉलेट एन्क्रिप्ट करें</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>पासफ़्रेज़ बदलें</translation>
+ </message>
</context>
<context>
<name>BanTableModel</name>
@@ -205,6 +261,10 @@
<source>Confirmed</source>
<translation>पक्का</translation>
</message>
+ <message>
+ <source>(no label)</source>
+ <translation>(कोई परचा नहीं )</translation>
+ </message>
</context>
<context>
<name>EditAddressDialog</name>
@@ -340,12 +400,28 @@
<translation>&amp;पता कॉपी करे</translation>
</message>
<message>
+ <source>Address</source>
+ <translation>पता </translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>परचा</translation>
+ </message>
+ <message>
<source>Wallet</source>
<translation>वॉलेट</translation>
</message>
</context>
<context>
<name>RecentRequestsTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>परचा</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(कोई परचा नहीं )</translation>
+ </message>
</context>
<context>
<name>SendCoinsDialog</name>
@@ -369,7 +445,11 @@
<source>Confirm the send action</source>
<translation>भेजने की पुष्टि करें</translation>
</message>
- </context>
+ <message>
+ <source>(no label)</source>
+ <translation>(कोई परचा नहीं )</translation>
+ </message>
+</context>
<context>
<name>SendCoinsEntry</name>
<message>
@@ -448,9 +528,33 @@
</context>
<context>
<name>TransactionTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>परचा</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(कोई परचा नहीं )</translation>
+ </message>
</context>
<context>
<name>TransactionView</name>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>कोमा द्वारा अलग की गई फ़ाइल (* .csv)</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>परचा</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>पता </translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>निर्यात विफल रहा</translation>
+ </message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
diff --git a/src/qt/locale/bitcoin_hr.ts b/src/qt/locale/bitcoin_hr.ts
index 2bd6c7df03..381c99946b 100644
--- a/src/qt/locale/bitcoin_hr.ts
+++ b/src/qt/locale/bitcoin_hr.ts
@@ -3,7 +3,7 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>Desni klik za uređivanje adresa i oznaka</translation>
+ <translation>Desni klik za uređivanje adrese ili oznake</translation>
</message>
<message>
<source>Create a new address</source>
@@ -15,19 +15,19 @@
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
- <translation>Kopiraj trenutno odabranu adresu u međuspremnik</translation>
+ <translation>Kopirajte trenutno odabranu adresu u međuspremnik</translation>
</message>
<message>
<source>&amp;Copy</source>
- <translation>&amp;Kopiraj</translation>
+ <translation>&amp;Kopirajte</translation>
</message>
<message>
<source>C&amp;lose</source>
- <translation>&amp;Zatvori</translation>
+ <translation>&amp;Zatvorite</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
- <translation>Brisanje trenutno odabrane adrese s popisa.</translation>
+ <translation>Obrišite trenutno odabranu adresu s popisa.</translation>
</message>
<message>
<source>Enter address or label to search</source>
@@ -35,35 +35,35 @@
</message>
<message>
<source>Export the data in the current tab to a file</source>
- <translation>Izvoz podataka iz trenutnog lista u datoteku</translation>
+ <translation>Izvezite podatke iz trenutne kartice u datoteku</translation>
</message>
<message>
<source>&amp;Export</source>
- <translation>&amp;Izvozi</translation>
+ <translation>&amp;Izvezite</translation>
</message>
<message>
<source>&amp;Delete</source>
- <translation>Iz&amp;briši</translation>
+ <translation>Iz&amp;brišite</translation>
</message>
<message>
<source>Choose the address to send coins to</source>
- <translation>Odaberi adresu na koju šalješ novac</translation>
+ <translation>Odaberite adresu na koju ćete poslati novac</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
- <translation>Odaberi adresu na koju primaš novac</translation>
+ <translation>Odaberite adresu na koju ćete primiti novac</translation>
</message>
<message>
<source>C&amp;hoose</source>
- <translation>&amp;Odaberi</translation>
+ <translation>&amp;Odaberite</translation>
</message>
<message>
<source>Sending addresses</source>
- <translation>Adresa pošiljatelja</translation>
+ <translation>Adrese pošiljatelja</translation>
</message>
<message>
<source>Receiving addresses</source>
- <translation>Adresa primatelja</translation>
+ <translation>Adrese primatelja</translation>
</message>
<message>
<source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
@@ -71,23 +71,23 @@
</message>
<message>
<source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
- <translation>Ovo su vaše Bitcoin adrese za primanje novca. Preporučamo da koristite novu adresu za primanje za svaku transakciju.</translation>
+ <translation>Ovo su vaše Bitcoin adrese za primanje novca. Preporučeno je da koristite novu primateljsku adresu za svaku transakciju.</translation>
</message>
<message>
<source>&amp;Copy Address</source>
- <translation>&amp;Kopiraj adresu</translation>
+ <translation>&amp;Kopirajte adresu</translation>
</message>
<message>
<source>Copy &amp;Label</source>
- <translation>Kopiraj i označi</translation>
+ <translation>Kopirajte &amp;oznaku</translation>
</message>
<message>
<source>&amp;Edit</source>
- <translation>Uredi</translation>
+ <translation>&amp;Uredite</translation>
</message>
<message>
<source>Export Address List</source>
- <translation>Izvezi listu adresa</translation>
+ <translation>Izvezite listu adresa</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
@@ -136,12 +136,16 @@
<translation>Ponovite novu lozinku</translation>
</message>
<message>
+ <source>Show password</source>
+ <translation>Prikažite lozinku</translation>
+ </message>
+ <message>
<source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
- <translation>Unesite novu lozinku za novčanik. &lt;br/&gt;Molimo Vas da koristite zaporku od &lt;b&gt;deset ili više slučajnih znakova&lt;/b&gt;, ili &lt;b&gt;osam ili više riječi.&lt;/b&gt;</translation>
+ <translation>Unesite novu lozinku za novčanik. &lt;br/&gt;Molimo vas da koristite zaporku od &lt;b&gt;deset ili više slučajnih znakova&lt;/b&gt;, ili &lt;b&gt;osam ili više riječi.&lt;/b&gt;</translation>
</message>
<message>
<source>Encrypt wallet</source>
- <translation>Šifriranje novčanika</translation>
+ <translation>Šifrirajte novčanik</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
@@ -149,7 +153,7 @@
</message>
<message>
<source>Unlock wallet</source>
- <translation>Otključaj novčanik</translation>
+ <translation>Otključajte novčanik</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to decrypt the wallet.</source>
@@ -157,11 +161,11 @@
</message>
<message>
<source>Decrypt wallet</source>
- <translation>Dešifriranje novčanika.</translation>
+ <translation>Dešifrirajte novčanik</translation>
</message>
<message>
<source>Change passphrase</source>
- <translation>Promjena lozinke</translation>
+ <translation>Promijenite lozinku</translation>
</message>
<message>
<source>Enter the old passphrase and new passphrase to the wallet.</source>
@@ -169,7 +173,7 @@
</message>
<message>
<source>Confirm wallet encryption</source>
- <translation>Potvrdi šifriranje novčanika</translation>
+ <translation>Potvrdite šifriranje novčanika</translation>
</message>
<message>
<source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
@@ -184,6 +188,10 @@
<translation>Novčanik šifriran</translation>
</message>
<message>
+ <source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>%1 će se sada zatvoriti kako bi dovršio postupak šifriranja. Zapamtite da šifriranje vašeg novčanika ne može u potpunosti zaštititi vaše bitcoine od krađe preko zloćudnog softvera koji bi bio na vašem računalu.</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>VAŽNO: Sve prethodne pričuve vašeg novčanika trebale bi biti zamijenjene novo stvorenom, šifriranom datotekom novčanika. Zbog sigurnosnih razloga, prethodne pričuve nešifriranog novčanika će postati beskorisne čim počnete koristiti novi, šifrirani novčanik.</translation>
</message>
@@ -222,7 +230,15 @@
</context>
<context>
<name>BanTableModel</name>
- </context>
+ <message>
+ <source>IP/Netmask</source>
+ <translation>IP/Mrežna maska</translation>
+ </message>
+ <message>
+ <source>Banned Until</source>
+ <translation>Zabranjen do</translation>
+ </message>
+</context>
<context>
<name>BitcoinGUI</name>
<message>
@@ -231,7 +247,7 @@
</message>
<message>
<source>Synchronizing with network...</source>
- <translation>Usklađivanje s mrežom ...</translation>
+ <translation>Sinkronizira se s mrežom...</translation>
</message>
<message>
<source>&amp;Overview</source>
@@ -251,7 +267,7 @@
</message>
<message>
<source>Browse transaction history</source>
- <translation>Pretraži povijest transakcija</translation>
+ <translation>Pretražite povijest transakcija</translation>
</message>
<message>
<source>E&amp;xit</source>
@@ -259,35 +275,43 @@
</message>
<message>
<source>Quit application</source>
- <translation>Izlazak iz programa</translation>
+ <translation>Zatvorite aplikaciju</translation>
</message>
<message>
<source>&amp;About %1</source>
<translation>&amp;Više o %1</translation>
</message>
<message>
+ <source>Show information about %1</source>
+ <translation>Prikažite informacije o programu %1</translation>
+ </message>
+ <message>
<source>About &amp;Qt</source>
<translation>Više o &amp;Qt</translation>
</message>
<message>
<source>Show information about Qt</source>
- <translation>Prikaži informacije o Qt</translation>
+ <translation>Prikažite informacije o Qt</translation>
</message>
<message>
<source>&amp;Options...</source>
<translation>Pos&amp;tavke...</translation>
</message>
<message>
+ <source>Modify configuration options for %1</source>
+ <translation>Promijenite postavke za %1</translation>
+ </message>
+ <message>
<source>&amp;Encrypt Wallet...</source>
- <translation>Ši&amp;friraj novčanik...</translation>
+ <translation>Ši&amp;frirajte novčanik...</translation>
</message>
<message>
<source>&amp;Backup Wallet...</source>
- <translation>Spremi &amp;kopiju novčanika...</translation>
+ <translation>Spremite &amp;kopiju novčanika...</translation>
</message>
<message>
<source>&amp;Change Passphrase...</source>
- <translation>Promjena &amp;lozinke...</translation>
+ <translation>Promijenite &amp;lozinku...</translation>
</message>
<message>
<source>&amp;Sending addresses...</source>
@@ -299,15 +323,43 @@
</message>
<message>
<source>Open &amp;URI...</source>
- <translation>Otvori &amp;URI...</translation>
+ <translation>Otvorite &amp;URI...</translation>
+ </message>
+ <message>
+ <source>Wallet:</source>
+ <translation>Novčanik:</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>uobičajeni novčanik</translation>
+ </message>
+ <message>
+ <source>Click to disable network activity.</source>
+ <translation>Kliknite da isključite mrežnu aktivnost.</translation>
+ </message>
+ <message>
+ <source>Network activity disabled.</source>
+ <translation>Mrežna aktivnost isključena.</translation>
+ </message>
+ <message>
+ <source>Click to enable network activity again.</source>
+ <translation>Kliknite da ponovo uključite mrežnu aktivnost.</translation>
+ </message>
+ <message>
+ <source>Syncing Headers (%1%)...</source>
+ <translation>Sinkroniziraju se zaglavlja (%1%)...</translation>
</message>
<message>
<source>Reindexing blocks on disk...</source>
<translation>Re-indeksiranje blokova na disku...</translation>
</message>
<message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>Proxy je &lt;b&gt;uključen&lt;/b&gt;: %1</translation>
+ </message>
+ <message>
<source>Send coins to a Bitcoin address</source>
- <translation>Slanje novca na bitcoin adresu</translation>
+ <translation>Pošaljite novac na Bitcoin adresu</translation>
</message>
<message>
<source>Backup wallet to another location</source>
@@ -323,7 +375,7 @@
</message>
<message>
<source>Open debugging and diagnostic console</source>
- <translation>Otvori konzolu za dijagnostiku</translation>
+ <translation>Otvorite konzolu za dijagnostiku</translation>
</message>
<message>
<source>&amp;Verify message...</source>
@@ -339,31 +391,31 @@
</message>
<message>
<source>&amp;Send</source>
- <translation>&amp;Pošalji</translation>
+ <translation>&amp;Pošaljite</translation>
</message>
<message>
<source>&amp;Receive</source>
- <translation>Pri&amp;mi</translation>
+ <translation>Pri&amp;mite</translation>
</message>
<message>
<source>&amp;Show / Hide</source>
- <translation>Po&amp;kaži / Sakrij</translation>
+ <translation>Po&amp;kažite / Sakrijte</translation>
</message>
<message>
<source>Show or hide the main Window</source>
- <translation>Prikaži ili sakrij glavni prozor</translation>
+ <translation>Prikažite ili sakrijte glavni prozor</translation>
</message>
<message>
<source>Encrypt the private keys that belong to your wallet</source>
- <translation>Šifriranje privatnih ključeva koji u novčaniku</translation>
+ <translation>Šifrirajte privatne ključeve u novčaniku</translation>
</message>
<message>
<source>Sign messages with your Bitcoin addresses to prove you own them</source>
- <translation>Poruku potpišemo s bitcoin adresom, kako bi dokazali vlasništvo nad tom adresom</translation>
+ <translation>Poruku potpišemo s Bitcoin adresom, kako bi dokazali vlasništvo nad tom adresom</translation>
</message>
<message>
<source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
- <translation>Provjeravanje poruke, kao dokaz, da je potpisana navedenom bitcoin adresom</translation>
+ <translation>Provjerite poruku da je potpisana s navedenom Bitcoin adresom</translation>
</message>
<message>
<source>&amp;File</source>
@@ -383,19 +435,19 @@
</message>
<message>
<source>Request payments (generates QR codes and bitcoin: URIs)</source>
- <translation>Zatraži uplatu (stvara QR kod i bitcoin: URI adresu)</translation>
+ <translation>Zatražite uplatu (stvara QR kod i bitcoin: URI adresu)</translation>
</message>
<message>
<source>Show the list of used sending addresses and labels</source>
- <translation>Prikaži popis korištenih adresa i oznaka za slanje novca</translation>
+ <translation>Prikažite popis korištenih adresa i oznaka za slanje novca</translation>
</message>
<message>
<source>Show the list of used receiving addresses and labels</source>
- <translation>Prikaži popis korištenih adresa i oznaka za primanje novca</translation>
+ <translation>Prikažite popis korištenih adresa i oznaka za primanje novca</translation>
</message>
<message>
<source>Open a bitcoin: URI or payment request</source>
- <translation>Otvori bitcoin: URI adresu ili zahtjev za uplatu</translation>
+ <translation>Otvorite bitcoin: URI adresu ili zahtjev za uplatu</translation>
</message>
<message>
<source>&amp;Command-line options</source>
@@ -407,17 +459,21 @@
</message>
<message>
<source>Indexing blocks on disk...</source>
- <translation>Indeksiram blokove na disku...</translation>
+ <translation>Indeksiraju se blokovi na disku...</translation>
</message>
<message>
<source>Processing blocks on disk...</source>
- <translation>Procesiram blokove na disku...</translation>
+ <translation>Procesiraju se blokovi na disku...</translation>
</message>
<message numerus="yes">
<source>Processed %n block(s) of transaction history.</source>
<translation><numerusform>Obrađen %n blok povijesti transakcije.</numerusform><numerusform>Obrađeno %n bloka povijesti transakcije.</numerusform><numerusform>Obrađeno %n blokova povijesti transakcije.</numerusform></translation>
</message>
<message>
+ <source>%1 behind</source>
+ <translation>%1 iza</translation>
+ </message>
+ <message>
<source>Last received block was generated %1 ago.</source>
<translation>Zadnji primljeni blok je bio ustvaren prije %1.</translation>
</message>
@@ -442,10 +498,18 @@
<translation>Ažurno</translation>
</message>
<message>
+ <source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
+ <translation>Prikažite pomoć programa %1 kako biste ispisali moguće opcije preko terminala</translation>
+ </message>
+ <message>
<source>%1 client</source>
<translation>%1 klijent</translation>
</message>
<message>
+ <source>Connecting to peers...</source>
+ <translation>Spaja se na klijente...</translation>
+ </message>
+ <message>
<source>Catching up...</source>
<translation>Ažuriranje...</translation>
</message>
@@ -462,6 +526,11 @@
</translation>
</message>
<message>
+ <source>Wallet: %1
+</source>
+ <translation>Novčanik: %1</translation>
+ </message>
+ <message>
<source>Type: %1
</source>
<translation>Vrsta: %1
@@ -488,6 +557,14 @@
<translation>Dolazna transakcija</translation>
</message>
<message>
+ <source>HD key generation is &lt;b&gt;enabled&lt;/b&gt;</source>
+ <translation>Generiranje HD ključeva je &lt;b&gt;uključeno&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>HD key generation is &lt;b&gt;disabled&lt;/b&gt;</source>
+ <translation>Generiranje HD ključeva je &lt;b&gt;isključeno&lt;/b&gt;</translation>
+ </message>
+ <message>
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
<translation>Novčanik je &lt;b&gt;šifriran&lt;/b&gt; i trenutno &lt;b&gt;otključan&lt;/b&gt;</translation>
</message>
@@ -495,7 +572,11 @@
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
<translation>Novčanik je &lt;b&gt;šifriran&lt;/b&gt; i trenutno &lt;b&gt;zaključan&lt;/b&gt;</translation>
</message>
- </context>
+ <message>
+ <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
+ <translation>Dogodila se kobna greška. Bitcoin ne može više sigurno nastaviti te će se zatvoriti.</translation>
+ </message>
+</context>
<context>
<name>CoinControlDialog</name>
<message>
@@ -520,7 +601,11 @@
</message>
<message>
<source>Dust:</source>
- <translation>Prah:</translation>
+ <translation>Prašina:</translation>
+ </message>
+ <message>
+ <source>After Fee:</source>
+ <translation>Nakon naknade:</translation>
</message>
<message>
<source>Change:</source>
@@ -531,6 +616,14 @@
<translation>Izaberi sve/ništa</translation>
</message>
<message>
+ <source>Tree mode</source>
+ <translation>Prikažite kao stablo</translation>
+ </message>
+ <message>
+ <source>List mode</source>
+ <translation>Prikažite kao listu</translation>
+ </message>
+ <message>
<source>Amount</source>
<translation>Iznos</translation>
</message>
@@ -556,19 +649,55 @@
</message>
<message>
<source>Copy address</source>
- <translation>Kopiraj adresu</translation>
+ <translation>Kopirajte adresu</translation>
</message>
<message>
<source>Copy label</source>
- <translation>Kopiraj oznaku</translation>
+ <translation>Kopirajte oznaku</translation>
</message>
<message>
<source>Copy amount</source>
- <translation>Kopiraj iznos</translation>
+ <translation>Kopirajte iznos</translation>
</message>
<message>
<source>Copy transaction ID</source>
- <translation>Kopiraj ID transakcije</translation>
+ <translation>Kopirajte ID transakcije</translation>
+ </message>
+ <message>
+ <source>Lock unspent</source>
+ <translation>Zaključajte nepotrošen input</translation>
+ </message>
+ <message>
+ <source>Unlock unspent</source>
+ <translation>Otključajte nepotrošen input</translation>
+ </message>
+ <message>
+ <source>Copy quantity</source>
+ <translation>Kopirajte iznos</translation>
+ </message>
+ <message>
+ <source>Copy fee</source>
+ <translation>Kopirajte naknadu</translation>
+ </message>
+ <message>
+ <source>Copy after fee</source>
+ <translation>Kopirajte iznos nakon naknade</translation>
+ </message>
+ <message>
+ <source>Copy bytes</source>
+ <translation>Kopirajte količinu bajtova</translation>
+ </message>
+ <message>
+ <source>Copy dust</source>
+ <translation>Kopirajte prašinu</translation>
+ </message>
+ <message>
+ <source>Copy change</source>
+ <translation>Kopirajte ostatak</translation>
+ </message>
+ <message>
+ <source>(%1 locked)</source>
+ <translation>(%1 zaključen)</translation>
</message>
<message>
<source>yes</source>
@@ -579,15 +708,31 @@
<translation>ne</translation>
</message>
<message>
+ <source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
+ <translation>Oznaka postane crvene boje ako bilo koji primatelj dobije iznos manji od trenutnog praga "prašine" (sićušnog iznosa).</translation>
+ </message>
+ <message>
+ <source>Can vary +/- %1 satoshi(s) per input.</source>
+ <translation>Može varirati +/- %1 satoši(ja) po inputu.</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(nema oznake)</translation>
</message>
- </context>
+ <message>
+ <source>change from %1 (%2)</source>
+ <translation>ostatak od %1 (%2)</translation>
+ </message>
+ <message>
+ <source>(change)</source>
+ <translation>(ostatak)</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
<source>Edit Address</source>
- <translation>Uredi adresu</translation>
+ <translation>Uredite adresu</translation>
</message>
<message>
<source>&amp;Label</source>
@@ -595,11 +740,11 @@
</message>
<message>
<source>The label associated with this address list entry</source>
- <translation>Oznaka bitcoin adrese</translation>
+ <translation>Oznaka ovog zapisa u adresaru</translation>
</message>
<message>
<source>The address associated with this address list entry. This can only be modified for sending addresses.</source>
- <translation>Bitcoin adresa. Izmjene adrese su moguće samo za adrese za slanje.</translation>
+ <translation>Adresa ovog zapisa u adresaru. Može se mijenjati samo kod adresa za slanje.</translation>
</message>
<message>
<source>&amp;Address</source>
@@ -619,11 +764,19 @@
</message>
<message>
<source>The entered address "%1" is not a valid Bitcoin address.</source>
- <translation>Upisana adresa "%1" nije valjana bitcoin adresa.</translation>
+ <translation>Upisana adresa "%1" nije valjana Bitcoin adresa.</translation>
+ </message>
+ <message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Adresa "%1" već postoji kao primateljska adresa s oznakom "%2" te se ne može dodati kao pošiljateljska adresa.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>Unesena adresa "%1" postoji već u imeniku pod oznakom "%2".</translation>
</message>
<message>
<source>Could not unlock wallet.</source>
- <translation>Ne mogu otključati novčanik.</translation>
+ <translation>Ne može se otključati novčanik.</translation>
</message>
<message>
<source>New key generation failed.</source>
@@ -634,13 +787,21 @@
<name>FreespaceChecker</name>
<message>
<source>A new data directory will be created.</source>
- <translation>Stvoren će biti novi direktorij za podatke.</translation>
+ <translation>Bit će stvorena nova podatkovna mapa.</translation>
</message>
<message>
<source>name</source>
<translation>ime</translation>
</message>
<message>
+ <source>Directory already exists. Add %1 if you intend to create a new directory here.</source>
+ <translation>Mapa već postoji. Dodajte %1 ako namjeravate stvoriti novu mapu ovdje.</translation>
+ </message>
+ <message>
+ <source>Path already exists, and is not a directory.</source>
+ <translation>Put već postoji i nije mapa.</translation>
+ </message>
+ <message>
<source>Cannot create data directory here.</source>
<translation>Nije moguće stvoriti direktorij za podatke na tom mjestu.</translation>
</message>
@@ -656,6 +817,10 @@
<translation>(%1-bit)</translation>
</message>
<message>
+ <source>About %1</source>
+ <translation>O programu %1</translation>
+ </message>
+ <message>
<source>Command-line options</source>
<translation>Opcije programa u naredbenoj liniji</translation>
</message>
@@ -667,13 +832,65 @@
<translation>Dobrodošli</translation>
</message>
<message>
+ <source>Welcome to %1.</source>
+ <translation>Dobrodošli u %1.</translation>
+ </message>
+ <message>
+ <source>As this is the first time the program is launched, you can choose where %1 will store its data.</source>
+ <translation>Kako je ovo prvi put da je ova aplikacija pokrenuta, možete izabrati gdje će %1 spremati svoje podatke.</translation>
+ </message>
+ <message>
+ <source>When you click OK, %1 will begin to download and process the full %4 block chain (%2GB) starting with the earliest transactions in %3 when %4 initially launched.</source>
+ <translation>Kada kliknete OK, %1 počet će preuzimati i procesirati cijeli lanac blokova (%2GB) počevši s najranijim transakcijama u %3 kad je %4 prvi put pokrenut.</translation>
+ </message>
+ <message>
+ <source>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source>
+ <translation>Početna sinkronizacija je vrlo zahtjevna i može otkriti hardverske probleme kod vašeg računala koji su prije prošli nezamijećeno. Svaki put kad pokrenete %1, nastavit će preuzimati odakle je stao.</translation>
+ </message>
+ <message>
+ <source>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source>
+ <translation>Ako odlučite ograničiti spremanje lanca blokova pomoću pruninga (obrezivanja), treba preuzeti i procesirati povijesne podatke. Bit će obrisani naknadno kako bi se smanjila količina zauzetog prostora na disku.</translation>
+ </message>
+ <message>
+ <source>Use the default data directory</source>
+ <translation>Koristite uobičajenu podatkovnu mapu</translation>
+ </message>
+ <message>
+ <source>Use a custom data directory:</source>
+ <translation>Odaberite različitu podatkovnu mapu:</translation>
+ </message>
+ <message>
<source>Bitcoin</source>
<translation>Bitcoin</translation>
</message>
<message>
+ <source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
+ <translation>Bit će spremljeno barem %1 GB podataka u ovoj mapi te će se povećati tijekom vremena.</translation>
+ </message>
+ <message>
+ <source>Approximately %1 GB of data will be stored in this directory.</source>
+ <translation>Otprilike %1 GB podataka bit će spremljeno u ovoj mapi.</translation>
+ </message>
+ <message>
+ <source>%1 will download and store a copy of the Bitcoin block chain.</source>
+ <translation>%1 preuzet će i pohraniti kopiju Bitcoinovog lanca blokova.</translation>
+ </message>
+ <message>
+ <source>The wallet will also be stored in this directory.</source>
+ <translation>Novčanik bit će pohranjen u ovoj mapi.</translation>
+ </message>
+ <message>
+ <source>Error: Specified data directory "%1" cannot be created.</source>
+ <translation>Greška: Zadana podatkovna mapa "%1" ne može biti stvorena.</translation>
+ </message>
+ <message>
<source>Error</source>
<translation>Greška</translation>
</message>
+ <message numerus="yes">
+ <source>%n GB of free space available</source>
+ <translation><numerusform>Dostupno %n GB slobodnog prostora</numerusform><numerusform>Dostupno %n GB slobodnog prostora</numerusform><numerusform>Dostupno %n GB slobodnog prostora</numerusform></translation>
+ </message>
</context>
<context>
<name>ModalOverlay</name>
@@ -682,10 +899,50 @@
<translation>Oblik</translation>
</message>
<message>
+ <source>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source>
+ <translation>Nedavne transakcije možda još nisu vidljive pa vam stanje novčanika može biti netočno. Ove informacije bit će točne nakon što vaš novčanik dovrši sinkronizaciju s Bitcoinovom mrežom, kako je opisano dolje.</translation>
+ </message>
+ <message>
+ <source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
+ <translation>Mreža neće prihvatiti pokušaje trošenja bitcoina koji su utjecani sa strane transakcija koje još nisu vidljive.</translation>
+ </message>
+ <message>
+ <source>Number of blocks left</source>
+ <translation>Broj preostalih blokova</translation>
+ </message>
+ <message>
+ <source>Unknown...</source>
+ <translation>Nepoznato...</translation>
+ </message>
+ <message>
<source>Last block time</source>
<translation>Posljednje vrijeme bloka</translation>
</message>
- </context>
+ <message>
+ <source>Progress</source>
+ <translation>Napredak</translation>
+ </message>
+ <message>
+ <source>Progress increase per hour</source>
+ <translation>Postotak povećanja napretka na sat</translation>
+ </message>
+ <message>
+ <source>calculating...</source>
+ <translation>računa...</translation>
+ </message>
+ <message>
+ <source>Estimated time left until synced</source>
+ <translation>Preostalo vrijeme do završetka sinkronizacije</translation>
+ </message>
+ <message>
+ <source>Hide</source>
+ <translation>Sakrijte</translation>
+ </message>
+ <message>
+ <source>Unknown. Syncing Headers (%1)...</source>
+ <translation>Nepoznato. Sinkroniziraju se zaglavlja (%1)...</translation>
+ </message>
+</context>
<context>
<name>OpenURIDialog</name>
<message>
@@ -720,6 +977,14 @@
<translation>&amp;Glavno</translation>
</message>
<message>
+ <source>Automatically start %1 after logging in to the system.</source>
+ <translation>Automatski pokrenite %1 nakon prijave u sustav.</translation>
+ </message>
+ <message>
+ <source>&amp;Start %1 on system login</source>
+ <translation>&amp;Pokrenite %1 kod prijave u sustav</translation>
+ </message>
+ <message>
<source>Size of &amp;database cache</source>
<translation>Veličina predmemorije baze podataka</translation>
</message>
@@ -736,10 +1001,42 @@
<translation>IP adresa proxy servera (npr. IPv4: 127.0.0.1 / IPv6: ::1)</translation>
</message>
<message>
+ <source>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
+ <translation>Prikazuje se ako je isporučeni uobičajeni SOCKS5 proxy korišten radi dohvaćanja klijenata preko ovog tipa mreže.</translation>
+ </message>
+ <message>
+ <source>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</source>
+ <translation>Koristite zaseban SOCKS&amp;5 proxy kako biste dohvatili klijente preko Tora:</translation>
+ </message>
+ <message>
+ <source>Hide the icon from the system tray.</source>
+ <translation>Sakrijte ikonu sa sustavne trake.</translation>
+ </message>
+ <message>
+ <source>&amp;Hide tray icon</source>
+ <translation>&amp;Sakrijte ikonu</translation>
+ </message>
+ <message>
<source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source>
<translation>Minimizirati aplikaciju umjesto zatvoriti, kada se zatvori prozor. Kada je ova opcija omogućena, aplikacija će biti zatvorena tek nakon odabira naredbe Izlaz u izborniku.</translation>
</message>
<message>
+ <source>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 |.</source>
+ <translation>URL-ovi treće stranke (npr. preglednik blokova) koji se javljaju u kartici transakcija kao elementi kontekstnog izbornika. %s u URL-u zamijenjen je hashom transakcije. Višestruki URL-ovi su odvojeni vertikalnom crtom |.</translation>
+ </message>
+ <message>
+ <source>Active command-line options that override above options:</source>
+ <translation>Aktivne terminalne opcije koje poništavaju navedene opcije:</translation>
+ </message>
+ <message>
+ <source>Open the %1 configuration file from the working directory.</source>
+ <translation>Otvorite konfiguracijsku datoteku programa %1 s radne mape.</translation>
+ </message>
+ <message>
+ <source>Open Configuration File</source>
+ <translation>Otvorite konfiguracijsku datoteku</translation>
+ </message>
+ <message>
<source>Reset all client options to default.</source>
<translation>Nastavi sve postavke programa na početne vrijednosti.</translation>
</message>
@@ -752,10 +1049,42 @@
<translation>&amp;Mreža</translation>
</message>
<message>
+ <source>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</source>
+ <translation>Isključuje napredne mogućnosti ali će svi blokovi ipak biti potpuno validirani. Vraćanje na prijašnje stanje zahtijeva ponovo preuzimanje cijelog lanca blokova. Realna količina zauzetog prostora na disku može biti ponešto veća.</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Obrezujte pohranu &amp;blokova na</translation>
+ </message>
+ <message>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Vraćanje na prijašnje stanje zahtijeva ponovo preuzimanje cijelog lanca blokova.</translation>
+ </message>
+ <message>
+ <source>(0 = auto, &lt;0 = leave that many cores free)</source>
+ <translation>(0 = automatski odredite, &lt;0 = ostavite slobodno upravo toliko jezgri)</translation>
+ </message>
+ <message>
<source>W&amp;allet</source>
<translation>&amp;Novčanik</translation>
</message>
<message>
+ <source>Expert</source>
+ <translation>Stručne postavke</translation>
+ </message>
+ <message>
+ <source>Enable coin &amp;control features</source>
+ <translation>Uključite postavke kontroliranja inputa</translation>
+ </message>
+ <message>
+ <source>If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed.</source>
+ <translation>Ako isključite trošenje nepotvrđenog ostatka, ostatak transakcije ne može biti korišten dok ta transakcija ne dobije barem jednu potvrdu. Također utječe na to kako je vaše stanje računato.</translation>
+ </message>
+ <message>
<source>&amp;Spend unconfirmed change</source>
<translation>&amp;Trošenje nepotvrđenih vraćenih iznosa</translation>
</message>
@@ -768,6 +1097,22 @@
<translation>Mapiraj port koristeći &amp;UPnP</translation>
</message>
<message>
+ <source>Accept connections from outside.</source>
+ <translation>Prihvatite veze izvana.</translation>
+ </message>
+ <message>
+ <source>Allow incomin&amp;g connections</source>
+ <translation>Dozvolite dolazeće veze</translation>
+ </message>
+ <message>
+ <source>Connect to the Bitcoin network through a SOCKS5 proxy.</source>
+ <translation>Spojite se na Bitcoin mrežu kroz SOCKS5 proxy.</translation>
+ </message>
+ <message>
+ <source>&amp;Connect through SOCKS5 proxy (default proxy):</source>
+ <translation>&amp;Spojite se kroz SOCKS5 proxy (uobičajeni proxy)</translation>
+ </message>
+ <message>
<source>Proxy &amp;IP:</source>
<translation>Proxy &amp;IP:</translation>
</message>
@@ -780,6 +1125,26 @@
<translation>Proxy vrata (npr. 9050)</translation>
</message>
<message>
+ <source>Used for reaching peers via:</source>
+ <translation>Korišten za dohvaćanje klijenata preko:</translation>
+ </message>
+ <message>
+ <source>IPv4</source>
+ <translation>IPv4-a</translation>
+ </message>
+ <message>
+ <source>IPv6</source>
+ <translation>IPv6-a</translation>
+ </message>
+ <message>
+ <source>Tor</source>
+ <translation>Tora</translation>
+ </message>
+ <message>
+ <source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source>
+ <translation>Spojite se na Bitcoin mrežu kroz zaseban SOCKS5 proxy za povezivanje na Tor.</translation>
+ </message>
+ <message>
<source>&amp;Window</source>
<translation>&amp;Prozor</translation>
</message>
@@ -804,6 +1169,10 @@
<translation>Jezi&amp;k sučelja:</translation>
</message>
<message>
+ <source>The user interface language can be set here. This setting will take effect after restarting %1.</source>
+ <translation>Jezik korisničkog sučelja može se postaviti ovdje. Postavka će vrijediti nakon ponovnog pokretanja programa %1.</translation>
+ </message>
+ <message>
<source>&amp;Unit to show amounts in:</source>
<translation>&amp;Jedinica za prikaz iznosa:</translation>
</message>
@@ -812,6 +1181,14 @@
<translation>Izaberite željeni najmanji dio bitcoina koji će biti prikazan u sučelju i koji će se koristiti za plaćanje.</translation>
</message>
<message>
+ <source>Whether to show coin control features or not.</source>
+ <translation>Ovisi želite li prikazati mogućnosti kontroliranja inputa ili ne.</translation>
+ </message>
+ <message>
+ <source>&amp;Third party transaction URLs</source>
+ <translation>&amp;URL-ovi treće stranke o transakciji</translation>
+ </message>
+ <message>
<source>&amp;OK</source>
<translation>&amp;U redu</translation>
</message>
@@ -824,10 +1201,42 @@
<translation>standardne vrijednosti</translation>
</message>
<message>
+ <source>none</source>
+ <translation>ništa</translation>
+ </message>
+ <message>
+ <source>Confirm options reset</source>
+ <translation>Potvrdite resetiranje opcija</translation>
+ </message>
+ <message>
+ <source>Client restart required to activate changes.</source>
+ <translation>Potrebno je ponovno pokretanje klijenta kako bi se promjene aktivirale.</translation>
+ </message>
+ <message>
+ <source>Client will be shut down. Do you want to proceed?</source>
+ <translation>Zatvorit će se klijent. Želite li nastaviti?</translation>
+ </message>
+ <message>
+ <source>Configuration options</source>
+ <translation>Konfiguracijske postavke</translation>
+ </message>
+ <message>
+ <source>The configuration file is used to specify advanced user options which override GUI settings. Additionally, any command-line options will override this configuration file.</source>
+ <translation>Ova konfiguracijska datoteka je korištena za specificiranje napredne korisničke opcije koje će poništiti postavke GUI-a. Također će bilo koje opcije navedene preko terminala poništiti ovu konfiguracijsku datoteku.</translation>
+ </message>
+ <message>
<source>Error</source>
<translation>Greška</translation>
</message>
<message>
+ <source>The configuration file could not be opened.</source>
+ <translation>Konfiguracijska datoteka nije se mogla otvoriti.</translation>
+ </message>
+ <message>
+ <source>This change would require a client restart.</source>
+ <translation>Ova promjena zahtijeva da se klijent ponovo pokrene.</translation>
+ </message>
+ <message>
<source>The supplied proxy address is invalid.</source>
<translation>Priložena proxy adresa je nevažeća.</translation>
</message>
@@ -843,20 +1252,184 @@
<translation>Prikazani podatci mogu biti zastarjeli. Vaš novčanik se automatski sinkronizira s Bitcoin mrežom kada je veza uspostavljena, ali taj proces još nije završen.</translation>
</message>
<message>
+ <source>Watch-only:</source>
+ <translation>Isključivno promatrane adrese:</translation>
+ </message>
+ <message>
+ <source>Available:</source>
+ <translation>Dostupno:</translation>
+ </message>
+ <message>
+ <source>Your current spendable balance</source>
+ <translation>Trenutno stanje koje možete trošiti</translation>
+ </message>
+ <message>
+ <source>Pending:</source>
+ <translation>Neriješeno:</translation>
+ </message>
+ <message>
+ <source>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source>
+ <translation>Ukupan iznos transakcija koje se još moraju potvrditi te se ne računa kao stanje koje se može trošiti</translation>
+ </message>
+ <message>
+ <source>Immature:</source>
+ <translation>Nezrelo:</translation>
+ </message>
+ <message>
+ <source>Mined balance that has not yet matured</source>
+ <translation>Izrudareno stanje koje još nije dozrijevalo</translation>
+ </message>
+ <message>
+ <source>Balances</source>
+ <translation>Stanja</translation>
+ </message>
+ <message>
<source>Total:</source>
<translation>Ukupno:</translation>
</message>
- </context>
+ <message>
+ <source>Your current total balance</source>
+ <translation>Vaše trenutno svekupno stanje</translation>
+ </message>
+ <message>
+ <source>Your current balance in watch-only addresses</source>
+ <translation>Vaše trenutno stanje kod eksluzivno promatranih (watch-only) adresa</translation>
+ </message>
+ <message>
+ <source>Spendable:</source>
+ <translation>Stanje koje se može trošiti:</translation>
+ </message>
+ <message>
+ <source>Recent transactions</source>
+ <translation>Nedavne transakcije</translation>
+ </message>
+ <message>
+ <source>Unconfirmed transactions to watch-only addresses</source>
+ <translation>Nepotvrđene transakcije isključivo promatranim adresama</translation>
+ </message>
+ <message>
+ <source>Mined balance in watch-only addresses that has not yet matured</source>
+ <translation>Izrudareno stanje na isključivo promatranim adresama koje još nije dozrijevalo</translation>
+ </message>
+ <message>
+ <source>Current total balance in watch-only addresses</source>
+ <translation>Trenutno ukupno stanje na isključivo promatranim adresama</translation>
+ </message>
+</context>
<context>
<name>PaymentServer</name>
<message>
+ <source>Payment request error</source>
+ <translation>Greška kod zahtjeva za plaćanje</translation>
+ </message>
+ <message>
+ <source>Cannot start bitcoin: click-to-pay handler</source>
+ <translation>Ne može se pokrenuti klijent: rukovatelj "kliknite da platite"</translation>
+ </message>
+ <message>
<source>URI handling</source>
<translation>URI upravljanje</translation>
</message>
- </context>
+ <message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin://' nije ispravan URI. Koristite 'bitcoin:' umjesto toga.</translation>
+ </message>
+ <message>
+ <source>Payment request fetch URL is invalid: %1</source>
+ <translation>URL za dohvatu zahtjeva za plaćanje neispravan: %1</translation>
+ </message>
+ <message>
+ <source>Invalid payment address %1</source>
+ <translation>Nevažeća adresa za plaćanje %1</translation>
+ </message>
+ <message>
+ <source>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source>
+ <translation>Ne može se parsirati URI! Uzrok tomu može biti nevažeća Bitcoin adresa ili neispravni parametri kod URI-a.</translation>
+ </message>
+ <message>
+ <source>Payment request file handling</source>
+ <translation>Rukovanje datotekom zahtjeva za plaćanje</translation>
+ </message>
+ <message>
+ <source>Payment request file cannot be read! This can be caused by an invalid payment request file.</source>
+ <translation>Nije moguće iščitati datoteku zahtjeva za plaćanje! Uzrok tomu može biti nevažeća datoteka zahtjeva za plaćanje.</translation>
+ </message>
+ <message>
+ <source>Payment request rejected</source>
+ <translation>Zahtjev za plaćanje odbijen</translation>
+ </message>
+ <message>
+ <source>Payment request network doesn't match client network.</source>
+ <translation>Mreža zahtjeva za plaćanje ne poklapa se s mrežom klijenta.</translation>
+ </message>
+ <message>
+ <source>Payment request expired.</source>
+ <translation>Zahtjev za plaćanje istekao.</translation>
+ </message>
+ <message>
+ <source>Payment request is not initialized.</source>
+ <translation>Zahtjev za plaćanje nije inicijaliziran.</translation>
+ </message>
+ <message>
+ <source>Unverified payment requests to custom payment scripts are unsupported.</source>
+ <translation>Neprovjereni zahtjevi za plaćanje prilagođenim skriptima za plaćanje su nepodržani.</translation>
+ </message>
+ <message>
+ <source>Invalid payment request.</source>
+ <translation>Nevažeći zahtjev za plaćanje.</translation>
+ </message>
+ <message>
+ <source>Requested payment amount of %1 is too small (considered dust).</source>
+ <translation>Traženi iznos plaćanja %1 je premalen (smatra se "prašinom", sićušnim iznosom).</translation>
+ </message>
+ <message>
+ <source>Refund from %1</source>
+ <translation>Povrat iz %1</translation>
+ </message>
+ <message>
+ <source>Payment request %1 is too large (%2 bytes, allowed %3 bytes).</source>
+ <translation>Zahtjev za plaćanje %1 je prevelik (%2 bajt(ov)a, dozvoljeno %3 bajt(ov)a).</translation>
+ </message>
+ <message>
+ <source>Error communicating with %1: %2</source>
+ <translation>Greška kod komuniciranja s %1: %2</translation>
+ </message>
+ <message>
+ <source>Payment request cannot be parsed!</source>
+ <translation>Zahtjev za plaćanje ne može se parsirati!</translation>
+ </message>
+ <message>
+ <source>Bad response from server %1</source>
+ <translation>Neispravan odgovor sa strane servera %1</translation>
+ </message>
+ <message>
+ <source>Network request error</source>
+ <translation>Greška kod mrežnog zahtjeva</translation>
+ </message>
+ <message>
+ <source>Payment acknowledged</source>
+ <translation>Plaćanje priznato</translation>
+ </message>
+</context>
<context>
<name>PeerTableModel</name>
<message>
+ <source>User Agent</source>
+ <translation>Korisnički agent</translation>
+ </message>
+ <message>
+ <source>Node/Service</source>
+ <translation>Čvor/Servis</translation>
+ </message>
+ <message>
+ <source>NodeId</source>
+ <translation>NodeId (ID čvora)</translation>
+ </message>
+ <message>
+ <source>Ping</source>
+ <translation>Ping</translation>
+ </message>
+ <message>
<source>Sent</source>
<translation>Poslano</translation>
</message>
@@ -872,13 +1445,85 @@
<translation>Iznos</translation>
</message>
<message>
+ <source>Enter a Bitcoin address (e.g. %1)</source>
+ <translation>Unesite Bitcoin adresu (npr. %1)</translation>
+ </message>
+ <message>
+ <source>%1 d</source>
+ <translation>%1 d</translation>
+ </message>
+ <message>
+ <source>%1 h</source>
+ <translation>%1 h</translation>
+ </message>
+ <message>
+ <source>%1 m</source>
+ <translation>%1 m</translation>
+ </message>
+ <message>
+ <source>%1 s</source>
+ <translation>%1 s</translation>
+ </message>
+ <message>
+ <source>None</source>
+ <translation>Ništa</translation>
+ </message>
+ <message>
<source>N/A</source>
<translation>N/A</translation>
</message>
<message>
+ <source>%1 ms</source>
+ <translation>%1 ms</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n second(s)</source>
+ <translation><numerusform>%n sekund</numerusform><numerusform>%n sekundi</numerusform><numerusform>%n sekundi</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n minute(s)</source>
+ <translation><numerusform>%n minut</numerusform><numerusform>%n minuta</numerusform><numerusform>%n minuta</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n hour(s)</source>
+ <translation><numerusform>%n sat</numerusform><numerusform>%n sata</numerusform><numerusform>%n sati</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n day(s)</source>
+ <translation><numerusform>%n dan</numerusform><numerusform>%n dana</numerusform><numerusform>%n dana</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n week(s)</source>
+ <translation><numerusform>%n tjedan</numerusform><numerusform>%n tjedna</numerusform><numerusform>%n tjedana</numerusform></translation>
+ </message>
+ <message>
<source>%1 and %2</source>
<translation>%1 i %2</translation>
</message>
+ <message numerus="yes">
+ <source>%n year(s)</source>
+ <translation><numerusform>%n godina</numerusform><numerusform>%n godine</numerusform><numerusform>%n godina</numerusform></translation>
+ </message>
+ <message>
+ <source>%1 B</source>
+ <translation>%1 B</translation>
+ </message>
+ <message>
+ <source>%1 KB</source>
+ <translation>%1 KB</translation>
+ </message>
+ <message>
+ <source>%1 MB</source>
+ <translation>%1 MB</translation>
+ </message>
+ <message>
+ <source>%1 GB</source>
+ <translation>%1 GB</translation>
+ </message>
+ <message>
+ <source>%1 didn't yet exit safely...</source>
+ <translation>%1 se još nije sigurno zatvorio.</translation>
+ </message>
<message>
<source>unknown</source>
<translation>nepoznato</translation>
@@ -886,7 +1531,23 @@
</context>
<context>
<name>QObject::QObject</name>
- </context>
+ <message>
+ <source>Error parsing command line arguments: %1.</source>
+ <translation>Greška kod parsiranja argumenata unesnih preko terminala: %1.</translation>
+ </message>
+ <message>
+ <source>Error: Specified data directory "%1" does not exist.</source>
+ <translation>Greška: Zadana podatkovna mapa "%1" ne postoji.</translation>
+ </message>
+ <message>
+ <source>Error: Cannot parse configuration file: %1.</source>
+ <translation>Greška: Ne može se parsirati konfiguracijska datoteka: %1.</translation>
+ </message>
+ <message>
+ <source>Error: %1</source>
+ <translation>Greška: %1</translation>
+ </message>
+</context>
<context>
<name>QRImageWidget</name>
<message>
@@ -897,7 +1558,11 @@
<source>Save QR Code</source>
<translation>Spremi QR kod</translation>
</message>
- </context>
+ <message>
+ <source>PNG Image (*.png)</source>
+ <translation>PNG slika (*.png)</translation>
+ </message>
+</context>
<context>
<name>RPCConsole</name>
<message>
@@ -917,6 +1582,22 @@
<translation>Konzola za dijagnostiku</translation>
</message>
<message>
+ <source>General</source>
+ <translation>Općenito</translation>
+ </message>
+ <message>
+ <source>Using BerkeleyDB version</source>
+ <translation>Verzija BerkeleyDB-a</translation>
+ </message>
+ <message>
+ <source>Datadir</source>
+ <translation>Datadir (podatkovna mapa)</translation>
+ </message>
+ <message>
+ <source>Startup time</source>
+ <translation>Vrijeme pokretanja</translation>
+ </message>
+ <message>
<source>Network</source>
<translation>Mreža</translation>
</message>
@@ -937,6 +1618,30 @@
<translation>Trenutni broj blokova</translation>
</message>
<message>
+ <source>Memory Pool</source>
+ <translation>Memorijski bazen</translation>
+ </message>
+ <message>
+ <source>Current number of transactions</source>
+ <translation>Trenutan broj transakcija</translation>
+ </message>
+ <message>
+ <source>Memory usage</source>
+ <translation>Korištena memorija</translation>
+ </message>
+ <message>
+ <source>Wallet: </source>
+ <translation>Novčanik:</translation>
+ </message>
+ <message>
+ <source>(none)</source>
+ <translation>(ništa)</translation>
+ </message>
+ <message>
+ <source>&amp;Reset</source>
+ <translation>&amp;Resetirajte</translation>
+ </message>
+ <message>
<source>Received</source>
<translation>Primljeno</translation>
</message>
@@ -945,6 +1650,22 @@
<translation>Poslano</translation>
</message>
<message>
+ <source>&amp;Peers</source>
+ <translation>&amp;Klijenti</translation>
+ </message>
+ <message>
+ <source>Banned peers</source>
+ <translation>Zabranjeni klijenti</translation>
+ </message>
+ <message>
+ <source>Select a peer to view detailed information.</source>
+ <translation>Odaberite klijent kako biste vidjeli detaljne informacije.</translation>
+ </message>
+ <message>
+ <source>Whitelisted</source>
+ <translation>Na bijeloj listi</translation>
+ </message>
+ <message>
<source>Direction</source>
<translation>Smjer</translation>
</message>
@@ -953,10 +1674,74 @@
<translation>Verzija</translation>
</message>
<message>
+ <source>Starting Block</source>
+ <translation>Početni blok</translation>
+ </message>
+ <message>
+ <source>Synced Headers</source>
+ <translation>Broj sinkroniziranih zaglavlja</translation>
+ </message>
+ <message>
+ <source>Synced Blocks</source>
+ <translation>Broj sinkronizranih blokova</translation>
+ </message>
+ <message>
+ <source>User Agent</source>
+ <translation>Korisnički agent</translation>
+ </message>
+ <message>
+ <source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
+ <translation>Otvorite datoteku zapisa programa %1 iz trenutne podatkovne mape. Može potrajati nekoliko sekundi za velike datoteke zapisa.</translation>
+ </message>
+ <message>
+ <source>Decrease font size</source>
+ <translation>Smanjite veličinu fonta</translation>
+ </message>
+ <message>
+ <source>Increase font size</source>
+ <translation>Povećajte veličinu fonta</translation>
+ </message>
+ <message>
+ <source>Services</source>
+ <translation>Usluge</translation>
+ </message>
+ <message>
+ <source>Ban Score</source>
+ <translation>Broj zabrana</translation>
+ </message>
+ <message>
<source>Connection Time</source>
<translation>Trajanje veze</translation>
</message>
<message>
+ <source>Last Send</source>
+ <translation>Zadnja pošiljka</translation>
+ </message>
+ <message>
+ <source>Last Receive</source>
+ <translation>Zadnji primitak</translation>
+ </message>
+ <message>
+ <source>Ping Time</source>
+ <translation>Vrijeme pinga</translation>
+ </message>
+ <message>
+ <source>The duration of a currently outstanding ping.</source>
+ <translation>Trajanje trenutno izvanrednog pinga</translation>
+ </message>
+ <message>
+ <source>Ping Wait</source>
+ <translation>Zakašnjenje pinga</translation>
+ </message>
+ <message>
+ <source>Min Ping</source>
+ <translation>Min ping</translation>
+ </message>
+ <message>
+ <source>Time Offset</source>
+ <translation>Vremenski ofset</translation>
+ </message>
+ <message>
<source>Last block time</source>
<translation>Posljednje vrijeme bloka</translation>
</message>
@@ -977,10 +1762,114 @@
<translation>Ukupno:</translation>
</message>
<message>
+ <source>In:</source>
+ <translation>Dolazne:</translation>
+ </message>
+ <message>
+ <source>Out:</source>
+ <translation>Izlazne:</translation>
+ </message>
+ <message>
+ <source>Debug log file</source>
+ <translation>Datoteka ispisa za debagiranje</translation>
+ </message>
+ <message>
<source>Clear console</source>
<translation>Očisti konzolu</translation>
</message>
<message>
+ <source>1 &amp;hour</source>
+ <translation>1 &amp;sat</translation>
+ </message>
+ <message>
+ <source>1 &amp;day</source>
+ <translation>1 &amp;dan</translation>
+ </message>
+ <message>
+ <source>1 &amp;week</source>
+ <translation>1 &amp;tjedan</translation>
+ </message>
+ <message>
+ <source>1 &amp;year</source>
+ <translation>1 &amp;godinu</translation>
+ </message>
+ <message>
+ <source>&amp;Disconnect</source>
+ <translation>&amp;Odspojite</translation>
+ </message>
+ <message>
+ <source>Ban for</source>
+ <translation>Zabranite za</translation>
+ </message>
+ <message>
+ <source>&amp;Unban</source>
+ <translation>&amp;Ukinite zabranu</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>uobičajeni novčanik</translation>
+ </message>
+ <message>
+ <source>Welcome to the %1 RPC console.</source>
+ <translation>Dobrodošli u %1 RPC konzolu.</translation>
+ </message>
+ <message>
+ <source>Use up and down arrows to navigate history, and %1 to clear screen.</source>
+ <translation>Koristite tipke gore i dolje za izbor već korištenih naredbi. %1 kako biste očistili ekran i povijest naredbi.</translation>
+ </message>
+ <message>
+ <source>Type %1 for an overview of available commands.</source>
+ <translation>Utipkajte %1 za pregled dostupnih naredbi.</translation>
+ </message>
+ <message>
+ <source>For more information on using this console type %1.</source>
+ <translation>Za više informacija o korištenju ove konzole utipkajte %1.</translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation>UPOZORENJE: Prevaranti su aktivni i govore korisnicima da utipkaju naredbe ovdje kako bi ispraznili sadržaje njihovih novčanika. Ne koristite ovu konzolu bez da u potpunosti razumijete posljedice naredbe.</translation>
+ </message>
+ <message>
+ <source>Network activity disabled</source>
+ <translation>Mrežna aktivnost isključena</translation>
+ </message>
+ <message>
+ <source>Executing command without any wallet</source>
+ <translation>Izvršava se naredba bez bilo kakvog novčanika</translation>
+ </message>
+ <message>
+ <source>Executing command using "%1" wallet</source>
+ <translation>Izvršava se naredba koristeći novčanik "%1"</translation>
+ </message>
+ <message>
+ <source>(node id: %1)</source>
+ <translation>(ID čvora: %1)</translation>
+ </message>
+ <message>
+ <source>via %1</source>
+ <translation>preko %1</translation>
+ </message>
+ <message>
+ <source>never</source>
+ <translation>nikad</translation>
+ </message>
+ <message>
+ <source>Inbound</source>
+ <translation>Dolazni</translation>
+ </message>
+ <message>
+ <source>Outbound</source>
+ <translation>Izlazni</translation>
+ </message>
+ <message>
+ <source>Yes</source>
+ <translation>Da</translation>
+ </message>
+ <message>
+ <source>No</source>
+ <translation>Ne</translation>
+ </message>
+ <message>
<source>Unknown</source>
<translation>Nepoznato</translation>
</message>
@@ -1000,22 +1889,74 @@
<translation>&amp;Poruka:</translation>
</message>
<message>
+ <source>An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Bitcoin network.</source>
+ <translation>Opcionalna poruka koja se može dodati kao privitak zahtjevu za plaćanje. Bit će prikazana kad je zahtjev otvoren. Napomena: Ova poruka neće biti poslana zajedno s uplatom preko Bitcoin mreže.</translation>
+ </message>
+ <message>
+ <source>An optional label to associate with the new receiving address.</source>
+ <translation>Opcionalna oznaka koja će se povezati s novom primateljskom adresom.</translation>
+ </message>
+ <message>
+ <source>Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</source>
+ <translation>Koristite ovaj formular kako biste zahtijevali uplate. Sva su polja &lt;b&gt;opcionalna&lt;/b&gt;.</translation>
+ </message>
+ <message>
+ <source>An optional amount to request. Leave this empty or zero to not request a specific amount.</source>
+ <translation>Opcionalan iznos koji možete zahtijevati. Ostavite ovo prazno ili unesite nulu ako ne želite zahtijevati specifičan iznos.</translation>
+ </message>
+ <message>
<source>Clear all fields of the form.</source>
<translation>Obriši sva polja</translation>
</message>
<message>
+ <source>Clear</source>
+ <translation>Obrišite</translation>
+ </message>
+ <message>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <translation>Izvorne SegWit adrese (tzv. Bech32 ili BIP-173) smanjuju vaše transakcijske naknade ubuduće i nude bolju zaštitu protiv tipfelera, ali stari novčanici ih ne podržavaju. Kada je ova opcija isključena, bit će umjesto toga stvorena adresa koja je kompatibilna sa starijim novčanicima.</translation>
+ </message>
+ <message>
+ <source>Generate native segwit (Bech32) address</source>
+ <translation>Generirajte izvornu SegWit (Bech32) adresu</translation>
+ </message>
+ <message>
+ <source>Requested payments history</source>
+ <translation>Povijest zahtjeva za plaćanje</translation>
+ </message>
+ <message>
<source>&amp;Request payment</source>
<translation>&amp;Zatraži plaćanje</translation>
</message>
<message>
+ <source>Show the selected request (does the same as double clicking an entry)</source>
+ <translation>Prikazuje izabran zahtjev (isto učini dvostruki klik na zapis)</translation>
+ </message>
+ <message>
<source>Show</source>
<translation>Pokaži</translation>
</message>
<message>
+ <source>Remove the selected entries from the list</source>
+ <translation>Uklonite odabrane zapise s popisa</translation>
+ </message>
+ <message>
+ <source>Remove</source>
+ <translation>Uklonite</translation>
+ </message>
+ <message>
+ <source>Copy URI</source>
+ <translation>Kopirajte URI</translation>
+ </message>
+ <message>
<source>Copy label</source>
<translation>Kopiraj oznaku</translation>
</message>
<message>
+ <source>Copy message</source>
+ <translation>Kopirajte poruku</translation>
+ </message>
+ <message>
<source>Copy amount</source>
<translation>Kopiraj iznos</translation>
</message>
@@ -1039,6 +1980,14 @@
<translation>&amp;Spremi sliku...</translation>
</message>
<message>
+ <source>Request payment to %1</source>
+ <translation>&amp;Zatražite plaćanje na adresu %1</translation>
+ </message>
+ <message>
+ <source>Payment information</source>
+ <translation>Informacije o uplati</translation>
+ </message>
+ <message>
<source>URI</source>
<translation>URI</translation>
</message>
@@ -1093,7 +2042,15 @@
<source>(no message)</source>
<translation>(bez poruke)</translation>
</message>
- </context>
+ <message>
+ <source>(no amount requested)</source>
+ <translation>(nikakav iznos zahtijevan)</translation>
+ </message>
+ <message>
+ <source>Requested</source>
+ <translation>Zatraženo</translation>
+ </message>
+</context>
<context>
<name>SendCoinsDialog</name>
<message>
@@ -1101,6 +2058,18 @@
<translation>Slanje novca</translation>
</message>
<message>
+ <source>Coin Control Features</source>
+ <translation>Mogućnosti kontroliranja inputa</translation>
+ </message>
+ <message>
+ <source>Inputs...</source>
+ <translation>Inputi...</translation>
+ </message>
+ <message>
+ <source>automatically selected</source>
+ <translation>automatski izabrano</translation>
+ </message>
+ <message>
<source>Insufficient funds!</source>
<translation>Nedovoljna sredstva</translation>
</message>
@@ -1121,14 +2090,78 @@
<translation>Naknada:</translation>
</message>
<message>
+ <source>After Fee:</source>
+ <translation>Nakon naknade:</translation>
+ </message>
+ <message>
<source>Change:</source>
<translation>Vraćeno:</translation>
</message>
<message>
+ <source>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source>
+ <translation>Ako je ovo aktivirano, ali adresa u koju treba poslati ostatak je prazna ili nevažeća, onda će ostatak biti poslan u novo generiranu adresu.</translation>
+ </message>
+ <message>
+ <source>Custom change address</source>
+ <translation>Zadana adresa u koju će ostatak biti poslan</translation>
+ </message>
+ <message>
<source>Transaction Fee:</source>
<translation>Naknada za transakciju:</translation>
</message>
<message>
+ <source>Choose...</source>
+ <translation>Birajte...</translation>
+ </message>
+ <message>
+ <source>Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until you have validated the complete chain.</source>
+ <translation>Korištenje rezervnu naknadu može rezultirati slanjem transakcije kojoj može trebati nekoliko sati ili dana (ili pak nikad) da se potvrdi. Uzmite u obzir ručno biranje naknade ili pričekajte da se cijeli lanac validira.</translation>
+ </message>
+ <message>
+ <source>Warning: Fee estimation is currently not possible.</source>
+ <translation>Upozorenje: Procjena naknada trenutno nije moguća.</translation>
+ </message>
+ <message>
+ <source>collapse fee-settings</source>
+ <translation>Sažimajte opcije naknade</translation>
+ </message>
+ <message>
+ <source>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</source>
+ <translation>Zadajte prilagođeu naknadu po kB (1000 bajtova) virtualne veličine transakcije.
+
+Napomena: Budući da se naknada računa po bajtu, naknada od "100 satošija po kB" za transakciju veličine 500 bajtova (polovica od 1 kB) rezultirala bi ultimativno naknadom od samo 50 satošija.</translation>
+ </message>
+ <message>
+ <source>per kilobyte</source>
+ <translation>po kilobajtu</translation>
+ </message>
+ <message>
+ <source>Hide</source>
+ <translation>Sakrijte</translation>
+ </message>
+ <message>
+ <source>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
+ <translation>Plaćanje minimalnu naknadu je dovoljno ukoliko je volumen transakcija manja od prostora u blokovima. Budite svjesni da ovo može završiti tako da se transakcija nikad ne potvrdi ako je potražnja za Bitcoin transakcijama veća nego što mreža može procesirati.</translation>
+ </message>
+ <message>
+ <source>(read the tooltip)</source>
+ <translation>(pročitajte opis alata)</translation>
+ </message>
+ <message>
+ <source>Recommended:</source>
+ <translation>Preporučeno:</translation>
+ </message>
+ <message>
+ <source>Custom:</source>
+ <translation>Zadano:</translation>
+ </message>
+ <message>
+ <source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
+ <translation>(Pametna procjena naknada još nije inicijalizirana. Uobičajeno traje nekoliko blokova...)</translation>
+ </message>
+ <message>
<source>Send to multiple recipients at once</source>
<translation>Pošalji novce većem broju primatelja u jednoj transakciji</translation>
</message>
@@ -1145,6 +2178,18 @@
<translation>Prah:</translation>
</message>
<message>
+ <source>Confirmation time target:</source>
+ <translation>Ciljno vrijeme potvrde:</translation>
+ </message>
+ <message>
+ <source>Enable Replace-By-Fee</source>
+ <translation>Uključite Replace-By-Fee</translation>
+ </message>
+ <message>
+ <source>With Replace-By-Fee (BIP-125) you can increase a transaction's fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</source>
+ <translation>Pomoću mogućnosti Replace-By-Fee (BIP-125) možete povećati naknadu transakcije nakon što je poslana. Bez ovoga može biti preporučena veća naknada kako bi nadoknadila povećani rizik zakašnjenja transakcije.</translation>
+ </message>
+ <message>
<source>Clear &amp;All</source>
<translation>Obriši &amp;sve</translation>
</message>
@@ -1161,22 +2206,82 @@
<translation>&amp;Pošalji</translation>
</message>
<message>
+ <source>Copy quantity</source>
+ <translation>Kopiraj iznos</translation>
+ </message>
+ <message>
<source>Copy amount</source>
<translation>Kopiraj iznos</translation>
</message>
<message>
+ <source>Copy fee</source>
+ <translation>Kopirajte naknadu</translation>
+ </message>
+ <message>
+ <source>Copy after fee</source>
+ <translation>Kopirajte iznos nakon naknade</translation>
+ </message>
+ <message>
+ <source>Copy bytes</source>
+ <translation>Kopirajte količinu bajtova</translation>
+ </message>
+ <message>
+ <source>Copy dust</source>
+ <translation>Kopirajte sićušne iznose ("prašinu")</translation>
+ </message>
+ <message>
+ <source>Copy change</source>
+ <translation>Kopirajte ostatak</translation>
+ </message>
+ <message>
+ <source>%1 (%2 blocks)</source>
+ <translation>%1 (%2 blokova)</translation>
+ </message>
+ <message>
+ <source>%1 to %2</source>
+ <translation>%1 na %2</translation>
+ </message>
+ <message>
+ <source>Are you sure you want to send?</source>
+ <translation>Jeste li sigurni da želite poslati transakciju?</translation>
+ </message>
+ <message>
<source>or</source>
<translation>ili</translation>
</message>
<message>
+ <source>You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
+ <translation>Možete kasnije povećati naknadu (javlja Replace-By-Fee, BIP-125).</translation>
+ </message>
+ <message>
+ <source>from wallet %1</source>
+ <translation>iz novčanika %1</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction.</source>
+ <translation>Molim vas, pregledajte svoju transakciju.</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>Naknada za transakciju</translation>
</message>
<message>
+ <source>Not signalling Replace-By-Fee, BIP-125.</source>
+ <translation>Ne javlja Replace-By-Fee, BIP-125.</translation>
+ </message>
+ <message>
+ <source>Total Amount</source>
+ <translation>Ukupni iznos</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>Potvrdi slanje novca</translation>
</message>
<message>
+ <source>The recipient address is not valid. Please recheck.</source>
+ <translation>Adresa primatelja je nevažeća. Provjerite ponovno, molim vas.</translation>
+ </message>
+ <message>
<source>The amount to pay must be larger than 0.</source>
<translation>Iznos mora biti veći od 0.</translation>
</message>
@@ -1189,6 +2294,50 @@
<translation>Iznos je veći od stanja novčanika kad se doda naknada za transakcije od %1.</translation>
</message>
<message>
+ <source>Duplicate address found: addresses should only be used once each.</source>
+ <translation>Duplikatna adresa pronađena: adrese trebaju biti korištene samo jedanput.</translation>
+ </message>
+ <message>
+ <source>Transaction creation failed!</source>
+ <translation>Neuspješno stvorenje transakcije!</translation>
+ </message>
+ <message>
+ <source>The transaction was rejected with the following reason: %1</source>
+ <translation>Transakcija je bila odbijena zbog sljedećeg razloga: %1</translation>
+ </message>
+ <message>
+ <source>A fee higher than %1 is considered an absurdly high fee.</source>
+ <translation>Naknada veća od %1 smatra se apsurdno visokim naknadom.</translation>
+ </message>
+ <message>
+ <source>Payment request expired.</source>
+ <translation>Zahtjev za plaćanje istekao.</translation>
+ </message>
+ <message>
+ <source>Pay only the required fee of %1</source>
+ <translation>Platite samo potrebnu naknadu u iznosu od %1</translation>
+ </message>
+ <message numerus="yes">
+ <source>Estimated to begin confirmation within %n block(s).</source>
+ <translation><numerusform>Procijenjeno je da će početi potvrđivanje unutar %n bloka.</numerusform><numerusform>Procijenjeno je da će početi potvrđivanje unutar %n bloka.</numerusform><numerusform>Procijenjeno je da će početi potvrđivanje unutar %n blokova.</numerusform></translation>
+ </message>
+ <message>
+ <source>Warning: Invalid Bitcoin address</source>
+ <translation>Upozorenje: Nevažeća Bitcoin adresa</translation>
+ </message>
+ <message>
+ <source>Warning: Unknown change address</source>
+ <translation>Upozorenje: Nepoznata adresa u koju će ostatak biti poslan</translation>
+ </message>
+ <message>
+ <source>Confirm custom change address</source>
+ <translation>Potvrdite zadanu adresu u koju će ostatak biti poslan</translation>
+ </message>
+ <message>
+ <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source>
+ <translation>Adresa koju ste izabrali kamo ćete poslati ostatak nije dio ovog novčanika. Bilo koji iznosi u vašem novčaniku mogu biti poslani na ovu adresu. Jeste li sigurni?</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(nema oznake)</translation>
</message>
@@ -1208,6 +2357,18 @@
<translation>&amp;Oznaka:</translation>
</message>
<message>
+ <source>Choose previously used address</source>
+ <translation>Odaberite prethodno korištenu adresu</translation>
+ </message>
+ <message>
+ <source>This is a normal payment.</source>
+ <translation>Ovo je normalna uplata.</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address to send the payment to</source>
+ <translation>Bitcoin adresa na koju ćete poslati uplatu</translation>
+ </message>
+ <message>
<source>Alt+A</source>
<translation>Alt+A</translation>
</message>
@@ -1220,31 +2381,95 @@
<translation>Alt+P</translation>
</message>
<message>
+ <source>Remove this entry</source>
+ <translation>Obrišite ovaj zapis</translation>
+ </message>
+ <message>
+ <source>The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</source>
+ <translation>Naknada će biti oduzeta od poslanog iznosa. Primatelj će primiti manji iznos od onoga koji unesete u polje iznosa. Ako je odabrano više primatelja, onda će naknada biti podjednako raspodijeljena.</translation>
+ </message>
+ <message>
+ <source>S&amp;ubtract fee from amount</source>
+ <translation>Oduzmite naknadu od iznosa</translation>
+ </message>
+ <message>
+ <source>Use available balance</source>
+ <translation>Koristite dostupno stanje</translation>
+ </message>
+ <message>
<source>Message:</source>
<translation>Poruka:</translation>
</message>
<message>
+ <source>This is an unauthenticated payment request.</source>
+ <translation>Ovo je neautenticiran zahtjev za plaćanje.</translation>
+ </message>
+ <message>
+ <source>This is an authenticated payment request.</source>
+ <translation>Ovo je autenticiran zahtjev za plaćanje.</translation>
+ </message>
+ <message>
+ <source>Enter a label for this address to add it to the list of used addresses</source>
+ <translation>Unesite oznaku za ovu adresu kako bi ju dodali u vaš adresar</translation>
+ </message>
+ <message>
+ <source>A message that was attached to the bitcoin: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Bitcoin network.</source>
+ <translation>Poruka koja je dodana uplati: URI koji će biti spremljen s transakcijom za referencu. Napomena: Ova poruka neće biti poslana preko Bitcoin mreže.</translation>
+ </message>
+ <message>
<source>Pay To:</source>
<translation>Primatelj plaćanja:</translation>
</message>
<message>
+ <source>Memo:</source>
+ <translation>Zapis:</translation>
+ </message>
+ <message>
<source>Enter a label for this address to add it to your address book</source>
<translation>Unesite oznaku za ovu adresu kako bi ju dodali u vaš adresar</translation>
</message>
</context>
<context>
<name>SendConfirmationDialog</name>
- </context>
+ <message>
+ <source>Yes</source>
+ <translation>Da</translation>
+ </message>
+</context>
<context>
<name>ShutdownWindow</name>
- </context>
+ <message>
+ <source>%1 is shutting down...</source>
+ <translation>Zatvara se %1...</translation>
+ </message>
+ <message>
+ <source>Do not shut down the computer until this window disappears.</source>
+ <translation>Ne ugasite računalo dok ovaj prozor ne nestane.</translation>
+ </message>
+</context>
<context>
<name>SignVerifyMessageDialog</name>
<message>
+ <source>Signatures - Sign / Verify a Message</source>
+ <translation>Potpisi - Potpisujte / Provjerite poruku</translation>
+ </message>
+ <message>
<source>&amp;Sign Message</source>
<translation>&amp;Potpišite poruku</translation>
</message>
<message>
+ <source>You can sign messages/agreements with your addresses to prove you can receive bitcoins sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</source>
+ <translation>Možete potpisati poruke/dogovore svojim adresama kako biste dokazali da možete pristupiti bitcoinima poslanim na te adrese. Budite oprezni da ne potpisujte ništa nejasno ili nasumično, jer napadi phishingom vas mogu prevariti da prepišite svoj identitet njima. Potpisujte samo detaljno objašnjene izjave s kojima se slažete.</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address to sign the message with</source>
+ <translation>Bitcoin adresa pomoću koje ćete potpisati poruku</translation>
+ </message>
+ <message>
+ <source>Choose previously used address</source>
+ <translation>Odaberite prethodno korištenu adresu</translation>
+ </message>
+ <message>
<source>Alt+A</source>
<translation>Alt+A</translation>
</message>
@@ -1265,10 +2490,22 @@
<translation>Potpis</translation>
</message>
<message>
+ <source>Copy the current signature to the system clipboard</source>
+ <translation>Kopirajte trenutni potpis u međuspremnik</translation>
+ </message>
+ <message>
+ <source>Sign the message to prove you own this Bitcoin address</source>
+ <translation>Potpišite poruku kako biste dokazali da posjedujete ovu Bitcoin adresu</translation>
+ </message>
+ <message>
<source>Sign &amp;Message</source>
<translation>&amp;Potpišite poruku</translation>
</message>
<message>
+ <source>Reset all sign message fields</source>
+ <translation>Resetirajte sva polja formulara</translation>
+ </message>
+ <message>
<source>Clear &amp;All</source>
<translation>Obriši &amp;sve</translation>
</message>
@@ -1277,18 +2514,78 @@
<translation>&amp;Potvrdite poruku</translation>
</message>
<message>
+ <source>Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</source>
+ <translation>Unesite primateljevu adresu, poruku (provjerite da kopirate prekide crta, razmake, tabove, itd. točno) i potpis ispod da provjerite poruku. Pazite da ne pridodate veće značenje potpisu nego što je sadržano u samoj poruci kako biste izbjegli napad posrednika (MITM attack). Primijetite da ovo samo dokazuje da stranka koja potpisuje prima na adresu. Ne može dokažati da je neka stranka poslala transakciju!</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address the message was signed with</source>
+ <translation>Bitcoin adresa kojom je poruka potpisana</translation>
+ </message>
+ <message>
+ <source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
+ <translation>Provjerite poruku da budete sigurni da je potpisana zadanom Bitcoin adresom</translation>
+ </message>
+ <message>
<source>Verify &amp;Message</source>
<translation>&amp;Potvrdite poruku</translation>
</message>
<message>
+ <source>Reset all verify message fields</source>
+ <translation>Resetirajte sva polja provjeravanja poruke</translation>
+ </message>
+ <message>
+ <source>Click "Sign Message" to generate signature</source>
+ <translation>Kliknite "Potpišite poruku" da generirate potpis</translation>
+ </message>
+ <message>
+ <source>The entered address is invalid.</source>
+ <translation>Unesena adresa je neispravna.</translation>
+ </message>
+ <message>
+ <source>Please check the address and try again.</source>
+ <translation>Molim provjerite adresu i pokušajte ponovo.</translation>
+ </message>
+ <message>
+ <source>The entered address does not refer to a key.</source>
+ <translation>Unesena adresa ne odnosi se na ključ.</translation>
+ </message>
+ <message>
<source>Wallet unlock was cancelled.</source>
<translation>Otključavanje novčanika je otkazano.</translation>
</message>
<message>
+ <source>Private key for the entered address is not available.</source>
+ <translation>Privatni ključ za unesenu adresu nije dostupan.</translation>
+ </message>
+ <message>
+ <source>Message signing failed.</source>
+ <translation>Potpisivanje poruke neuspješno.</translation>
+ </message>
+ <message>
<source>Message signed.</source>
<translation>Poruka je potpisana.</translation>
</message>
- </context>
+ <message>
+ <source>The signature could not be decoded.</source>
+ <translation>Potpis nije mogao biti dešifriran.</translation>
+ </message>
+ <message>
+ <source>Please check the signature and try again.</source>
+ <translation>Molim provjerite potpis i pokušajte ponovo.</translation>
+ </message>
+ <message>
+ <source>The signature did not match the message digest.</source>
+ <translation>Potpis se ne poklapa sa sažetkom poruke (message digest).</translation>
+ </message>
+ <message>
+ <source>Message verification failed.</source>
+ <translation>Provjera poruke neuspješna.</translation>
+ </message>
+ <message>
+ <source>Message verified.</source>
+ <translation>Poruka provjerena.</translation>
+ </message>
+</context>
<context>
<name>SplashScreen</name>
<message>
@@ -1298,14 +2595,42 @@
</context>
<context>
<name>TrafficGraphWidget</name>
- </context>
+ <message>
+ <source>KB/s</source>
+ <translation>KB/s</translation>
+ </message>
+</context>
<context>
<name>TransactionDesc</name>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Otvoren za još %n blok</numerusform><numerusform>Otvoren za još %n bloka</numerusform><numerusform>Otvoren za još %n blokova</numerusform></translation>
+ </message>
<message>
<source>Open until %1</source>
<translation>Otvoren do %1</translation>
</message>
<message>
+ <source>conflicted with a transaction with %1 confirmations</source>
+ <translation>subokljen s transakcijom broja potvrde %1</translation>
+ </message>
+ <message>
+ <source>0/unconfirmed, %1</source>
+ <translation>0/nepotvrđeno, %1</translation>
+ </message>
+ <message>
+ <source>in memory pool</source>
+ <translation>u memorijskom bazenu</translation>
+ </message>
+ <message>
+ <source>not in memory pool</source>
+ <translation>nije u memorijskom bazenu</translation>
+ </message>
+ <message>
+ <source>abandoned</source>
+ <translation>napušteno</translation>
+ </message>
+ <message>
<source>%1/unconfirmed</source>
<translation>%1/nepotvrđeno</translation>
</message>
@@ -1346,6 +2671,10 @@
<translation>vlastita adresa</translation>
</message>
<message>
+ <source>watch-only</source>
+ <translation>isključivo promatrano</translation>
+ </message>
+ <message>
<source>label</source>
<translation>oznaka</translation>
</message>
@@ -1353,6 +2682,10 @@
<source>Credit</source>
<translation>Uplaćeno</translation>
</message>
+ <message numerus="yes">
+ <source>matures in %n more block(s)</source>
+ <translation><numerusform>dozrije za još %n blok</numerusform><numerusform>dozrije za još %n bloka</numerusform><numerusform>dozrije za još %n blokova</numerusform></translation>
+ </message>
<message>
<source>not accepted</source>
<translation>Nije prihvaćeno</translation>
@@ -1362,6 +2695,14 @@
<translation>Zaduženje</translation>
</message>
<message>
+ <source>Total debit</source>
+ <translation>Ukupni debit</translation>
+ </message>
+ <message>
+ <source>Total credit</source>
+ <translation>Ukupni kredit</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>Naknada za transakciju</translation>
</message>
@@ -1382,6 +2723,30 @@
<translation>ID transakcije</translation>
</message>
<message>
+ <source>Transaction total size</source>
+ <translation>Ukupna veličina transakcije</translation>
+ </message>
+ <message>
+ <source>Transaction virtual size</source>
+ <translation>Virtualna veličina transakcije</translation>
+ </message>
+ <message>
+ <source>Output index</source>
+ <translation>Indeks outputa</translation>
+ </message>
+ <message>
+ <source>Merchant</source>
+ <translation>Trgovac</translation>
+ </message>
+ <message>
+ <source>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
+ <translation>Generirani novčići moraju dozrijeti %1 blokova prije nego što mogu biti potrošeni. Kada ste generirali ovaj blok, bio je emitiran na mreži kako bi bio dodan lancu blokova. Ako ne uspije ući u lanac, stanje će mu promijeniti na "neprihvaćeno" i neće se moći trošiti. Ovo se može dogoditi povremeno ako drugi čvor generira blok u roku od nekoliko sekundi od vas.</translation>
+ </message>
+ <message>
+ <source>Debug information</source>
+ <translation>Informacije za debugiranje</translation>
+ </message>
+ <message>
<source>Transaction</source>
<translation>Transakcija</translation>
</message>
@@ -1393,14 +2758,26 @@
<source>Amount</source>
<translation>Iznos</translation>
</message>
- </context>
+ <message>
+ <source>true</source>
+ <translation>istina</translation>
+ </message>
+ <message>
+ <source>false</source>
+ <translation>laž</translation>
+ </message>
+</context>
<context>
<name>TransactionDescDialog</name>
<message>
<source>This pane shows a detailed description of the transaction</source>
<translation>Ovaj prozor prikazuje detaljni opis transakcije</translation>
</message>
- </context>
+ <message>
+ <source>Details for %1</source>
+ <translation>Detalji za %1</translation>
+ </message>
+</context>
<context>
<name>TransactionTableModel</name>
<message>
@@ -1415,15 +2792,39 @@
<source>Label</source>
<translation>Oznaka</translation>
</message>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Otvoren za još %n blok</numerusform><numerusform>Otvoren za još %n bloka</numerusform><numerusform>Otvoren za još %n blokova</numerusform></translation>
+ </message>
<message>
<source>Open until %1</source>
<translation>Otvoren do %1</translation>
</message>
<message>
+ <source>Unconfirmed</source>
+ <translation>Nepotvrđeno</translation>
+ </message>
+ <message>
+ <source>Abandoned</source>
+ <translation>Napušteno</translation>
+ </message>
+ <message>
+ <source>Confirming (%1 of %2 recommended confirmations)</source>
+ <translation>Potvrđuje se (%1 od %2 preporučenih potvrda)</translation>
+ </message>
+ <message>
<source>Confirmed (%1 confirmations)</source>
<translation>Potvrđen (%1 potvrda)</translation>
</message>
<message>
+ <source>Conflicted</source>
+ <translation>Sukobljeno</translation>
+ </message>
+ <message>
+ <source>Immature (%1 confirmations, will be available after %2)</source>
+ <translation>Nezrelo (%1 potvrda/e, bit će dostupno nakon %2)</translation>
+ </message>
+ <message>
<source>Generated but not accepted</source>
<translation>Generirano, ali nije prihvaćeno</translation>
</message>
@@ -1448,6 +2849,10 @@
<translation>Rudareno</translation>
</message>
<message>
+ <source>watch-only</source>
+ <translation>isključivo promatrano</translation>
+ </message>
+ <message>
<source>(n/a)</source>
<translation>(n/d)</translation>
</message>
@@ -1468,6 +2873,14 @@
<translation>Vrsta transakcije.</translation>
</message>
<message>
+ <source>Whether or not a watch-only address is involved in this transaction.</source>
+ <translation>Ovisi je li isključivo promatrana adresa povezana s ovom transakcijom ili ne.</translation>
+ </message>
+ <message>
+ <source>User-defined intent/purpose of the transaction.</source>
+ <translation>Korisničko definirana namjera transakcije.</translation>
+ </message>
+ <message>
<source>Amount removed from or added to balance.</source>
<translation>Iznos odbijen od ili dodan k saldu.</translation>
</message>
@@ -1523,10 +2936,22 @@
<translation>Ostalo</translation>
</message>
<message>
+ <source>Enter address, transaction id, or label to search</source>
+ <translation>Unesite adresu, ID transakcije ili oznaku za pretragu</translation>
+ </message>
+ <message>
<source>Min amount</source>
<translation>Min iznos</translation>
</message>
<message>
+ <source>Abandon transaction</source>
+ <translation>Napustite transakciju</translation>
+ </message>
+ <message>
+ <source>Increase transaction fee</source>
+ <translation>Povećajte transakcijsku naknadu</translation>
+ </message>
+ <message>
<source>Copy address</source>
<translation>Kopiraj adresu</translation>
</message>
@@ -1543,6 +2968,14 @@
<translation>Kopiraj ID transakcije</translation>
</message>
<message>
+ <source>Copy raw transaction</source>
+ <translation>Kopirajte sirovu transakciju</translation>
+ </message>
+ <message>
+ <source>Copy full transaction details</source>
+ <translation>Kopirajte potpune transakcijske detalje</translation>
+ </message>
+ <message>
<source>Edit label</source>
<translation>Izmjeni oznaku</translation>
</message>
@@ -1551,6 +2984,10 @@
<translation>Prikaži detalje transakcije</translation>
</message>
<message>
+ <source>Export Transaction History</source>
+ <translation>Izvozite povijest transakcija</translation>
+ </message>
+ <message>
<source>Comma separated file (*.csv)</source>
<translation>Datoteka podataka odvojenih zarezima (*.csv)</translation>
</message>
@@ -1559,6 +2996,10 @@
<translation>Potvrđeno</translation>
</message>
<message>
+ <source>Watch-only</source>
+ <translation>Isključivo promatrano</translation>
+ </message>
+ <message>
<source>Date</source>
<translation>Datum</translation>
</message>
@@ -1583,6 +3024,18 @@
<translation>Izvoz neuspješan</translation>
</message>
<message>
+ <source>There was an error trying to save the transaction history to %1.</source>
+ <translation>Nastala je greška pokušavajući snimiti povijest transakcija na %1.</translation>
+ </message>
+ <message>
+ <source>Exporting Successful</source>
+ <translation>Izvoz uspješan</translation>
+ </message>
+ <message>
+ <source>The transaction history was successfully saved to %1.</source>
+ <translation>Povijest transakcija je bila uspješno snimljena na %1.</translation>
+ </message>
+ <message>
<source>Range:</source>
<translation>Raspon:</translation>
</message>
@@ -1593,17 +3046,61 @@
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
- </context>
+ <message>
+ <source>Unit to show amounts in. Click to select another unit.</source>
+ <translation>Jedinica u kojoj ćete prikazati iznose. Kliknite da izabrate drugu jedinicu.</translation>
+ </message>
+</context>
<context>
<name>WalletFrame</name>
- </context>
+ <message>
+ <source>No wallet has been loaded.</source>
+ <translation>Nije pokrenut nikakav novčanik.</translation>
+ </message>
+</context>
<context>
<name>WalletModel</name>
<message>
<source>Send Coins</source>
<translation>Slanje novca</translation>
</message>
- </context>
+ <message>
+ <source>Fee bump error</source>
+ <translation>Greška kod povećanja naknade</translation>
+ </message>
+ <message>
+ <source>Increasing transaction fee failed</source>
+ <translation>Povećavanje transakcijske naknade neuspješno</translation>
+ </message>
+ <message>
+ <source>Do you want to increase the fee?</source>
+ <translation>Želite li povećati naknadu?</translation>
+ </message>
+ <message>
+ <source>Current fee:</source>
+ <translation>Trenutna naknada:</translation>
+ </message>
+ <message>
+ <source>Increase:</source>
+ <translation>Povećanje:</translation>
+ </message>
+ <message>
+ <source>New fee:</source>
+ <translation>Nova naknada:</translation>
+ </message>
+ <message>
+ <source>Confirm fee bump</source>
+ <translation>Potvrdite povećanje naknade</translation>
+ </message>
+ <message>
+ <source>Can't sign transaction.</source>
+ <translation>Transakcija ne može biti potpisana.</translation>
+ </message>
+ <message>
+ <source>Could not commit transaction</source>
+ <translation>Transakcija ne može biti izvršena.</translation>
+ </message>
+</context>
<context>
<name>WalletView</name>
<message>
@@ -1626,30 +3123,522 @@
<source>Backup Failed</source>
<translation>Arhiviranje nije uspjelo</translation>
</message>
- </context>
+ <message>
+ <source>There was an error trying to save the wallet data to %1.</source>
+ <translation>Nastala je greška pokušavajući snimiti podatke novčanika na %1.</translation>
+ </message>
+ <message>
+ <source>Backup Successful</source>
+ <translation>Sigurnosna kopija uspješna</translation>
+ </message>
+ <message>
+ <source>The wallet data was successfully saved to %1.</source>
+ <translation>Podaci novčanika su bili uspješno snimljeni na %1.</translation>
+ </message>
+ <message>
+ <source>Cancel</source>
+ <translation>Odustanite</translation>
+ </message>
+</context>
<context>
<name>bitcoin-core</name>
<message>
+ <source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
+ <translation>Distribuirano pod MIT licencom softvera. Vidite pripadajuću datoteku %s ili %s.</translation>
+ </message>
+ <message>
+ <source>Prune configured below the minimum of %d MiB. Please use a higher number.</source>
+ <translation>Obrezivanje postavljeno ispod minimuma od %d MiB. Molim koristite veći broj.</translation>
+ </message>
+ <message>
+ <source>Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</source>
+ <translation>Obrezivanje: zadnja sinkronizacija novčanika ide dalje od obrezivanih podataka. Morate koristiti -reindex (ponovo preuzeti cijeli lanac blokova u slučaju obrezivanog čvora)</translation>
+ </message>
+ <message>
+ <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source>
+ <translation>Ponovno skeniranje nije moguće u obrezanim načinu (pruned mode). Morat ćete koristiti -reindex, što će ponovno preuzeti cijeli lanac blokova.</translation>
+ </message>
+ <message>
+ <source>Error: A fatal internal error occurred, see debug.log for details</source>
+ <translation>Greška: Dogodila se kobna interna greška. Vidite debug.log za detalje</translation>
+ </message>
+ <message>
+ <source>Pruning blockstore...</source>
+ <translation>Obrezuje se blockstore...</translation>
+ </message>
+ <message>
+ <source>Unable to start HTTP server. See debug log for details.</source>
+ <translation>Ne može se pokrenuti HTTP server. Vidite debug.log za više detalja.</translation>
+ </message>
+ <message>
<source>Bitcoin Core</source>
<translation>Bitcoin Core</translation>
</message>
<message>
+ <source>The %s developers</source>
+ <translation>Ekipa %s</translation>
+ </message>
+ <message>
+ <source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
+ <translation>Program ne može pristupiti podatkovnoj mapi %s. %s je vjerojatno već pokrenut.</translation>
+ </message>
+ <message>
+ <source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
+ <translation>Ne može ponuditi specifične veze i dati addrman da traži izlazne veze istovremeno.</translation>
+ </message>
+ <message>
+ <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
+ <translation>Greška kod iščitanja %s! Svi ključevi su ispravno učitani, ali transakcijski podaci ili zapisi u adresaru mogu biti nepotpuni ili netočni.</translation>
+ </message>
+ <message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>Grupirajte outpute po adresi, birajući sve ili ništa umjesto biranja po outputu. Privatnost je povećana jer je adresa korištena samo jedanput (osim ako netko šalje na tu adresu nakon trošenja iz nje), ali može rezultirati neznatno većim naknadama jer suboptimalno biranje novčića može nastati zbog dodanog ograničenja (uobičajeno: %u)</translation>
+ </message>
+ <message>
+ <source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
+ <translation>Molimo provjerite jesu li datum i vrijeme na vašem računalu točni. Ako je vaš sat krivo namješten, %s neće raditi ispravno.</translation>
+ </message>
+ <message>
+ <source>Please contribute if you find %s useful. Visit %s for further information about the software.</source>
+ <translation>Molimo vas da doprinijete programu %s ako ga smatrate korisnim. Posjetite %s za više informacija.</translation>
+ </message>
+ <message>
+ <source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source>
+ <translation>Baza blokova sadrži blok koji je naizgled iz budućnosti. Može to biti posljedica krivo namještenog datuma i vremena na vašem računalu. Obnovite bazu blokova samo ako ste sigurni da su točni datum i vrijeme na vašem računalu.</translation>
+ </message>
+ <message>
+ <source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
+ <translation>Ovo je eksperimentalna verzija za testiranje - koristite je na vlastitu odgovornost - ne koristite je za rudarenje ili trgovačke primjene</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
+ <translation>Ovo je transakcijska naknada koju možete odbaciti ako je ostatak manji od "prašine" (sićušnih iznosa) po ovoj stopi</translation>
+ </message>
+ <message>
+ <source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
+ <translation>Ne mogu se ponovo odigrati blokovi. Morat ćete ponovo složiti bazu koristeći -reindex-chainstate.</translation>
+ </message>
+ <message>
+ <source>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</source>
+ <translation>Baza se ne može povratiti na stanje prije raskola. Morat ćete ponovno preuzeti lanac blokova</translation>
+ </message>
+ <message>
+ <source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source>
+ <translation>Upozorenje: Čini se da se mreža ne slaže u potpunosti! Izgleda da su neki rudari suočeni s poteškoćama.</translation>
+ </message>
+ <message>
+ <source>Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source>
+ <translation>Upozorenje: Izgleda da se ne slažemo u potpunosti s našim klijentima! Možda ćete se vi ili ostali čvorovi morati ažurirati.</translation>
+ </message>
+ <message>
+ <source>%d of last 100 blocks have unexpected version</source>
+ <translation>%d od zadnjih 100 blokova ima neočekivanu verziju</translation>
+ </message>
+ <message>
+ <source>%s corrupt, salvage failed</source>
+ <translation>%s pokvaren, spašavanje neuspješno</translation>
+ </message>
+ <message>
+ <source>-maxmempool must be at least %d MB</source>
+ <translation>-maxmempool mora biti barem %d MB</translation>
+ </message>
+ <message>
+ <source>Cannot resolve -%s address: '%s'</source>
+ <translation>Ne može se razriješiti adresa -%s: '%s'</translation>
+ </message>
+ <message>
+ <source>Change index out of range</source>
+ <translation>Indeks ostatka izvan dosega</translation>
+ </message>
+ <message>
+ <source>Copyright (C) %i-%i</source>
+ <translation>Copyright (C) %i-%i</translation>
+ </message>
+ <message>
+ <source>Corrupted block database detected</source>
+ <translation>Pokvarena baza blokova otkrivena</translation>
+ </message>
+ <message>
+ <source>Do you want to rebuild the block database now?</source>
+ <translation>Želite li sada obnoviti bazu blokova?</translation>
+ </message>
+ <message>
+ <source>Error creating %s: You can't create non-HD wallets with this version.</source>
+ <translation>Greška kod stvaranja %s. S ovom verzijom ne možete stvoriti novčanike koji nisu HD.</translation>
+ </message>
+ <message>
+ <source>Error initializing block database</source>
+ <translation>Greška kod inicijaliziranja baze blokova</translation>
+ </message>
+ <message>
+ <source>Error initializing wallet database environment %s!</source>
+ <translation>Greška kod inicijaliziranja okoline baze novčanika %s!</translation>
+ </message>
+ <message>
+ <source>Error loading %s</source>
+ <translation>Greška kod pokretanja programa %s!</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>Greška kod učitavanja %s: Privatni ključevi mogu biti isključeni samo tijekom stvaranja</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Wallet corrupted</source>
+ <translation>Greška kod učitavanja %s: Novčanik pokvaren</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Wallet requires newer version of %s</source>
+ <translation>Greška kod učitavanja %s: Novčanik zahtijeva noviju verziju softvera %s.</translation>
+ </message>
+ <message>
+ <source>Error loading block database</source>
+ <translation>Greška kod pokretanja baze blokova</translation>
+ </message>
+ <message>
+ <source>Error opening block database</source>
+ <translation>Greška kod otvaranja baze blokova</translation>
+ </message>
+ <message>
<source>Error: Disk space is low!</source>
<translation>Pogreška: Nema dovoljno prostora na disku!</translation>
</message>
<message>
+ <source>Failed to listen on any port. Use -listen=0 if you want this.</source>
+ <translation>Neuspješno slušanje na svim portovima. Koristite -listen=0 ako to želite.</translation>
+ </message>
+ <message>
+ <source>Failed to rescan the wallet during initialization</source>
+ <translation>Neuspješno ponovo skeniranje novčanika tijekom inicijalizacije</translation>
+ </message>
+ <message>
+ <source>Importing...</source>
+ <translation>Uvozi se...</translation>
+ </message>
+ <message>
+ <source>Incorrect or no genesis block found. Wrong datadir for network?</source>
+ <translation>Neispravan ili nepostojeći blok geneze. Možda je kriva podatkovna mapa za mrežu?</translation>
+ </message>
+ <message>
+ <source>Initialization sanity check failed. %s is shutting down.</source>
+ <translation>Brzinska provjera inicijalizacije neuspješna. %s se zatvara.</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -%s=&lt;amount&gt;: '%s'</source>
+ <translation>Neispravan iznos za -%s=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -discardfee=&lt;amount&gt;: '%s'</source>
+ <translation>Neispravan iznos za -discardfee=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -fallbackfee=&lt;amount&gt;: '%s'</source>
+ <translation>Neispravan iznos za -fallbackfee=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Zadana mapa blokova "%s" ne postoji.</translation>
+ </message>
+ <message>
+ <source>Upgrading txindex database</source>
+ <translation>Ažurira se txindex baza</translation>
+ </message>
+ <message>
+ <source>Loading P2P addresses...</source>
+ <translation>Pokreće se popis P2P adresa...</translation>
+ </message>
+ <message>
+ <source>Loading banlist...</source>
+ <translation>Pokreće se popis zabrana...</translation>
+ </message>
+ <message>
+ <source>Not enough file descriptors available.</source>
+ <translation>Nema dovoljno dostupnih datotečnih opisivača.</translation>
+ </message>
+ <message>
+ <source>Prune cannot be configured with a negative value.</source>
+ <translation>Obrezivanje (prune) ne može biti postavljeno na negativnu vrijednost.</translation>
+ </message>
+ <message>
+ <source>Prune mode is incompatible with -txindex.</source>
+ <translation>Način obreživanja (pruning) nekompatibilan je s parametrom -txindex.</translation>
+ </message>
+ <message>
+ <source>Replaying blocks...</source>
+ <translation>Odigraju se ponovno blokovi...</translation>
+ </message>
+ <message>
+ <source>Rewinding blocks...</source>
+ <translation>Premotavaju se blokovi...</translation>
+ </message>
+ <message>
+ <source>The source code is available from %s.</source>
+ <translation>Izvorni kod je dostupan na %s.</translation>
+ </message>
+ <message>
+ <source>Transaction fee and change calculation failed</source>
+ <translation>Neuspješno računanje ostatka i transakcijske naknade</translation>
+ </message>
+ <message>
+ <source>Unable to bind to %s on this computer. %s is probably already running.</source>
+ <translation>Ne može se povezati na %s na ovom računalu. %s je vjerojatno već pokrenut.</translation>
+ </message>
+ <message>
+ <source>Unable to generate keys</source>
+ <translation>Ne mogu se generirati ključevi</translation>
+ </message>
+ <message>
+ <source>Unsupported argument -benchmark ignored, use -debug=bench.</source>
+ <translation>Nepodržan argument -benchmark ignoriran. Koristite -debug=bench.</translation>
+ </message>
+ <message>
+ <source>Unsupported argument -debugnet ignored, use -debug=net.</source>
+ <translation>Nepodržan argument -debugnet ignoriran. Koristite -debug=net.</translation>
+ </message>
+ <message>
+ <source>Unsupported argument -tor found, use -onion.</source>
+ <translation>Nepodržan argument -tor pronađen. Koristite -onion.</translation>
+ </message>
+ <message>
+ <source>Unsupported logging category %s=%s.</source>
+ <translation>Nepodržana kategorija zapisa %s=%s.</translation>
+ </message>
+ <message>
+ <source>Upgrading UTXO database</source>
+ <translation>Ažurira se UTXO baza</translation>
+ </message>
+ <message>
+ <source>User Agent comment (%s) contains unsafe characters.</source>
+ <translation>Komentar pod "Korisnički agent" (%s) sadrži nesigurne znakove.</translation>
+ </message>
+ <message>
+ <source>Verifying blocks...</source>
+ <translation>Provjeravaju se blokovi...</translation>
+ </message>
+ <message>
+ <source>Wallet needed to be rewritten: restart %s to complete</source>
+ <translation>Novčanik je trebao prepravak: ponovo pokrenite %s</translation>
+ </message>
+ <message>
+ <source>Error: Listening for incoming connections failed (listen returned error %s)</source>
+ <translation>Greška: Neuspješno slušanje dolažećih veza (listen je izbacio grešku %s)</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -maxtxfee=&lt;amount&gt;: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
+ <translation>Neispravan iznos za -maxtxfee=&lt;amount&gt;: '%s' (mora biti barem minimalnu naknadu za proslijeđivanje od %s kako se ne bi zapela transakcija)</translation>
+ </message>
+ <message>
+ <source>The transaction amount is too small to send after the fee has been deducted</source>
+ <translation>Iznos transakcije je premalen za poslati nakon naknade</translation>
+ </message>
+ <message>
+ <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
+ <translation>Morat ćete ponovno složiti bazu koristeći -reindex kako biste se vratili na neobrezivan način (unpruned mode). Ovo će ponovno preuzeti cijeli lanac blokova.</translation>
+ </message>
+ <message>
+ <source>Error loading %s: You can't disable HD on an already existing HD wallet</source>
+ <translation>Greška kod učitavanja %s: Ne možete isključiti HD na već postojećem HD novčaniku</translation>
+ </message>
+ <message>
+ <source>Error reading from database, shutting down.</source>
+ <translation>Greška kod iščitanja baze. Zatvara se klijent.</translation>
+ </message>
+ <message>
+ <source>Error upgrading chainstate database</source>
+ <translation>Greška kod ažuriranja baze stanja lanca</translation>
+ </message>
+ <message>
<source>Information</source>
<translation>Informacija</translation>
</message>
<message>
+ <source>Invalid -onion address or hostname: '%s'</source>
+ <translation>Neispravna -onion adresa ili ime računala: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid -proxy address or hostname: '%s'</source>
+ <translation>Neispravna -proxy adresa ili ime računala: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -paytxfee=&lt;amount&gt;: '%s' (must be at least %s)</source>
+ <translation>Neispravan iznos za -paytxfee=&lt;amount&gt;: '%s' (mora biti barem %s)</translation>
+ </message>
+ <message>
+ <source>Invalid netmask specified in -whitelist: '%s'</source>
+ <translation>Neispravna mrežna maska zadana u -whitelist: '%s'</translation>
+ </message>
+ <message>
+ <source>Need to specify a port with -whitebind: '%s'</source>
+ <translation>Treba zadati port pomoću -whitebind: '%s'</translation>
+ </message>
+ <message>
+ <source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
+ <translation>Smanjuje se -maxconnections sa %d na %d zbog sustavnih ograničenja.</translation>
+ </message>
+ <message>
+ <source>Signing transaction failed</source>
+ <translation>Potpisivanje transakcije neuspješno</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" does not exist</source>
+ <translation>Zadan -walletdir "%s" ne postoji</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is a relative path</source>
+ <translation>Zadan -walletdir "%s" je relativan put</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is not a directory</source>
+ <translation>Zadan -walletdir "%s" nije mapa</translation>
+ </message>
+ <message>
+ <source>The transaction amount is too small to pay the fee</source>
+ <translation>Transakcijiski iznos je premalen da plati naknadu</translation>
+ </message>
+ <message>
+ <source>This is experimental software.</source>
+ <translation>Ovo je eksperimentalni softver.</translation>
+ </message>
+ <message>
+ <source>Transaction amount too small</source>
+ <translation>Transakcijski iznos premalen</translation>
+ </message>
+ <message>
+ <source>Transaction too large for fee policy</source>
+ <translation>Transakcija prevelika za politiku naknada</translation>
+ </message>
+ <message>
+ <source>Transaction too large</source>
+ <translation>Transakcija prevelika</translation>
+ </message>
+ <message>
+ <source>Unable to bind to %s on this computer (bind returned error %s)</source>
+ <translation>Ne može se povezati na %s na ovom računalu. (povezivanje je vratilo grešku %s)</translation>
+ </message>
+ <message>
+ <source>Unable to generate initial keys</source>
+ <translation>Ne mogu se generirati početni ključevi</translation>
+ </message>
+ <message>
+ <source>Verifying wallet(s)...</source>
+ <translation>Provjerava(ju) se novčanik/(ci)...</translation>
+ </message>
+ <message>
+ <source>Wallet %s resides outside wallet directory %s</source>
+ <translation>Novčanik %s nalazi se izvan mape novčanika %s</translation>
+ </message>
+ <message>
<source>Warning</source>
<translation>Upozorenje</translation>
</message>
<message>
+ <source>Warning: unknown new rules activated (versionbit %i)</source>
+ <translation>Upozorenje: nepoznata nova pravila aktivirana (versionbit %i)</translation>
+ </message>
+ <message>
+ <source>Zapping all transactions from wallet...</source>
+ <translation>Brišu se sve transakcije iz novčanika...</translation>
+ </message>
+ <message>
+ <source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
+ <translation>-maxtxfee je postavljen preveliko. Naknade ove veličine će biti plaćene na individualnoj transakciji.</translation>
+ </message>
+ <message>
+ <source>Error loading %s: You can't enable HD on an already existing non-HD wallet</source>
+ <translation>Greška kod učitavanja %s: Ne možete uključiti HD na već postojećem novčaniku koji nije HD</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you may pay when fee estimates are not available.</source>
+ <translation>Ovo je transakcijska naknada koju ćete možda platiti kada su nedostupne procjene naknada.</translation>
+ </message>
+ <message>
+ <source>This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.</source>
+ <translation>Ovaj proizvod sadrži softver razvijen sa strane OpenSSL Projecta za upotrebu u OpenSSL Toolkitu %s, kriptografski softver koji je napisao Eric Young te UPnP softver koji je napisao Thomas Bernard.</translation>
+ </message>
+ <message>
+ <source>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source>
+ <translation>Ukupna duljina stringa verzije mreže (%i) prelazi maksimalnu duljinu (%i). Smanjite broj ili veličinu komentara o korisničkom agentu (uacomments).</translation>
+ </message>
+ <message>
+ <source>Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.</source>
+ <translation>Nepodržan argument -socks pronađen. Postavljanje verziju SOCKS-a nije više moguće. Samo su SOCKS5 proxyji podržani.</translation>
+ </message>
+ <message>
+ <source>Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay.</source>
+ <translation>Nepodržan argument -whitelistalwaysrelay ignoriran. Koristite -whitelistelay i/ili -whitelistforcerelay.</translation>
+ </message>
+ <message>
+ <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source>
+ <translation>Upozorenje: Zbiva se rudarenje blokova nepoznatih verzija! Moguće je da su nepoznata pravila na snazi</translation>
+ </message>
+ <message>
+ <source>Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup.</source>
+ <translation>Upozorenje: Datoteka novčanika je pokvarena, ali su podaci spašeni! Original %s snimljen je kao %s u %s; ako su transakcije ili stanje neispravni, onda biste trebali restorirati sa sigurnosne kopije (backupa).</translation>
+ </message>
+ <message>
+ <source>%s is set very high!</source>
+ <translation>%s je postavljen preveliko!</translation>
+ </message>
+ <message>
+ <source>Error loading wallet %s. Duplicate -wallet filename specified.</source>
+ <translation>Greška kod učitavanja novčanika %s. Duplikat imena novčanika zadan.</translation>
+ </message>
+ <message>
+ <source>Keypool ran out, please call keypoolrefill first</source>
+ <translation>Ispraznio se bazen ključeva. Molim pozovite keypoolrefill najprije</translation>
+ </message>
+ <message>
+ <source>Starting network threads...</source>
+ <translation>Pokreću se mrežne niti...</translation>
+ </message>
+ <message>
+ <source>The wallet will avoid paying less than the minimum relay fee.</source>
+ <translation>Ovaj novčanik će izbjegavati plaćanje manje od minimalne naknade prijenosa.</translation>
+ </message>
+ <message>
+ <source>This is the minimum transaction fee you pay on every transaction.</source>
+ <translation>Ovo je minimalna transakcijska naknada koju plaćate za svaku transakciju.</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you will pay if you send a transaction.</source>
+ <translation>Ovo je transakcijska naknada koju ćete platiti ako pošaljete transakciju.</translation>
+ </message>
+ <message>
+ <source>Transaction amounts must not be negative</source>
+ <translation>Iznosi transakcije ne smiju biti negativni</translation>
+ </message>
+ <message>
+ <source>Transaction has too long of a mempool chain</source>
+ <translation>Transakcija ima prevelik lanac memorijskog bazena</translation>
+ </message>
+ <message>
+ <source>Transaction must have at least one recipient</source>
+ <translation>Transakcija mora imati barem jednog primatelja</translation>
+ </message>
+ <message>
+ <source>Unknown network specified in -onlynet: '%s'</source>
+ <translation>Nepoznata mreža zadana kod -onlynet: '%s'</translation>
+ </message>
+ <message>
<source>Insufficient funds</source>
<translation>Nedovoljna sredstva</translation>
</message>
<message>
+ <source>Can't generate a change-address key. Private keys are disabled for this wallet.</source>
+ <translation>Ne može se generirati ključ adrese na koju će se poslati ostatak. Privatni ključevi su isključeni kod ovog novčanika.</translation>
+ </message>
+ <message>
+ <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
+ <translation>Ne može se ažurirati novčanik koji nije HD bez ažuriranja radi podrške za bazen ključeva prije raskola. Molim koristite -upgradewallet=169900 ili -upgradewallet bez zadane verzije.</translation>
+ </message>
+ <message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>Neuspješno procjenjivanje naknada. Fallbackfee je isključena. Pričekajte nekoliko blokova ili uključite -fallbackfee.</translation>
+ </message>
+ <message>
+ <source>Cannot write to data directory '%s'; check permissions.</source>
+ <translation>Nije moguće pisati u podatkovnu mapu '%s'; provjerite dozvole.</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>Učitavanje indeksa blokova...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_id.ts b/src/qt/locale/bitcoin_id.ts
new file mode 100644
index 0000000000..1a4c7236d1
--- /dev/null
+++ b/src/qt/locale/bitcoin_id.ts
@@ -0,0 +1,969 @@
+<TS language="id" version="2.1">
+<context>
+ <name>AddressBookPage</name>
+ <message>
+ <source>Right-click to edit address or label</source>
+ <translation>Klik kanan untuk mengubah alamat atau label</translation>
+ </message>
+ <message>
+ <source>Create a new address</source>
+ <translation>Buat sebuah alamat baru</translation>
+ </message>
+ <message>
+ <source>&amp;New</source>
+ <translation>&amp;Baru</translation>
+ </message>
+ <message>
+ <source>Copy the currently selected address to the system clipboard</source>
+ <translation>Salin alamat yang dipilih ke dalam clipboard sistem</translation>
+ </message>
+ <message>
+ <source>&amp;Copy</source>
+ <translation>&amp;Salin</translation>
+ </message>
+ <message>
+ <source>C&amp;lose</source>
+ <translation>&amp;Tutup</translation>
+ </message>
+ <message>
+ <source>Delete the currently selected address from the list</source>
+ <translation>Hapus alamat yang dipilih dari daftar</translation>
+ </message>
+ <message>
+ <source>Enter address or label to search</source>
+ <translation>Masukkan alamat atau label untuk mencari</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>Ekspor data dalam tab saat ini ke file</translation>
+ </message>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp;Export</translation>
+ </message>
+ <message>
+ <source>&amp;Delete</source>
+ <translation>&amp;Hapus</translation>
+ </message>
+ <message>
+ <source>Choose the address to send coins to</source>
+ <translation>Pilih alamat untuk mengirim koin kepada</translation>
+ </message>
+ <message>
+ <source>Choose the address to receive coins with</source>
+ <translation>Pilih alamat untuk menerima koin</translation>
+ </message>
+ <message>
+ <source>C&amp;hoose</source>
+ <translation>P&amp;ilih</translation>
+ </message>
+ <message>
+ <source>Sending addresses</source>
+ <translation>Alamat mengirim</translation>
+ </message>
+ <message>
+ <source>Receiving addresses</source>
+ <translation>Alamat menerima</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
+ <translation>Ini adalah alamat Bitcoin anda untuk mengirim pembayaran. Selalu periksa jumlah dan alamat penerima sebelum mengirim koin</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
+ <translation>Ini adalah alamat Bitcoin anda untuk menerima pembayaran. Disarankan untuk menggunakan alamat penerimaan baru untuk setiap transaksi</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Address</source>
+ <translation>&amp;Salin Alamat</translation>
+ </message>
+ <message>
+ <source>Copy &amp;Label</source>
+ <translation>Copy &amp;Label</translation>
+ </message>
+ <message>
+ <source>&amp;Edit</source>
+ <translation>&amp;Edit</translation>
+ </message>
+ <message>
+ <source>Export Address List</source>
+ <translation>Export Daftar Alamat</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>File dipisahkan dengan Comma (*.csv)</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Exporting Gagal</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the address list to %1. Please try again.</source>
+ <translation>Terjadi kesalahan saat menyimpan daftar alamat %1. Silahkan coba kembali.</translation>
+ </message>
+</context>
+<context>
+ <name>AddressTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>Label</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Alamat</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(tanpa label)</translation>
+ </message>
+</context>
+<context>
+ <name>AskPassphraseDialog</name>
+ <message>
+ <source>Passphrase Dialog</source>
+ <translation>Dialog passphrase</translation>
+ </message>
+ <message>
+ <source>Enter passphrase</source>
+ <translation>Masukan passphrase</translation>
+ </message>
+ <message>
+ <source>New passphrase</source>
+ <translation>Passphrase baru</translation>
+ </message>
+ <message>
+ <source>Repeat new passphrase</source>
+ <translation>Ulangi passphrase baru</translation>
+ </message>
+ <message>
+ <source>Show password</source>
+ <translation>Tampilkan kata sandi</translation>
+ </message>
+ <message>
+ <source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
+ <translation>Masukkan passphrase baru ke dompet.&lt;br/&gt;Harap gunakan passphrase dari &lt;b&gt;sepuluh atau lebih karakter acak&lt;/b&gt;, or &lt;b&gt;delapan atau lebih kata&lt;/b&gt;.</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>Enkripsi wallet</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>Tindakan ini memerlukan passphrase untuk membuka wallet anda</translation>
+ </message>
+ <message>
+ <source>Unlock wallet</source>
+ <translation>Buka wallet</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>Tindakan ini memerlukan passphrase untuk mendekripsi wallet anda</translation>
+ </message>
+ <message>
+ <source>Decrypt wallet</source>
+ <translation>Dekripsi wallet</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>Ganti passphrase</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase to the wallet.</source>
+ <translation>Masukan passphrase lama dan passphrase baru ke wallet</translation>
+ </message>
+ <message>
+ <source>Confirm wallet encryption</source>
+ <translation>Konfirmasi proses enkripsi wallet</translation>
+ </message>
+ <message>
+ <source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
+ <translation>Peringatan: Jika anda mengenkripsi wallet anda dan lupa/hilang passphrase anda, anda akan ‭&lt;b&gt; KEHILANGAN SEMUA BITCOIN ANDA &lt;/b&gt;!</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to encrypt your wallet?</source>
+ <translation>Apa anda yakin ingin mengenkripsi wallet anda?</translation>
+ </message>
+ <message>
+ <source>Wallet encrypted</source>
+ <translation>Wallet terenkripsi</translation>
+ </message>
+ <message>
+ <source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>%1 akan ditutup sekarang untuk menyelesaikan proses enkripsi. Ingat bahwa mengenkripsi dompet Anda tidak dapat sepenuhnya melindungi bitcoin Anda dari pencurian oleh malware yang menginfeksi komputer Anda.</translation>
+ </message>
+ <message>
+ <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
+ <translation>PENTING: Semua cadangan sebelumnya yang Anda buat dari file dompet Anda harus diganti dengan file dompet terenkripsi yang baru dibuat. Untuk alasan keamanan, cadangan sebelumnya dari file dompet yang tidak terenkripsi akan menjadi tidak berguna segera setelah Anda mulai menggunakan dompet terenkripsi yang baru.</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed</source>
+ <translation>Enkripsi wallet gagal</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <translation>Enkripsi wallet gagal karena kesalahan internal. Wallet anda belum terenkripsi</translation>
+ </message>
+ <message>
+ <source>The supplied passphrases do not match.</source>
+ <translation>Passphrase yang dimasukan tidak cocok.</translation>
+ </message>
+ <message>
+ <source>Wallet unlock failed</source>
+ <translation>Gagal membuka wallet</translation>
+ </message>
+ <message>
+ <source>The passphrase entered for the wallet decryption was incorrect.</source>
+ <translation>Passphrase yang dimasukan untuk dekripsi wallet salah</translation>
+ </message>
+ <message>
+ <source>Wallet decryption failed</source>
+ <translation>Gagal mendekripsi wallet</translation>
+ </message>
+ <message>
+ <source>Wallet passphrase was successfully changed.</source>
+ <translation>Passphrase wallet berhasil diganti</translation>
+ </message>
+ <message>
+ <source>Warning: The Caps Lock key is on!</source>
+ <translation>Peringatan: Caps Lock key menyala!</translation>
+ </message>
+</context>
+<context>
+ <name>BanTableModel</name>
+ <message>
+ <source>IP/Netmask</source>
+ <translation>IP/Netmask</translation>
+ </message>
+ <message>
+ <source>Banned Until</source>
+ <translation>Dibanned hingga</translation>
+ </message>
+</context>
+<context>
+ <name>BitcoinGUI</name>
+ <message>
+ <source>Sign &amp;message...</source>
+ <translation>&amp;Verifikasi pesan...</translation>
+ </message>
+ <message>
+ <source>Synchronizing with network...</source>
+ <translation>Sedang sinkronisasi dengan jaringan</translation>
+ </message>
+ <message>
+ <source>&amp;Overview</source>
+ <translation>&amp;Overview</translation>
+ </message>
+ <message>
+ <source>Node</source>
+ <translation>Node</translation>
+ </message>
+ <message>
+ <source>Show general overview of wallet</source>
+ <translation>Tampilkan gambaran umum dompet</translation>
+ </message>
+ <message>
+ <source>&amp;Transactions</source>
+ <translation>&amp;Transaksi</translation>
+ </message>
+ <message>
+ <source>Browse transaction history</source>
+ <translation>Telusuri history transaksi</translation>
+ </message>
+ <message>
+ <source>E&amp;xit</source>
+ <translation>&amp;Keluar</translation>
+ </message>
+ <message>
+ <source>Quit application</source>
+ <translation>Tutup aplikasi</translation>
+ </message>
+ <message>
+ <source>&amp;About %1</source>
+ <translation>&amp;Tentang %1</translation>
+ </message>
+ <message>
+ <source>Show information about %1</source>
+ <translation>Tunjukan informasi tentang %1</translation>
+ </message>
+ <message>
+ <source>About &amp;Qt</source>
+ <translation>Tentang &amp;Qt</translation>
+ </message>
+ <message>
+ <source>Show information about Qt</source>
+ <translation>Tampilkan informasi tentang Qt</translation>
+ </message>
+ <message>
+ <source>&amp;Options...</source>
+ <translation>&amp;Pilihan...</translation>
+ </message>
+ <message>
+ <source>Modify configuration options for %1</source>
+ <translation>Ubah pilihan konfigurasi untuk %1</translation>
+ </message>
+ <message>
+ <source>&amp;Encrypt Wallet...</source>
+ <translation>&amp;Enkripsi wallet...</translation>
+ </message>
+ <message>
+ <source>&amp;Backup Wallet...</source>
+ <translation>&amp;Backup wallet</translation>
+ </message>
+ <message>
+ <source>&amp;Change Passphrase...</source>
+ <translation>&amp;Ganti Passphrase...</translation>
+ </message>
+ <message>
+ <source>&amp;Sending addresses...</source>
+ <translation>&amp;Mengirim alamat</translation>
+ </message>
+ <message>
+ <source>&amp;Receiving addresses...</source>
+ <translation>&amp;Menerima alamat...</translation>
+ </message>
+ <message>
+ <source>Open &amp;URI...</source>
+ <translation>Buka &amp;URI...</translation>
+ </message>
+ <message>
+ <source>Wallet:</source>
+ <translation>Dompet:</translation>
+ </message>
+ <message>
+ <source>Click to disable network activity.</source>
+ <translation>Klik untuk menonaktifkan aktifitas network</translation>
+ </message>
+ <message>
+ <source>Network activity disabled.</source>
+ <translation>Aktifitas network tidak aktif</translation>
+ </message>
+ <message>
+ <source>Click to enable network activity again.</source>
+ <translation>Klik untuk mengaktifkan kembali network</translation>
+ </message>
+ <message>
+ <source>Send coins to a Bitcoin address</source>
+ <translation>Kirim koin ke alamat Bitcoin</translation>
+ </message>
+ <message>
+ <source>Backup wallet to another location</source>
+ <translation>Backup wallet ke lokasi lain</translation>
+ </message>
+ <message>
+ <source>Change the passphrase used for wallet encryption</source>
+ <translation>Ganti passphrase yang digunakan untuk enkripsi wallet</translation>
+ </message>
+ <message>
+ <source>&amp;Debug window</source>
+ <translation>&amp;Debug window</translation>
+ </message>
+ <message>
+ <source>Open debugging and diagnostic console</source>
+ <translation>Buka debugging dan diagnostic console</translation>
+ </message>
+ <message>
+ <source>&amp;Verify message...</source>
+ <translation>&amp;Verifikasi pesan...</translation>
+ </message>
+ <message>
+ <source>Bitcoin</source>
+ <translation>Bitcoin</translation>
+ </message>
+ <message>
+ <source>Wallet</source>
+ <translation>Wallet</translation>
+ </message>
+ <message>
+ <source>&amp;Send</source>
+ <translation>&amp;Kirim</translation>
+ </message>
+ <message>
+ <source>&amp;Receive</source>
+ <translation>&amp;Terima</translation>
+ </message>
+ <message>
+ <source>&amp;Show / Hide</source>
+ <translation>&amp;Tampilkan/ Sembunyikan</translation>
+ </message>
+ <message>
+ <source>Show or hide the main Window</source>
+ <translation>Tampilkan atau sembunyikan tampilan utama</translation>
+ </message>
+ <message>
+ <source>Encrypt the private keys that belong to your wallet</source>
+ <translation>Enkripsi private keys wallet anda</translation>
+ </message>
+ <message>
+ <source>Sign messages with your Bitcoin addresses to prove you own them</source>
+ <translation>Tambahkan pesan di alamat Bitcoin untuk membuktikan bahwa anda pemiliknya</translation>
+ </message>
+ <message>
+ <source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
+ <translation>Verifikasi pesan untuk memastikan bahwa telah ditanda tangani dengan alamat Bitcoin tertentu</translation>
+ </message>
+ <message>
+ <source>&amp;File</source>
+ <translation>&amp;File</translation>
+ </message>
+ <message>
+ <source>&amp;Settings</source>
+ <translation>&amp;Pengaturan</translation>
+ </message>
+ <message>
+ <source>&amp;Help</source>
+ <translation>&amp;Bantuan</translation>
+ </message>
+ <message>
+ <source>Tabs toolbar</source>
+ <translation>Toolbar Tabs</translation>
+ </message>
+ <message>
+ <source>Show the list of used sending addresses and labels</source>
+ <translation>Tampilkan daftar alamat kirim yg telah digunakan dan label</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Error</translation>
+ </message>
+ <message>
+ <source>Warning</source>
+ <translation>Peringatan</translation>
+ </message>
+ <message>
+ <source>Information</source>
+ <translation>Informasi</translation>
+ </message>
+ <message>
+ <source>Up to date</source>
+ <translation>Terkini</translation>
+ </message>
+ <message>
+ <source>%1 client</source>
+ <translation>%1 client</translation>
+ </message>
+ <message>
+ <source>Connecting to peers...</source>
+ <translation>Menghubungkan ke peers...</translation>
+ </message>
+ <message>
+ <source>Catching up...</source>
+ <translation>Catching up...</translation>
+ </message>
+ <message>
+ <source>Date: %1
+</source>
+ <translation>Tanggal: %1
+</translation>
+ </message>
+ <message>
+ <source>Amount: %1
+</source>
+ <translation>Jumlah: %1
+</translation>
+ </message>
+ <message>
+ <source>Type: %1
+</source>
+ <translation>Tipe: %1
+</translation>
+ </message>
+ <message>
+ <source>Label: %1
+</source>
+ <translation>Label: %1
+</translation>
+ </message>
+ <message>
+ <source>Address: %1
+</source>
+ <translation>Alamat: %1
+</translation>
+ </message>
+ <message>
+ <source>Sent transaction</source>
+ <translation>Transaksi terkirim</translation>
+ </message>
+ <message>
+ <source>Incoming transaction</source>
+ <translation>Transaksi datang</translation>
+ </message>
+ <message>
+ <source>HD key generation is &lt;b&gt;enabled&lt;/b&gt;</source>
+ <translation>Pembuatan kunci HD &lt;b&gt;diaktifkan&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>HD key generation is &lt;b&gt;disabled&lt;/b&gt;</source>
+ <translation>Pembuatan kunci HD &lt;b&gt;dinonaktifkan&lt;/b&gt;</translation>
+ </message>
+ </context>
+<context>
+ <name>CoinControlDialog</name>
+ <message>
+ <source>Coin Selection</source>
+ <translation>Pemilihan koin</translation>
+ </message>
+ <message>
+ <source>Quantity:</source>
+ <translation>Jumlah:</translation>
+ </message>
+ <message>
+ <source>Bytes:</source>
+ <translation>Bytes:</translation>
+ </message>
+ <message>
+ <source>Amount:</source>
+ <translation>Jumlah:</translation>
+ </message>
+ <message>
+ <source>Fee:</source>
+ <translation>Biaya:</translation>
+ </message>
+ <message>
+ <source>Dust:</source>
+ <translation>Dust:</translation>
+ </message>
+ <message>
+ <source>After Fee:</source>
+ <translation>Setelah biaya:</translation>
+ </message>
+ <message>
+ <source>Change:</source>
+ <translation>Ganti:</translation>
+ </message>
+ <message>
+ <source>(un)select all</source>
+ <translation>(tidak)pilih semua</translation>
+ </message>
+ <message>
+ <source>Tree mode</source>
+ <translation>Mode pohon</translation>
+ </message>
+ <message>
+ <source>List mode</source>
+ <translation>Mode list</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Jumlah</translation>
+ </message>
+ <message>
+ <source>Received with label</source>
+ <translation>Diterima dengan label</translation>
+ </message>
+ <message>
+ <source>Received with address</source>
+ <translation>Diterima dengan alamat</translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>Tanggal</translation>
+ </message>
+ <message>
+ <source>Confirmations</source>
+ <translation>Konfirmasi</translation>
+ </message>
+ <message>
+ <source>Confirmed</source>
+ <translation>Telah dikonfirmasi</translation>
+ </message>
+ <message>
+ <source>Copy address</source>
+ <translation>Salin alamat</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Salin label</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Salin jumla</translation>
+ </message>
+ <message>
+ <source>Copy transaction ID</source>
+ <translation>Salin ID transaksi</translation>
+ </message>
+ <message>
+ <source>Lock unspent</source>
+ <translation>Kunci yang tidak digunakan</translation>
+ </message>
+ <message>
+ <source>Unlock unspent</source>
+ <translation>Buka kunci yang tidak digunakan</translation>
+ </message>
+ <message>
+ <source>Copy quantity</source>
+ <translation>Salin jumlah</translation>
+ </message>
+ <message>
+ <source>Copy fee</source>
+ <translation>Salin biaya</translation>
+ </message>
+ <message>
+ <source>Copy after fee</source>
+ <translation>Salin setelah biaya</translation>
+ </message>
+ <message>
+ <source>Copy bytes</source>
+ <translation>salin bytes</translation>
+ </message>
+ <message>
+ <source>Copy dust</source>
+ <translation>Salin dust</translation>
+ </message>
+ <message>
+ <source>Copy change</source>
+ <translation>Salin perubahan</translation>
+ </message>
+ <message>
+ <source>(%1 locked)</source>
+ <translation>(%1 terkunci)</translation>
+ </message>
+ <message>
+ <source>yes</source>
+ <translation>Ya</translation>
+ </message>
+ <message>
+ <source>no</source>
+ <translation>Tidak</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(tanpa label)</translation>
+ </message>
+ <message>
+ <source>(change)</source>
+ <translation>(ganti)</translation>
+ </message>
+</context>
+<context>
+ <name>EditAddressDialog</name>
+ <message>
+ <source>Edit Address</source>
+ <translation>Ubah Alamat</translation>
+ </message>
+ <message>
+ <source>&amp;Label</source>
+ <translation>&amp;Label</translation>
+ </message>
+ <message>
+ <source>New sending address</source>
+ <translation>Alamat pengirim baru</translation>
+ </message>
+ <message>
+ <source>Edit receiving address</source>
+ <translation>Ubah alamat penerima</translation>
+ </message>
+ <message>
+ <source>Edit sending address</source>
+ <translation>Ubah alamat pengieim</translation>
+ </message>
+ <message>
+ <source>Could not unlock wallet.</source>
+ <translation>Tidak bisa membuka wallet</translation>
+ </message>
+ <message>
+ <source>New key generation failed.</source>
+ <translation>Pembuatan kunci baru gagal.</translation>
+ </message>
+</context>
+<context>
+ <name>FreespaceChecker</name>
+ <message>
+ <source>name</source>
+ <translation>nama</translation>
+ </message>
+ </context>
+<context>
+ <name>HelpMessageDialog</name>
+ <message>
+ <source>version</source>
+ <translation>versi</translation>
+ </message>
+ <message>
+ <source>(%1-bit)</source>
+ <translation>(%1-bit)</translation>
+ </message>
+ <message>
+ <source>About %1</source>
+ <translation>Tentang %1</translation>
+ </message>
+ </context>
+<context>
+ <name>Intro</name>
+ <message>
+ <source>Bitcoin</source>
+ <translation>Bitcoin</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Error</translation>
+ </message>
+ </context>
+<context>
+ <name>ModalOverlay</name>
+ </context>
+<context>
+ <name>OpenURIDialog</name>
+ </context>
+<context>
+ <name>OptionsDialog</name>
+ <message>
+ <source>Error</source>
+ <translation>Error</translation>
+ </message>
+ </context>
+<context>
+ <name>OverviewPage</name>
+ </context>
+<context>
+ <name>PaymentServer</name>
+ </context>
+<context>
+ <name>PeerTableModel</name>
+ </context>
+<context>
+ <name>QObject</name>
+ <message>
+ <source>Amount</source>
+ <translation>Jumlah</translation>
+ </message>
+ </context>
+<context>
+ <name>QObject::QObject</name>
+ </context>
+<context>
+ <name>QRImageWidget</name>
+ </context>
+<context>
+ <name>RPCConsole</name>
+ </context>
+<context>
+ <name>ReceiveCoinsDialog</name>
+ <message>
+ <source>Copy label</source>
+ <translation>Salin label</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Salin jumla</translation>
+ </message>
+</context>
+<context>
+ <name>ReceiveRequestDialog</name>
+ <message>
+ <source>Address</source>
+ <translation>Alamat</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Jumlah</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Label</translation>
+ </message>
+ <message>
+ <source>Wallet</source>
+ <translation>Wallet</translation>
+ </message>
+ </context>
+<context>
+ <name>RecentRequestsTableModel</name>
+ <message>
+ <source>Date</source>
+ <translation>Tanggal</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Label</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(tanpa label)</translation>
+ </message>
+ </context>
+<context>
+ <name>SendCoinsDialog</name>
+ <message>
+ <source>Quantity:</source>
+ <translation>Jumlah:</translation>
+ </message>
+ <message>
+ <source>Bytes:</source>
+ <translation>Bytes:</translation>
+ </message>
+ <message>
+ <source>Amount:</source>
+ <translation>Jumlah:</translation>
+ </message>
+ <message>
+ <source>Fee:</source>
+ <translation>Biaya:</translation>
+ </message>
+ <message>
+ <source>After Fee:</source>
+ <translation>Setelah biaya:</translation>
+ </message>
+ <message>
+ <source>Change:</source>
+ <translation>Ganti:</translation>
+ </message>
+ <message>
+ <source>Dust:</source>
+ <translation>Dust:</translation>
+ </message>
+ <message>
+ <source>Copy quantity</source>
+ <translation>Salin jumlah</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Salin jumla</translation>
+ </message>
+ <message>
+ <source>Copy after fee</source>
+ <translation>Salin setelah biaya</translation>
+ </message>
+ <message>
+ <source>Copy bytes</source>
+ <translation>salin bytes</translation>
+ </message>
+ <message>
+ <source>Copy dust</source>
+ <translation>Salin dust</translation>
+ </message>
+ <message>
+ <source>Copy change</source>
+ <translation>Salin perubahan</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(tanpa label)</translation>
+ </message>
+</context>
+<context>
+ <name>SendCoinsEntry</name>
+ </context>
+<context>
+ <name>SendConfirmationDialog</name>
+ </context>
+<context>
+ <name>ShutdownWindow</name>
+ </context>
+<context>
+ <name>SignVerifyMessageDialog</name>
+ </context>
+<context>
+ <name>SplashScreen</name>
+ </context>
+<context>
+ <name>TrafficGraphWidget</name>
+ </context>
+<context>
+ <name>TransactionDesc</name>
+ <message>
+ <source>Date</source>
+ <translation>Tanggal</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Jumlah</translation>
+ </message>
+ </context>
+<context>
+ <name>TransactionDescDialog</name>
+ </context>
+<context>
+ <name>TransactionTableModel</name>
+ <message>
+ <source>Date</source>
+ <translation>Tanggal</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Label</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(tanpa label)</translation>
+ </message>
+ </context>
+<context>
+ <name>TransactionView</name>
+ <message>
+ <source>Copy address</source>
+ <translation>Salin alamat</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Salin label</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Salin jumla</translation>
+ </message>
+ <message>
+ <source>Copy transaction ID</source>
+ <translation>Salin ID transaksi</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>File dipisahkan dengan Comma (*.csv)</translation>
+ </message>
+ <message>
+ <source>Confirmed</source>
+ <translation>Telah dikonfirmasi</translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>Tanggal</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Label</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Alamat</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Exporting gagal</translation>
+ </message>
+ </context>
+<context>
+ <name>UnitDisplayStatusBarControl</name>
+ </context>
+<context>
+ <name>WalletFrame</name>
+ </context>
+<context>
+ <name>WalletModel</name>
+ </context>
+<context>
+ <name>WalletView</name>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp;Export</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>Ekspor data dalam tab saat ini ke file</translation>
+ </message>
+ </context>
+<context>
+ <name>bitcoin-core</name>
+ <message>
+ <source>Information</source>
+ <translation>Informasi</translation>
+ </message>
+ <message>
+ <source>Warning</source>
+ <translation>Peringatan</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Error</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/qt/locale/bitcoin_id_ID.ts b/src/qt/locale/bitcoin_id_ID.ts
index 3c79d54e94..a487936fd2 100644
--- a/src/qt/locale/bitcoin_id_ID.ts
+++ b/src/qt/locale/bitcoin_id_ID.ts
@@ -326,6 +326,14 @@
<translation>Buka &amp;URI</translation>
</message>
<message>
+ <source>Wallet:</source>
+ <translation>Wallet:</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>wallet default</translation>
+ </message>
+ <message>
<source>Click to disable network activity.</source>
<translation>Klik untuk menonaktifkan aktivitas jaringan.</translation>
</message>
@@ -346,6 +354,10 @@
<translation>Mengindex ulang blok di dalam disk...</translation>
</message>
<message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>Proxy di &lt;b&gt;aktifkan&lt;/b&gt;: %1</translation>
+ </message>
+ <message>
<source>Send coins to a Bitcoin address</source>
<translation>Kirim koin ke alamat Bitcoin</translation>
</message>
@@ -514,6 +526,12 @@
</translation>
</message>
<message>
+ <source>Wallet: %1
+</source>
+ <translation>Wallet: %1
+</translation>
+ </message>
+ <message>
<source>Type: %1
</source>
<translation>Tipe: %1
@@ -698,7 +716,15 @@
<source>(no label)</source>
<translation>(tidak ada label)</translation>
</message>
- </context>
+ <message>
+ <source>change from %1 (%2)</source>
+ <translation>kembalian dari %1 (%2)</translation>
+ </message>
+ <message>
+ <source>(change)</source>
+ <translation>(kembalian)</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -738,6 +764,14 @@
<translation>Alamat yang dimasukkan "%1" bukanlah alamat Bitcoin yang valid.</translation>
</message>
<message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Alamat "%1" sudah ada sebagai alamat penerimaan dengan label "%2" sehingga tidak bisa ditambah sebagai alamat pengiriman.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>Alamat "%1" yang dimasukkan sudah ada di dalam buku alamat dengan label "%2".</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>Tidak dapat membuka dompet.</translation>
</message>
@@ -870,6 +904,10 @@
<translation>Transaksi-transaksi terkini mungkin belum terlihat dan oleh karenanya, saldo dompet Anda mungkin tidak tepat. Informasi ini akan akurat ketika dompet Anda tersinkronisasi dengan jaringan Bitcoin, seperti rincian berikut.</translation>
</message>
<message>
+ <source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
+ <translation>Usaha untuk menggunakan bitcoin yang dipengaruhi oleh transaksi yang belum terlihat tidak akan diterima oleh jaringan.</translation>
+ </message>
+ <message>
<source>Number of blocks left</source>
<translation>Jumlah blok tersisa</translation>
</message>
@@ -882,6 +920,14 @@
<translation>Waktu blok terakhir</translation>
</message>
<message>
+ <source>Progress</source>
+ <translation>Perkembangan</translation>
+ </message>
+ <message>
+ <source>Progress increase per hour</source>
+ <translation>Peningkatan perkembangan per jam</translation>
+ </message>
+ <message>
<source>calculating...</source>
<translation>menghitung...</translation>
</message>
@@ -916,7 +962,11 @@
<source>Select payment request file</source>
<translation>Pilih data permintaan pembayaran</translation>
</message>
- </context>
+ <message>
+ <source>Select payment request file to open</source>
+ <translation>Pilih data permintaan pembayaran yang akan dibuka</translation>
+ </message>
+</context>
<context>
<name>OptionsDialog</name>
<message>
@@ -952,10 +1002,22 @@
<translation>Alamat IP proxy (cth. IPv4: 127.0.0.1 / IPv6: ::1)</translation>
</message>
<message>
+ <source>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
+ <translation>Perlihatkan apabila proxy SOCKS5 default digunakan untuk berhungan dengan orang lain lewat tipe jaringan ini.</translation>
+ </message>
+ <message>
+ <source>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</source>
+ <translation>Menggunakan proxy SOCKS5 tersendiri untuk berhubungan dengan orang lain melalui layanan Tor:</translation>
+ </message>
+ <message>
<source>Hide the icon from the system tray.</source>
<translation>Sembunyikan ikon dari system tray.</translation>
</message>
<message>
+ <source>&amp;Hide tray icon</source>
+ <translation>&amp;Sembunyikan ikon tray</translation>
+ </message>
+ <message>
<source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source>
<translation>Minimalisasi aplikasi ketika jendela ditutup. Ketika pilihan ini dipilih, aplikasi akan menutup seluruhnya jika anda memilih Keluar di menu yang tersedia.</translation>
</message>
@@ -968,6 +1030,10 @@
<translation>Pilihan command-line yang aktif menimpa diatas opsi: </translation>
</message>
<message>
+ <source>Open the %1 configuration file from the working directory.</source>
+ <translation>Buka file konfigurasi %1 dari direktori kerja.</translation>
+ </message>
+ <message>
<source>Open Configuration File</source>
<translation>Buka Berkas Konfigurasi</translation>
</message>
@@ -984,6 +1050,18 @@
<translation>&amp;Jaringan</translation>
</message>
<message>
+ <source>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</source>
+ <translation>Menonaktifkan beberapa fitur canggih akan tetapi semua block akan tetap divalidasi seutuhnya. Mengembalikan pengaturan ini memerlukan untuk mengunduh seluruh blockchain. Penggunaan disk mungkin akan lebih tinggi.</translation>
+ </message>
+ <message>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Mengembalikan pengaturan ini membutuhkan pengunduhan seluruh blockchain lagi. </translation>
+ </message>
+ <message>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
<translation>(0 = auto, &lt;0 = leave that many cores free)</translation>
</message>
@@ -1028,6 +1106,10 @@
<translation>Hubungkan ke jaringan Bitcoin melalui SOCKS5 proxy.</translation>
</message>
<message>
+ <source>&amp;Connect through SOCKS5 proxy (default proxy):</source>
+ <translation>&amp;Hubungkan melalui proxy SOCKS5 (proxy default):</translation>
+ </message>
+ <message>
<source>Proxy &amp;IP:</source>
<translation>IP Proxy:</translation>
</message>
@@ -1040,6 +1122,10 @@
<translation>Port proxy (cth. 9050)</translation>
</message>
<message>
+ <source>Used for reaching peers via:</source>
+ <translation>Digunakan untuk berhubungan dengan peers melalui:</translation>
+ </message>
+ <message>
<source>IPv4</source>
<translation>IPv4</translation>
</message>
@@ -1052,6 +1138,10 @@
<translation>Tor</translation>
</message>
<message>
+ <source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source>
+ <translation>Koneksi ke jaringan bitcoin melalui proxy SOCKS5 yang berbeda untuk layanan Tor tersembunyi.</translation>
+ </message>
+ <message>
<source>&amp;Window</source>
<translation>&amp;Jendela</translation>
</message>
@@ -1092,6 +1182,10 @@
<translation>Ingin menunjukkan cara pengaturan koin atau tidak.</translation>
</message>
<message>
+ <source>&amp;Third party transaction URLs</source>
+ <translation>&amp;URL transaksi pihak ketiga</translation>
+ </message>
+ <message>
<source>&amp;OK</source>
<translation>&amp;YA</translation>
</message>
@@ -1120,6 +1214,14 @@
<translation>Klien akan dimatikan, apakah anda hendak melanjutkan?</translation>
</message>
<message>
+ <source>Configuration options</source>
+ <translation>Konfigurasi pengaturan</translation>
+ </message>
+ <message>
+ <source>The configuration file is used to specify advanced user options which override GUI settings. Additionally, any command-line options will override this configuration file.</source>
+ <translation>File konfigurasi digunakan untuk menspesifikkan pilihan khusus pengguna yang akan menimpa pengaturan GUI. Sebagai tambahan, pengaturan command-line apapun akan menimpa file konfigurasi itu.</translation>
+ </message>
+ <message>
<source>Error</source>
<translation>Terjadi sebuah kesalahan</translation>
</message>
@@ -1147,6 +1249,10 @@
<translation>Informasi terlampir mungkin sudah kedaluwarsa. Dompet Anda secara otomatis mensinkronisasi dengan jaringan Bitcoin ketika sebuah hubungan terbentuk, namun proses ini belum selesai.</translation>
</message>
<message>
+ <source>Watch-only:</source>
+ <translation>Hanya lihat:</translation>
+ </message>
+ <message>
<source>Available:</source>
<translation>Tersedia:</translation>
</message>
@@ -1183,16 +1289,76 @@
<translation>Jumlah saldo Anda sekarang</translation>
</message>
<message>
+ <source>Your current balance in watch-only addresses</source>
+ <translation>Saldomu di alamat hanya lihat</translation>
+ </message>
+ <message>
+ <source>Spendable:</source>
+ <translation>Bisa digunakan:</translation>
+ </message>
+ <message>
<source>Recent transactions</source>
<translation>Transaksi-transaksi terkini</translation>
</message>
- </context>
+ <message>
+ <source>Unconfirmed transactions to watch-only addresses</source>
+ <translation>Transaksi yang belum terkonfirmasi ke alamat hanya lihat</translation>
+ </message>
+ <message>
+ <source>Mined balance in watch-only addresses that has not yet matured</source>
+ <translation>Saldo hasil mining di alamat hanya lihat yang belum bisa digunakan</translation>
+ </message>
+ <message>
+ <source>Current total balance in watch-only addresses</source>
+ <translation>Jumlah saldo di alamat hanya lihat</translation>
+ </message>
+</context>
<context>
<name>PaymentServer</name>
<message>
<source>Payment request error</source>
<translation>Terjadi kesalahan pada permintaan pembayaran</translation>
</message>
+ <message>
+ <source>Cannot start bitcoin: click-to-pay handler</source>
+ <translation>Tidak bisa memulai bitcoin: handler click-to-pay</translation>
+ </message>
+ <message>
+ <source>URI handling</source>
+ <translation>Pengelolaan URI</translation>
+ </message>
+ <message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin://' bukanlah alamat URI yang valid. Silakan gunakan 'bitcoin:'.</translation>
+ </message>
+ <message>
+ <source>Payment request fetch URL is invalid: %1</source>
+ <translation>URL permintaan pembayaran tidak valid: %1</translation>
+ </message>
+ <message>
+ <source>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source>
+ <translation>URI tidak bisa dimengerti! Hal ini bisa disebabkan karena alamat Bitcoin yang tidak sah atau parameter URI yang tidak tepat.</translation>
+ </message>
+ <message>
+ <source>Payment request file handling</source>
+ <translation>Pengelolaan file permintaan pembayaran</translation>
+ </message>
+ <message>
+ <source>Payment request file cannot be read! This can be caused by an invalid payment request file.</source>
+ <translation>File permintaan pembayaran tidak bisa dibaca! Hal ini bisa disebabkan karena file permintaan pembayaran tidak valid.</translation>
+ </message>
+ <message>
+ <source>Payment request rejected</source>
+ <translation>Permintaan pembayaran ditolak</translation>
+ </message>
+ <message>
+ <source>Payment request network doesn't match client network.</source>
+ <translation>Jaringan permintaan pembayaran tidak sesuai dengan jaringan klien.</translation>
+ </message>
+ <message>
+ <source>Payment request expired.</source>
+ <translation>Permintaan pembayaran telah kadaluarsa.</translation>
+ </message>
</context>
<context>
<name>PeerTableModel</name>
@@ -1375,6 +1541,10 @@
<translation>1 &amp;tahun</translation>
</message>
<message>
+ <source>default wallet</source>
+ <translation>wallet default</translation>
+ </message>
+ <message>
<source>Yes</source>
<translation>Ya</translation>
</message>
@@ -1631,6 +1801,10 @@
<translation>Biaya Transaksi</translation>
</message>
<message>
+ <source>Payment request expired.</source>
+ <translation>Permintaan pembayaran telah kadaluarsa.</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(tidak ada label)</translation>
</message>
diff --git a/src/qt/locale/bitcoin_is.ts b/src/qt/locale/bitcoin_is.ts
new file mode 100644
index 0000000000..6a4e26ac22
--- /dev/null
+++ b/src/qt/locale/bitcoin_is.ts
@@ -0,0 +1,997 @@
+<TS language="is" version="2.1">
+<context>
+ <name>AddressBookPage</name>
+ <message>
+ <source>Right-click to edit address or label</source>
+ <translation>Smelltu á hægri músatakka til að breyta færslugildi eða merkingu</translation>
+ </message>
+ <message>
+ <source>Create a new address</source>
+ <translation>Búa til nýtt færslugildi</translation>
+ </message>
+ <message>
+ <source>&amp;New</source>
+ <translation>&amp;Nýtt</translation>
+ </message>
+ <message>
+ <source>Copy the currently selected address to the system clipboard</source>
+ <translation>Afrita valið færslugildi í klemmuspjald</translation>
+ </message>
+ <message>
+ <source>&amp;Copy</source>
+ <translation>&amp;Afrita</translation>
+ </message>
+ <message>
+ <source>C&amp;lose</source>
+ <translation>&amp;Loka</translation>
+ </message>
+ <message>
+ <source>Delete the currently selected address from the list</source>
+ <translation>Eyða völdu færslugildi úr listanum</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>Flytja gögn í flipanum í skrá</translation>
+ </message>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp;Flytja út</translation>
+ </message>
+ <message>
+ <source>&amp;Delete</source>
+ <translation>&amp;Eyða</translation>
+ </message>
+ <message>
+ <source>Choose the address to send coins to</source>
+ <translation>Veldu færslugildi sem greiða skal til</translation>
+ </message>
+ <message>
+ <source>Choose the address to receive coins with</source>
+ <translation>Veldu færslugildi sem á að taka við mynt</translation>
+ </message>
+ <message>
+ <source>C&amp;hoose</source>
+ <translation>&amp;Veldu</translation>
+ </message>
+ <message>
+ <source>Sending addresses</source>
+ <translation>Færslugildi sem senda frá sér</translation>
+ </message>
+ <message>
+ <source>Receiving addresses</source>
+ <translation>Færslugildi sem þiggja til sín</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
+ <translation>Þetta eru Bitcoin færslugildin sem senda greiðslur. Skoðið ævinlega vel upphæðina og færslugildin sem þiggja greiðslur áður en mynt er send.</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
+ <translation>Þetta eru Bitcoin færslugildin sem þiggja greiðslur. Mælt er með að nota aldrei sama færslugildið til að þiggja fleiri en eina greiðslu.</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Address</source>
+ <translation>&amp;Afrita færslugildi</translation>
+ </message>
+ <message>
+ <source>Copy &amp;Label</source>
+ <translation>Afrita og &amp;Merkja</translation>
+ </message>
+ <message>
+ <source>&amp;Edit</source>
+ <translation>&amp;Breyta</translation>
+ </message>
+ <message>
+ <source>Export Address List</source>
+ <translation>Flytja út færslulista</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>Gildi aðskilin með kommu (*.csv)</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Útflutningur tókst ekki</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the address list to %1. Please try again.</source>
+ <translation>Ekki tókst að vista færslugildalistann á %1. Reyndu aftur.</translation>
+ </message>
+</context>
+<context>
+ <name>AddressTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>Merking</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Færslugildi</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(engin merking)</translation>
+ </message>
+</context>
+<context>
+ <name>AskPassphraseDialog</name>
+ <message>
+ <source>Passphrase Dialog</source>
+ <translation>Lykilsetning</translation>
+ </message>
+ <message>
+ <source>Enter passphrase</source>
+ <translation>Skráðu lykilsetningu</translation>
+ </message>
+ <message>
+ <source>New passphrase</source>
+ <translation>Ný lykilsetning</translation>
+ </message>
+ <message>
+ <source>Repeat new passphrase</source>
+ <translation>Endurtaktu nýja lykilsetningu</translation>
+ </message>
+ <message>
+ <source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
+ <translation>Skráðu nýju lykilsetninguna í veskið. &lt;br/&gt;Vinsamlegast notaðu lykilsetningu með &lt;b&gt;tíu eða fleiri slembibókstöfum&lt;/b&gt;, eða &lt;b&gt;átta eða fleiri orðum&lt;/b&gt;.</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>Dulkóða veski</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>Þessi aðgerð þarf að fá lykilsetninguna þína til að opna veskið.</translation>
+ </message>
+ <message>
+ <source>Unlock wallet</source>
+ <translation>Opna veskið</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>Þessi aðgerð þarf lykilsetninguna þína til að dulráða veskið.</translation>
+ </message>
+ <message>
+ <source>Decrypt wallet</source>
+ <translation>Dulráða veskið</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>Breyta lykilsetningu</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase to the wallet.</source>
+ <translation>Skráðu gömlu lykilsetninguna og þá nýju í veskið.</translation>
+ </message>
+ <message>
+ <source>Confirm wallet encryption</source>
+ <translation>Staðfesta dulkóðun veskis</translation>
+ </message>
+ <message>
+ <source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
+ <translation>Viðvörun: Ef þú dulkóðar veskið og týnir lykilsetningunn þá munt þú &lt;b&gt;TAPA ALLRI ÞINNI BITCOIN MYNT&lt;/b&gt;!</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to encrypt your wallet?</source>
+ <translation>Ertu viss um að þú viljir dulkóða veskið þitt?</translation>
+ </message>
+ <message>
+ <source>Wallet encrypted</source>
+ <translation>Veski dulkóðað</translation>
+ </message>
+ <message>
+ <source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>%1 lokast núna til að dulkóðun klárist. Mundu að dulkóðun veskis kemur ekki að fullu í veg fyrir að mynt verði stolið úr tölvunni þinni með aðstoð smitforrita. </translation>
+ </message>
+ <message>
+ <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
+ <translation>MIKILVÆGT: Nýja dulkóðaða veskisskráin þarf að koma í staðinn fyrir öll fyrri afrit sem þú hefur gert af upprunalegu veskisskránni. Af öryggisástæðum munu öll fyrri afrit af ódulkóðaða veskinu verða óvirk um leið og þú byrjar að nota nýja, dulkóðaða veskið.</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed</source>
+ <translation>Dulkóðun veskis mistókst</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <translation>Dulkóðun veskis mistóks vegna innri villu. Veskið þitt var ekki dulkóðað.</translation>
+ </message>
+ <message>
+ <source>The supplied passphrases do not match.</source>
+ <translation>Lykilsetningarnar eru ekki þær sömu.</translation>
+ </message>
+ <message>
+ <source>Wallet unlock failed</source>
+ <translation>Ekki tókst að opna veskið</translation>
+ </message>
+ <message>
+ <source>The passphrase entered for the wallet decryption was incorrect.</source>
+ <translation>Lykilsetningin sem notuð var til að dulráða veskið var ekki rétt.</translation>
+ </message>
+ <message>
+ <source>Wallet decryption failed</source>
+ <translation>Ekki tókst að dulráða veski</translation>
+ </message>
+ <message>
+ <source>Wallet passphrase was successfully changed.</source>
+ <translation>Það tókst að breyta lykilsetningu veskis.</translation>
+ </message>
+ <message>
+ <source>Warning: The Caps Lock key is on!</source>
+ <translation>Viðvörun: Kveikt er á HÁSTÖFUM!</translation>
+ </message>
+</context>
+<context>
+ <name>BanTableModel</name>
+ <message>
+ <source>IP/Netmask</source>
+ <translation>IP/Netgríma</translation>
+ </message>
+ <message>
+ <source>Banned Until</source>
+ <translation>Bannað til</translation>
+ </message>
+</context>
+<context>
+ <name>BitcoinGUI</name>
+ <message>
+ <source>Sign &amp;message...</source>
+ <translation>Undirrita &amp;skilaboð</translation>
+ </message>
+ <message>
+ <source>Synchronizing with network...</source>
+ <translation>Samstilli við netið...</translation>
+ </message>
+ <message>
+ <source>&amp;Overview</source>
+ <translation>&amp;Yfirlit</translation>
+ </message>
+ <message>
+ <source>Node</source>
+ <translation>Hnútur</translation>
+ </message>
+ <message>
+ <source>Show general overview of wallet</source>
+ <translation>Sýna almennt yfirlit af veski</translation>
+ </message>
+ <message>
+ <source>&amp;Transactions</source>
+ <translation>&amp;Færslur</translation>
+ </message>
+ <message>
+ <source>Browse transaction history</source>
+ <translation>Skoða færslusögu</translation>
+ </message>
+ <message>
+ <source>E&amp;xit</source>
+ <translation>&amp;Hætta</translation>
+ </message>
+ <message>
+ <source>Quit application</source>
+ <translation>Hætta í forriti</translation>
+ </message>
+ <message>
+ <source>&amp;About %1</source>
+ <translation>&amp;Um %1</translation>
+ </message>
+ <message>
+ <source>Show information about %1</source>
+ <translation>Sýna upplýsingar um %1</translation>
+ </message>
+ <message>
+ <source>About &amp;Qt</source>
+ <translation>Um &amp;Qt</translation>
+ </message>
+ <message>
+ <source>Show information about Qt</source>
+ <translation>Sýna upplýsingar um Qt</translation>
+ </message>
+ <message>
+ <source>&amp;Options...</source>
+ <translation>&amp;Valkostir...</translation>
+ </message>
+ <message>
+ <source>Modify configuration options for %1</source>
+ <translation>Breyta samstillingum fyrir %1</translation>
+ </message>
+ <message>
+ <source>&amp;Encrypt Wallet...</source>
+ <translation>&amp;Dulkóða veski...</translation>
+ </message>
+ <message>
+ <source>&amp;Backup Wallet...</source>
+ <translation>&amp;Öryggisafrit á veski...</translation>
+ </message>
+ <message>
+ <source>&amp;Change Passphrase...</source>
+ <translation>&amp;Breyta lykilsetningu</translation>
+ </message>
+ <message>
+ <source>&amp;Sending addresses...</source>
+ <translation>&amp;Sendi færslugildi...</translation>
+ </message>
+ <message>
+ <source>&amp;Receiving addresses...</source>
+ <translation>&amp;Tek við færslugildum...</translation>
+ </message>
+ <message>
+ <source>Open &amp;URI...</source>
+ <translation>Opna &amp;URL...</translation>
+ </message>
+ <message>
+ <source>Click to disable network activity.</source>
+ <translation>Smelltu til að loka fyrir netumferð.</translation>
+ </message>
+ <message>
+ <source>Network activity disabled.</source>
+ <translation>Slökkt á netumferð.</translation>
+ </message>
+ <message>
+ <source>Click to enable network activity again.</source>
+ <translation>Smelltu til að hefja aftur netumferð.</translation>
+ </message>
+ <message>
+ <source>Syncing Headers (%1%)...</source>
+ <translation>Samstilli hausa (%1%)...</translation>
+ </message>
+ <message>
+ <source>Reindexing blocks on disk...</source>
+ <translation>Endurraða blokkum á drifi...</translation>
+ </message>
+ <message>
+ <source>Send coins to a Bitcoin address</source>
+ <translation>Senda mynt í Bitcoin færslugildi</translation>
+ </message>
+ <message>
+ <source>Backup wallet to another location</source>
+ <translation>Öryggisafrita veski á annan stað</translation>
+ </message>
+ <message>
+ <source>Change the passphrase used for wallet encryption</source>
+ <translation>Breyta lykilsetningunni sem gildir um dulkóðun veskis</translation>
+ </message>
+ <message>
+ <source>&amp;Debug window</source>
+ <translation>&amp;Kembunargluggi</translation>
+ </message>
+ <message>
+ <source>Open debugging and diagnostic console</source>
+ <translation>Opna kembunar- og greiningarstjórnborð</translation>
+ </message>
+ <message>
+ <source>&amp;Verify message...</source>
+ <translation>&amp;Yfirfara skilaboð...</translation>
+ </message>
+ <message>
+ <source>Bitcoin</source>
+ <translation>Bitcoin</translation>
+ </message>
+ <message>
+ <source>Wallet</source>
+ <translation>Veski</translation>
+ </message>
+ <message>
+ <source>&amp;Send</source>
+ <translation>&amp;Senda</translation>
+ </message>
+ <message>
+ <source>&amp;Receive</source>
+ <translation>&amp;Taka við</translation>
+ </message>
+ <message>
+ <source>&amp;Show / Hide</source>
+ <translation>&amp;Sýna / Fela</translation>
+ </message>
+ <message>
+ <source>Show or hide the main Window</source>
+ <translation>Sýna eða fela megin glugga</translation>
+ </message>
+ <message>
+ <source>Encrypt the private keys that belong to your wallet</source>
+ <translation>Dulkóða einkalyklana sem tilheyra veskinu þínu</translation>
+ </message>
+ <message>
+ <source>Sign messages with your Bitcoin addresses to prove you own them</source>
+ <translation>Kvitta undir skilaboð með Bitcoin færslugildunum þínum til að sanna að þú eigir þau</translation>
+ </message>
+ <message>
+ <source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
+ <translation>Yfirfara skilaboð til að tryggja að kvittað hafi verið fyrir þau með tilteknum Bitcoin færslugildum</translation>
+ </message>
+ <message>
+ <source>&amp;File</source>
+ <translation>&amp;Skrá</translation>
+ </message>
+ <message>
+ <source>&amp;Settings</source>
+ <translation>&amp;Stillingar</translation>
+ </message>
+ <message>
+ <source>&amp;Help</source>
+ <translation>&amp;Hjálp</translation>
+ </message>
+ <message>
+ <source>Tabs toolbar</source>
+ <translation>Tólaborð flipa</translation>
+ </message>
+ <message>
+ <source>Request payments (generates QR codes and bitcoin: URIs)</source>
+ <translation>Óska eftir greiðslum (býr til QR kóða og bitcoin: URI)</translation>
+ </message>
+ <message>
+ <source>Show the list of used sending addresses and labels</source>
+ <translation>Sýna lista yfir færslugildi sem notuð hafa verið til sendingar og merkingar þeirra</translation>
+ </message>
+ <message>
+ <source>Show the list of used receiving addresses and labels</source>
+ <translation>Sýna færslugildi sem notuð hafa verið til að taka við mynt og merkingar þeirra</translation>
+ </message>
+ <message>
+ <source>Open a bitcoin: URI or payment request</source>
+ <translation>Opna bitcoin: URI eða greiðslubeiðni</translation>
+ </message>
+ <message>
+ <source>Indexing blocks on disk...</source>
+ <translation>Raða blokkum á drifi</translation>
+ </message>
+ <message>
+ <source>Processing blocks on disk...</source>
+ <translation>Vinn úr blokkum á drifi...</translation>
+ </message>
+ <message>
+ <source>%1 behind</source>
+ <translation>%1 á eftir</translation>
+ </message>
+ <message>
+ <source>Last received block was generated %1 ago.</source>
+ <translation>Síðasta viðtekna blokk var búin til fyrir %1 síðan.</translation>
+ </message>
+ <message>
+ <source>Transactions after this will not yet be visible.</source>
+ <translation>Færslur á eftir þessari munu ekki sjást.</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Villa</translation>
+ </message>
+ <message>
+ <source>Warning</source>
+ <translation>Viðvörun</translation>
+ </message>
+ <message>
+ <source>Information</source>
+ <translation>Upplýsingar</translation>
+ </message>
+ <message>
+ <source>Up to date</source>
+ <translation>Uppfært</translation>
+ </message>
+ <message>
+ <source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
+ <translation>Sýna %1 hjálparskilaboðin til að fá lista yfir valkosti Bitcoin aðgerðir í skipanalínu</translation>
+ </message>
+ <message>
+ <source>%1 client</source>
+ <translation>%1 biðlarar</translation>
+ </message>
+ <message>
+ <source>Connecting to peers...</source>
+ <translation>Tengist jafningjum...</translation>
+ </message>
+ <message>
+ <source>Catching up...</source>
+ <translation>Færist nær...</translation>
+ </message>
+ <message>
+ <source>Date: %1
+</source>
+ <translation>Dagsetning: %1
+</translation>
+ </message>
+ <message>
+ <source>Amount: %1
+</source>
+ <translation>Upphæð: %1
+</translation>
+ </message>
+ <message>
+ <source>Type: %1
+</source>
+ <translation>Tegund: %1
+</translation>
+ </message>
+ <message>
+ <source>Label: %1
+</source>
+ <translation>Merki: %1
+</translation>
+ </message>
+ <message>
+ <source>Address: %1
+</source>
+ <translation>Færslugildi: %1
+</translation>
+ </message>
+ <message>
+ <source>Sent transaction</source>
+ <translation>Send færsla</translation>
+ </message>
+ <message>
+ <source>Incoming transaction</source>
+ <translation>Móttökufærsla</translation>
+ </message>
+ <message>
+ <source>HD key generation is &lt;b&gt;enabled&lt;/b&gt;</source>
+ <translation>HD lyklagerð er &lt;b&gt;virkjuð&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>HD key generation is &lt;b&gt;disabled&lt;/b&gt;</source>
+ <translation>HD lyklagerð er &lt;b&gt;óvirk&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
+ <translation>Veskið er &lt;b&gt;dulkóðað&lt;/b&gt; og núna &lt;b&gt;ólæst&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
+ <translation>Veskið er &lt;b&gt;dulkóðað&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
+ <translation>Alvarleg villa átti sér stað. Bitcoin getur ekki haldið áfram með öruggum hætti og stoppar hér.</translation>
+ </message>
+</context>
+<context>
+ <name>CoinControlDialog</name>
+ <message>
+ <source>Coin Selection</source>
+ <translation>Myntval</translation>
+ </message>
+ <message>
+ <source>Quantity:</source>
+ <translation>Magn:</translation>
+ </message>
+ <message>
+ <source>Bytes:</source>
+ <translation>Bæti:</translation>
+ </message>
+ <message>
+ <source>Amount:</source>
+ <translation>Upphæð:</translation>
+ </message>
+ <message>
+ <source>Fee:</source>
+ <translation>Gjald:</translation>
+ </message>
+ <message>
+ <source>Dust:</source>
+ <translation>Ryk:</translation>
+ </message>
+ <message>
+ <source>After Fee:</source>
+ <translation>Eftirgjald:</translation>
+ </message>
+ <message>
+ <source>Change:</source>
+ <translation>Skiptimynt:</translation>
+ </message>
+ <message>
+ <source>(un)select all</source>
+ <translation>(af)velja allt</translation>
+ </message>
+ <message>
+ <source>Tree mode</source>
+ <translation>Hrísluhamur</translation>
+ </message>
+ <message>
+ <source>List mode</source>
+ <translation>Listahamur</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Upphæð</translation>
+ </message>
+ <message>
+ <source>Received with label</source>
+ <translation>Móttekið með merkingu</translation>
+ </message>
+ <message>
+ <source>Received with address</source>
+ <translation>Móttekið með færslugildi</translation>
+ </message>
+ <message>
+ <source>Copy address</source>
+ <translation>Afrita færslugildi</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Afrita merki</translation>
+ </message>
+ <message>
+ <source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
+ <translation>Þetta merki verður rautt ef einhver viðtakandi tekur við upphæð sem er lægri en núgildandi þröskuldur.</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(ekkert merki)</translation>
+ </message>
+ </context>
+<context>
+ <name>EditAddressDialog</name>
+ <message>
+ <source>Edit Address</source>
+ <translation>Breyta færslugildi</translation>
+ </message>
+ <message>
+ <source>&amp;Label</source>
+ <translation>&amp;Merki</translation>
+ </message>
+ <message>
+ <source>The label associated with this address list entry</source>
+ <translation>Merking tengd þessu færslugildi</translation>
+ </message>
+ <message>
+ <source>The address associated with this address list entry. This can only be modified for sending addresses.</source>
+ <translation>Færslugildið sem tengt er þessari færslu. Þessu má einungis breyta þegar sent er.</translation>
+ </message>
+ <message>
+ <source>&amp;Address</source>
+ <translation>Nýtt móttökufærslugildi</translation>
+ </message>
+ <message>
+ <source>New sending address</source>
+ <translation>Nýtt sendingarfærslugildi</translation>
+ </message>
+ <message>
+ <source>Edit receiving address</source>
+ <translation>Breyta móttökufærslugildi</translation>
+ </message>
+ <message>
+ <source>Edit sending address</source>
+ <translation>Breyta sendingarfærslugildi</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is not a valid Bitcoin address.</source>
+ <translation>Færslugildið sem slegið var inn "%1" er ekki leyfilegt Bitcoin færslugildi.</translation>
+ </message>
+ </context>
+<context>
+ <name>FreespaceChecker</name>
+ </context>
+<context>
+ <name>HelpMessageDialog</name>
+ </context>
+<context>
+ <name>Intro</name>
+ <message>
+ <source>Bitcoin</source>
+ <translation>Bitcoin</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Villa</translation>
+ </message>
+ </context>
+<context>
+ <name>ModalOverlay</name>
+ <message>
+ <source>Number of blocks left</source>
+ <translation>Fjöldi blokka sem eftir eru</translation>
+ </message>
+ <message>
+ <source>Last block time</source>
+ <translation>Tími síðustu blokkar</translation>
+ </message>
+ </context>
+<context>
+ <name>OpenURIDialog</name>
+ </context>
+<context>
+ <name>OptionsDialog</name>
+ <message>
+ <source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source>
+ <translation>IP tala staðgengils (t.d. IPv4: 127.0.0.1 / IPv6: ::1)</translation>
+ </message>
+ <message>
+ <source>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 |.</source>
+ <translation>URL frá þriðja aðila (t.d. blokkarskoðari) sem birtast í færsluflipanum sem samhengisatriði. %s í URL-inu skipt út fyrir færslutvíkross. Mörg URL eru aðskilin með lóðréttu striki |.</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Villa</translation>
+ </message>
+ <message>
+ <source>The supplied proxy address is invalid.</source>
+ <translation>Uppgefið færslugildi staðgengils er ógilt.</translation>
+ </message>
+</context>
+<context>
+ <name>OverviewPage</name>
+ <message>
+ <source>Mined balance that has not yet matured</source>
+ <translation>Námuunnin innistæða sem hefur enn ekki komið fram</translation>
+ </message>
+ <message>
+ <source>Your current balance in watch-only addresses</source>
+ <translation>Innistæða færslugilda sem eru einungis til skoðunar</translation>
+ </message>
+ <message>
+ <source>Unconfirmed transactions to watch-only addresses</source>
+ <translation>Óstaðfestar færslur til færslugilda sem eru einungis til skoðunar</translation>
+ </message>
+ <message>
+ <source>Mined balance in watch-only addresses that has not yet matured</source>
+ <translation>Námuunnin innistæða á færslugildum sem eru einungis til skoðunar og hafa ekki komið fram</translation>
+ </message>
+ <message>
+ <source>Current total balance in watch-only addresses</source>
+ <translation>Innistæða á færslugildum sem eru einungis til skoðunar</translation>
+ </message>
+</context>
+<context>
+ <name>PaymentServer</name>
+ <message>
+ <source>Invalid payment address %1</source>
+ <translation>Ógilt færslugildi til greiðslu %1</translation>
+ </message>
+ </context>
+<context>
+ <name>PeerTableModel</name>
+ </context>
+<context>
+ <name>QObject</name>
+ <message>
+ <source>Amount</source>
+ <translation>Upphæð</translation>
+ </message>
+ </context>
+<context>
+ <name>QObject::QObject</name>
+ </context>
+<context>
+ <name>QRImageWidget</name>
+ </context>
+<context>
+ <name>RPCConsole</name>
+ <message>
+ <source>Block chain</source>
+ <translation>Blokkarkeðja</translation>
+ </message>
+ <message>
+ <source>Current number of blocks</source>
+ <translation>Núverandi fjöldi blokka</translation>
+ </message>
+ <message>
+ <source>Starting Block</source>
+ <translation>Upphafsblokk</translation>
+ </message>
+ <message>
+ <source>Synced Blocks</source>
+ <translation>Samhæfðar blokkir</translation>
+ </message>
+ <message>
+ <source>Last block time</source>
+ <translation>Tími síðustu blokkar</translation>
+ </message>
+ </context>
+<context>
+ <name>ReceiveCoinsDialog</name>
+ <message>
+ <source>&amp;Label:</source>
+ <translation>&amp;Merki:</translation>
+ </message>
+ <message>
+ <source>An optional label to associate with the new receiving address.</source>
+ <translation>Valfrjálst merki sem tengist nýju móttökufærslutölunni.</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Afrita merki</translation>
+ </message>
+ </context>
+<context>
+ <name>ReceiveRequestDialog</name>
+ <message>
+ <source>Address</source>
+ <translation>Vistfang</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Upphæð</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Merki</translation>
+ </message>
+ <message>
+ <source>Wallet</source>
+ <translation>Veski</translation>
+ </message>
+ <message>
+ <source>Resulting URI too long, try to reduce the text for label / message.</source>
+ <translation>URI varð of langt, reyndu að minnka texta í merki / skilaboðum.</translation>
+ </message>
+ </context>
+<context>
+ <name>RecentRequestsTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>Merki</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(ekkert merki)</translation>
+ </message>
+ </context>
+<context>
+ <name>SendCoinsDialog</name>
+ <message>
+ <source>Quantity:</source>
+ <translation>Magn:</translation>
+ </message>
+ <message>
+ <source>Bytes:</source>
+ <translation>Bæti:</translation>
+ </message>
+ <message>
+ <source>Amount:</source>
+ <translation>Upphæð:</translation>
+ </message>
+ <message>
+ <source>Fee:</source>
+ <translation>Gjald:</translation>
+ </message>
+ <message>
+ <source>After Fee:</source>
+ <translation>Eftirgjald:</translation>
+ </message>
+ <message>
+ <source>Change:</source>
+ <translation>Skiptimynt:</translation>
+ </message>
+ <message>
+ <source>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
+ <translation>Það er í lagi að greiða einungis lágmarksupphæðina svo framarlega sem færslurúmtakið er minna en plássið í blokkunum. En gætið þess að þegar það er meiri eftirspurn eftir bitcoin færslum en netið getur unnið úr þá gæti svo farið að færslurnar verða aldrei samþykktar.</translation>
+ </message>
+ <message>
+ <source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
+ <translation>(Smart gjald er ekki gangsett ennþá. Þetta tekur venjulega nokkrar blokkir...)</translation>
+ </message>
+ <message>
+ <source>Dust:</source>
+ <translation>Ryk:</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(ekkert merki)</translation>
+ </message>
+</context>
+<context>
+ <name>SendCoinsEntry</name>
+ <message>
+ <source>&amp;Label:</source>
+ <translation>&amp;Merki:</translation>
+ </message>
+ </context>
+<context>
+ <name>SendConfirmationDialog</name>
+ </context>
+<context>
+ <name>ShutdownWindow</name>
+ </context>
+<context>
+ <name>SignVerifyMessageDialog</name>
+ </context>
+<context>
+ <name>SplashScreen</name>
+ </context>
+<context>
+ <name>TrafficGraphWidget</name>
+ </context>
+<context>
+ <name>TransactionDesc</name>
+ <message>
+ <source>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
+ <translation>Fullgerð mynt verður að nýta %1 blokkir. Þegar þú bjóst til þessa blokk, þá var jafnóðum tilkynnt á netinu að hún eigi að bætast við blokkakeðjuna. Ef hún kemst ekki í keðjuna þá mun staða hennar breytast í "ósamþykkt" og ekki verður hægt að nota hana. Þetta gerist annað slagið ef annar hnútpunktur klárar blokk nokkrum sekúndum á undan þinni.</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Upphæð</translation>
+ </message>
+ </context>
+<context>
+ <name>TransactionDescDialog</name>
+ </context>
+<context>
+ <name>TransactionTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>Merki</translation>
+ </message>
+ <message>
+ <source>Mined</source>
+ <translation>Námuunnið</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(ekkert merki)</translation>
+ </message>
+ </context>
+<context>
+ <name>TransactionView</name>
+ <message>
+ <source>Mined</source>
+ <translation>Námuunnið</translation>
+ </message>
+ <message>
+ <source>Copy address</source>
+ <translation>Afrita færslugildi</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Afrita merki</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>Gildi aðskilin með kommu (*.csv)</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Merki</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Vistfang</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Útflutningur tókst ekki</translation>
+ </message>
+ </context>
+<context>
+ <name>UnitDisplayStatusBarControl</name>
+ </context>
+<context>
+ <name>WalletFrame</name>
+ </context>
+<context>
+ <name>WalletModel</name>
+ </context>
+<context>
+ <name>WalletView</name>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp;Flytja út</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>Flytja gögn í flipanum í skrá</translation>
+ </message>
+ </context>
+<context>
+ <name>bitcoin-core</name>
+ <message>
+ <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
+ <translation>Villa við lestur %s! Allir lyklar fóru inn á réttan hátt, en færslugögn eða færslugildi gætu verið röng eða horfin.</translation>
+ </message>
+ <message>
+ <source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source>
+ <translation>Viðvörun: Netið er ekki í fullu samræmi! Einhver námuvinnsla virðist í ólagi.</translation>
+ </message>
+ <message>
+ <source>Information</source>
+ <translation>Upplýsingar</translation>
+ </message>
+ <message>
+ <source>Warning</source>
+ <translation>Viðvörun</translation>
+ </message>
+ <message>
+ <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source>
+ <translation>Viðvörun: Óþekkt blokkarútgáfa í námavinnslu! Það er mögulegt að óþekktum reglum sé fylgt</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Villa</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/qt/locale/bitcoin_it.ts b/src/qt/locale/bitcoin_it.ts
index 5b24fcdf46..7513e3e30e 100644
--- a/src/qt/locale/bitcoin_it.ts
+++ b/src/qt/locale/bitcoin_it.ts
@@ -3169,6 +3169,10 @@ Nota: poiché la commissione è calcolata su base per byte, una commissione di "
<translation>Errore lettura %s! Tutte le chiavi sono state lette correttamente, ma i dati delle transazioni o della rubrica potrebbero essere mancanti o non corretti.</translation>
</message>
<message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>Raggruppa gli output per indirizzo, selezionando tutti o nessuno, invece di selezionarli in base all'output. La privacy è migliorata in quanto un indirizzo viene usato una sola volta (a meno che qualcuno vi invii denaro dopo che avevate già speso da quell'indirizzo), ma può comportare commissioni leggermente più alte visto che le limitazioni aggiunte possono condurre a una selezione subottimale degli input da spendere (default: %u)</translation>
+ </message>
+ <message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation>Per favore controllate che la data del computer e l'ora siano corrette! Se il vostro orologio è sbagliato %s non funzionerà correttamente.</translation>
</message>
@@ -3305,6 +3309,10 @@ Nota: poiché la commissione è calcolata su base per byte, una commissione di "
<translation>Importo non valido per -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>La cartella specificata "%s" non esiste.</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Aggiornamento del database txindex</translation>
</message>
diff --git a/src/qt/locale/bitcoin_it_IT.ts b/src/qt/locale/bitcoin_it_IT.ts
index eccf015bf5..d97d888f23 100644
--- a/src/qt/locale/bitcoin_it_IT.ts
+++ b/src/qt/locale/bitcoin_it_IT.ts
@@ -232,7 +232,7 @@
</message>
<message>
<source>Banned Until</source>
- <translation>bannato fino </translation>
+ <translation>Bannato fino </translation>
</message>
</context>
<context>
@@ -760,7 +760,7 @@
</message>
<message>
<source>The wallet will also be stored in this directory.</source>
- <translation>Il portafoglio sara' </translation>
+ <translation>Il wallet verra' inoltre memorizzato su questa cartella.</translation>
</message>
<message>
<source>Error</source>
@@ -770,6 +770,14 @@
<context>
<name>ModalOverlay</name>
<message>
+ <source>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source>
+ <translation>Le transazioni recenti potrebbero non essere ancora visibili, e quindi il saldo del wallet potrebbe essere incorretto. Questa informazione verra' corretta non appena il tuo wallet ha finito di sincronizzarsi con la rete bitcoin, come specificato di seguito.</translation>
+ </message>
+ <message>
+ <source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
+ <translation>Tentare di spendere bitcoin che sono affetti da transazioni non ancora mostrate, non verra' accettato dal network.</translation>
+ </message>
+ <message>
<source>Number of blocks left</source>
<translation>Numero di blocchi rimanente</translation>
</message>
diff --git a/src/qt/locale/bitcoin_ja.ts b/src/qt/locale/bitcoin_ja.ts
index 7650694e15..2ee78dbb29 100644
--- a/src/qt/locale/bitcoin_ja.ts
+++ b/src/qt/locale/bitcoin_ja.ts
@@ -3,11 +3,11 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>右クリックでアドレスまたはラベルを編集します</translation>
+ <translation>右クリックでアドレスまたはラベルを編集</translation>
</message>
<message>
<source>Create a new address</source>
- <translation>新規アドレスの作成</translation>
+ <translation>新しいアドレスを作成</translation>
</message>
<message>
<source>&amp;New</source>
@@ -15,7 +15,7 @@
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
- <translation>現在選択されているアドレスをシステムのクリップボードにコピーする</translation>
+ <translation>現在選択されているアドレスをシステムのクリップボードにコピー</translation>
</message>
<message>
<source>&amp;Copy</source>
@@ -27,15 +27,15 @@
</message>
<message>
<source>Delete the currently selected address from the list</source>
- <translation>選択されたアドレスを一覧から削除する</translation>
+ <translation>選択されたアドレスを一覧から削除</translation>
</message>
<message>
<source>Enter address or label to search</source>
- <translation>検索するアドレスまたはラベルを入力</translation>
+ <translation>検索したいアドレスまたはラベルを入力</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
- <translation>ファイルに現在のタブのデータをエクスポート</translation>
+ <translation>このタブのデータをファイルにエクスポート</translation>
</message>
<message>
<source>&amp;Export</source>
@@ -47,11 +47,11 @@
</message>
<message>
<source>Choose the address to send coins to</source>
- <translation>送信先のアドレスを選択</translation>
+ <translation>コインを送りたいアドレスを選択</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
- <translation>支払いを受け取るアドレスを指定する</translation>
+ <translation>コインを受け取りたいアドレスを選択</translation>
</message>
<message>
<source>C&amp;hoose</source>
@@ -59,19 +59,19 @@
</message>
<message>
<source>Sending addresses</source>
- <translation>送金用</translation>
+ <translation>送金先アドレス</translation>
</message>
<message>
<source>Receiving addresses</source>
- <translation>受け取りアドレス</translation>
+ <translation>受取用アドレス</translation>
</message>
<message>
<source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
- <translation>これらは支払いを送信するためのあなたの Bitcoin アドレスです。コインを送信する前に、常に額と受信アドレスを確認してください。</translation>
+ <translation>これらは、あなたが知っている支払い送り先の Bitcoin アドレスです。コインを送る前に、必ず金額と送金先アドレスを確認してください。</translation>
</message>
<message>
<source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
- <translation>これらは支払いを受け取るためのビットコインアドレスです。トランザクションごとに新しい受け取り用アドレスを作成することが推奨されます。</translation>
+ <translation>これらは支払いを受け取るための、あなたの Bitcoin アドレスです。トランザクションごとに新しい受け取り用アドレスを作成することが推奨されます。</translation>
</message>
<message>
<source>&amp;Copy Address</source>
@@ -149,7 +149,7 @@
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
- <translation>この操作はウォレットをアンロックするためにパスフレーズが必要です。</translation>
+ <translation>この操作を続行するには、ウォレットをアンロックするためのパスフレーズが必要です。</translation>
</message>
<message>
<source>Unlock wallet</source>
@@ -157,7 +157,7 @@
</message>
<message>
<source>This operation needs your wallet passphrase to decrypt the wallet.</source>
- <translation>この操作はウォレットの暗号化解除のためにパスフレーズが必要です。</translation>
+ <translation>この操作を続行するには、ウォレットの暗号化を解除するためのパスフレーズが必要です。</translation>
</message>
<message>
<source>Decrypt wallet</source>
@@ -177,7 +177,7 @@
</message>
<message>
<source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
- <translation>警告: もしもあなたのウォレットを暗号化してパスフレーズを失ってしまったなら、&lt;b&gt;あなたの Bitcoin はすべて失われます&lt;/b&gt;!</translation>
+ <translation>警告: もしもあなたのウォレットを暗号化してパスフレーズを忘れてしまったら、&lt;b&gt;あなたの Bitcoin はすべて失われます&lt;/b&gt;!</translation>
</message>
<message>
<source>Are you sure you wish to encrypt your wallet?</source>
@@ -189,11 +189,11 @@
</message>
<message>
<source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
- <translation>暗号化処理を完了させるため %1 をいますぐ終了します。ウォレットの暗号化では、コンピュータに感染したマルウェアなどによるビットコインの盗難から完全に守ることはできないことにご注意ください。</translation>
+ <translation>暗号化処理を完了させるため %1 をいますぐ終了します。ウォレットを暗号化しても、コンピュータに感染したマルウェアなどによる Bitcoin の盗難を完全に防ぐことはできないことにご注意ください。</translation>
</message>
<message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
- <translation>重要: 過去のウォレット ファイルのバックアップは、暗号化された新しいウォレット ファイルに取り替える必要があります。セキュリティ上の理由により、暗号化された新しいウォレットを使い始めると、暗号化されていないウォレット ファイルのバックアップはすぐに使えなくなります。</translation>
+ <translation>重要: 今までに作成されたウォレット ファイルのバックアップは、暗号化された新しいウォレット ファイルに置き換える必要があります。セキュリティ上の理由により、暗号化された新しいウォレットを使い始めると、暗号化されていないウォレット ファイルのバックアップはすぐに使えなくなります。</translation>
</message>
<message>
<source>Wallet encryption failed</source>
@@ -225,7 +225,7 @@
</message>
<message>
<source>Warning: The Caps Lock key is on!</source>
- <translation>警告: Caps Lock キーがオンになっています!</translation>
+ <translation>警告: Caps Lock キーがオンになっています!</translation>
</message>
</context>
<context>
@@ -247,7 +247,7 @@
</message>
<message>
<source>Synchronizing with network...</source>
- <translation>ネットワークに同期中……</translation>
+ <translation>ネットワークと同期しています……</translation>
</message>
<message>
<source>&amp;Overview</source>
@@ -347,11 +347,11 @@
</message>
<message>
<source>Syncing Headers (%1%)...</source>
- <translation>未知。ヘッダを同期しています (%1%)...</translation>
+ <translation>ヘッダーを同期しています (%1%)...</translation>
</message>
<message>
<source>Reindexing blocks on disk...</source>
- <translation>ディスク上のブロックのインデックスを再作成中...</translation>
+ <translation>ディスク上のブロックのインデックスを再作成しています...</translation>
</message>
<message>
<source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
@@ -395,7 +395,7 @@
</message>
<message>
<source>&amp;Receive</source>
- <translation>入金 (&amp;R)</translation>
+ <translation>受取 (&amp;R)</translation>
</message>
<message>
<source>&amp;Show / Hide</source>
@@ -411,11 +411,11 @@
</message>
<message>
<source>Sign messages with your Bitcoin addresses to prove you own them</source>
- <translation>あなたが所有していることを証明するために、あなたの Bitcoin アドレスでメッセージに署名してください</translation>
+ <translation>Bitcoin アドレスをあなたが所有していることを証明するために、そのアドレスでメッセージに署名してください</translation>
</message>
<message>
<source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
- <translation>指定された Bitcoin アドレスで署名されたことを確認するためにメッセージを検証します</translation>
+ <translation>指定された Bitcoin アドレスで署名されたことを確認するために、メッセージを検証してください</translation>
</message>
<message>
<source>&amp;File</source>
@@ -435,15 +435,15 @@
</message>
<message>
<source>Request payments (generates QR codes and bitcoin: URIs)</source>
- <translation>支払いを要求する (QRコードとbitcoin:ではじまるURIを生成する)</translation>
+ <translation>支払いをリクエストする (QRコードとbitcoin:ではじまるURIを生成する)</translation>
</message>
<message>
<source>Show the list of used sending addresses and labels</source>
- <translation>使用済みの送金用アドレスとラベルの一覧を表示する</translation>
+ <translation>使用したことのある送金用アドレスとラベルの一覧を表示する</translation>
</message>
<message>
<source>Show the list of used receiving addresses and labels</source>
- <translation>支払いを受け取るアドレスとラベルのリストを表示する</translation>
+ <translation>使用したことのある受け取り用アドレスとラベルの一覧を表示する</translation>
</message>
<message>
<source>Open a bitcoin: URI or payment request</source>
@@ -455,7 +455,7 @@
</message>
<message numerus="yes">
<source>%n active connection(s) to Bitcoin network</source>
- <translation><numerusform>%n の Bitcoin ネットワークへのアクティブな接続</numerusform></translation>
+ <translation><numerusform>Bitcoin ネットワークへのアクティブな接続は %n 個</numerusform></translation>
</message>
<message>
<source>Indexing blocks on disk...</source>
@@ -479,7 +479,7 @@
</message>
<message>
<source>Transactions after this will not yet be visible.</source>
- <translation>この後の取引はまだ表示されません。</translation>
+ <translation>この後の取引はまだ表示されていません。</translation>
</message>
<message>
<source>Error</source>
@@ -575,7 +575,7 @@
</message>
<message>
<source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
- <translation>致命的なエラーが発生しました。Bitcoin は安全に継続することができず終了するでしょう。</translation>
+ <translation>致命的なエラーが発生しました。Bitcoin を安全に動作し続けることができないため終了します。</translation>
</message>
</context>
<context>
@@ -710,7 +710,7 @@
</message>
<message>
<source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
- <translation>少なくともひとつの受取額が現在のダスト閾値を下回る場合にはこのラベルは赤くなります。</translation>
+ <translation>少なくともひとつの受取額が現在のダスト閾値を下回る場合に、このラベルは赤くなります。</translation>
</message>
<message>
<source>Can vary +/- %1 satoshi(s) per input.</source>
@@ -753,15 +753,15 @@
</message>
<message>
<source>New sending address</source>
- <translation>新しい送信アドレス</translation>
+ <translation>新しい送金用アドレス</translation>
</message>
<message>
<source>Edit receiving address</source>
- <translation>入金アドレスを編集</translation>
+ <translation>受け取り用アドレスを編集</translation>
</message>
<message>
<source>Edit sending address</source>
- <translation>送信アドレスを編集</translation>
+ <translation>送金用アドレスを編集</translation>
</message>
<message>
<source>The entered address "%1" is not a valid Bitcoin address.</source>
@@ -769,11 +769,11 @@
</message>
<message>
<source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
- <translation>アドレス "%1" は既に受取用アドレスにラベル "%2" として存在するので、送金用アドレスとしては追加できません。</translation>
+ <translation>アドレス "%1" は、既に受け取り用アドレスにラベル "%2" として存在するので、送金用アドレスとしては追加できません。</translation>
</message>
<message>
<source>The entered address "%1" is already in the address book with label "%2".</source>
- <translation>入力されたアドレス"%1"はすでにアドレス帳のラベル"%2"に存在します。</translation>
+ <translation>入力されたアドレス"%1"は、既にアドレス帳にラベル "%2" として存在します。</translation>
</message>
<message>
<source>Could not unlock wallet.</source>
@@ -796,7 +796,7 @@
</message>
<message>
<source>Directory already exists. Add %1 if you intend to create a new directory here.</source>
- <translation>ディレクトリがもうあります。 新しいのディレクトリを作るつもりなら%1を書いてください。</translation>
+ <translation>ディレクトリが既に存在します。 新しいディレクトリを作成したい場合は %1 を追加してください。</translation>
</message>
<message>
<source>Path already exists, and is not a directory.</source>
@@ -846,19 +846,19 @@
</message>
<message>
<source>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source>
- <translation>この初期同期には多大なリソースを消費し、あなたのコンピュータにこれまで見つからなかったハードウェア上の問題を露呈させるかもしれません。%1 を実行する度に、中断された時点からダウンロードを継続します。</translation>
+ <translation>この初期同期には多大なリソースを消費し、あなたのコンピュータでこれまで見つからなかったハードウェア上の問題が発生する場合があります。%1 を実行する度に、中断された時点からダウンロードを再開します。</translation>
</message>
<message>
<source>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source>
- <translation>ブロックチェーンの保存容量に制限を設けることを選択した場合 (剪定) にも、過去のデータのダウンロードおよび処理が必要になります。しかしこれらのデータはディスク使用量を低く抑えるためにその後削除されるでしょう</translation>
+ <translation>ブロックチェーンの保存容量に制限を設けることを選択した場合 (剪定) にも、過去のデータのダウンロードおよび処理が必要になります。しかし、これらのデータはディスク使用量を低く抑えるために、後で削除されます。</translation>
</message>
<message>
<source>Use the default data directory</source>
- <translation>初期値のデータ ディレクトリを使用</translation>
+ <translation>既定のデータ ディレクトリを使用</translation>
</message>
<message>
<source>Use a custom data directory:</source>
- <translation>任意のデータ ディレクトリを使用:</translation>
+ <translation>カスタムのデータ ディレクトリを使用:</translation>
</message>
<message>
<source>Bitcoin</source>
@@ -905,11 +905,11 @@
</message>
<message>
<source>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source>
- <translation>確認できない最近のトランザクションがあるかもしれません。これによりウォレットの残高は不正確なものである可能性があります。この情報はウォレットが一度ビットコインネットワークへの同期が完了すると正確なものとなります。詳細は下記を参照してください。</translation>
+ <translation>最近のトランザクションがまだ表示されていない可能性があります。そのため、ウォレットの残高が正しく表示されていないかもしれません。この情報は、ウォレットが Bitcoin ネットワークへの同期が完了すると正確なものとなります。詳細は下記を参照してください。</translation>
</message>
<message>
<source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
- <translation>まだ表示されていないトランザクションが影響するビットコインを使用しようとすると、ネットワークから認証がなされないでしょう。</translation>
+ <translation>まだ表示されていないトランザクションが影響する Bitcoin を使用しようとすると、ネットワークから認証を受けられません。</translation>
</message>
<message>
<source>Number of blocks left</source>
@@ -3200,6 +3200,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>%s の読み込みエラー! すべてのキーは正しく読み取れますが、取引データやアドレス帳のエントリが失われたか、正しくない可能性があります。</translation>
</message>
<message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>出力ごとではなく、アドレス単位に出力をまとめて選択します。(後からまたそのアドレスに支払われない限り)アドレスが一度しか使用されないためプライバシーが向上します。ただし追加の制限により最適ではないコイン選択が発生した場合に、わずかに高い手数料となる可能性があります。(初期値: %u)</translation>
+ </message>
+ <message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation>あなたのPCの日付と時刻が正しいことを確認して下さい! もしあなたの時計が正しくなければ %s が正確に動作しません。</translation>
</message>
@@ -3341,6 +3345,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>不正な額 -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>指定のブロックディレクトリ"%s"は存在しません。</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>txindex データベースを更新しています</translation>
</message>
@@ -3481,12 +3489,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>取引の署名に失敗しました</translation>
</message>
<message>
- <source>Specified blocks directory "%s" does not exist.
-</source>
- <translation>指定のブロックディレクトリ"%s"が存在しません。
-</translation>
- </message>
- <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>トランザクションの金額が小さすぎて手数料を支払えません</translation>
</message>
diff --git a/src/qt/locale/bitcoin_ka.ts b/src/qt/locale/bitcoin_ka.ts
index 3fd95eb6f5..57053ac62d 100644
--- a/src/qt/locale/bitcoin_ka.ts
+++ b/src/qt/locale/bitcoin_ka.ts
@@ -200,6 +200,14 @@
<translation>საფულის დაშიფვრა წარუმატებით დამთვრდა</translation>
</message>
<message>
+ <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <translation>საფულის დაშიფრვა ვერ მოხდა შიდა შეცდომის გამო. თქვენი საფულე არ არის დაშიფრული.</translation>
+ </message>
+ <message>
+ <source>The supplied passphrases do not match.</source>
+ <translation>მოწოდებული ფრაზა-პაროლები არ ემთხვევა ერთმანეთს.</translation>
+ </message>
+ <message>
<source>Wallet unlock failed</source>
<translation>საფულის გახსნა წარუმატებლად შესრულდა</translation>
</message>
diff --git a/src/qt/locale/bitcoin_ko.ts b/src/qt/locale/bitcoin_ko.ts
new file mode 100644
index 0000000000..aa05383a83
--- /dev/null
+++ b/src/qt/locale/bitcoin_ko.ts
@@ -0,0 +1,471 @@
+<TS language="ko" version="2.1">
+<context>
+ <name>AddressBookPage</name>
+ <message>
+ <source>Right-click to edit address or label</source>
+ <translation>마우스 오른쪽 클릭을 해서 라벨이나 주소를 편집할 수 있습니다</translation>
+ </message>
+ <message>
+ <source>Create a new address</source>
+ <translation>새로운 주소 발급받기</translation>
+ </message>
+ <message>
+ <source>&amp;New</source>
+ <translation>&amp;발급받기</translation>
+ </message>
+ <message>
+ <source>Copy the currently selected address to the system clipboard</source>
+ <translation>선택된 주소 클립보드에 붙여넣기</translation>
+ </message>
+ <message>
+ <source>&amp;Copy</source>
+ <translation>&amp;복사</translation>
+ </message>
+ <message>
+ <source>C&amp;lose</source>
+ <translation>&amp;닫기</translation>
+ </message>
+ <message>
+ <source>Delete the currently selected address from the list</source>
+ <translation>선택한 주소 리스트에서 삭제</translation>
+ </message>
+ <message>
+ <source>Enter address or label to search</source>
+ <translation>검색할 주소나 라벨을 입력하세요</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>현재 탭의 데이터를 파일로 내보냅니다</translation>
+ </message>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp;내보내기</translation>
+ </message>
+ <message>
+ <source>&amp;Delete</source>
+ <translation>&amp;삭제하기</translation>
+ </message>
+ <message>
+ <source>Choose the address to send coins to</source>
+ <translation>코인을 보낼 주소를 선택하세요</translation>
+ </message>
+ <message>
+ <source>Choose the address to receive coins with</source>
+ <translation>코인을 받을 주소를 선택하세요</translation>
+ </message>
+ <message>
+ <source>C&amp;hoose</source>
+ <translation>&amp;선택하기</translation>
+ </message>
+ <message>
+ <source>Sending addresses</source>
+ <translation>보낼 주소</translation>
+ </message>
+ <message>
+ <source>Receiving addresses</source>
+ <translation>받을 주소</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
+ <translation>이것은 비트코인 전송을 위한 주소입니다. 코인을 보내기 전에 항상 받는 주소와 수량을 확인하세요</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
+ <translation>이것은 비트코인 수신을 위한 주소입니다. 코인을 받을 때 마다 항상 다른 주소를 사용 하시는 것을 권장합니다.</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Address</source>
+ <translation>&amp;주소 복사</translation>
+ </message>
+ <message>
+ <source>Copy &amp;Label</source>
+ <translation>&amp;라벨 복사</translation>
+ </message>
+ <message>
+ <source>&amp;Edit</source>
+ <translation>&amp;편집</translation>
+ </message>
+ <message>
+ <source>Export Address List</source>
+ <translation>주소 목록 내보내기</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>내보내기 실패</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the address list to %1. Please try again.</source>
+ <translation>주소 목록을 %1 로 저장하는 작업이 실패했습니다. 다시 시도해 주세요.</translation>
+ </message>
+</context>
+<context>
+ <name>AddressTableModel</name>
+ </context>
+<context>
+ <name>AskPassphraseDialog</name>
+ <message>
+ <source>Enter passphrase</source>
+ <translation>암호문 입력</translation>
+ </message>
+ <message>
+ <source>New passphrase</source>
+ <translation>새 암호문</translation>
+ </message>
+ <message>
+ <source>Repeat new passphrase</source>
+ <translation>새 암호문 다시 입력</translation>
+ </message>
+ <message>
+ <source>Show password</source>
+ <translation>비밀번호 보이기</translation>
+ </message>
+ <message>
+ <source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
+ <translation>새 암호문을 지갑에 입력합니다. &lt;br/&gt;이러한 형식의 암호문을 사용해 주십시요.&lt;b&gt;10개 이상의 임의 문자&lt;/b&gt; 또는 &lt;b&gt;8개 이상의 단어&lt;/b&gt;.</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>지갑 암호화</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>이 작업을 수행하려면 지갑을 잠금 해제 할 암호문이 필요합니다.</translation>
+ </message>
+ <message>
+ <source>Unlock wallet</source>
+ <translation>지갑 잠금 해제</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>이 작업을 수행하려면 지갑을 복호화할 암호문이 필요합니다.</translation>
+ </message>
+ <message>
+ <source>Decrypt wallet</source>
+ <translation>지갑 복호화</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>암호문 변경</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase to the wallet.</source>
+ <translation>예전 암호문과 변경할 암호문을 입력하십시요.</translation>
+ </message>
+ <message>
+ <source>Confirm wallet encryption</source>
+ <translation>지갑 암호화 확인</translation>
+ </message>
+ <message>
+ <source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
+ <translation>경고: 지갑이 암호화된 상태로 당신의 암호문을 잃어버리셨다면, 당신은 &lt;b&gt;모든 비트코인을 잃게됩니다&lt;/b&gt;!</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to encrypt your wallet?</source>
+ <translation>진짜로 지갑을 암호화 할까요?</translation>
+ </message>
+ <message>
+ <source>Wallet encrypted</source>
+ <translation>지갑 암호화</translation>
+ </message>
+ <message>
+ <source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>%1 이(가) 암호화 작업을 위해 종료됩니다. 이 암호화 작업이 바이러스로부터 비트코인을 완전히 지키지 못한다는 점을 기억하십시요.</translation>
+ </message>
+ <message>
+ <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
+ <translation>중요: 예전에 만들었던 모든 백업 파일은 새로 만들어진 암호화된 지갑 파일으로 교체됩니다. 보안을 위해서 예전에 만들었던 암호화 되지 않은 백업 파일은 당신이 새로 암호화된 지갑을 사용하고 사용할 수 없게 됩니다. </translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed</source>
+ <translation>지갑 암호화 실패</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <translation>내부 오류로 인해 지갑 암호화가 실패했습니다. 지갑이 암호화 되지 않았습니다.</translation>
+ </message>
+ <message>
+ <source>The supplied passphrases do not match.</source>
+ <translation>입력한 암호문이 일치하지 않습니다.</translation>
+ </message>
+ <message>
+ <source>Wallet unlock failed</source>
+ <translation>지갑 잠금 해제 실패</translation>
+ </message>
+ <message>
+ <source>The passphrase entered for the wallet decryption was incorrect.</source>
+ <translation>지갑 복호화를 위한 암호문이 일치하지 않습니다.</translation>
+ </message>
+ <message>
+ <source>Wallet decryption failed</source>
+ <translation>지갑 복호화 실패</translation>
+ </message>
+ <message>
+ <source>Wallet passphrase was successfully changed.</source>
+ <translation>지갑 암호문이 성공적으로 변경되었습니다.</translation>
+ </message>
+ <message>
+ <source>Warning: The Caps Lock key is on!</source>
+ <translation>주의: Caps Lock이 켜져있습니다!</translation>
+ </message>
+</context>
+<context>
+ <name>BanTableModel</name>
+ <message>
+ <source>IP/Netmask</source>
+ <translation>IP/Netmask</translation>
+ </message>
+ <message>
+ <source>Banned Until</source>
+ <translation>특정 시점까지 차단</translation>
+ </message>
+</context>
+<context>
+ <name>BitcoinGUI</name>
+ <message>
+ <source>Sign &amp;message...</source>
+ <translation>&amp;메시지에 서명하기...</translation>
+ </message>
+ <message>
+ <source>Synchronizing with network...</source>
+ <translation>네트워크와 동기화중...</translation>
+ </message>
+ <message>
+ <source>&amp;Overview</source>
+ <translation>&amp;개요</translation>
+ </message>
+ <message>
+ <source>Node</source>
+ <translation>노드</translation>
+ </message>
+ <message>
+ <source>Show general overview of wallet</source>
+ <translation>지갑의 일반적인 개요 표시</translation>
+ </message>
+ <message>
+ <source>&amp;Transactions</source>
+ <translation>&amp;거래</translation>
+ </message>
+ <message>
+ <source>Browse transaction history</source>
+ <translation>거래 기록 보기</translation>
+ </message>
+ <message>
+ <source>E&amp;xit</source>
+ <translation>&amp;나가기</translation>
+ </message>
+ <message>
+ <source>Quit application</source>
+ <translation>애플리케이션 종료</translation>
+ </message>
+ <message>
+ <source>&amp;About %1</source>
+ <translation>&amp;%1에 대해</translation>
+ </message>
+ <message>
+ <source>Show information about %1</source>
+ <translation>%1에 대한 정보 표시</translation>
+ </message>
+ <message>
+ <source>About &amp;Qt</source>
+ <translation>&amp;Qt에 대해</translation>
+ </message>
+ <message>
+ <source>Show information about Qt</source>
+ <translation>Qt에 대한 정보 표시</translation>
+ </message>
+ <message>
+ <source>&amp;Options...</source>
+ <translation>&amp;설정...</translation>
+ </message>
+ <message>
+ <source>Modify configuration options for %1</source>
+ <translation>%1에 대한 구성 수정</translation>
+ </message>
+ <message>
+ <source>&amp;Encrypt Wallet...</source>
+ <translation>&amp;지갑 암호화...</translation>
+ </message>
+ <message>
+ <source>&amp;Backup Wallet...</source>
+ <translation>&amp;지갑 백업...</translation>
+ </message>
+ <message>
+ <source>&amp;Change Passphrase...</source>
+ <translation>&amp;암호문 변경...</translation>
+ </message>
+ <message>
+ <source>&amp;Sending addresses...</source>
+ <translation>&amp;보낼 주소...</translation>
+ </message>
+ <message>
+ <source>&amp;Receiving addresses...</source>
+ <translation>&amp;받을 주소...</translation>
+ </message>
+ <message>
+ <source>Open &amp;URI...</source>
+ <translation>&amp;URL 열기</translation>
+ </message>
+ <message>
+ <source>Wallet:</source>
+ <translation>지갑:</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>기본 지갑</translation>
+ </message>
+ <message>
+ <source>Click to disable network activity.</source>
+ <translation>클릭해서 네트워크 활동 중지</translation>
+ </message>
+ <message>
+ <source>Network activity disabled.</source>
+ <translation>네트워크 활동 중지</translation>
+ </message>
+ <message>
+ <source>Click to enable network activity again.</source>
+ <translation>클릭해서 네트워크 활동 활성화</translation>
+ </message>
+ <message>
+ <source>Syncing Headers (%1%)...</source>
+ <translation>헤더 동기화 (%1%)...</translation>
+ </message>
+ <message>
+ <source>Reindexing blocks on disk...</source>
+ <translation>디스크에서 블럭 불러오는 중</translation>
+ </message>
+ <message>
+ <source>Send coins to a Bitcoin address</source>
+ <translation>비트코인 주소로 코인 보내기</translation>
+ </message>
+ <message>
+ <source>Backup wallet to another location</source>
+ <translation>이 지갑을 다른 곳으로 백업</translation>
+ </message>
+ <message>
+ <source>Change the passphrase used for wallet encryption</source>
+ <translation>지갑 암호화에 사용될 암호문 바꾸기</translation>
+ </message>
+ </context>
+<context>
+ <name>CoinControlDialog</name>
+ </context>
+<context>
+ <name>EditAddressDialog</name>
+ </context>
+<context>
+ <name>FreespaceChecker</name>
+ </context>
+<context>
+ <name>HelpMessageDialog</name>
+ </context>
+<context>
+ <name>Intro</name>
+ </context>
+<context>
+ <name>ModalOverlay</name>
+ </context>
+<context>
+ <name>OpenURIDialog</name>
+ </context>
+<context>
+ <name>OptionsDialog</name>
+ </context>
+<context>
+ <name>OverviewPage</name>
+ </context>
+<context>
+ <name>PaymentServer</name>
+ </context>
+<context>
+ <name>PeerTableModel</name>
+ </context>
+<context>
+ <name>QObject</name>
+ </context>
+<context>
+ <name>QObject::QObject</name>
+ </context>
+<context>
+ <name>QRImageWidget</name>
+ </context>
+<context>
+ <name>RPCConsole</name>
+ <message>
+ <source>default wallet</source>
+ <translation>기본 지갑</translation>
+ </message>
+ </context>
+<context>
+ <name>ReceiveCoinsDialog</name>
+ </context>
+<context>
+ <name>ReceiveRequestDialog</name>
+ </context>
+<context>
+ <name>RecentRequestsTableModel</name>
+ </context>
+<context>
+ <name>SendCoinsDialog</name>
+ </context>
+<context>
+ <name>SendCoinsEntry</name>
+ </context>
+<context>
+ <name>SendConfirmationDialog</name>
+ </context>
+<context>
+ <name>ShutdownWindow</name>
+ </context>
+<context>
+ <name>SignVerifyMessageDialog</name>
+ </context>
+<context>
+ <name>SplashScreen</name>
+ </context>
+<context>
+ <name>TrafficGraphWidget</name>
+ </context>
+<context>
+ <name>TransactionDesc</name>
+ </context>
+<context>
+ <name>TransactionDescDialog</name>
+ </context>
+<context>
+ <name>TransactionTableModel</name>
+ </context>
+<context>
+ <name>TransactionView</name>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>내보내기 실패</translation>
+ </message>
+ </context>
+<context>
+ <name>UnitDisplayStatusBarControl</name>
+ </context>
+<context>
+ <name>WalletFrame</name>
+ </context>
+<context>
+ <name>WalletModel</name>
+ </context>
+<context>
+ <name>WalletView</name>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp;내보내기</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>현재 탭의 데이터를 파일로 내보냅니다</translation>
+ </message>
+ </context>
+<context>
+ <name>bitcoin-core</name>
+ <message>
+ <source>Transaction amounts must not be negative</source>
+ <translation>송금액은 마이너스가 될 수 없습니다</translation>
+ </message>
+ </context>
+</TS> \ No newline at end of file
diff --git a/src/qt/locale/bitcoin_ko_KR.ts b/src/qt/locale/bitcoin_ko_KR.ts
index a7a0767464..76ac713e05 100644
--- a/src/qt/locale/bitcoin_ko_KR.ts
+++ b/src/qt/locale/bitcoin_ko_KR.ts
@@ -3108,7 +3108,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Could not commit transaction</source>
- <translation>트랜잭션을 커밋 할 수 없습니다.</translation>
+ <translation>거래를 커밋 할 수 없습니다.</translation>
</message>
</context>
<context>
@@ -3337,6 +3337,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>유효하지 않은 금액 -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>디렉토리 "%s"에 지정한 블록들이 존재하지 않습니다.</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>txindex 데이터베이스 업테이트중</translation>
</message>
@@ -3489,11 +3493,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>애러: 지정한 지갑 폴더 "%s"은 디렉토리가 아닙니다.</translation>
</message>
<message>
- <source>Specified blocks directory "%s" does not exist.
-</source>
- <translation>지정한 블록 폴더 "%s"는 존재하지 않습니다.</translation>
- </message>
- <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>거래액이 수수료를 지불하기엔 너무 작습니다</translation>
</message>
diff --git a/src/qt/locale/bitcoin_lt.ts b/src/qt/locale/bitcoin_lt.ts
index b4207524c9..26d89de8e9 100644
--- a/src/qt/locale/bitcoin_lt.ts
+++ b/src/qt/locale/bitcoin_lt.ts
@@ -3,7 +3,7 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>Spustelėkite dešinįjį klavišą norint keisti adresą arba etiketę</translation>
+ <translation>Spustelėkite dešinįjį klaviša norint keisti adresą arba etiketę</translation>
</message>
<message>
<source>Create a new address</source>
@@ -49,14 +49,74 @@
<source>Choose the address to send coins to</source>
<translation>Pasirinkite adresą, kuriam siūsite monetas</translation>
</message>
- </context>
+ <message>
+ <source>Choose the address to receive coins with</source>
+ <translation>Pasirinkite adresą su kuriuo gauti monetas</translation>
+ </message>
+ <message>
+ <source>C&amp;hoose</source>
+ <translation>P&amp;asirinkti</translation>
+ </message>
+ <message>
+ <source>Sending addresses</source>
+ <translation>Išsiuntimo adresai</translation>
+ </message>
+ <message>
+ <source>Receiving addresses</source>
+ <translation>Gavimo adresai</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
+ <translation>Tai yra jūsų Bitcoin adresai išeinantiems mokėjimams. Visada pasitikrinkite sumą ir gavėjo adresą prieš siunčiant lėšas. </translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
+ <translation>Tai yra Jūsų Bitcoin adresai įeinantiems mokėjimams. Kiekvienam mokėjimui rekomenduojama naudoti naują adresą. </translation>
+ </message>
+ <message>
+ <source>&amp;Copy Address</source>
+ <translation>&amp;Kopijuoti adresą</translation>
+ </message>
+ <message>
+ <source>Copy &amp;Label</source>
+ <translation>Kopijuoti &amp;Žymę</translation>
+ </message>
+ <message>
+ <source>&amp;Edit</source>
+ <translation>&amp;Keisti</translation>
+ </message>
+ <message>
+ <source>Export Address List</source>
+ <translation>Eksportuoti adresų sąrašą</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>Kableliais atskirtų duomenų failas (*.csv)</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Eksportavimas nepavyko</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the address list to %1. Please try again.</source>
+ <translation>Bandant išsaugoti adresų sąrašą - įvyko klaida keliant į %1. Prašome bandyti dar kartą.</translation>
+ </message>
+</context>
<context>
<name>AddressTableModel</name>
<message>
+ <source>Label</source>
+ <translation>Žymė</translation>
+ </message>
+ <message>
<source>Address</source>
<translation>Adresas</translation>
</message>
- </context>
+ <message>
+ <source>(no label)</source>
+ <translation>(nėra žymės)</translation>
+ </message>
+</context>
<context>
<name>AskPassphraseDialog</name>
<message>
@@ -75,7 +135,95 @@
<source>Repeat new passphrase</source>
<translation>Pakartokite naują slaptafrazę</translation>
</message>
- </context>
+ <message>
+ <source>Show password</source>
+ <translation>Rodyti slaptažodį</translation>
+ </message>
+ <message>
+ <source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
+ <translation>Įveskite naują piniginės slaptafrazę.&lt;br/&gt;Prašome naudoti slaptafrazę iš &lt;b&gt; 10 ar daugiau atsitiktinių simbolių&lt;/b&gt; arba &lt;b&gt;aštuonių ar daugiau žodžių&lt;/b&gt;.</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>Užšifruoti piniginę</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>Ši operacija reikalauja jūsų piniginės slaptafrazės jai atrakinti.</translation>
+ </message>
+ <message>
+ <source>Unlock wallet</source>
+ <translation>Atrakinti piniginę</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>Ši operacija reikalauja jūsų piniginės slaptafrazės jai iššifruoti.</translation>
+ </message>
+ <message>
+ <source>Decrypt wallet</source>
+ <translation>Iššifruoti piniginę</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>Pakeisti slaptafrazę</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase to the wallet.</source>
+ <translation>Įveskite seną ir naują piniginės slaptafrazes.</translation>
+ </message>
+ <message>
+ <source>Confirm wallet encryption</source>
+ <translation>Patvirtinkite piniginės užšifravimą</translation>
+ </message>
+ <message>
+ <source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
+ <translation>Dėmesio: jei užšifruosite savo piniginę ir pamesite slaptafrazę, jūs&lt;b&gt;PRARASITE VISUS SAVO BITCOINUS&lt;/b&gt;! </translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to encrypt your wallet?</source>
+ <translation>Ar tikrai norite šifruoti savo piniginę?</translation>
+ </message>
+ <message>
+ <source>Wallet encrypted</source>
+ <translation>Piniginė užšifruota</translation>
+ </message>
+ <message>
+ <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
+ <translation>SVARBU: Betkokios ankstesnės jūsų piniginės atsarginės kopijos turėtų būti pakeistos naujai sugeneruotu, užšifruotu piniginės failu. Dėl saugumo sumetimų, anstesnės neužšifruotos piniginės kopijos failas taps nenaudingu nuo momento, kai nauja ir užšifruota piniginė bus pradėta naudoti. </translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed</source>
+ <translation>Nepavyko užšifruoti piniginę</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <translation>Dėl vidinės klaidos nepavyko užšifruoti piniginę.Piniginė neužšifruota.</translation>
+ </message>
+ <message>
+ <source>The supplied passphrases do not match.</source>
+ <translation>Įvestos slaptafrazės nesutampa.</translation>
+ </message>
+ <message>
+ <source>Wallet unlock failed</source>
+ <translation>Nepavyko atrakinti piniginę</translation>
+ </message>
+ <message>
+ <source>The passphrase entered for the wallet decryption was incorrect.</source>
+ <translation>Neteisingai įvestas slaptažodis piniginės iššifravimui.</translation>
+ </message>
+ <message>
+ <source>Wallet decryption failed</source>
+ <translation>Nepavyko iššifruoti piniginės</translation>
+ </message>
+ <message>
+ <source>Wallet passphrase was successfully changed.</source>
+ <translation>Piniginės slaptažodis sėkmingai pakeistas.</translation>
+ </message>
+ <message>
+ <source>Warning: The Caps Lock key is on!</source>
+ <translation>Įspėjimas: įjungtas Caps Lock klavišas!</translation>
+ </message>
+</context>
<context>
<name>BanTableModel</name>
<message>
@@ -142,6 +290,10 @@
<translation>&amp;Parinktys...</translation>
</message>
<message>
+ <source>Modify configuration options for %1</source>
+ <translation>Keisti %1 konfigūracijos galimybes</translation>
+ </message>
+ <message>
<source>&amp;Encrypt Wallet...</source>
<translation>&amp;Užšifruoti piniginę...</translation>
</message>
@@ -166,6 +318,30 @@
<translation>Atidaryti &amp;URI...</translation>
</message>
<message>
+ <source>Wallet:</source>
+ <translation>Piniginė</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>numatyta piniginė</translation>
+ </message>
+ <message>
+ <source>Click to disable network activity.</source>
+ <translation>Spauskite norėdami išjungti tinklo veiklą.</translation>
+ </message>
+ <message>
+ <source>Network activity disabled.</source>
+ <translation>Tinklo veikla išjungta.</translation>
+ </message>
+ <message>
+ <source>Click to enable network activity again.</source>
+ <translation>Spauskite norėdami įjungti tinklo veiklą.</translation>
+ </message>
+ <message>
+ <source>Syncing Headers (%1%)...</source>
+ <translation>Sinchronizuojamos Antraštės (%1%)...</translation>
+ </message>
+ <message>
<source>Reindexing blocks on disk...</source>
<translation>Blokai iš naujo indeksuojami...</translation>
</message>
@@ -222,6 +398,14 @@
<translation>Užšifruoti privačius raktus, kurie priklauso jūsų piniginei</translation>
</message>
<message>
+ <source>Sign messages with your Bitcoin addresses to prove you own them</source>
+ <translation>Pasirašydami žinutes su savo Bitcoin adresais įrodysite jog esate jų savininkas </translation>
+ </message>
+ <message>
+ <source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
+ <translation>Patikrinkite žinutę, jog įsitikintumėte, kad ją pasirašė nurodytas Bitcoin adresas</translation>
+ </message>
+ <message>
<source>&amp;File</source>
<translation>&amp;Failas</translation>
</message>
@@ -242,6 +426,18 @@
<translation>Komandinės eilutės parametrai</translation>
</message>
<message>
+ <source>Processing blocks on disk...</source>
+ <translation>Blokai apdirbami diske...</translation>
+ </message>
+ <message>
+ <source>Last received block was generated %1 ago.</source>
+ <translation>Paskutinis gautas blokas buvo sukurtas prieš %1.</translation>
+ </message>
+ <message>
+ <source>Transactions after this will not yet be visible.</source>
+ <translation>Sekančios operacijos dar nebus matomos. </translation>
+ </message>
+ <message>
<source>Error</source>
<translation>Klaida</translation>
</message>
@@ -258,6 +454,10 @@
<translation>Atnaujinta</translation>
</message>
<message>
+ <source>%1 client</source>
+ <translation>%1 klientas</translation>
+ </message>
+ <message>
<source>Catching up...</source>
<translation>Vejamasi...</translation>
</message>
@@ -268,6 +468,36 @@
</translation>
</message>
<message>
+ <source>Amount: %1
+</source>
+ <translation>Suma: %1
+</translation>
+ </message>
+ <message>
+ <source>Wallet: %1
+</source>
+ <translation>Piniginė: %1
+</translation>
+ </message>
+ <message>
+ <source>Type: %1
+</source>
+ <translation>Spausti: %1
+</translation>
+ </message>
+ <message>
+ <source>Label: %1
+</source>
+ <translation>Antraštė: %1
+</translation>
+ </message>
+ <message>
+ <source>Address: %1
+</source>
+ <translation>Adresas: %1
+</translation>
+ </message>
+ <message>
<source>Sent transaction</source>
<translation>Sandoris nusiųstas</translation>
</message>
@@ -342,6 +572,50 @@
<source>Confirmed</source>
<translation>Patvirtintas</translation>
</message>
+ <message>
+ <source>Copy address</source>
+ <translation>Kopijuoti adresą</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Kopijuoti žymę</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Kopijuoti sumą</translation>
+ </message>
+ <message>
+ <source>Copy transaction ID</source>
+ <translation>Sandorio ID</translation>
+ </message>
+ <message>
+ <source>Copy fee</source>
+ <translation>Kopijuoti mokestį</translation>
+ </message>
+ <message>
+ <source>Copy after fee</source>
+ <translation>Kopijuoti po mokesčio</translation>
+ </message>
+ <message>
+ <source>Copy bytes</source>
+ <translation>Kopijuoti baitus</translation>
+ </message>
+ <message>
+ <source>Copy change</source>
+ <translation>Kopijuoti keisti</translation>
+ </message>
+ <message>
+ <source>yes</source>
+ <translation>taip</translation>
+ </message>
+ <message>
+ <source>no</source>
+ <translation>ne</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(nėra žymės)</translation>
+ </message>
</context>
<context>
<name>EditAddressDialog</name>
@@ -387,6 +661,10 @@
<translation>Bitcoin</translation>
</message>
<message>
+ <source>The wallet will also be stored in this directory.</source>
+ <translation>Piniginė taip pat bus saugojama šiame direktyve.</translation>
+ </message>
+ <message>
<source>Error</source>
<translation>Klaida</translation>
</message>
@@ -701,6 +979,10 @@
<translation>Išvalyti konsolę</translation>
</message>
<message>
+ <source>default wallet</source>
+ <translation>numatyta piniginė</translation>
+ </message>
+ <message>
<source>never</source>
<translation>Niekada</translation>
</message>
@@ -731,7 +1013,15 @@
<source>Clear</source>
<translation>Išvalyti</translation>
</message>
- </context>
+ <message>
+ <source>Copy label</source>
+ <translation>Kopijuoti žymę</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Kopijuoti sumą</translation>
+ </message>
+</context>
<context>
<name>ReceiveRequestDialog</name>
<message>
@@ -747,12 +1037,24 @@
<translation>Adresas</translation>
</message>
<message>
+ <source>Label</source>
+ <translation>Žymė</translation>
+ </message>
+ <message>
<source>Wallet</source>
<translation>Piniginė</translation>
</message>
</context>
<context>
<name>RecentRequestsTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>Žymė</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(nėra žymės)</translation>
+ </message>
</context>
<context>
<name>SendCoinsDialog</name>
@@ -817,10 +1119,34 @@
<translation>&amp;Siųsti</translation>
</message>
<message>
+ <source>Copy amount</source>
+ <translation>Kopijuoti sumą</translation>
+ </message>
+ <message>
+ <source>Copy fee</source>
+ <translation>Kopijuoti mokestį</translation>
+ </message>
+ <message>
+ <source>Copy after fee</source>
+ <translation>Kopijuoti po mokesčio</translation>
+ </message>
+ <message>
+ <source>Copy bytes</source>
+ <translation>Kopijuoti baitus</translation>
+ </message>
+ <message>
+ <source>Copy change</source>
+ <translation>Kopijuoti keisti</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>Sandorio mokestis</translation>
</message>
- </context>
+ <message>
+ <source>(no label)</source>
+ <translation>(nėra žymės)</translation>
+ </message>
+</context>
<context>
<name>SendCoinsEntry</name>
<message>
@@ -935,13 +1261,49 @@
</context>
<context>
<name>TransactionTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>Žymė</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(nėra žymės)</translation>
+ </message>
</context>
<context>
<name>TransactionView</name>
<message>
+ <source>Copy address</source>
+ <translation>Kopijuoti adresą</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Kopijuoti žymę</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Kopijuoti sumą</translation>
+ </message>
+ <message>
+ <source>Copy transaction ID</source>
+ <translation>Sandorio ID</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>Kableliais atskirtų duomenų failas (*.csv)</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Žymė</translation>
+ </message>
+ <message>
<source>Address</source>
<translation>Adresas</translation>
</message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Eksportavimas nepavyko</translation>
+ </message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
diff --git a/src/qt/locale/bitcoin_ml.ts b/src/qt/locale/bitcoin_ml.ts
new file mode 100644
index 0000000000..fdd3bd74bd
--- /dev/null
+++ b/src/qt/locale/bitcoin_ml.ts
@@ -0,0 +1,336 @@
+<TS language="ml" version="2.1">
+<context>
+ <name>AddressBookPage</name>
+ <message>
+ <source>Right-click to edit address or label</source>
+ <translation>വിലാസം അല്ലെങ്കിൽ ലേബൽ എഡിറ്റുചെയ്യാൻ വലത് ക്ലിക്കുചെയ്യുക</translation>
+ </message>
+ <message>
+ <source>Create a new address</source>
+ <translation>ഒരു പുതിയ വിലാസം സൃഷ്ടിക്കുക</translation>
+ </message>
+ <message>
+ <source>&amp;New</source>
+ <translation>&amp;പുതിയത്</translation>
+ </message>
+ <message>
+ <source>Copy the currently selected address to the system clipboard</source>
+ <translation>നിലവിൽ തിരഞ്ഞെടുത്ത വിലാസം സിസ്റ്റം ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തുക</translation>
+ </message>
+ <message>
+ <source>&amp;Copy</source>
+ <translation>&amp; പകർത്തുക
+</translation>
+ </message>
+ <message>
+ <source>C&amp;lose</source>
+ <translation>അ&amp;ടയ്ക്കുക</translation>
+ </message>
+ <message>
+ <source>Delete the currently selected address from the list</source>
+ <translation>പട്ടികയിൽ നിന്ന് നിലവിൽ തിരഞ്ഞെടുത്ത വിലാസം ഇല്ലാതാക്കുക</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>നിലവിലെ ടാബിൽ ഒരു ഫയലിൽ ഡാറ്റ എക്സ്പോർട്ട് ചെയ്യുക</translation>
+ </message>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp; കയറ്റുമതി ചെയ്യുക</translation>
+ </message>
+ <message>
+ <source>&amp;Delete</source>
+ <translation>&amp;ഇല്ലാതാക്കുക</translation>
+ </message>
+ <message>
+ <source>Choose the address to send coins to</source>
+ <translation>നാണയങ്ങൾ അയയ്ക്കാനുള്ള വിലാസം തിരഞ്ഞെടുക്കുക</translation>
+ </message>
+ <message>
+ <source>Choose the address to receive coins with</source>
+ <translation>നാണയങ്ങൾ സ്വീകരിക്കാൻ വിലാസം തിരഞ്ഞെടുക്കുക</translation>
+ </message>
+ <message>
+ <source>C&amp;hoose</source>
+ <translation>തി&amp;രഞ്ഞെടുക്കുക</translation>
+ </message>
+ <message>
+ <source>Sending addresses</source>
+ <translation>വിലാസങ്ങൾ അയയ്ക്കുന്നു</translation>
+ </message>
+ <message>
+ <source>Receiving addresses</source>
+ <translation>സ്വീകരിക്കുന്ന വിലാസങ്ങൾ</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
+ <translation>പേയ്മെന്റുകൾ അയയ്ക്കുന്നതിനുള്ള നിങ്ങളുടെ ബിറ്റ്കോയിൻ വിലാസങ്ങളാണ് ഇവ. നാണയങ്ങൾ അയയ്ക്കുന്നതിനുമുമ്പ് എല്ലായ്പ്പോഴും തുകയും സ്വീകരിക്കുന്ന വിലാസവും പരിശോധിക്കുക.</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
+ <translation>പേയ്മെന്റ് സ്വീകരിക്കുന്നതിന് നിങ്ങളുടെ ബിറ്റ്കോയിൻ വിലാസങ്ങളാണ് ഇവ. ഓരോ ഇടപാടിനും പുതിയ സ്വീകരിക്കുന്ന വിലാസം ഉപയോഗിക്കുന്നതാണ് ഉചിതം.</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Address</source>
+ <translation>&amp;വിലാസം പകർത്തുക</translation>
+ </message>
+ <message>
+ <source>Copy &amp;Label</source>
+ <translation>പകർത്തുക &amp;ലേബൽ</translation>
+ </message>
+ <message>
+ <source>&amp;Edit</source>
+ <translation>&amp;ചിട്ടപ്പെടുത്തുക</translation>
+ </message>
+ <message>
+ <source>Export Address List</source>
+ <translation>കയറ്റുമതി വിലാസ ലിസ്റ്റ്</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>കോമയാൽ വേർതിരിച്ച ഫയൽ (* .csv)</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>കയറ്റുമതി പരാജയപ്പെട്ടു</translation>
+ </message>
+ </context>
+<context>
+ <name>AddressTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>ലേബൽ</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>വിലാസം</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(ലേബൽ ഇല്ല)</translation>
+ </message>
+</context>
+<context>
+ <name>AskPassphraseDialog</name>
+ <message>
+ <source>Passphrase Dialog</source>
+ <translation>രഹസ്യപദപ്രയോഗ സംഭാഷണം</translation>
+ </message>
+ <message>
+ <source>Enter passphrase</source>
+ <translation>രഹസ്യപദപ്രയോഗം നൽകുക</translation>
+ </message>
+ <message>
+ <source>New passphrase</source>
+ <translation>പുതിയ രഹസ്യപദപ്രയോഗം</translation>
+ </message>
+ <message>
+ <source>Repeat new passphrase</source>
+ <translation>പുതിയ രഹസ്യപദപ്രയോഗം ആവർത്തിക്കുക</translation>
+ </message>
+ <message>
+ <source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
+ <translation>വാലറ്റിൽ പുതിയ രഹസ്യപദപ്രയോഗം നൽകുക. &lt;br/&gt; &lt;b&gt; പത്തോ അതിലധികമോ റാൻഡം പ്രതീകങ്ങൾ &lt;/ b&gt; അല്ലെങ്കിൽ &lt;b&gt; എട്ട് അല്ലെങ്കിൽ അതിൽ കൂടുതൽ വാക്കുകൾ &lt;/ b&gt; ഒരു രഹസ്യപദപ്രയോഗം ഉപയോഗിക്കുക.</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>വാലറ്റ് എൻക്രിപ്റ്റ് ചെയ്യുക</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>നിങ്ങളുടെ വാലറ്റ് അൺലോക്കുചെയ്യാൻ ഈ പ്രവർത്തനത്തിന് നിങ്ങളുടെ വാലറ്റ് രഹസ്യപദപ്രയോഗം ആവശ്യമാണ്.</translation>
+ </message>
+ <message>
+ <source>Unlock wallet</source>
+ <translation>വാലറ്റ് അൺലോക്ക് ചെയ്യുക</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>ഈ പ്രവർത്തനത്തിന് വാലറ്റ് ഡീക്രിപ്റ്റ് ചെയ്യുന്നതിന് നിങ്ങളുടെ വാലറ്റ് പാസ്ഫ്രെയ്സ് ആവശ്യമാണ്.</translation>
+ </message>
+ <message>
+ <source>Decrypt wallet</source>
+ <translation>വാലറ്റ് ഡീക്രിപ്റ്റ് ചെയ്യുക</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>പാസ്ഫ്രെയ്സ് മാറ്റുക</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase to the wallet.</source>
+ <translation>പഴയ രഹസ്യപദപ്രയോഗം പുതിയ രഹസ്യപദപ്രയോഗം വാലറ്റിൽ നൽകുക.</translation>
+ </message>
+ <message>
+ <source>Confirm wallet encryption</source>
+ <translation>വാലറ്റ് എൻക്രിപ്ഷൻ സ്ഥിരീകരിക്കുക</translation>
+ </message>
+ <message>
+ <source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
+ <translation>മുന്നറിയിപ്പ്: നിങ്ങളുടെ വാലറ്റ് എൻക്രിപ്റ്റ് ചെയ്ത് പാസ്ഫ്രെയ്സ് നഷ്ടപ്പെടുകയാണെങ്കിൽ, നിങ്ങളുടെ എല്ലാ ബിറ്റ്കൊയിനുകളും നഷ്ടപ്പെടും!</translation>
+ </message>
+ </context>
+<context>
+ <name>BanTableModel</name>
+ </context>
+<context>
+ <name>BitcoinGUI</name>
+ </context>
+<context>
+ <name>CoinControlDialog</name>
+ <message>
+ <source>(no label)</source>
+ <translation>(ലേബൽ ഇല്ല)</translation>
+ </message>
+ </context>
+<context>
+ <name>EditAddressDialog</name>
+ </context>
+<context>
+ <name>FreespaceChecker</name>
+ </context>
+<context>
+ <name>HelpMessageDialog</name>
+ </context>
+<context>
+ <name>Intro</name>
+ </context>
+<context>
+ <name>ModalOverlay</name>
+ </context>
+<context>
+ <name>OpenURIDialog</name>
+ </context>
+<context>
+ <name>OptionsDialog</name>
+ </context>
+<context>
+ <name>OverviewPage</name>
+ </context>
+<context>
+ <name>PaymentServer</name>
+ </context>
+<context>
+ <name>PeerTableModel</name>
+ </context>
+<context>
+ <name>QObject</name>
+ </context>
+<context>
+ <name>QObject::QObject</name>
+ </context>
+<context>
+ <name>QRImageWidget</name>
+ </context>
+<context>
+ <name>RPCConsole</name>
+ </context>
+<context>
+ <name>ReceiveCoinsDialog</name>
+ </context>
+<context>
+ <name>ReceiveRequestDialog</name>
+ <message>
+ <source>Address</source>
+ <translation>വിലാസം</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>ലേബൽ</translation>
+ </message>
+ </context>
+<context>
+ <name>RecentRequestsTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>ലേബൽ</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(ലേബൽ ഇല്ല)</translation>
+ </message>
+ </context>
+<context>
+ <name>SendCoinsDialog</name>
+ <message>
+ <source>(no label)</source>
+ <translation>(ലേബൽ ഇല്ല)</translation>
+ </message>
+</context>
+<context>
+ <name>SendCoinsEntry</name>
+ </context>
+<context>
+ <name>SendConfirmationDialog</name>
+ </context>
+<context>
+ <name>ShutdownWindow</name>
+ </context>
+<context>
+ <name>SignVerifyMessageDialog</name>
+ </context>
+<context>
+ <name>SplashScreen</name>
+ </context>
+<context>
+ <name>TrafficGraphWidget</name>
+ </context>
+<context>
+ <name>TransactionDesc</name>
+ </context>
+<context>
+ <name>TransactionDescDialog</name>
+ </context>
+<context>
+ <name>TransactionTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>ലേബൽ</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(ലേബൽ ഇല്ല)</translation>
+ </message>
+ </context>
+<context>
+ <name>TransactionView</name>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>കോമയാൽ വേർതിരിച്ച ഫയൽ (* .csv)</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>ലേബൽ</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>വിലാസം</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>കയറ്റുമതി പരാജയപ്പെട്ടു</translation>
+ </message>
+ </context>
+<context>
+ <name>UnitDisplayStatusBarControl</name>
+ </context>
+<context>
+ <name>WalletFrame</name>
+ </context>
+<context>
+ <name>WalletModel</name>
+ </context>
+<context>
+ <name>WalletView</name>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp; കയറ്റുമതി ചെയ്യുക</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>നിലവിലെ ടാബിൽ ഒരു ഫയലിൽ ഡാറ്റ എക്സ്പോർട്ട് ചെയ്യുക</translation>
+ </message>
+ </context>
+<context>
+ <name>bitcoin-core</name>
+ </context>
+</TS> \ No newline at end of file
diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts
index b495eeaf51..152d89f878 100644
--- a/src/qt/locale/bitcoin_nl.ts
+++ b/src/qt/locale/bitcoin_nl.ts
@@ -3200,6 +3200,10 @@ Notitie: Omdat de vergoeding per byte wordt gerekend, zal een vergoeding van "10
<translation>Waarschuwing: Fout bij het lezen van %s! Alle sleutels zijn in goede orde uitgelezen, maar transactiedata of adresboeklemma's zouden kunnen ontbreken of fouten bevatten.</translation>
</message>
<message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>Groepeer outputs per adres, alles of niets selecterend, in plaats van per output. Privacy wordt verbeterd omdat een adres slechts één keer wordt gebruikt (tenzij iemand ernaar stuurt na ermee te betalen), maar kan resulteren in iets hogere fees, als gevolg van suboptimale coin selectie ten gevolge van de toegevoegde beperking (standaard: %u)</translation>
+ </message>
+ <message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation>Waarschuwing: Controleer dat de datum en tijd van uw computer correct zijn ingesteld! Bij een onjuist ingestelde klok zal %s niet goed werken.</translation>
</message>
@@ -3340,6 +3344,10 @@ Notitie: Omdat de vergoeding per byte wordt gerekend, zal een vergoeding van "10
<translation>Ongeldig bedrag voor -fallbackfee=&lt;bedrag&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Opgegeven blocks map "%s" bestaat niet.</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Upgraden txindex database</translation>
</message>
@@ -3492,12 +3500,6 @@ Notitie: Omdat de vergoeding per byte wordt gerekend, zal een vergoeding van "10
<translation>Opgegeven -walletdir "%s" is geen map</translation>
</message>
<message>
- <source>Specified blocks directory "%s" does not exist.
-</source>
- <translation>Opgegeven blocks map "%s" bestaat niet.
-</translation>
- </message>
- <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>Het transactiebedrag is te klein om transactiekosten in rekening te brengen</translation>
</message>
diff --git a/src/qt/locale/bitcoin_pl.ts b/src/qt/locale/bitcoin_pl.ts
index 0026841975..03474f4df5 100644
--- a/src/qt/locale/bitcoin_pl.ts
+++ b/src/qt/locale/bitcoin_pl.ts
@@ -3342,6 +3342,11 @@ Zwróć uwagę, że poprawnie zweryfikowana wiadomość potwierdza to, że nadaw
<translation>Nieprawidłowa kwota dla -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Podany folder bloków "%s" nie istnieje.
+</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Aktualizowanie bazy txindex</translation>
</message>
@@ -3494,12 +3499,6 @@ Zwróć uwagę, że poprawnie zweryfikowana wiadomość potwierdza to, że nadaw
<translation>Podany -walletdir "%s" nie jest katalogiem</translation>
</message>
<message>
- <source>Specified blocks directory "%s" does not exist.
-</source>
- <translation>Podany folder bloków "%s" nie istnieje.
-</translation>
- </message>
- <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>Zbyt niska kwota transakcji by zapłacić opłatę</translation>
</message>
diff --git a/src/qt/locale/bitcoin_pt_BR.ts b/src/qt/locale/bitcoin_pt_BR.ts
index 3877fee25d..9fa0f88ac3 100644
--- a/src/qt/locale/bitcoin_pt_BR.ts
+++ b/src/qt/locale/bitcoin_pt_BR.ts
@@ -3,7 +3,7 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>Clique com o botão direito para editar o endereço ou rótulo</translation>
+ <translation>Clique com o botão direito para editar o endereço ou rótulo </translation>
</message>
<message>
<source>Create a new address</source>
@@ -3336,6 +3336,11 @@ Nota: Como a taxa é calculada por byte, uma taxa de "100 satoshis por kB" por
<translation>Quantidade inválida para -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>
+Diretório de blocos especificados "%s" não existe.</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Atualizando banco de dados txindex</translation>
</message>
@@ -3488,12 +3493,6 @@ Nota: Como a taxa é calculada por byte, uma taxa de "100 satoshis por kB" por
<translation>O -walletdir "%s" especificado não é um diretório</translation>
</message>
<message>
- <source>Specified blocks directory "%s" does not exist.
-</source>
- <translation>O diretório de blocos especificado "%s" não existe.
-</translation>
- </message>
- <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>A quantidade da transação é pequena demais para pagar a taxa</translation>
</message>
diff --git a/src/qt/locale/bitcoin_pt_PT.ts b/src/qt/locale/bitcoin_pt_PT.ts
index 48068409be..d203c490e9 100644
--- a/src/qt/locale/bitcoin_pt_PT.ts
+++ b/src/qt/locale/bitcoin_pt_PT.ts
@@ -71,7 +71,7 @@
</message>
<message>
<source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
- <translation>Estes são os seus endereços Bitcoin para receber pagamentos. É recomendado que utilize um endereço novo para cada transacção.</translation>
+ <translation>Estes são os seus endereços Bitcoin para receber pagamentos. É recomendado que utilize um endereço novo para cada transação.</translation>
</message>
<message>
<source>&amp;Copy Address</source>
@@ -189,7 +189,7 @@
</message>
<message>
<source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
- <translation>%1 irá agora ser fechado para terminar o processo de encriptação. Recorde que a encriptação da sua carteira não protegerá totalmente os seus bitcoins de serem roubados por programas maliciosos que infectem o seu computador.</translation>
+ <translation>%1 irá agora ser fechado para terminar o processo de encriptação. Recorde que a encriptação da sua carteira não protegerá totalmente os seus bitcoins de serem roubados por programas maliciosos que infetem o seu computador.</translation>
</message>
<message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
@@ -780,7 +780,7 @@
<name>FreespaceChecker</name>
<message>
<source>A new data directory will be created.</source>
- <translation>Será criada uma nova diretoria de dados.</translation>
+ <translation>Será criada uma nova pasta de dados.</translation>
</message>
<message>
<source>name</source>
@@ -792,11 +792,11 @@
</message>
<message>
<source>Path already exists, and is not a directory.</source>
- <translation>O caminho já existe, e este não é uma pasta.</translation>
+ <translation>O caminho já existe, e não é uma pasta.</translation>
</message>
<message>
<source>Cannot create data directory here.</source>
- <translation>Não é possível criar aqui uma diretoria de dados.</translation>
+ <translation>Não é possível criar aqui uma pasta de dados.</translation>
</message>
</context>
<context>
@@ -854,11 +854,11 @@
</message>
<message>
<source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
- <translation>No mínimo %1 GB de dados irão ser armazenados neste diretório. </translation>
+ <translation>No mínimo %1 GB de dados irão ser armazenados nesta pasta. </translation>
</message>
<message>
<source>Approximately %1 GB of data will be stored in this directory.</source>
- <translation>Aproximadamente %1 GB de dados irão ser guardados nesta directoria. </translation>
+ <translation>Aproximadamente %1 GB de dados irão ser guardados nesta pasta. </translation>
</message>
<message>
<source>%1 will download and store a copy of the Bitcoin block chain.</source>
@@ -866,7 +866,7 @@
</message>
<message>
<source>The wallet will also be stored in this directory.</source>
- <translation>A carteira também será guardada nesta directoria.</translation>
+ <translation>A carteira também será guardada nesta pasta.</translation>
</message>
<message>
<source>Error: Specified data directory "%1" cannot be created.</source>
@@ -1024,7 +1024,7 @@
</message>
<message>
<source>Open the %1 configuration file from the working directory.</source>
- <translation>Abrir o ficheiro de configuração %1 da directoria aberta.</translation>
+ <translation>Abrir o ficheiro de configuração %1 da pasta aberta.</translation>
</message>
<message>
<source>Open Configuration File</source>
@@ -1100,11 +1100,11 @@
</message>
<message>
<source>&amp;Port:</source>
- <translation>&amp;Porto:</translation>
+ <translation>&amp;Porta:</translation>
</message>
<message>
<source>Port of the proxy (e.g. 9050)</source>
- <translation>Porto do proxy (p.ex. 9050)</translation>
+ <translation>Porta do proxy (por ex. 9050)</translation>
</message>
<message>
<source>Used for reaching peers via:</source>
@@ -1235,7 +1235,7 @@
</message>
<message>
<source>Watch-only:</source>
- <translation>Modo-verificação:</translation>
+ <translation>Apenas vigiar:</translation>
</message>
<message>
<source>Available:</source>
@@ -1263,7 +1263,7 @@
</message>
<message>
<source>Balances</source>
- <translation>Balanços</translation>
+ <translation>Saldos</translation>
</message>
<message>
<source>Total:</source>
@@ -1271,11 +1271,11 @@
</message>
<message>
<source>Your current total balance</source>
- <translation>O seu saldo total actual</translation>
+ <translation>O seu saldo total atual</translation>
</message>
<message>
<source>Your current balance in watch-only addresses</source>
- <translation>O seu balanço atual em endereços de apenas observação</translation>
+ <translation>O seu balanço atual em endereços de apenas vigiar</translation>
</message>
<message>
<source>Spendable:</source>
@@ -1287,15 +1287,15 @@
</message>
<message>
<source>Unconfirmed transactions to watch-only addresses</source>
- <translation>Transações não confirmadas para endereços modo-verificação</translation>
+ <translation>Transações não confirmadas para endereços de apenas vigiar</translation>
</message>
<message>
<source>Mined balance in watch-only addresses that has not yet matured</source>
- <translation>Saldo minado ainda não disponivél de endereços modo-verificação</translation>
+ <translation>Saldo minado ainda não disponível de endereços de apenas vigiar</translation>
</message>
<message>
<source>Current total balance in watch-only addresses</source>
- <translation>Saldo disponivél em enderços modo-verificação</translation>
+ <translation>Saldo disponível em endereços de apenas vigiar</translation>
</message>
</context>
<context>
@@ -1326,7 +1326,7 @@
</message>
<message>
<source>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source>
- <translation>URI não foi lido correctamente! Isto pode ser causado por um endereço Bitcoin inválido ou por parâmetros URI malformados.</translation>
+ <translation>URI não foi lido corretamente! Isto pode ser causado por um endereço Bitcoin inválido ou por parâmetros URI malformados.</translation>
</message>
<message>
<source>Payment request file handling</source>
@@ -1428,7 +1428,7 @@
</message>
<message>
<source>Enter a Bitcoin address (e.g. %1)</source>
- <translation>Entre um endereço Bitcoin (ex. %1)</translation>
+ <translation>Introduza um endereço Bitcoin (ex. %1)</translation>
</message>
<message>
<source>%1 d</source>
@@ -1519,7 +1519,7 @@
</message>
<message>
<source>Error: Specified data directory "%1" does not exist.</source>
- <translation>Erro: Pasta de dados especificada "%1" não existe.</translation>
+ <translation>Erro: a pasta de dados especificada "%1" não existe.</translation>
</message>
<message>
<source>Error: Cannot parse configuration file: %1.</source>
@@ -1601,7 +1601,7 @@
</message>
<message>
<source>Current number of blocks</source>
- <translation>Número actual de blocos</translation>
+ <translation>Número atual de blocos</translation>
</message>
<message>
<source>Memory Pool</source>
@@ -1609,7 +1609,7 @@
</message>
<message>
<source>Current number of transactions</source>
- <translation>Número actual de transacções</translation>
+ <translation>Número atual de transações</translation>
</message>
<message>
<source>Memory usage</source>
@@ -1653,7 +1653,7 @@
</message>
<message>
<source>Direction</source>
- <translation>Direcção</translation>
+ <translation>Direção</translation>
</message>
<message>
<source>Version</source>
@@ -1677,7 +1677,7 @@
</message>
<message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
- <translation>Abrir o ficheiro de registo de depuração %1 da pasta de dados actual. Isto pode demorar alguns segundos para ficheiros de registo maiores.</translation>
+ <translation>Abrir o ficheiro de registo de depuração %1 da pasta de dados atual. Isto pode demorar alguns segundos para ficheiros de registo maiores.</translation>
</message>
<message>
<source>Decrease font size</source>
@@ -1701,11 +1701,11 @@
</message>
<message>
<source>Last Send</source>
- <translation>Ultimo Envio</translation>
+ <translation>Último Envio</translation>
</message>
<message>
<source>Last Receive</source>
- <translation>Ultimo Recebimento</translation>
+ <translation>Último Recebimento</translation>
</message>
<message>
<source>Ping Time</source>
@@ -1809,7 +1809,7 @@
</message>
<message>
<source>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.</source>
- <translation>AVISO: Burlões têm estado activos, dizendo a utilizadores para digitar comandos aqui, roubando assim os conteúdos das suas carteiras. Não utilize esta consola sem perceber perfeitamente as ramificações de um comando.</translation>
+ <translation>AVISO: alguns burlões têm estado ativos, dizendo a utilizadores para digitarem comandos aqui, roubando assim os conteúdos das suas carteiras. Não utilize esta consola sem perceber perfeitamente as ramificações de um comando.</translation>
</message>
<message>
<source>Network activity disabled</source>
@@ -1896,7 +1896,7 @@
</message>
<message>
<source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
- <translation>Endereços nativos SegWit (também conhecidos como Bech32 ou BIP-173) reduzem as taxas da sua transação mais tarde e oferecem melhor protecção contra erros, mas carteiras antigas não os suportam. Quando não selecionado, um endereço compatível com carteiras antigas irá ser criado em vez.</translation>
+ <translation>Endereços nativos SegWit (também conhecidos como Bech32 ou BIP-173) reduzem as taxas da sua transação mais tarde e oferecem melhor proteção contra erros, mas carteiras antigas não os suportam. Quando não selecionado, um endereço compatível com carteiras antigas irá ser criado em vez.</translation>
</message>
<message>
<source>Generate native segwit (Bech32) address</source>
@@ -1912,7 +1912,7 @@
</message>
<message>
<source>Show the selected request (does the same as double clicking an entry)</source>
- <translation>Mostrar o pedido seleccionado (faz o mesmo que clicar 2 vezes numa entrada)</translation>
+ <translation>Mostrar o pedido selecionado (faz o mesmo que clicar 2 vezes numa entrada)</translation>
</message>
<message>
<source>Show</source>
@@ -1920,7 +1920,7 @@
</message>
<message>
<source>Remove the selected entries from the list</source>
- <translation>Remover as entradas seleccionadas da lista</translation>
+ <translation>Remover as entradas selecionadas da lista</translation>
</message>
<message>
<source>Remove</source>
@@ -2113,7 +2113,7 @@
</message>
<message>
<source>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
- <translation>Pode pagar somente a taxa minima desde que haja um volume de transações inferior ao espaço nos blocos. No entanto tenha em atenção que esta opção poderá acabar em uma transação nunca confirmada assim que os pedidos de transações excedam a capacidade de processamento da rede.</translation>
+ <translation>Pode pagar somente a taxa mínima desde que haja um volume de transações inferior ao espaço nos blocos. No entanto tenha em atenção que esta opção poderá acabar em uma transação nunca confirmada assim que os pedidos de transações excedam a capacidade de processamento da rede.</translation>
</message>
<message>
<source>(read the tooltip)</source>
@@ -2125,7 +2125,7 @@
</message>
<message>
<source>Custom:</source>
- <translation>Uso:</translation>
+ <translation>Personalizado:</translation>
</message>
<message>
<source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
@@ -2442,7 +2442,7 @@
</message>
<message>
<source>Copy the current signature to the system clipboard</source>
- <translation>Copiar a assinatura actual para a área de transferência</translation>
+ <translation>Copiar a assinatura atual para a área de transferência</translation>
</message>
<message>
<source>Sign the message to prove you own this Bitcoin address</source>
@@ -2466,7 +2466,7 @@
</message>
<message>
<source>Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</source>
- <translation>Introduza o endereço de assinatura, mensagem (assegure-se que copia quebras de linha, espaços, tabulações, etc. exactamente) e assinatura abaixo para verificar a mensagem. Tenha atenção para não ler mais na assinatura do que o que estiver na mensagem assinada, para evitar ser enganado por um atacante que se encontre entre si e quem assinou a mensagem.</translation>
+ <translation>Introduza o endereço de assinatura, mensagem (assegure-se que copia quebras de linha, espaços, tabulações, etc. exatamente) e assinatura abaixo para verificar a mensagem. Tenha atenção para não ler mais na assinatura do que o que estiver na mensagem assinada, para evitar ser enganado por um atacante que se encontre entre si e quem assinou a mensagem.</translation>
</message>
<message>
<source>The Bitcoin address the message was signed with</source>
@@ -2615,7 +2615,7 @@
</message>
<message>
<source>watch-only</source>
- <translation>vigiar apenas</translation>
+ <translation>apenas vigiar</translation>
</message>
<message>
<source>label</source>
@@ -2793,7 +2793,7 @@
</message>
<message>
<source>watch-only</source>
- <translation>vigiar apenas</translation>
+ <translation>apenas vigiar</translation>
</message>
<message>
<source>(n/a)</source>
@@ -2817,7 +2817,7 @@
</message>
<message>
<source>Whether or not a watch-only address is involved in this transaction.</source>
- <translation>Se um endereço de monitoração está ou não envolvido nesta transação.</translation>
+ <translation>Se um endereço de apenas vigiar está ou não envolvido nesta transação.</translation>
</message>
<message>
<source>User-defined intent/purpose of the transaction.</source>
@@ -2940,7 +2940,7 @@
</message>
<message>
<source>Watch-only</source>
- <translation>Vigiar apenas</translation>
+ <translation>Apenas vigiar</translation>
</message>
<message>
<source>Date</source>
@@ -3118,6 +3118,10 @@
<translation>Os programadores de %s</translation>
</message>
<message>
+ <source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
+ <translation>Não foi possível obter o bloqueio de escrita no da pasta de dados %s. %s provavelmente já está a ser executado.</translation>
+ </message>
+ <message>
<source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
<translation>Não é possível fornecer conexões específicas e ter o addrman a procurar conexões de saída ao mesmo tempo.</translation>
</message>
@@ -3131,7 +3135,7 @@
</message>
<message>
<source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source>
- <translation>A base de dados de blocos contém um bloco que aparenta ser do futuro. Isto pode ser causado por uma data incorrecta definida no seu computador. Reconstrua apenas a base de dados de blocos caso tenha a certeza de que a data e hora do seu computador estão correctos.</translation>
+ <translation>A base de dados de blocos contém um bloco que aparenta ser do futuro. Isto pode ser causado por uma data incorreta definida no seu computador. Reconstrua apenas a base de dados de blocos caso tenha a certeza de que a data e hora do seu computador estão corretos.</translation>
</message>
<message>
<source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
@@ -3167,7 +3171,7 @@
</message>
<message>
<source>Corrupted block database detected</source>
- <translation>Cadeia de blocos corrompida detectada</translation>
+ <translation>Detetada cadeia de blocos corrompida</translation>
</message>
<message>
<source>Do you want to rebuild the block database now?</source>
@@ -3234,6 +3238,11 @@
<translation>Valor inválido para -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>
+A pasta de blocos especificados "%s" não existe.</translation>
+ </message>
+ <message>
<source>Loading P2P addresses...</source>
<translation>A carregar endereços de P2P...</translation>
</message>
@@ -3307,7 +3316,7 @@
</message>
<message>
<source>The transaction amount is too small to send after the fee has been deducted</source>
- <translation>O montante da transacção é demasiado baixo após a dedução da taxa</translation>
+ <translation>O montante da transação é demasiado baixo após a dedução da taxa</translation>
</message>
<message>
<source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
@@ -3346,8 +3355,12 @@
<translation>Falhou assinatura da transação</translation>
</message>
<message>
+ <source>Specified -walletdir "%s" is not a directory</source>
+ <translation>O -walletdir "%s" especificado não é uma pasta</translation>
+ </message>
+ <message>
<source>The transaction amount is too small to pay the fee</source>
- <translation>O montante da transacção é demasiado baixo para pagar a taxa</translation>
+ <translation>O montante da transação é demasiado baixo para pagar a taxa</translation>
</message>
<message>
<source>This is experimental software.</source>
@@ -3359,7 +3372,7 @@
</message>
<message>
<source>Transaction too large for fee policy</source>
- <translation>Transacção demasiado grande para a política de taxas</translation>
+ <translation>Transação demasiado grande para a política de taxas</translation>
</message>
<message>
<source>Transaction too large</source>
@@ -3378,6 +3391,10 @@
<translation>A verificar a(s) carteira(s)...</translation>
</message>
<message>
+ <source>Wallet %s resides outside wallet directory %s</source>
+ <translation>A carteira %s reside fora da pasta da carteira %s</translation>
+ </message>
+ <message>
<source>Warning</source>
<translation>Aviso</translation>
</message>
@@ -3391,7 +3408,7 @@
</message>
<message>
<source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
- <translation>-maxtxfee está definido com um valor muito alto! Taxas desta magnitude podem ser pagas numa única transacção.</translation>
+ <translation>-maxtxfee está definido com um valor muito alto! Taxas desta magnitude podem ser pagas numa única transação.</translation>
</message>
<message>
<source>This is the transaction fee you may pay when fee estimates are not available.</source>
@@ -3454,6 +3471,10 @@
<translation>Fundos insuficientes</translation>
</message>
<message>
+ <source>Cannot write to data directory '%s'; check permissions.</source>
+ <translation>Não foi possível escrever na pasta de dados '%s': verifique as permissões.</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>A carregar o índice de blocos...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_ro.ts b/src/qt/locale/bitcoin_ro.ts
index b5a478c611..bef83e831a 100644
--- a/src/qt/locale/bitcoin_ro.ts
+++ b/src/qt/locale/bitcoin_ro.ts
@@ -501,6 +501,10 @@
<translation>Schimbă:</translation>
</message>
<message>
+ <source>(un)select all</source>
+ <translation>(de)selectează tot</translation>
+ </message>
+ <message>
<source>Tree mode</source>
<translation>Mod arbore</translation>
</message>
diff --git a/src/qt/locale/bitcoin_ro_RO.ts b/src/qt/locale/bitcoin_ro_RO.ts
index 3ca1878067..e5b17cfbad 100644
--- a/src/qt/locale/bitcoin_ro_RO.ts
+++ b/src/qt/locale/bitcoin_ro_RO.ts
@@ -23,7 +23,7 @@
</message>
<message>
<source>C&amp;lose</source>
- <translation>Închide</translation>
+ <translation>Î&amp;nchide</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
@@ -326,6 +326,14 @@
<translation>Deschide &amp;URI...</translation>
</message>
<message>
+ <source>Wallet:</source>
+ <translation>Portofel:</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>portofel implicit</translation>
+ </message>
+ <message>
<source>Click to disable network activity.</source>
<translation>Click pentru a opri activitatea retelei.</translation>
</message>
@@ -346,6 +354,10 @@
<translation>Se reindexează blocurile pe disc...</translation>
</message>
<message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>Proxy este&lt;b&gt;activat&lt;/b&gt;:%1</translation>
+ </message>
+ <message>
<source>Send coins to a Bitcoin address</source>
<translation>Trimite monede către o adresă Bitcoin</translation>
</message>
@@ -514,6 +526,12 @@
</translation>
</message>
<message>
+ <source>Wallet: %1
+</source>
+ <translation>Portofel: %1
+</translation>
+ </message>
+ <message>
<source>Type: %1
</source>
<translation>Tip: %1
@@ -746,6 +764,14 @@
<translation>Adresa introdusă "%1" nu este o adresă Bitcoin validă.</translation>
</message>
<message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Adresa "%1" exista deja ca si adresa de primire cu eticheta "%2" si deci nu poate fi folosita ca si adresa de trimitere.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>Adresa introdusa "%1" este deja in lista de adrese cu eticheta "%2"</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>Portofelul nu a putut fi deblocat.</translation>
</message>
@@ -1024,6 +1050,22 @@
<translation>Reţea</translation>
</message>
<message>
+ <source>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</source>
+ <translation>Dezactiveaza unele caracteristici avansate insa toate blocurile vor fi validate pe deplin. Inversarea acestei setari necesita re-descarcarea intregului blockchain. Utilizarea reala a discului poate fi ceva mai mare.</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Reductie &amp;block storage la </translation>
+ </message>
+ <message>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation> Inversarea acestei setari necesita re-descarcarea intregului blockchain.</translation>
+ </message>
+ <message>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
<translation>(0 = automat, &lt;0 = lasă atîtea nuclee libere)</translation>
</message>
@@ -1290,6 +1332,10 @@
<translation>Gestionare URI</translation>
</message>
<message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin://' nu este un URI valid. Folositi 'bitcoin:' in loc.</translation>
+ </message>
+ <message>
<source>Payment request fetch URL is invalid: %1</source>
<translation>URL-ul cererii de plată preluat nu este valid: %1</translation>
</message>
@@ -1487,10 +1533,18 @@
<context>
<name>QObject::QObject</name>
<message>
+ <source>Error parsing command line arguments: %1.</source>
+ <translation>Eroare la analiza argumentelor linie de comanda: %1</translation>
+ </message>
+ <message>
<source>Error: Specified data directory "%1" does not exist.</source>
<translation>Eroare: Directorul de date specificat "%1" nu există.</translation>
</message>
<message>
+ <source>Error: Cannot parse configuration file: %1.</source>
+ <translation>Eroare: Nu se poate analiza fişierul de configuraţie: %1.</translation>
+ </message>
+ <message>
<source>Error: %1</source>
<translation>Eroare: %1</translation>
</message>
@@ -1581,6 +1635,14 @@
<translation>Memorie folosită</translation>
</message>
<message>
+ <source>Wallet: </source>
+ <translation>Portofel:</translation>
+ </message>
+ <message>
+ <source>(none)</source>
+ <translation>(nimic)</translation>
+ </message>
+ <message>
<source>&amp;Reset</source>
<translation>&amp;Resetare</translation>
</message>
@@ -1749,6 +1811,10 @@
<translation>&amp;Unban</translation>
</message>
<message>
+ <source>default wallet</source>
+ <translation>portofel implicit</translation>
+ </message>
+ <message>
<source>Welcome to the %1 RPC console.</source>
<translation>Bun venit la consola %1 RPC.</translation>
</message>
@@ -1773,6 +1839,14 @@
<translation>Activitatea retelei a fost oprita.</translation>
</message>
<message>
+ <source>Executing command without any wallet</source>
+ <translation>Executarea comenzii fara nici un portofel.</translation>
+ </message>
+ <message>
+ <source>Executing command using "%1" wallet</source>
+ <translation>Executarea comenzii folosind portofelul "%1"</translation>
+ </message>
+ <message>
<source>(node id: %1)</source>
<translation>(node id: %1)</translation>
</message>
@@ -2057,6 +2131,14 @@
<translation>inchide setarile de taxare</translation>
</message>
<message>
+ <source>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</source>
+ <translation>Specificati o taxa anume pe kB (1000 byte) din marimea virtuala a tranzactiei.
+
+Nota: Cum taxa este calculata per byte, o taxa de "100 satoshi per kB" pentru o tranzactie de 500 byte (jumatate de kB) va produce o taxa de doar 50 satoshi.</translation>
+ </message>
+ <message>
<source>per kilobyte</source>
<translation>per kilooctet</translation>
</message>
@@ -2177,6 +2259,14 @@
<translation>Puteti creste taxa mai tarziu (semnaleaza Replace-By-Fee, BIP-125).</translation>
</message>
<message>
+ <source>from wallet %1</source>
+ <translation>de la portofelul %1</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction.</source>
+ <translation>Va rugam sa revizuiti tranzactia.</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>Taxă tranzacţie</translation>
</message>
@@ -2185,6 +2275,10 @@
<translation>Nu se semnalizeaza Replace-By-Fee, BIP-125</translation>
</message>
<message>
+ <source>Total Amount</source>
+ <translation>Suma totală</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>Confirmă trimiterea monedelor</translation>
</message>
@@ -2638,6 +2732,10 @@
<translation>Dimensiune totala tranzacţie</translation>
</message>
<message>
+ <source>Transaction virtual size</source>
+ <translation>Dimensiune virtuala a tranzactiei</translation>
+ </message>
+ <message>
<source>Output index</source>
<translation>Index debit</translation>
</message>
@@ -3038,7 +3136,11 @@
<source>The wallet data was successfully saved to %1.</source>
<translation>Datele portofelului s-au salvat cu succes la %1.</translation>
</message>
- </context>
+ <message>
+ <source>Cancel</source>
+ <translation>Anulare</translation>
+ </message>
+</context>
<context>
<name>bitcoin-core</name>
<message>
@@ -3090,6 +3192,10 @@
<translation>Eroare la citirea %s! Toate cheile sînt citite corect, dar datele tranzactiei sau anumite intrări din agenda sînt incorecte sau lipsesc.</translation>
</message>
<message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>Gruparea a iesirilor pe adrese, selectand totul sau nimic, in loc de selectarea bazata per-iesire. Intimitatea este imbunatatita deoarece o adresa este folosita doar o singura data (cu exceptia cuiva care trimite catre aceiasi adresa dup ce cheltuie din ea), dar se pot genera taxe mai mari deoarece poate rezulta o selectare suboptimala a monedelor datorata limitarii impuse (implicit: %u)</translation>
+ </message>
+ <message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation>Vă rugăm verificaţi dacă data/timpul calculatorului dvs. sînt corecte! Dacă ceasul calcultorului este gresit, %s nu va funcţiona corect.</translation>
</message>
@@ -3174,6 +3280,10 @@
<translation>Eroare la încărcarea %s</translation>
</message>
<message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>Eroare la incarcarea %s: Cheile private pot fi dezactivate doar in momentul crearii</translation>
+ </message>
+ <message>
<source>Error loading %s: Wallet corrupted</source>
<translation>Eroare la încărcarea %s: Portofel corupt</translation>
</message>
@@ -3226,6 +3336,14 @@
<translation>Suma nevalidă pentru -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Directorul de blocuri "%s" specificat nu exista.</translation>
+ </message>
+ <message>
+ <source>Upgrading txindex database</source>
+ <translation>Actualizarea bazei de date txindex</translation>
+ </message>
+ <message>
<source>Loading P2P addresses...</source>
<translation>Încărcare adrese P2P...</translation>
</message>
@@ -3266,6 +3384,10 @@
<translation>Nu se poate efectua legatura la %s pe acest computer. %s probabil ruleaza deja.</translation>
</message>
<message>
+ <source>Unable to generate keys</source>
+ <translation>Nu s-au putut genera cheile</translation>
+ </message>
+ <message>
<source>Unsupported argument -benchmark ignored, use -debug=bench.</source>
<translation>Argumentul nesuportat -benchmark este ignorat, folositi debug=bench.</translation>
</message>
@@ -3494,6 +3616,22 @@
<translation>Fonduri insuficiente</translation>
</message>
<message>
+ <source>Can't generate a change-address key. Private keys are disabled for this wallet.</source>
+ <translation>Nu se poate genera o adresa pentru rest. Cheile private sunt dezactivate pentru acest portofel.</translation>
+ </message>
+ <message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>Estimarea taxei a esuat. Taxa implicita este dezactivata. Asteptati cateva blocuri, sau activati -fallbackfee.</translation>
+ </message>
+ <message>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <translation>Atentie: S-au detectat chei private in portofelul {%s} cu cheile private dezactivate</translation>
+ </message>
+ <message>
+ <source>Cannot write to data directory '%s'; check permissions.</source>
+ <translation>Nu se poate scrie in directorul de date '%s"; verificati permisiunile.</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>Încărcare index bloc...</translation>
</message>
@@ -3518,4 +3656,4 @@
<translation>Eroare</translation>
</message>
</context>
-</TS> \ No newline at end of file
+</TS>
diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts
index 0cbee61fc5..9c81fe9cdb 100644
--- a/src/qt/locale/bitcoin_ru.ts
+++ b/src/qt/locale/bitcoin_ru.ts
@@ -337,6 +337,10 @@
</context>
<context>
<name>ReceiveCoinsDialog</name>
+ <message>
+ <source>Clear</source>
+ <translation>Очистить</translation>
+ </message>
</context>
<context>
<name>ReceiveRequestDialog</name>
diff --git a/src/qt/locale/bitcoin_ru_RU.ts b/src/qt/locale/bitcoin_ru_RU.ts
index ea30f39969..430f2e4191 100644
--- a/src/qt/locale/bitcoin_ru_RU.ts
+++ b/src/qt/locale/bitcoin_ru_RU.ts
@@ -15,7 +15,7 @@
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
- <translation>Скопировать текущий выбранный адрес в буфер обмена системы</translation>
+ <translation>Скопировать выбранный адрес в буфер обмена</translation>
</message>
<message>
<source>&amp;Copy</source>
@@ -55,7 +55,7 @@
</message>
<message>
<source>C&amp;hoose</source>
- <translation>В&amp;ыбрать</translation>
+ <translation>Выбрать</translation>
</message>
<message>
<source>Sending addresses</source>
@@ -67,11 +67,11 @@
</message>
<message>
<source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
- <translation>Это ваши биткойн адреса для отправки платежа. Всегда проверяйте сумму и адрес получателя перед отправкой платежа.</translation>
+ <translation>Это ваши Bitcoin адреса для отправки платежа. Всегда проверяйте сумму и адрес получателя перед отправкой платежа.</translation>
</message>
<message>
<source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
- <translation>Это ваши биткойн адреса для получения платежей. Настоятельно рекомендуем использовать новые адреса для получения каждой транзакции.</translation>
+ <translation>Это ваши Bitcoin адреса для получения платежей. Настоятельно рекомендуем использовать новые адреса для получения каждой транзакции.</translation>
</message>
<message>
<source>&amp;Copy Address</source>
@@ -137,19 +137,19 @@
</message>
<message>
<source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
- <translation>Введите новый пароль для кошелька.&lt;br/&gt; Пожалуйста используйте пароль из &lt;b&gt; десяти или более произвольных символов&lt;/b&gt;, или &lt;b&gt;восемь или боле слов&lt;/b&gt;</translation>
+ <translation>Введите новый пароль для кошелька.&lt;br/&gt; Пожалуйста используйте пароль из &lt;b&gt; десяти или более произвольных символов&lt;/b&gt;, или &lt;b&gt;восемь или более слов&lt;/b&gt;</translation>
</message>
<message>
<source>Encrypt wallet</source>
- <translation>Зашифровать бумажник</translation>
+ <translation>Зашифровать кошелек</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
- <translation>Эта операция требует вашего пароля для разблокировки бумажника</translation>
+ <translation>Эта операция требует вашего пароля для разблокировки кошелька</translation>
</message>
<message>
<source>Unlock wallet</source>
- <translation>Разблокировать бумажник</translation>
+ <translation>Разблокировать кошелек</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to decrypt the wallet.</source>
@@ -157,7 +157,7 @@
</message>
<message>
<source>Decrypt wallet</source>
- <translation>Расшифровать бумажник</translation>
+ <translation>Расшифровать кошелек</translation>
</message>
<message>
<source>Change passphrase</source>
@@ -165,11 +165,11 @@
</message>
<message>
<source>Enter the old passphrase and new passphrase to the wallet.</source>
- <translation>Введите старый и новый пароль для кошелька.</translation>
+ <translation>Введите старый и новый пароль к кошельку.</translation>
</message>
<message>
<source>Confirm wallet encryption</source>
- <translation>Подтвердите шифрование бумажника</translation>
+ <translation>Подтвердите шифрование кошелька</translation>
</message>
<message>
<source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
@@ -181,7 +181,7 @@
</message>
<message>
<source>Wallet encrypted</source>
- <translation>Бумажник зашифрован</translation>
+ <translation>Кошелек зашифрован</translation>
</message>
<message>
<source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
@@ -200,6 +200,10 @@
<translation>Шифрование кошелька завершилось неудачно из-за внутренней ошибки. Ваш кошелек не был зашифрован.</translation>
</message>
<message>
+ <source>The supplied passphrases do not match.</source>
+ <translation>Пароли не совпадают.</translation>
+ </message>
+ <message>
<source>Wallet unlock failed</source>
<translation>Ошибка разблокировки кошелька</translation>
</message>
@@ -251,7 +255,7 @@
</message>
<message>
<source>Show general overview of wallet</source>
- <translation>Отобразить общий обзор кошелька</translation>
+ <translation>Отобразить главное окно кошелька</translation>
</message>
<message>
<source>&amp;Transactions</source>
@@ -299,7 +303,7 @@
</message>
<message>
<source>&amp;Backup Wallet...</source>
- <translation>&amp;Создать резервную копию бумажника</translation>
+ <translation>&amp;Создать резервную копию кошелька</translation>
</message>
<message>
<source>&amp;Change Passphrase...</source>
@@ -346,6 +350,10 @@
<translation>Реиндексация блоков на диске...</translation>
</message>
<message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>Прокси &lt;b&gt;включен&lt;/b&gt;: %1</translation>
+ </message>
+ <message>
<source>Send coins to a Bitcoin address</source>
<translation>Послать средства на биткойн адрес</translation>
</message>
@@ -422,6 +430,10 @@
<translation>Панель вкладок</translation>
</message>
<message>
+ <source>Request payments (generates QR codes and bitcoin: URIs)</source>
+ <translation>Запросить платеж</translation>
+ </message>
+ <message>
<source>Show the list of used sending addresses and labels</source>
<translation>Показать список использованных адресов и меток получателей</translation>
</message>
@@ -437,6 +449,10 @@
<source>&amp;Command-line options</source>
<translation>Опции командной строки</translation>
</message>
+ <message numerus="yes">
+ <source>%n active connection(s) to Bitcoin network</source>
+ <translation><numerusform>%n активное подключение к сети Bitcoin</numerusform><numerusform>%n активных подключения к сети Bitcoin</numerusform><numerusform>%n активных подключений к сети Bitcoin</numerusform><numerusform>%n активных подключений к сети Bitcoin</numerusform></translation>
+ </message>
<message>
<source>Indexing blocks on disk...</source>
<translation>Выполняется индексирование блоков на диске...</translation>
@@ -445,6 +461,10 @@
<source>Processing blocks on disk...</source>
<translation>Выполняется обработка блоков на диске...</translation>
</message>
+ <message numerus="yes">
+ <source>Processed %n block(s) of transaction history.</source>
+ <translation><numerusform>Обработан %n блок истории транзакций.</numerusform><numerusform>Обработано %n блока истории транзакций.</numerusform><numerusform>Обработано %n блоков истории транзакций.</numerusform><numerusform>Обработано %n блоков истории транзакций.</numerusform></translation>
+ </message>
<message>
<source>%1 behind</source>
<translation>Выполнено %1</translation>
@@ -573,6 +593,10 @@
<translation>Сдача:</translation>
</message>
<message>
+ <source>(un)select all</source>
+ <translation>Выбрать все</translation>
+ </message>
+ <message>
<source>Tree mode</source>
<translation>Режим дерева</translation>
</message>
@@ -696,10 +720,18 @@
<translation>Изменить адрес отправки</translation>
</message>
<message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>Введённый адрес "%1" уже существует в адресной книге с меткой "%2".</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>Невозможно разблокировать кошелек.</translation>
</message>
- </context>
+ <message>
+ <source>New key generation failed.</source>
+ <translation>Произошла ошибка при генерации нового ключа.</translation>
+ </message>
+</context>
<context>
<name>FreespaceChecker</name>
<message>
@@ -711,6 +743,14 @@
<translation>имя</translation>
</message>
<message>
+ <source>Directory already exists. Add %1 if you intend to create a new directory here.</source>
+ <translation>Каталог уже существует. Добавьте %1, если хотите создать новую директорию здесь.</translation>
+ </message>
+ <message>
+ <source>Path already exists, and is not a directory.</source>
+ <translation>Данный путь уже существует и это не каталог.</translation>
+ </message>
+ <message>
<source>Cannot create data directory here.</source>
<translation>Невозможно создать директорию данных здесь.</translation>
</message>
@@ -741,6 +781,14 @@
<translation>Добро пожаловать в %1.</translation>
</message>
<message>
+ <source>As this is the first time the program is launched, you can choose where %1 will store its data.</source>
+ <translation>Поскольку программа запущена впервые, вы должны указать где %1 будет хранить данные.</translation>
+ </message>
+ <message>
+ <source>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source>
+ <translation>Если вы указали сокращать объём хранимого блокчейна (pruning), все исторические данные все равно должны быть скачаны и обработаны, но впоследствии они будут удалены для экономии места на диске.</translation>
+ </message>
+ <message>
<source>Use the default data directory</source>
<translation>Использовать стандартную директорию данных</translation>
</message>
@@ -866,6 +914,10 @@
<translation>IP-адрес прокси-сервера (к примеру, IPv4: 127.0.0.1 / IPv6: ::1)</translation>
</message>
<message>
+ <source>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</source>
+ <translation>Использовать отдельные SOCKS&amp;5 прокси для подключения к пирам через Tor hidden services:</translation>
+ </message>
+ <message>
<source>Hide the icon from the system tray.</source>
<translation>Убрать значок с области уведомлений.</translation>
</message>
@@ -890,10 +942,18 @@
<translation>&amp;Сеть</translation>
</message>
<message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Сокращать объём хранимого блокчейна до</translation>
+ </message>
+ <message>
<source>GB</source>
<translation>ГБ</translation>
</message>
<message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Отмена этой настройки требует повторного скачивания всего блокчейна.</translation>
+ </message>
+ <message>
<source>W&amp;allet</source>
<translation>К&amp;ошелёк</translation>
</message>
@@ -902,6 +962,10 @@
<translation>Эксперт</translation>
</message>
<message>
+ <source>Enable coin &amp;control features</source>
+ <translation>Включить возможность &amp;управления монетами</translation>
+ </message>
+ <message>
<source>&amp;Spend unconfirmed change</source>
<translation>&amp;Тратить неподтвержденную сдачу</translation>
</message>
@@ -938,6 +1002,10 @@
<translation>Порт прокси: (напр. 9050)</translation>
</message>
<message>
+ <source>Used for reaching peers via:</source>
+ <translation>Используется для подключения к пирам по:</translation>
+ </message>
+ <message>
<source>IPv4</source>
<translation>IPv4</translation>
</message>
@@ -950,6 +1018,10 @@
<translation>Tor</translation>
</message>
<message>
+ <source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source>
+ <translation>Соединяться к Биткоин-сети через отдельные SOCKS5 прокси через Tor hidden services:</translation>
+ </message>
+ <message>
<source>&amp;Window</source>
<translation>&amp;Окно</translation>
</message>
@@ -958,6 +1030,10 @@
<translation>Отобразить только значок в области уведомлений после сворачивания окна.</translation>
</message>
<message>
+ <source>M&amp;inimize on close</source>
+ <translation>З&amp;акрыть при сворачивании</translation>
+ </message>
+ <message>
<source>User Interface &amp;language:</source>
<translation>Язык пользовательского интерфейса:</translation>
</message>
@@ -1021,6 +1097,14 @@
<translation>Форма</translation>
</message>
<message>
+ <source>Watch-only:</source>
+ <translation>Только просмотр:</translation>
+ </message>
+ <message>
+ <source>Available:</source>
+ <translation>Доступно:</translation>
+ </message>
+ <message>
<source>Your current spendable balance</source>
<translation>Ваш доступный баланс</translation>
</message>
@@ -1064,6 +1148,10 @@
<translation>Ошибка запроса платежа</translation>
</message>
<message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin://' не верный URI. Используйте 'bitcoin:' вместо этого.</translation>
+ </message>
+ <message>
<source>Invalid payment address %1</source>
<translation>Неверный адрес %1</translation>
</message>
@@ -1235,6 +1323,10 @@
<translation>Количество соединений</translation>
</message>
<message>
+ <source>Block chain</source>
+ <translation>Блокчейн</translation>
+ </message>
+ <message>
<source>Current number of blocks</source>
<translation>Текущее количество блоков</translation>
</message>
@@ -1275,6 +1367,10 @@
<translation>Заблокированные пиры</translation>
</message>
<message>
+ <source>Select a peer to view detailed information.</source>
+ <translation>Выберите пира для просмотра детальной информации.</translation>
+ </message>
+ <message>
<source>Version</source>
<translation>Версия</translation>
</message>
@@ -1406,6 +1502,14 @@
<translation>Отчистить</translation>
</message>
<message>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <translation>"Родные" segwit адреса (Bech32 или BIP-173) в дальнейшем уменьшат комиссии ваших транзакций и предоставят улучшенную защиту от опечаток, однако старые кошельки не поддерживают эти адреса. Если не выбрано, будет создан совместимый со старыми кошелёк.</translation>
+ </message>
+ <message>
+ <source>Generate native segwit (Bech32) address</source>
+ <translation>Создать "родной" segwit (Bech32) адрес </translation>
+ </message>
+ <message>
<source>Requested payments history</source>
<translation>История платежных запросов</translation>
</message>
@@ -1639,6 +1743,10 @@
<translation>Баланс:</translation>
</message>
<message>
+ <source>Confirm the send action</source>
+ <translation>Подтвердить отправку</translation>
+ </message>
+ <message>
<source>Copy quantity</source>
<translation>Копировать количество</translation>
</message>
@@ -1707,6 +1815,14 @@
<translation>Истекло время ожидания запроса платежа</translation>
</message>
<message>
+ <source>Warning: Invalid Bitcoin address</source>
+ <translation>Предупреждение: Неверный Bitcoin адрес</translation>
+ </message>
+ <message>
+ <source>Warning: Unknown change address</source>
+ <translation>Предупреждение: Неизвестный адрес сдачи</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(нет метки)</translation>
</message>
@@ -2240,6 +2356,10 @@
<translation>Предупреждение</translation>
</message>
<message>
+ <source>Starting network threads...</source>
+ <translation>Запуск сетевых потоков...</translation>
+ </message>
+ <message>
<source>This is the minimum transaction fee you pay on every transaction.</source>
<translation>Это минимальная комиссия, которую вы платите для любой транзакции</translation>
</message>
diff --git a/src/qt/locale/bitcoin_sk.ts b/src/qt/locale/bitcoin_sk.ts
index 86c5c8abda..89ed850da3 100644
--- a/src/qt/locale/bitcoin_sk.ts
+++ b/src/qt/locale/bitcoin_sk.ts
@@ -768,6 +768,14 @@
<translation>Vložená adresa "%1" nieje platnou adresou Bitcoin.</translation>
</message>
<message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Adresa "%1" už existuje ako prijímacia adresa s označením "%2" .Nemôže tak byť pridaná ako odosielacia adresa.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>Zadaná adresa "%1" sa už nachádza v zozname adries s označením "%2".</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>Nepodarilo sa odomknúť peňaženku.</translation>
</message>
@@ -1046,10 +1054,22 @@
<translation>&amp;Sieť</translation>
</message>
<message>
+ <source>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</source>
+ <translation>Zakáže niektoré pokročilé funkcie, ale všetky bloky budú stále plne overené. Obnovenie tohto nastavenia vyžaduje opätovné prevzatie celého blockchainu. Skutočné využitie disku môže byť o niečo vyššie.</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Redukovať priestor pre &amp;bloky na</translation>
+ </message>
+ <message>
<source>GB</source>
<translation>GB</translation>
</message>
<message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Obnovenie tohto nastavenia vyžaduje opätovné prevzatie celého blockchainu.</translation>
+ </message>
+ <message>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
<translation>(0 = auto, &lt;0 = nechať toľko jadier voľných)</translation>
</message>
@@ -1517,10 +1537,18 @@
<context>
<name>QObject::QObject</name>
<message>
+ <source>Error parsing command line arguments: %1.</source>
+ <translation>Chyba pri spracovaní argumentov príkazového riadku: %1.</translation>
+ </message>
+ <message>
<source>Error: Specified data directory "%1" does not exist.</source>
<translation>Chyba: Zadaný adresár pre dáta „%1“ neexistuje.</translation>
</message>
<message>
+ <source>Error: Cannot parse configuration file: %1.</source>
+ <translation>Chyba: Konfiguračný súbor sa nedá spracovať: %1.</translation>
+ </message>
+ <message>
<source>Error: %1</source>
<translation>Chyba: %1</translation>
</message>
@@ -1816,6 +1844,14 @@
<translation>Sieťová aktivita zakázaná</translation>
</message>
<message>
+ <source>Executing command without any wallet</source>
+ <translation>Príkaz sa vykonáva bez peňaženky</translation>
+ </message>
+ <message>
+ <source>Executing command using "%1" wallet</source>
+ <translation>Príkaz sa vykonáva s použitím peňaženky "%1"</translation>
+ </message>
+ <message>
<source>(node id: %1)</source>
<translation>(ID uzlu: %1)</translation>
</message>
@@ -2100,6 +2136,14 @@
<translation>zbaliť nastavenia poplatkov</translation>
</message>
<message>
+ <source>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</source>
+ <translation>Špecifikujte vlastný poplatok za kB (1000 bajtov) virtuálnej veľkosti transakcie.
+
+Poznámka: Keďže poplatok je počítaný za bajt, poplatok o hodnote "100 satoshi za kB" a veľkosti transakcie 500 bajtov (polovica z 1 kB) by stál len 50 satoshi.</translation>
+ </message>
+ <message>
<source>per kilobyte</source>
<translation>za kilobajt</translation>
</message>
@@ -2224,6 +2268,10 @@
<translation>z peňaženky %1</translation>
</message>
<message>
+ <source>Please, review your transaction.</source>
+ <translation>Prosím, skontrolujte Vašu transakciu.</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>Transakčný poplatok</translation>
</message>
@@ -2689,6 +2737,10 @@
<translation>Celková veľkosť transakcie</translation>
</message>
<message>
+ <source>Transaction virtual size</source>
+ <translation>Virtuálna veľkosť transakcie</translation>
+ </message>
+ <message>
<source>Output index</source>
<translation>Index výstupu</translation>
</message>
@@ -3149,6 +3201,10 @@
<translation>Nastala chyba pri čítaní súboru %s! Všetkz kľúče sa prečítali správne, ale dáta o transakcíách alebo záznamy v adresári môžu chýbať alebo byť nesprávne.</translation>
</message>
<message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>Zoskupenie výstupov podľa adresy, pri vybratí všetkých alebo žiadnych, namiesto vybratia na báze za výstup. Zlepší sa súkromie, keďže adresa je použitá len raz (pokiaľ nikto na túto adresu už nič nepošle po minutí), ale môže vyústiť v trochu väčšie poplatky keďže ne-optimalizovaný výber mincí môže vyústiť v pridané obmedzenia (predvolené: %u)</translation>
+ </message>
+ <message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation>Prosím skontrolujte systémový čas a dátum. Keď je váš čas nesprávny, %s nebude fungovať správne.</translation>
</message>
@@ -3233,6 +3289,10 @@
<translation>Chyba načítania %s</translation>
</message>
<message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>Chyba pri načítaní %s: Súkromné kľúče môžu byť zakázané len počas vytvárania</translation>
+ </message>
+ <message>
<source>Error loading %s: Wallet corrupted</source>
<translation>Chyba načítania %s: Peňaženka je poškodená</translation>
</message>
@@ -3285,6 +3345,10 @@
<translation>Neplatná suma pre -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Zadaný adresár blokov "%s" neexistuje.</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Inovuje sa txindex databáza</translation>
</message>
@@ -3437,12 +3501,6 @@
<translation>Uvedený -walletdir "%s" nie je priečinok</translation>
</message>
<message>
- <source>Specified blocks directory "%s" does not exist.
-</source>
- <translation>Uvedený priečinok s dátami "%s" neexistuje.
-</translation>
- </message>
- <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>Suma transakcie je príliš malá na zaplatenie poplatku</translation>
</message>
@@ -3575,6 +3633,22 @@
<translation>Nedostatok prostriedkov</translation>
</message>
<message>
+ <source>Can't generate a change-address key. Private keys are disabled for this wallet.</source>
+ <translation>Nie je možné vygenerovať kľúč na zmenu adresy. Súkromné kľúče sú pre túto peňaženku zakázané.</translation>
+ </message>
+ <message>
+ <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
+ <translation>Nie je možné vylepšiť peňaženku bez HD bez aktualizácie, ktorá podporuje delenie keypoolu. Použite prosím -upgradewallet=169900 alebo -upgradewallet bez špecifikovania verzie.</translation>
+ </message>
+ <message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>Odhad poplatku sa nepodaril. Fallbackfee je zakázaný. Počkajte niekoľko blokov alebo povoľte -fallbackfee.</translation>
+ </message>
+ <message>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <translation>Upozornenie: Boli zistené súkromné kľúče v peňaženke {%s} so zakázanými súkromnými kľúčmi.</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>Načítavanie zoznamu blokov...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_sl_SI.ts b/src/qt/locale/bitcoin_sl_SI.ts
index 91080a774a..08b00984c0 100644
--- a/src/qt/locale/bitcoin_sl_SI.ts
+++ b/src/qt/locale/bitcoin_sl_SI.ts
@@ -1874,6 +1874,10 @@
<translation>Brišem vse transakcije iz denarnice ...</translation>
</message>
<message>
+ <source>Transaction must have at least one recipient</source>
+ <translation>Transakcija mora imeti vsaj enega prejemnika.</translation>
+ </message>
+ <message>
<source>Unknown network specified in -onlynet: '%s'</source>
<translation>Neznano omrežje določeno v -onlynet: '%s'.</translation>
</message>
diff --git a/src/qt/locale/bitcoin_sn.ts b/src/qt/locale/bitcoin_sn.ts
new file mode 100644
index 0000000000..895962cb45
--- /dev/null
+++ b/src/qt/locale/bitcoin_sn.ts
@@ -0,0 +1,367 @@
+<TS language="sn" version="2.1">
+<context>
+ <name>AddressBookPage</name>
+ <message>
+ <source>Create a new address</source>
+ <translation>Gadzira Kero Itsva</translation>
+ </message>
+ <message>
+ <source>&amp;New</source>
+ <translation>Itsva</translation>
+ </message>
+ <message>
+ <source>&amp;Copy</source>
+ <translation>&amp;Kopera</translation>
+ </message>
+ <message>
+ <source>C&amp;lose</source>
+ <translation>Vhara</translation>
+ </message>
+ <message>
+ <source>&amp;Delete</source>
+ <translation>Dzima</translation>
+ </message>
+ <message>
+ <source>Sending addresses</source>
+ <translation>Makero ekutumira</translation>
+ </message>
+ <message>
+ <source>Receiving addresses</source>
+ <translation>Makero ekutambira</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Address</source>
+ <translation>Kopera Kero</translation>
+ </message>
+ <message>
+ <source>&amp;Edit</source>
+ <translation>&amp;Gadzirisa</translation>
+ </message>
+ </context>
+<context>
+ <name>AddressTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>Zita</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Kero</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(hapana zita)</translation>
+ </message>
+</context>
+<context>
+ <name>AskPassphraseDialog</name>
+ </context>
+<context>
+ <name>BanTableModel</name>
+ <message>
+ <source>Banned Until</source>
+ <translation>Wakavharirwa Kusvika</translation>
+ </message>
+</context>
+<context>
+ <name>BitcoinGUI</name>
+ <message>
+ <source>E&amp;xit</source>
+ <translation>Buda</translation>
+ </message>
+ <message>
+ <source>Quit application</source>
+ <translation>Vhara Application</translation>
+ </message>
+ <message>
+ <source>&amp;About %1</source>
+ <translation>&amp;Kuma %1</translation>
+ </message>
+ <message>
+ <source>Show information about %1</source>
+ <translation>Taridza ruzivo rwekuma %1</translation>
+ </message>
+ <message>
+ <source>Show information about Qt</source>
+ <translation>Taridza ruzivo rwe Qt</translation>
+ </message>
+ <message>
+ <source>&amp;Sending addresses...</source>
+ <translation>&amp;Makero ekutumira nawo</translation>
+ </message>
+ <message>
+ <source>&amp;Receiving addresses...</source>
+ <translation>&amp;Makero ekutambira nawo</translation>
+ </message>
+ <message>
+ <source>Open &amp;URI...</source>
+ <translation>Vhura &amp;URI</translation>
+ </message>
+ <message>
+ <source>Bitcoin</source>
+ <translation>Bitcoin</translation>
+ </message>
+ <message>
+ <source>Wallet</source>
+ <translation>Chikwama</translation>
+ </message>
+ <message>
+ <source>&amp;Send</source>
+ <translation>&amp;Tumira</translation>
+ </message>
+ <message>
+ <source>&amp;Receive</source>
+ <translation>&amp;Tambira</translation>
+ </message>
+ <message>
+ <source>&amp;Show / Hide</source>
+ <translation>&amp;Taridza/Usataridza</translation>
+ </message>
+ <message>
+ <source>&amp;File</source>
+ <translation>&amp;Faira</translation>
+ </message>
+ <message>
+ <source>&amp;Help</source>
+ <translation>&amp;Rubatsiro</translation>
+ </message>
+ <message>
+ <source>%1 behind</source>
+ <translation>%1 kumashure</translation>
+ </message>
+ <message>
+ <source>Warning</source>
+ <translation>Hokoyo</translation>
+ </message>
+ <message>
+ <source>Information</source>
+ <translation>Ruzivo</translation>
+ </message>
+ </context>
+<context>
+ <name>CoinControlDialog</name>
+ <message>
+ <source>Amount</source>
+ <translation>Marii </translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>Zuva</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(hapana zita)</translation>
+ </message>
+ </context>
+<context>
+ <name>EditAddressDialog</name>
+ </context>
+<context>
+ <name>FreespaceChecker</name>
+ </context>
+<context>
+ <name>HelpMessageDialog</name>
+ </context>
+<context>
+ <name>Intro</name>
+ <message>
+ <source>Bitcoin</source>
+ <translation>Bitcoin</translation>
+ </message>
+ </context>
+<context>
+ <name>ModalOverlay</name>
+ </context>
+<context>
+ <name>OpenURIDialog</name>
+ </context>
+<context>
+ <name>OptionsDialog</name>
+ </context>
+<context>
+ <name>OverviewPage</name>
+ </context>
+<context>
+ <name>PaymentServer</name>
+ </context>
+<context>
+ <name>PeerTableModel</name>
+ </context>
+<context>
+ <name>QObject</name>
+ <message>
+ <source>Amount</source>
+ <translation>Marii </translation>
+ </message>
+ <message>
+ <source>Enter a Bitcoin address (e.g. %1)</source>
+ <translation>Nyora kero ye Bitcoin (sekuti %1)</translation>
+ </message>
+ <message>
+ <source>%1 d</source>
+ <translation>%1 d</translation>
+ </message>
+ <message>
+ <source>%1 h</source>
+ <translation>%1 h</translation>
+ </message>
+ <message>
+ <source>%1 m</source>
+ <translation>%1 m</translation>
+ </message>
+ <message>
+ <source>%1 s</source>
+ <translation>%1 s</translation>
+ </message>
+ <message>
+ <source>None</source>
+ <translation>Hapana</translation>
+ </message>
+ <message>
+ <source>N/A</source>
+ <translation>Hapana </translation>
+ </message>
+ </context>
+<context>
+ <name>QObject::QObject</name>
+ </context>
+<context>
+ <name>QRImageWidget</name>
+ </context>
+<context>
+ <name>RPCConsole</name>
+ <message>
+ <source>N/A</source>
+ <translation>Hapana </translation>
+ </message>
+ </context>
+<context>
+ <name>ReceiveCoinsDialog</name>
+ </context>
+<context>
+ <name>ReceiveRequestDialog</name>
+ <message>
+ <source>Address</source>
+ <translation>Kero</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Marii </translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Zita</translation>
+ </message>
+ <message>
+ <source>Wallet</source>
+ <translation>Chikwama</translation>
+ </message>
+ </context>
+<context>
+ <name>RecentRequestsTableModel</name>
+ <message>
+ <source>Date</source>
+ <translation>Zuva</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Zita</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(hapana zita)</translation>
+ </message>
+ </context>
+<context>
+ <name>SendCoinsDialog</name>
+ <message>
+ <source>(no label)</source>
+ <translation>(hapana zita)</translation>
+ </message>
+</context>
+<context>
+ <name>SendCoinsEntry</name>
+ </context>
+<context>
+ <name>SendConfirmationDialog</name>
+ </context>
+<context>
+ <name>ShutdownWindow</name>
+ </context>
+<context>
+ <name>SignVerifyMessageDialog</name>
+ </context>
+<context>
+ <name>SplashScreen</name>
+ </context>
+<context>
+ <name>TrafficGraphWidget</name>
+ </context>
+<context>
+ <name>TransactionDesc</name>
+ <message>
+ <source>Date</source>
+ <translation>Zuva</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Marii </translation>
+ </message>
+ </context>
+<context>
+ <name>TransactionDescDialog</name>
+ </context>
+<context>
+ <name>TransactionTableModel</name>
+ <message>
+ <source>Date</source>
+ <translation>Zuva</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Zita</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(hapana zita)</translation>
+ </message>
+ </context>
+<context>
+ <name>TransactionView</name>
+ <message>
+ <source>Date</source>
+ <translation>Zuva</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Zita</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Kero</translation>
+ </message>
+ </context>
+<context>
+ <name>UnitDisplayStatusBarControl</name>
+ </context>
+<context>
+ <name>WalletFrame</name>
+ </context>
+<context>
+ <name>WalletModel</name>
+ </context>
+<context>
+ <name>WalletView</name>
+ </context>
+<context>
+ <name>bitcoin-core</name>
+ <message>
+ <source>Information</source>
+ <translation>Ruzivo</translation>
+ </message>
+ <message>
+ <source>Warning</source>
+ <translation>Hokoyo</translation>
+ </message>
+ </context>
+</TS> \ No newline at end of file
diff --git a/src/qt/locale/bitcoin_sv.ts b/src/qt/locale/bitcoin_sv.ts
index d0fcef9297..405cde3c99 100644
--- a/src/qt/locale/bitcoin_sv.ts
+++ b/src/qt/locale/bitcoin_sv.ts
@@ -327,6 +327,14 @@ Var vänlig och försök igen.</translation>
<translation>Öppna &amp;URI...</translation>
</message>
<message>
+ <source>Wallet:</source>
+ <translation>Plånbok:</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>Standardplånbok</translation>
+ </message>
+ <message>
<source>Click to disable network activity.</source>
<translation>Klicka för att inaktivera nätverksaktivitet.</translation>
</message>
@@ -347,6 +355,10 @@ Var vänlig och försök igen.</translation>
<translation>Återindexerar block på disken...</translation>
</message>
<message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>Proxy är &lt;b&gt; aktiverad &lt;/b&gt;: %1</translation>
+ </message>
+ <message>
<source>Send coins to a Bitcoin address</source>
<translation>Skicka bitcoin till en Bitcoin-adress</translation>
</message>
@@ -515,6 +527,12 @@ Var vänlig och försök igen.</translation>
</translation>
</message>
<message>
+ <source>Wallet: %1
+</source>
+ <translation>Plånbok: %1
+</translation>
+ </message>
+ <message>
<source>Type: %1
</source>
<translation>Typ: %1
@@ -751,6 +769,14 @@ Var vänlig och försök igen.</translation>
<translation>Den angivna adressen "%1" är inte en giltig Bitcoin-adress.</translation>
</message>
<message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Adress "%1" finns redan som en mottagande adress med etikett "%2" och kan därför anges som utgående adress.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>Den angivna adressen "%1" finns redan i adressboken med etikett "%2".</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>Kunde inte låsa upp plånboken.</translation>
</message>
@@ -824,6 +850,10 @@ Var vänlig och försök igen.</translation>
<translation>Denna första synkronisering är väldigt krävande, och kan påvisa hårdvaruproblem med din dator som tidigare inte visats sig. Varje gång du kör %1, kommer nerladdningen att fortsätta där den avslutades.</translation>
</message>
<message>
+ <source>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source>
+ <translation>Om du valt att begränsa storleken på blockkedjan (pruning), måste historisk data fortfarande bli nedladdad och processerad, men kommer bli borttagen för att minimera hårddiskutrymme.</translation>
+ </message>
+ <message>
<source>Use the default data directory</source>
<translation>Använd den förvalda datakatalogen</translation>
</message>
@@ -1025,6 +1055,22 @@ Var vänlig och försök igen.</translation>
<translation>&amp;Nätverk</translation>
</message>
<message>
+ <source>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</source>
+ <translation>Stänger av en del avancerade funktioner, men samtliga block kommer fortfarande verifieras. Vid avstängning av denna inställning kommer den fullständiga blockkedjan behövas laddas ned igen. Det använda hårddiskutrymmet kan öka något.</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Trim- &amp; block-utrymme till</translation>
+ </message>
+ <message>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Vid avstängning av denna inställning kommer den fullständiga blockkedjan behövas laddas ned igen.</translation>
+ </message>
+ <message>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
<translation>(0 = auto, &lt;0 = lämna så många kärnor lediga)</translation>
</message>
@@ -1291,6 +1337,10 @@ Var vänlig och försök igen.</translation>
<translation>URI-hantering</translation>
</message>
<message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin://' är inte en accepterad URI. Använd 'bitcoin:' istället.</translation>
+ </message>
+ <message>
<source>Payment request fetch URL is invalid: %1</source>
<translation>Hämtningsadressen för betalningsbegäran är ogiltig: %1</translation>
</message>
@@ -1488,10 +1538,18 @@ Var vänlig och försök igen.</translation>
<context>
<name>QObject::QObject</name>
<message>
+ <source>Error parsing command line arguments: %1.</source>
+ <translation>Kunde inte tolka argumentet: %1.</translation>
+ </message>
+ <message>
<source>Error: Specified data directory "%1" does not exist.</source>
<translation>Fel: Angiven datakatalog "%1" finns inte.</translation>
</message>
<message>
+ <source>Error: Cannot parse configuration file: %1.</source>
+ <translation>Fel: Kan inte tolka konfigurationsfil: %1.</translation>
+ </message>
+ <message>
<source>Error: %1</source>
<translation>Fel: %1</translation>
</message>
@@ -1582,6 +1640,14 @@ Var vänlig och försök igen.</translation>
<translation>Minnesåtgång</translation>
</message>
<message>
+ <source>Wallet: </source>
+ <translation>Plånbok:</translation>
+ </message>
+ <message>
+ <source>(none)</source>
+ <translation>(ingen)</translation>
+ </message>
+ <message>
<source>&amp;Reset</source>
<translation>&amp;Återställ</translation>
</message>
@@ -1750,6 +1816,10 @@ Var vänlig och försök igen.</translation>
<translation>&amp;Ta bort blockering</translation>
</message>
<message>
+ <source>default wallet</source>
+ <translation>Standardplånbok</translation>
+ </message>
+ <message>
<source>Welcome to the %1 RPC console.</source>
<translation>Välkommen till %1 RPC-konsolen.</translation>
</message>
@@ -1774,6 +1844,14 @@ Var vänlig och försök igen.</translation>
<translation>Nätverksaktivitet inaktiverad</translation>
</message>
<message>
+ <source>Executing command without any wallet</source>
+ <translation>Utför instruktion utan plånbok</translation>
+ </message>
+ <message>
+ <source>Executing command using "%1" wallet</source>
+ <translation>Utför instruktion med plånbok "%1"</translation>
+ </message>
+ <message>
<source>(node id: %1)</source>
<translation>(nod-id: %1)</translation>
</message>
@@ -1845,6 +1923,14 @@ Var vänlig och försök igen.</translation>
<translation>Rensa</translation>
</message>
<message>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <translation>Bech32-addresser (BIP-173) är billigare att spendera från och har bättre skytt mot skrivfel mot konstnaden att äldre plånböcker inte förstår dem. När inte valet är gjort kommer en address som är kompatibel med äldre plånböcker att skapas istället.</translation>
+ </message>
+ <message>
+ <source>Generate native segwit (Bech32) address</source>
+ <translation>Skapa Bech32-adress</translation>
+ </message>
+ <message>
<source>Requested payments history</source>
<translation>Historik för begärda betalningar</translation>
</message>
@@ -2050,6 +2136,14 @@ Var vänlig och försök igen.</translation>
<translation>Fäll ihop avgiftsinställningarna</translation>
</message>
<message>
+ <source>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</source>
+ <translation>Ange en egen avgift per kB (1 000 bytes) av transaktionens virtuella storlek.
+
+Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut för en transaktion på 500 bytes (en halv kB) om man valt "100 satoshi per kB" som egen avgift.</translation>
+ </message>
+ <message>
<source>per kilobyte</source>
<translation>per kilobyte</translation>
</message>
@@ -2170,6 +2264,14 @@ Var vänlig och försök igen.</translation>
<translation>Du kan välja att höja avgiften senare (med ersättande avgift, BIP-125).</translation>
</message>
<message>
+ <source>from wallet %1</source>
+ <translation>från plånbok: %1</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction.</source>
+ <translation>Var vänlig se över din transaktion.</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>Transaktionsavgift</translation>
</message>
@@ -2178,6 +2280,10 @@ Var vänlig och försök igen.</translation>
<translation>Använder inte ersättande avgift, BIP-125.</translation>
</message>
<message>
+ <source>Total Amount</source>
+ <translation>Totalt</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>Bekräfta att mynt ska skickas</translation>
</message>
@@ -2631,6 +2737,10 @@ Var vänlig och försök igen.</translation>
<translation>Transaktionens totala storlek</translation>
</message>
<message>
+ <source>Transaction virtual size</source>
+ <translation>Transaktionens virtuella storlek</translation>
+ </message>
+ <message>
<source>Output index</source>
<translation>Utmatningsindex</translation>
</message>
@@ -2965,6 +3075,10 @@ Var vänlig och försök igen.</translation>
<translation>Skicka Bitcoins</translation>
</message>
<message>
+ <source>Fee bump error</source>
+ <translation>Avgiftsökningsfel</translation>
+ </message>
+ <message>
<source>Increasing transaction fee failed</source>
<translation>Ökning av avgiften lyckades inte</translation>
</message>
@@ -3031,7 +3145,11 @@ Var vänlig och försök igen.</translation>
<source>The wallet data was successfully saved to %1.</source>
<translation>Plånbokens data sparades till %1.</translation>
</message>
- </context>
+ <message>
+ <source>Cancel</source>
+ <translation>Avbryt</translation>
+ </message>
+</context>
<context>
<name>bitcoin-core</name>
<message>
@@ -3075,10 +3193,18 @@ Var vänlig och försök igen.</translation>
<translation>Kan inte låsa data-mappen %s. %s körs förmodligen redan.</translation>
</message>
<message>
+ <source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
+ <translation>Kan inte låta addrman hitta utgående uppkopplingar samtidigt som specifika uppkopplingar skapas</translation>
+ </message>
+ <message>
<source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
<translation>Fel vid läsning av %s! Alla nycklar lästes korrekt, men transaktionsdata eller poster i adressboken kanske saknas eller är felaktiga.</translation>
</message>
<message>
+ <source>Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)</source>
+ <translation>Gruppera utgående transaktioner med adress, där samtliga eller inga används, istället för att välja vid varje utgående transaktion. Detta ökar din integritet genom att en adress enbart används en gång (om inte någon skickar till den efteråt), men kan öka dina utgifter då valet av ingående transaktioner görs suboptimalt (standardvärde: %u)</translation>
+ </message>
+ <message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation>Vänligen kolla så att din dators datum och tid är korrekt! Om din klocka går fel kommer %s inte att fungera korrekt.</translation>
</message>
@@ -3095,6 +3221,10 @@ Var vänlig och försök igen.</translation>
<translation>Detta är ett förhandstestbygge - använd på egen risk - använd inte för brytning eller handelsapplikationer</translation>
</message>
<message>
+ <source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
+ <translation>Detta är transaktionsavgiften som slängs borta om det är mindre än damm på denna nivå</translation>
+ </message>
+ <message>
<source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
<translation>Kunde inte spela om block. Du kommer att behöva bygga om databasen med -reindex-chainstate.</translation>
</message>
@@ -3143,6 +3273,10 @@ Var vänlig och försök igen.</translation>
<translation>Vill du bygga om blockdatabasen nu?</translation>
</message>
<message>
+ <source>Error creating %s: You can't create non-HD wallets with this version.</source>
+ <translation>Fel vid skapande av %s: Det är inte möjligt att skapa icke-HD plånböcker med denna version.</translation>
+ </message>
+ <message>
<source>Error initializing block database</source>
<translation>Fel vid initiering av blockdatabasen</translation>
</message>
@@ -3155,6 +3289,10 @@ Var vänlig och försök igen.</translation>
<translation>Fel vid inläsning av %s</translation>
</message>
<message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>Fel vid laddning av %s: Privata nycklar kan enbart bli avstängda vid skapelsen</translation>
+ </message>
+ <message>
<source>Error loading %s: Wallet corrupted</source>
<translation>Fel vid inläsning av %s: Plånboken är korrupt</translation>
</message>
@@ -3207,6 +3345,14 @@ Var vänlig och försök igen.</translation>
<translation>Ogiltigt belopp för -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Den specificerade mappen för block "%s" existerar inte.</translation>
+ </message>
+ <message>
+ <source>Upgrading txindex database</source>
+ <translation>Uppgraderar databas för txindex</translation>
+ </message>
+ <message>
<source>Loading P2P addresses...</source>
<translation>Läser in P2P-adresser...</translation>
</message>
@@ -3247,6 +3393,10 @@ Var vänlig och försök igen.</translation>
<translation>Det går inte att binda till %s på den här datorn. %s är förmodligen redan igång.</translation>
</message>
<message>
+ <source>Unable to generate keys</source>
+ <translation>Lyckas inte generera nycklar</translation>
+ </message>
+ <message>
<source>Unsupported argument -benchmark ignored, use -debug=bench.</source>
<translation>Argumentet -benchmark stöds inte och ignoreras, använd -debug=bench.</translation>
</message>
@@ -3383,6 +3533,10 @@ Var vänlig och försök igen.</translation>
<translation>Verifierar plånbok(er)...</translation>
</message>
<message>
+ <source>Wallet %s resides outside wallet directory %s</source>
+ <translation>Plånbok %s är lokaliserad utanför plånboksmappen %s</translation>
+ </message>
+ <message>
<source>Warning</source>
<translation>Varning</translation>
</message>
@@ -3439,6 +3593,10 @@ Var vänlig och försök igen.</translation>
<translation>Fel vid inläsningen av plånbok %s. Dublett -wallet filnamn angavs.</translation>
</message>
<message>
+ <source>Keypool ran out, please call keypoolrefill first</source>
+ <translation>Nyckelpoolen har tagit slut, var vänlig anropa keypoolrefill först.</translation>
+ </message>
+ <message>
<source>Starting network threads...</source>
<translation>Startar nätverkstrådar...</translation>
</message>
@@ -3475,6 +3633,26 @@ Var vänlig och försök igen.</translation>
<translation>Otillräckligt med bitcoins</translation>
</message>
<message>
+ <source>Can't generate a change-address key. Private keys are disabled for this wallet.</source>
+ <translation>Kan inte generera en nyckel för växeladress. Privata nycklar är avstängda för denna plånboken.</translation>
+ </message>
+ <message>
+ <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
+ <translation>Kan inte uppgradera till en icke-HD delad plånbok utan att uppgradera till att stödja nyckelpoolen innan splittring. Var vänlig använd -upgradewallet=169900 eller -upgradewallet utan version specificerad.</translation>
+ </message>
+ <message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>Avgiftsestimering misslyckades. Fallbackfee är avstänt. Var vänlig vänta några block, alternativt aktivera -fallbackfee.</translation>
+ </message>
+ <message>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <translation>Varning: Privata nycklar upptäckta i plånbok (%s) vilken har dessa inaktiverade</translation>
+ </message>
+ <message>
+ <source>Cannot write to data directory '%s'; check permissions.</source>
+ <translation>Kan inte skriva till mapp "%s", var vänlig se över filbehörigheter.</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>Laddar blockindex...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_szl.ts b/src/qt/locale/bitcoin_szl.ts
new file mode 100644
index 0000000000..9559df2a7d
--- /dev/null
+++ b/src/qt/locale/bitcoin_szl.ts
@@ -0,0 +1,2123 @@
+<TS language="szl" version="2.1">
+<context>
+ <name>AddressBookPage</name>
+ <message>
+ <source>Right-click to edit address or label</source>
+ <translation>Kliknij prawy knefel mysze, coby edytować adresã abo etyketã</translation>
+ </message>
+ <message>
+ <source>Create a new address</source>
+ <translation>Zrychtuj nowõ adresã</translation>
+ </message>
+ <message>
+ <source>&amp;New</source>
+ <translation>&amp;Nowy</translation>
+ </message>
+ <message>
+ <source>Copy the currently selected address to the system clipboard</source>
+ <translation>Skopiyruj aktualnie ôbranõ adresã do skrytki</translation>
+ </message>
+ <message>
+ <source>&amp;Copy</source>
+ <translation>&amp;Kopiyruj</translation>
+ </message>
+ <message>
+ <source>C&amp;lose</source>
+ <translation>&amp;Zawrzij</translation>
+ </message>
+ <message>
+ <source>Delete the currently selected address from the list</source>
+ <translation>Wychrōń zaznaczōnõ adresã z brify</translation>
+ </message>
+ <message>
+ <source>Enter address or label to search</source>
+ <translation>Wkludź adresã abo etyketã coby wyszukać</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>Eksportuj dane z aktywnyj szkarty do zbioru</translation>
+ </message>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp;Eksportuj</translation>
+ </message>
+ <message>
+ <source>&amp;Delete</source>
+ <translation>&amp;Wychrōń</translation>
+ </message>
+ <message>
+ <source>Choose the address to send coins to</source>
+ <translation>Ôbier adresã, na kerõ chcesz posłać mōnety</translation>
+ </message>
+ <message>
+ <source>Choose the address to receive coins with</source>
+ <translation>Ôbier adresã, na kerõ chcesz dostać mōnety</translation>
+ </message>
+ <message>
+ <source>C&amp;hoose</source>
+ <translation>Ô&amp;bier</translation>
+ </message>
+ <message>
+ <source>Sending addresses</source>
+ <translation>Adresy posyłaniŏ</translation>
+ </message>
+ <message>
+ <source>Receiving addresses</source>
+ <translation>Adresy ôdbiyraniŏ</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
+ <translation>Tukej sōm adresy Bitcoin na kere posyłŏsz płaty. Dycki wybaduj wielość i adresã ôdbiyrŏcza przed posłaniym mōnet.</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
+ <translation>Tukej sōm adresy Bitcoin do ôdbiyraniŏ płatōw. Zalycŏ sie używaniŏ nowych adres ôdbiorczych dlŏ kożdyj transakcyje.</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Address</source>
+ <translation>&amp;Kopiyruj Adresã</translation>
+ </message>
+ <message>
+ <source>Copy &amp;Label</source>
+ <translation>Kopiyruj &amp;Etyketã</translation>
+ </message>
+ <message>
+ <source>&amp;Edit</source>
+ <translation>&amp;Edytuj</translation>
+ </message>
+ <message>
+ <source>Export Address List</source>
+ <translation>Eksportuj wykŏz adres</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>Zbiōr *.CSV (daty rozdzielane kōmami)</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Eksportowanie niy podarziło sie</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the address list to %1. Please try again.</source>
+ <translation>Przitrefiōł sie feler w czasie spamiyntowaniŏ brify adres do %1. Proszã sprōbować zaś.</translation>
+ </message>
+</context>
+<context>
+ <name>AddressTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>Etyketa</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Adresa</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(chyba etykety)</translation>
+ </message>
+</context>
+<context>
+ <name>AskPassphraseDialog</name>
+ <message>
+ <source>Passphrase Dialog</source>
+ <translation>Ôkiynko Hasła</translation>
+ </message>
+ <message>
+ <source>Enter passphrase</source>
+ <translation>Wkludź hasło</translation>
+ </message>
+ <message>
+ <source>New passphrase</source>
+ <translation>Nowe hasło</translation>
+ </message>
+ <message>
+ <source>Repeat new passphrase</source>
+ <translation>Powtōrz nowe hasło</translation>
+ </message>
+ <message>
+ <source>Show password</source>
+ <translation>Pokŏż hasło</translation>
+ </message>
+ <message>
+ <source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
+ <translation>Wkludź nowe hasło do portmanyja.&lt;br/&gt;Proszã używać hasła słożōnego z &lt;b&gt;10 abo wiyncyj losowych liter&lt;/b&gt; abo &lt;b&gt;8 abo wiyncyj słōw.&lt;/b&gt;.</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>Zaszyfruj portmanyj</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>Ta ôperacyjŏ wymŏgŏ hasła do portmanyja coby ôdszperować portmanyj.</translation>
+ </message>
+ <message>
+ <source>Unlock wallet</source>
+ <translation>Ôdszperuj portmanyj.</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>Ta ôperacyjŏ wymŏgŏ hasła do portmanyja coby ôdszyfrować portmanyj.</translation>
+ </message>
+ <message>
+ <source>Decrypt wallet</source>
+ <translation>Ôdszyfruj portmanyj</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>Pōmiyń hasło</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase to the wallet.</source>
+ <translation>Podej stare i nowe hasło do portmanyja.</translation>
+ </message>
+ <message>
+ <source>Confirm wallet encryption</source>
+ <translation>Przituplikuj szyfrowanie portmanyja</translation>
+ </message>
+ <message>
+ <source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
+ <translation>Pozōr: jeźli zaszyfrujesz swōj portmanyj i stracisz hasło &lt;b&gt;STRACISZ WSZYJSKE SWOJE BITCOINY&lt;/b&gt;!</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to encrypt your wallet?</source>
+ <translation>Na isto chcesz zaszyfrować swōj portmanyj?</translation>
+ </message>
+ <message>
+ <source>Wallet encrypted</source>
+ <translation>Portmanyj zaszyfrowany</translation>
+ </message>
+ <message>
+ <source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>%1 zawrzi sie coby dokōńczyć proces szyfrowaniŏ. Pamiyntej, iże szyfrowanie portmanyja ganc niy zabezpieczŏ Twojich bitcoinów przed chabiyniym bez wirusy abo trojany mogōnce zakażać Twōj kōmputer.</translation>
+ </message>
+ <message>
+ <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
+ <translation>WŎŻNE: Wszyjske wykōnane wczaśnij kopije zbioru portmanyja winny być umiyniōne na nowe, szyfrowane zbiory. Z powodōw bezpiyczyństwa, piyrwyjsze kopije niyszyfrowanych zbiorōw portmanyja stōnõ sie bezużyteczne jak ino zaczniesz używać nowego, szyfrowanego portmanyja.</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed</source>
+ <translation>Zaszyfrowanie portmanyja niy podarziło sie</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <translation>Zaszyfrowanie portmanyja niy podarziło sie bez wnyntrzny feler. Twōj portmanyj niy ôstoł zaszyfrowany.</translation>
+ </message>
+ <message>
+ <source>The supplied passphrases do not match.</source>
+ <translation>Podane hasła niy sōm take same.</translation>
+ </message>
+ <message>
+ <source>Wallet unlock failed</source>
+ <translation>Ôdszperowanie portmanyja niy podarziło sie</translation>
+ </message>
+ <message>
+ <source>The passphrase entered for the wallet decryption was incorrect.</source>
+ <translation>Wkludzōne hasło do ôdszyfrowaniŏ portmanyja je niynŏleżne.</translation>
+ </message>
+ <message>
+ <source>Wallet decryption failed</source>
+ <translation>Ôdszyfrowanie portmanyja niy podarziło sie</translation>
+ </message>
+ <message>
+ <source>Wallet passphrase was successfully changed.</source>
+ <translation>Hasło do portmanyja ôstało sprŏwnie pōmiyniōne.</translation>
+ </message>
+ <message>
+ <source>Warning: The Caps Lock key is on!</source>
+ <translation>Pozōr: Caps Lock je zapuszczōny!</translation>
+ </message>
+</context>
+<context>
+ <name>BanTableModel</name>
+ <message>
+ <source>IP/Netmask</source>
+ <translation>IP/Maska necu</translation>
+ </message>
+ <message>
+ <source>Banned Until</source>
+ <translation>Szpera do</translation>
+ </message>
+</context>
+<context>
+ <name>BitcoinGUI</name>
+ <message>
+ <source>Sign &amp;message...</source>
+ <translation>Szkryftnij &amp;wiadōmość</translation>
+ </message>
+ <message>
+ <source>Synchronizing with network...</source>
+ <translation>Synchrōnizacyjŏ z necym...</translation>
+ </message>
+ <message>
+ <source>&amp;Overview</source>
+ <translation>&amp;Podsumowanie</translation>
+ </message>
+ <message>
+ <source>Show general overview of wallet</source>
+ <translation>Pokazuje ôgōlny widok portmanyja</translation>
+ </message>
+ <message>
+ <source>&amp;Transactions</source>
+ <translation>&amp;Transakcyje</translation>
+ </message>
+ <message>
+ <source>Browse transaction history</source>
+ <translation>Przeglōndej historyjõ transakcyji</translation>
+ </message>
+ <message>
+ <source>E&amp;xit</source>
+ <translation>&amp;Zakōńcz</translation>
+ </message>
+ <message>
+ <source>Quit application</source>
+ <translation>Zawrzij aplikacyjõ</translation>
+ </message>
+ <message>
+ <source>&amp;About %1</source>
+ <translation>&amp;Ô %1</translation>
+ </message>
+ <message>
+ <source>Show information about %1</source>
+ <translation>Pokŏż informacyje ô %1</translation>
+ </message>
+ <message>
+ <source>About &amp;Qt</source>
+ <translation>Ô &amp;Qt</translation>
+ </message>
+ <message>
+ <source>Show information about Qt</source>
+ <translation>Pokŏż informacyje ô Qt</translation>
+ </message>
+ <message>
+ <source>&amp;Options...</source>
+ <translation>Ô&amp;pcyje...</translation>
+ </message>
+ <message>
+ <source>Modify configuration options for %1</source>
+ <translation>Zmiyń ôpcyje kōnfiguracyje dlŏ %1</translation>
+ </message>
+ <message>
+ <source>&amp;Encrypt Wallet...</source>
+ <translation>&amp;Zaszyfruj Portmanyj...</translation>
+ </message>
+ <message>
+ <source>&amp;Backup Wallet...</source>
+ <translation>Zrychtuj kopijõ ibrycznõ</translation>
+ </message>
+ <message>
+ <source>&amp;Change Passphrase...</source>
+ <translation>Pōmiyń &amp;hasło</translation>
+ </message>
+ <message>
+ <source>&amp;Sending addresses...</source>
+ <translation>&amp;Adresy posyłaniŏ</translation>
+ </message>
+ <message>
+ <source>&amp;Receiving addresses...</source>
+ <translation>Ad&amp;resy ôdbiyraniŏ</translation>
+ </message>
+ <message>
+ <source>Open &amp;URI...</source>
+ <translation>Ôdewrzij &amp;URI...</translation>
+ </message>
+ <message>
+ <source>Wallet:</source>
+ <translation>Portmanyj:</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>wychodny portmanyj</translation>
+ </message>
+ <message>
+ <source>Click to disable network activity.</source>
+ <translation>Kliknij coby zastawić aktywność necowõ.</translation>
+ </message>
+ <message>
+ <source>Network activity disabled.</source>
+ <translation>Aktywność necowŏ ôstała zastawiōnŏ.</translation>
+ </message>
+ <message>
+ <source>Click to enable network activity again.</source>
+ <translation>Kliknij, coby zaś zapuścić aktywność necowõ.</translation>
+ </message>
+ <message>
+ <source>Syncing Headers (%1%)...</source>
+ <translation>Synchrōnizowanie nŏgōwkōw (%1%)...</translation>
+ </message>
+ <message>
+ <source>Reindexing blocks on disk...</source>
+ <translation>Pōnowne indeksowanie blokōw na dysku...</translation>
+ </message>
+ <message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>Proxy je &lt;b&gt;zapuszczone&lt;/b&gt;: %1</translation>
+ </message>
+ <message>
+ <source>Send coins to a Bitcoin address</source>
+ <translation>Poślij mōnety na adresã Bitcoin</translation>
+ </message>
+ <message>
+ <source>Backup wallet to another location</source>
+ <translation>Ibryczny portmanyj w inkszyj lokalizacyje</translation>
+ </message>
+ <message>
+ <source>Change the passphrase used for wallet encryption</source>
+ <translation>Pōmiyń hasło użyte do szyfrowaniŏ portmanyja</translation>
+ </message>
+ <message>
+ <source>&amp;Debug window</source>
+ <translation>Ôkno &amp;debugowaniŏ</translation>
+ </message>
+ <message>
+ <source>Open debugging and diagnostic console</source>
+ <translation>Ôdewrzij kōnsolã debugowaniŏ i diagnostyki</translation>
+ </message>
+ <message>
+ <source>&amp;Verify message...</source>
+ <translation>&amp;Weryfikuj wiadōmość...</translation>
+ </message>
+ <message>
+ <source>Bitcoin</source>
+ <translation>Bitcoin</translation>
+ </message>
+ <message>
+ <source>Wallet</source>
+ <translation>Portmanyj</translation>
+ </message>
+ <message>
+ <source>&amp;Send</source>
+ <translation>&amp;Poślij</translation>
+ </message>
+ <message>
+ <source>&amp;Receive</source>
+ <translation>Ôd&amp;bier</translation>
+ </message>
+ <message>
+ <source>&amp;Show / Hide</source>
+ <translation>Pokŏż / &amp;Skryj</translation>
+ </message>
+ <message>
+ <source>Show or hide the main Window</source>
+ <translation>Pokazuje abo skrywŏ bazowe ôkno</translation>
+ </message>
+ <message>
+ <source>Encrypt the private keys that belong to your wallet</source>
+ <translation>Szyfruj klucze prywatne, kere sōm we twojim portmanyju</translation>
+ </message>
+ <message>
+ <source>Sign messages with your Bitcoin addresses to prove you own them</source>
+ <translation>Podpisz wiadōmości swojōm adresōm coby dowiyść jejich posiadanie</translation>
+ </message>
+ <message>
+ <source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
+ <translation>Zweryfikuj wiadōmość, coby wejzdrzeć sie, iże ôstała podpisanŏ podanōm adresōm Bitcoin.</translation>
+ </message>
+ <message>
+ <source>&amp;File</source>
+ <translation>&amp;Zbiōr</translation>
+ </message>
+ <message>
+ <source>&amp;Settings</source>
+ <translation>&amp;Nasztalowania</translation>
+ </message>
+ <message>
+ <source>&amp;Help</source>
+ <translation>Pō&amp;moc</translation>
+ </message>
+ <message>
+ <source>Tabs toolbar</source>
+ <translation>Lajsta szkart</translation>
+ </message>
+ <message>
+ <source>Request payments (generates QR codes and bitcoin: URIs)</source>
+ <translation>Żōndej płatu (gyneruje kod QR jak tyż URI bitcoin:)</translation>
+ </message>
+ <message>
+ <source>Show the list of used sending addresses and labels</source>
+ <translation>Pokŏż wykŏz adres i etyket użytych do posyłaniŏ</translation>
+ </message>
+ <message>
+ <source>Show the list of used receiving addresses and labels</source>
+ <translation>Pokŏż wykŏz adres i etyket użytych do ôdbiyraniŏ</translation>
+ </message>
+ <message>
+ <source>Open a bitcoin: URI or payment request</source>
+ <translation>Ôdewrzij URI bitcoin: abo żōndanie płatu</translation>
+ </message>
+ <message>
+ <source>&amp;Command-line options</source>
+ <translation>Ôp&amp;cyje piski kōmynd</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n active connection(s) to Bitcoin network</source>
+ <translation><numerusform>%n aktywne połōnczynie do necu Bitcoin</numerusform><numerusform>%n aktywnych połōnczyń do necu Bitcoin</numerusform><numerusform>%n aktywnych skuplowań do necu Bitcoin</numerusform></translation>
+ </message>
+ <message>
+ <source>Indexing blocks on disk...</source>
+ <translation>Indeksowanie blokōw na dysku...</translation>
+ </message>
+ <message>
+ <source>Processing blocks on disk...</source>
+ <translation>Przetwŏrzanie blokōw na dysku...</translation>
+ </message>
+ <message numerus="yes">
+ <source>Processed %n block(s) of transaction history.</source>
+ <translation><numerusform>Przetworzōno %n blok historyji transakcyji.</numerusform><numerusform>Przetworzōno %n blokōw historyji transakcyji.</numerusform><numerusform>Przetworzōno %n blokōw historyji transakcyji.</numerusform></translation>
+ </message>
+ <message>
+ <source>%1 behind</source>
+ <translation>%1 za</translation>
+ </message>
+ <message>
+ <source>Last received block was generated %1 ago.</source>
+ <translation>Ôstatni dostany blok ôstoł wygynerowany %1 tymu.</translation>
+ </message>
+ <message>
+ <source>Transactions after this will not yet be visible.</source>
+ <translation>Transakcyje po tym mōmyncie niy bydōm jeszcze widzialne.</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Feler</translation>
+ </message>
+ <message>
+ <source>Warning</source>
+ <translation>Pozōr</translation>
+ </message>
+ <message>
+ <source>Information</source>
+ <translation>Informacyjŏ</translation>
+ </message>
+ <message>
+ <source>Up to date</source>
+ <translation>Terŏźny</translation>
+ </message>
+ <message>
+ <source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
+ <translation>Pokŏż pōmoc %1 coby zobŏczyć wykŏz wszyjskich ôpcyji piski nakŏzań.</translation>
+ </message>
+ <message>
+ <source>%1 client</source>
+ <translation>%1 klijynt</translation>
+ </message>
+ <message>
+ <source>Catching up...</source>
+ <translation>Trwŏ synchrōnizacyjŏ...</translation>
+ </message>
+ <message>
+ <source>Date: %1
+</source>
+ <translation>Datōm: %1
+</translation>
+ </message>
+ <message>
+ <source>Amount: %1
+</source>
+ <translation>Kwota: %1
+</translation>
+ </message>
+ <message>
+ <source>Wallet: %1
+</source>
+ <translation>Portmanyj: %1
+</translation>
+ </message>
+ <message>
+ <source>Type: %1
+</source>
+ <translation>Zorta: %1
+</translation>
+ </message>
+ <message>
+ <source>Label: %1
+</source>
+ <translation>Etyketa: %1
+</translation>
+ </message>
+ <message>
+ <source>Address: %1
+</source>
+ <translation>Adresa: %1
+</translation>
+ </message>
+ <message>
+ <source>Sent transaction</source>
+ <translation>Transakcyjŏ wysłanŏ</translation>
+ </message>
+ <message>
+ <source>Incoming transaction</source>
+ <translation>Transakcyjŏ przichodzōncŏ</translation>
+ </message>
+ <message>
+ <source>HD key generation is &lt;b&gt;enabled&lt;/b&gt;</source>
+ <translation>Gynerowanie kluczy HD je &lt;b&gt;zapuszczone&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>HD key generation is &lt;b&gt;disabled&lt;/b&gt;</source>
+ <translation>Gynerowanie kluczy HD je &lt;b&gt;zastawiōne&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
+ <translation>Portmanyj je &lt;b&gt;zaszyfrowany&lt;/b&gt; i terŏźnie &lt;b&gt;ôdszperowany&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
+ <translation>Portmanyj je &lt;b&gt;zaszyfrowany&lt;/b&gt; i terŏźnie &lt;b&gt;zaszperowany&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
+ <translation>Przitrefiōł sie krytyczny feler. Bitcoin niy poradzi kōntynuować bezpiycznie i ôstanie zawrzity.</translation>
+ </message>
+</context>
+<context>
+ <name>CoinControlDialog</name>
+ <message>
+ <source>Coin Selection</source>
+ <translation>Ôbiōr mōnet</translation>
+ </message>
+ <message>
+ <source>Quantity:</source>
+ <translation>Wielość:</translation>
+ </message>
+ <message>
+ <source>Bytes:</source>
+ <translation>Bajtōw:</translation>
+ </message>
+ <message>
+ <source>Amount:</source>
+ <translation>Kwota:</translation>
+ </message>
+ <message>
+ <source>Fee:</source>
+ <translation>Ôpłŏcka:</translation>
+ </message>
+ <message>
+ <source>Dust:</source>
+ <translation>Sztaub:</translation>
+ </message>
+ <message>
+ <source>After Fee:</source>
+ <translation>Po ôpłŏcce:</translation>
+ </message>
+ <message>
+ <source>Change:</source>
+ <translation>Wydŏwka:</translation>
+ </message>
+ <message>
+ <source>(un)select all</source>
+ <translation>Zaznacz/Ôdznacz wszyjsko</translation>
+ </message>
+ <message>
+ <source>Tree mode</source>
+ <translation>Tryb strōma</translation>
+ </message>
+ <message>
+ <source>List mode</source>
+ <translation>Tryb wykŏzu</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Kwota</translation>
+ </message>
+ <message>
+ <source>Received with label</source>
+ <translation>Ôdebrane z etyketōm</translation>
+ </message>
+ <message>
+ <source>Received with address</source>
+ <translation>Ôdebrane z adresōm</translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>Datōm</translation>
+ </message>
+ <message>
+ <source>Confirmations</source>
+ <translation>Przituplowania</translation>
+ </message>
+ <message>
+ <source>Confirmed</source>
+ <translation>Przituplowany</translation>
+ </message>
+ <message>
+ <source>Copy address</source>
+ <translation>Kopiyruj adresã</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Kopiyruj etyketã</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Kopiyruj kwotã</translation>
+ </message>
+ <message>
+ <source>Copy transaction ID</source>
+ <translation>Kopiyruj ID transakcyje</translation>
+ </message>
+ <message>
+ <source>Lock unspent</source>
+ <translation>Zaszperuj niywydane</translation>
+ </message>
+ <message>
+ <source>Unlock unspent</source>
+ <translation>Ôdszperuj niywydane</translation>
+ </message>
+ <message>
+ <source>Copy quantity</source>
+ <translation>Kopiyruj wielość</translation>
+ </message>
+ <message>
+ <source>Copy fee</source>
+ <translation>Kopiyruj ôpłŏckã</translation>
+ </message>
+ <message>
+ <source>Copy after fee</source>
+ <translation>Kopiyruj wielość po ôpłŏcce</translation>
+ </message>
+ <message>
+ <source>Copy bytes</source>
+ <translation>Kopiyruj wielość bajtōw</translation>
+ </message>
+ <message>
+ <source>Copy dust</source>
+ <translation>Kopiyruj sztaub</translation>
+ </message>
+ <message>
+ <source>Copy change</source>
+ <translation>Kopiyruj wydŏwkã</translation>
+ </message>
+ <message>
+ <source>(%1 locked)</source>
+ <translation>(%1 zaszperowane)</translation>
+ </message>
+ <message>
+ <source>yes</source>
+ <translation>ja</translation>
+ </message>
+ <message>
+ <source>no</source>
+ <translation>niy</translation>
+ </message>
+ <message>
+ <source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
+ <translation>Ta etyketa stŏwŏ sie czyrwōnŏ jeźli keryś z ôdbiyrŏczy dostŏwŏ kwotã myńszõ aniżeli terŏźny prōg sztaubu.</translation>
+ </message>
+ <message>
+ <source>Can vary +/- %1 satoshi(s) per input.</source>
+ <translation>Chwiyrŏ sie +/- %1 satoshi na wchōd.</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(chyba etykety)</translation>
+ </message>
+ <message>
+ <source>change from %1 (%2)</source>
+ <translation>wydŏwka z %1 (%2)</translation>
+ </message>
+ <message>
+ <source>(change)</source>
+ <translation>(wydŏwka)</translation>
+ </message>
+</context>
+<context>
+ <name>EditAddressDialog</name>
+ <message>
+ <source>Edit Address</source>
+ <translation>Edytuj adresã</translation>
+ </message>
+ <message>
+ <source>&amp;Label</source>
+ <translation>&amp;Etyketa</translation>
+ </message>
+ <message>
+ <source>The label associated with this address list entry</source>
+ <translation>Etyketa ôbwiōnzanŏ z tym wpisym na wykŏzie adres</translation>
+ </message>
+ <message>
+ <source>The address associated with this address list entry. This can only be modified for sending addresses.</source>
+ <translation>Ta adresa je ôbwiōnzanŏ z wpisym na wykŏzie adres. Może być zmodyfikowany jyno dlŏ adres posyłajōncych.</translation>
+ </message>
+ <message>
+ <source>&amp;Address</source>
+ <translation>&amp;Adresa</translation>
+ </message>
+ <message>
+ <source>New sending address</source>
+ <translation>Nowŏ adresa posyłaniŏ</translation>
+ </message>
+ <message>
+ <source>Edit receiving address</source>
+ <translation>Edytuj adresã ôdbiōru</translation>
+ </message>
+ <message>
+ <source>Edit sending address</source>
+ <translation>Edytuj adresã posyłaniŏ</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is not a valid Bitcoin address.</source>
+ <translation>Wkludzōnŏ adresa "%1" niyma nŏleżnōm adresōm Bitcoin.</translation>
+ </message>
+ <message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Adresa "%1" już je za adresã ôdbiorczõ z etyketōm "%2" i bez to niy idzie jeji przidać za adresã nadŏwcy.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>Wkludzōnŏ adresa "%1" już je w ksiōnżce adres z ôpisym "%2".</translation>
+ </message>
+ <message>
+ <source>Could not unlock wallet.</source>
+ <translation>Niy idzie było ôdszperować portmanyja.</translation>
+ </message>
+ <message>
+ <source>New key generation failed.</source>
+ <translation>Gynerowanie nowego klucza niy podarziło sie.</translation>
+ </message>
+</context>
+<context>
+ <name>FreespaceChecker</name>
+ <message>
+ <source>A new data directory will be created.</source>
+ <translation>Bydzie zrychtowany nowy folder danych.</translation>
+ </message>
+ <message>
+ <source>name</source>
+ <translation>miano</translation>
+ </message>
+ <message>
+ <source>Directory already exists. Add %1 if you intend to create a new directory here.</source>
+ <translation>Katalog już je. Przidej %1 jeźli mŏsz zastrojynie zrychtować tukej nowy katalog.</translation>
+ </message>
+ <message>
+ <source>Cannot create data directory here.</source>
+ <translation>Niy idzie było tukej zrychtować folderu datōw.</translation>
+ </message>
+</context>
+<context>
+ <name>HelpMessageDialog</name>
+ <message>
+ <source>version</source>
+ <translation>wersyjŏ</translation>
+ </message>
+ <message>
+ <source>(%1-bit)</source>
+ <translation>(%1-bit)</translation>
+ </message>
+ <message>
+ <source>About %1</source>
+ <translation>Ô %1</translation>
+ </message>
+ <message>
+ <source>Command-line options</source>
+ <translation>Ôpcyje piski kōmynd</translation>
+ </message>
+</context>
+<context>
+ <name>Intro</name>
+ <message>
+ <source>Welcome</source>
+ <translation>Witej</translation>
+ </message>
+ <message>
+ <source>Welcome to %1.</source>
+ <translation>Witej w %1.</translation>
+ </message>
+ <message>
+ <source>As this is the first time the program is launched, you can choose where %1 will store its data.</source>
+ <translation>Pōniywŏż je to piyrsze sztartniyńcie programu, możesz ôbrać kaj %1 bydzie spamiyntować swoje daty.</translation>
+ </message>
+ <message>
+ <source>When you click OK, %1 will begin to download and process the full %4 block chain (%2GB) starting with the earliest transactions in %3 when %4 initially launched.</source>
+ <translation>Kej naciśniesz OK, %1 zacznie pobiyrać i przetwŏrzać cołkõ %4 keta blokōw (%2GB) przi zaczynaniu ôd piyrszych transakcyji w %3 kej %4 ôstoł sztartniynty.</translation>
+ </message>
+ <message>
+ <source>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source>
+ <translation>Wstympnŏ synchrōnizacyjŏ je barzo wymŏgajōncŏ i może wyzdradzić wczaśnij niyzaôbserwowane niyprzileżytości sprzyntowe. Za kożdym sztartniyńciym %1 pobiyranie bydzie kōntynuowane ôd placu w kerym ôstało zastawiōne.</translation>
+ </message>
+ <message>
+ <source>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source>
+ <translation>Jeźli ôbrołś ôpcyjõ ukrōcyniŏ spamiyntowaniŏ kety blokōw (przicinanie) daty historyczne cołki czas bydōm musiały być pobrane i przetworzōne, jednak po tym ôstanõ wychrōniōne coby ôgraniczyć użycie dysku.</translation>
+ </message>
+ <message>
+ <source>Use the default data directory</source>
+ <translation>Użyj wychodnego folderu datōw</translation>
+ </message>
+ <message>
+ <source>Use a custom data directory:</source>
+ <translation>Użyj ôbranego folderu datōw</translation>
+ </message>
+ <message>
+ <source>Bitcoin</source>
+ <translation>Bitcoin</translation>
+ </message>
+ <message>
+ <source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
+ <translation>Co nojmynij %1 GB datōw ôstanie spamiyntane w tym katalogu, daty te bydōm z czasym corŏz srogsze.</translation>
+ </message>
+ <message>
+ <source>Approximately %1 GB of data will be stored in this directory.</source>
+ <translation>Kole %1 GB datōw ôstanie spamiyntane w tym katalogu.</translation>
+ </message>
+ <message>
+ <source>%1 will download and store a copy of the Bitcoin block chain.</source>
+ <translation>%1 sebiere i spamiyntŏ kopijõ kety blokōw Bitcoin.</translation>
+ </message>
+ <message>
+ <source>The wallet will also be stored in this directory.</source>
+ <translation>Portmanyj tyż ôstanie spamiyntany w tym katalogu.</translation>
+ </message>
+ <message>
+ <source>Error: Specified data directory "%1" cannot be created.</source>
+ <translation>Feler: podany folder datōw "%1" niy mōg ôstać zrychtowany.</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Feler</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n GB of free space available</source>
+ <translation><numerusform>%n GB swobodnego placu dostympne</numerusform><numerusform>%n GB swobodnego placu dostympne</numerusform><numerusform>%n GB swobodnego placu dostympne</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>(of %n GB needed)</source>
+ <translation><numerusform>(z %n GB przidajnego)</numerusform><numerusform>(z %n GB przidajnych)</numerusform><numerusform>(z %n GB przidajnych)</numerusform></translation>
+ </message>
+</context>
+<context>
+ <name>ModalOverlay</name>
+ <message>
+ <source>Form</source>
+ <translation>Formular</translation>
+ </message>
+ <message>
+ <source>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source>
+ <translation>Świyże transakcyje mogōm niy być jeszcze widzialne, a tedyć saldo portmanyja może być niynŏleżne. Te detale bydōm nŏleżne, kej portmanyj zakōńczy synchrōnizacyjõ z necym bitcoin, zgodnie z miyniōnym ôpisym.</translation>
+ </message>
+ <message>
+ <source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
+ <translation>Prōba wydaniŏ bitcoinōw kere niy sōm jeszcze wyświytlōne za transakcyjŏ ôstanie ôdciepniyntŏ bez nec.</translation>
+ </message>
+ <message>
+ <source>Number of blocks left</source>
+ <translation>Ôstało blokōw</translation>
+ </message>
+ <message>
+ <source>Unknown...</source>
+ <translation>Niyznōme...</translation>
+ </message>
+ <message>
+ <source>Last block time</source>
+ <translation>Czŏs ôstatnigo bloku</translation>
+ </message>
+ <message>
+ <source>Progress</source>
+ <translation>Postymp</translation>
+ </message>
+ <message>
+ <source>Progress increase per hour</source>
+ <translation>Przirost postympu na godzinã</translation>
+ </message>
+ <message>
+ <source>calculating...</source>
+ <translation>ôbliczanie...</translation>
+ </message>
+ <message>
+ <source>Estimated time left until synced</source>
+ <translation>Przewidowany czŏs abszlusu synchrōnizacyje</translation>
+ </message>
+ <message>
+ <source>Hide</source>
+ <translation>Skryj</translation>
+ </message>
+ <message>
+ <source>Unknown. Syncing Headers (%1)...</source>
+ <translation>Niyznōme. Synchrōnizowanie nŏgōwkōw (%1)...</translation>
+ </message>
+</context>
+<context>
+ <name>OpenURIDialog</name>
+ <message>
+ <source>Open URI</source>
+ <translation>Ôdewrzij URI</translation>
+ </message>
+ <message>
+ <source>Open payment request from URI or file</source>
+ <translation>Ôdewrzij żōndanie płatu z URI abo zbioru</translation>
+ </message>
+ <message>
+ <source>URI:</source>
+ <translation>URI:</translation>
+ </message>
+ <message>
+ <source>Select payment request file</source>
+ <translation>Ôtwōrz żōndanie płatu ze zbioru</translation>
+ </message>
+ <message>
+ <source>Select payment request file to open</source>
+ <translation>Ôbier zbiōr żōndaniŏ płatu do ôdewrzyniŏ</translation>
+ </message>
+</context>
+<context>
+ <name>OptionsDialog</name>
+ <message>
+ <source>Options</source>
+ <translation>Ôpcyje</translation>
+ </message>
+ <message>
+ <source>&amp;Main</source>
+ <translation>&amp;Bazowe</translation>
+ </message>
+ <message>
+ <source>Automatically start %1 after logging in to the system.</source>
+ <translation>Autōmatycznie sztartnij %1 po wlogowaniu do systymu.</translation>
+ </message>
+ <message>
+ <source>&amp;Start %1 on system login</source>
+ <translation>&amp;Sztartuj %1 w czasie logowaniŏ do systymu</translation>
+ </message>
+ <message>
+ <source>Size of &amp;database cache</source>
+ <translation>Srogość bufōra bazy datōw</translation>
+ </message>
+ <message>
+ <source>MB</source>
+ <translation>MB</translation>
+ </message>
+ <message>
+ <source>Number of script &amp;verification threads</source>
+ <translation>Wielość wōntkōw &amp;weryfikacyje skryptu</translation>
+ </message>
+ <message>
+ <source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source>
+ <translation>Adresa IP proxy (bp. IPv4: 127.0.0.1 / IPv6: ::1)</translation>
+ </message>
+ <message>
+ <source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source>
+ <translation>Minimalizuje zamiast zakōńczyć fungowanie aplikacyje przi zawiyraniu ôkna. Kej ta ôpcyjŏ je zapuszczonŏ, aplikacyjŏ zakōńczy fungowanie po ôbraniu Zawrzij w myni.</translation>
+ </message>
+ <message>
+ <source>Active command-line options that override above options:</source>
+ <translation>Aktywne ôpcyje piski kōmynd, kere nadpisujōm powyższe ôpcyje:</translation>
+ </message>
+ <message>
+ <source>Open the %1 configuration file from the working directory.</source>
+ <translation>Ôdewrzij %1 zbiōr kōnfiguracyje z czynnego katalogu.</translation>
+ </message>
+ <message>
+ <source>Open Configuration File</source>
+ <translation>Ôdewrzij zbiōr kōnfiguracyje</translation>
+ </message>
+ <message>
+ <source>Reset all client options to default.</source>
+ <translation>Prziwrōć wszyjske wychodne ustawiyniŏ klijynta.</translation>
+ </message>
+ <message>
+ <source>&amp;Reset Options</source>
+ <translation>&amp;Resetuj Ôpcyje</translation>
+ </message>
+ <message>
+ <source>&amp;Network</source>
+ <translation>&amp;Nec</translation>
+ </message>
+ <message>
+ <source>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</source>
+ <translation>Zastawiŏ niykere moderne funkcyje, ale wszyjske bloki durch bydōm w połni wybadowane. Cŏfniyńcie tego nasztalowaniŏ fołdruje pōnownego sebraniŏ cołkij kety blokōw. Istne spotrzebowanie dysku może być cosikej wyższe.</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Przitnij skłŏd &amp;blokōw do</translation>
+ </message>
+ <message>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Cŏfniyńcie tego ustawiyniŏ wymŏgŏ pōnownego sebraniŏ cołkij kety blokōw.</translation>
+ </message>
+ <message>
+ <source>(0 = auto, &lt;0 = leave that many cores free)</source>
+ <translation>(0 = autōmatycznie, &lt;0 = ôstŏw tela swobodnych drzyni)</translation>
+ </message>
+ <message>
+ <source>W&amp;allet</source>
+ <translation>Portm&amp;anyj</translation>
+ </message>
+ <message>
+ <source>Expert</source>
+ <translation>Ekspert</translation>
+ </message>
+ <message>
+ <source>Enable coin &amp;control features</source>
+ <translation>Zapuść funkcyje kōntroli mōnet</translation>
+ </message>
+ <message>
+ <source>If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed.</source>
+ <translation>Jeźli zastawisz możebność wydaniŏ niyprzituplikowanyj wydanej wydŏwki, wydŏwka z transakcyje niy bydzie mogła ôstać użytŏ, podwiela ta transakcyjŏ niy bydzie miała nojmynij jednego przituplowaniŏ. To tyż mŏ wpływ na porachowanie Twojigo salda.</translation>
+ </message>
+ <message>
+ <source>&amp;Spend unconfirmed change</source>
+ <translation>&amp;Wydej niyprzituplowanõ wydŏwkã</translation>
+ </message>
+ <message>
+ <source>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source>
+ <translation>Autōmatycznie ôdewrzij port klijynta Bitcoin na routerze. Ta ôpcyjŏ funguje ino jeźli twōj router podpiyrŏ UPnP i je ôno zapuszczone.</translation>
+ </message>
+ <message>
+ <source>Map port using &amp;UPnP</source>
+ <translation>Mapuj port przi używaniu &amp;UPnP</translation>
+ </message>
+ <message>
+ <source>Accept connections from outside.</source>
+ <translation>Akceptuj skuplowania ôd zewnōntrz.</translation>
+ </message>
+ <message>
+ <source>Allow incomin&amp;g connections</source>
+ <translation>Zwōl na skuplowania przichodzōnce</translation>
+ </message>
+ <message>
+ <source>Connect to the Bitcoin network through a SOCKS5 proxy.</source>
+ <translation>Skupluj sie z necym Bitcoin bez SOCKS5 proxy.</translation>
+ </message>
+ <message>
+ <source>&amp;Connect through SOCKS5 proxy (default proxy):</source>
+ <translation>&amp;Skupluj bez proxy SOCKS5 (wychodne proxy):</translation>
+ </message>
+ <message>
+ <source>Proxy &amp;IP:</source>
+ <translation>Proxy &amp;IP:</translation>
+ </message>
+ <message>
+ <source>&amp;Port:</source>
+ <translation>&amp;Port:</translation>
+ </message>
+ <message>
+ <source>Port of the proxy (e.g. 9050)</source>
+ <translation>Port ôd proxy (bp. 9050)</translation>
+ </message>
+ <message>
+ <source>IPv4</source>
+ <translation>IPv4</translation>
+ </message>
+ <message>
+ <source>IPv6</source>
+ <translation>IPv6</translation>
+ </message>
+ <message>
+ <source>Tor</source>
+ <translation>Tor</translation>
+ </message>
+ <message>
+ <source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source>
+ <translation>Skupluj sie z necym Bitcoin ze pōmocōm ôsobnego proxy SOCKS5 dlŏ necu TOR</translation>
+ </message>
+ <message>
+ <source>&amp;Window</source>
+ <translation>Ô&amp;kno</translation>
+ </message>
+ <message>
+ <source>User Interface &amp;language:</source>
+ <translation>Gŏdka &amp;używŏcza:</translation>
+ </message>
+ <message>
+ <source>The user interface language can be set here. This setting will take effect after restarting %1.</source>
+ <translation>Idzie sam nasztalować gŏdka interfejsu używŏcza. Nasztalowanie prziniesie skutki po resztarcie %1.</translation>
+ </message>
+ <message>
+ <source>&amp;OK</source>
+ <translation>&amp;OK</translation>
+ </message>
+ <message>
+ <source>&amp;Cancel</source>
+ <translation>&amp;Pociep</translation>
+ </message>
+ <message>
+ <source>default</source>
+ <translation>wychodny</translation>
+ </message>
+ <message>
+ <source>none</source>
+ <translation>żŏdyn</translation>
+ </message>
+ <message>
+ <source>Configuration options</source>
+ <translation>Ôpcyje kōnfiguracyje</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Feler</translation>
+ </message>
+ </context>
+<context>
+ <name>OverviewPage</name>
+ <message>
+ <source>Form</source>
+ <translation>Formular</translation>
+ </message>
+ <message>
+ <source>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source>
+ <translation>Wyświytlanŏ informacyjŏ może być niyterŏźnŏ. Twōj portmanyj synchrōnizuje sie autōmatycznie z necym bitcoin zarŏz po tym, jak zrychtowane je skuplowanie, ale proces tyn niy ôstoł jeszcze skōńczōny.</translation>
+ </message>
+ <message>
+ <source>Available:</source>
+ <translation>Dostympne:</translation>
+ </message>
+ <message>
+ <source>Pending:</source>
+ <translation>Czekajōnce:</translation>
+ </message>
+ <message>
+ <source>Balances</source>
+ <translation>Salda</translation>
+ </message>
+ <message>
+ <source>Total:</source>
+ <translation>Cuzamyn:</translation>
+ </message>
+ <message>
+ <source>Your current total balance</source>
+ <translation>Twoje terŏźne saldo</translation>
+ </message>
+ </context>
+<context>
+ <name>PaymentServer</name>
+ <message>
+ <source>URI handling</source>
+ <translation>Bedynōng URI</translation>
+ </message>
+ <message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin://' to niyma nŏleżne URI. Użyj 'bitcoin:'.</translation>
+ </message>
+ <message>
+ <source>Payment request network doesn't match client network.</source>
+ <translation>Nec żōndaniŏ płatu niy ôdpadŏ necu klijynta.</translation>
+ </message>
+ <message>
+ <source>Unverified payment requests to custom payment scripts are unsupported.</source>
+ <translation>Niyzweryfikowane żōndaniŏ płatu do włŏsnych skryptōw płatu sōm niypodpiyrane.</translation>
+ </message>
+ <message>
+ <source>Network request error</source>
+ <translation>Feler żōndaniŏ necu</translation>
+ </message>
+ <message>
+ <source>Payment acknowledged</source>
+ <translation>Płat przituplowany</translation>
+ </message>
+</context>
+<context>
+ <name>PeerTableModel</name>
+ <message>
+ <source>User Agent</source>
+ <translation>Agynt Używŏcza</translation>
+ </message>
+ <message>
+ <source>Ping</source>
+ <translation>Ping</translation>
+ </message>
+ <message>
+ <source>Received</source>
+ <translation>Ôdebrane</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <source>Amount</source>
+ <translation>Kwota</translation>
+ </message>
+ <message>
+ <source>Enter a Bitcoin address (e.g. %1)</source>
+ <translation>Wkludź adresã Bitcoin (bp. %1)</translation>
+ </message>
+ <message>
+ <source>%1 d</source>
+ <translation>%1 d</translation>
+ </message>
+ <message>
+ <source>%1 h</source>
+ <translation>%1 h</translation>
+ </message>
+ <message>
+ <source>%1 m</source>
+ <translation>%1 m</translation>
+ </message>
+ <message>
+ <source>%1 s</source>
+ <translation>%1 s</translation>
+ </message>
+ <message>
+ <source>N/A</source>
+ <translation>N/A</translation>
+ </message>
+ <message>
+ <source>%1 ms</source>
+ <translation>%1 ms</translation>
+ </message>
+ <message>
+ <source>%1 B</source>
+ <translation>%1 B</translation>
+ </message>
+ <message>
+ <source>%1 KB</source>
+ <translation>%1 KB</translation>
+ </message>
+ <message>
+ <source>%1 MB</source>
+ <translation>%1 MB</translation>
+ </message>
+ <message>
+ <source>%1 GB</source>
+ <translation>%1 GB</translation>
+ </message>
+ <message>
+ <source>unknown</source>
+ <translation>niyznōme</translation>
+ </message>
+</context>
+<context>
+ <name>QObject::QObject</name>
+ <message>
+ <source>Error: %1</source>
+ <translation>Feler: %1</translation>
+ </message>
+</context>
+<context>
+ <name>QRImageWidget</name>
+ <message>
+ <source>&amp;Save Image...</source>
+ <translation>&amp;Spamiyntej Ôbrŏzek</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Image</source>
+ <translation>&amp;Kopiyruj Ôbrŏzek</translation>
+ </message>
+ <message>
+ <source>Save QR Code</source>
+ <translation>Spamiyntej kod QR</translation>
+ </message>
+ <message>
+ <source>PNG Image (*.png)</source>
+ <translation>Ôbrŏzek PNG (*.png)</translation>
+ </message>
+</context>
+<context>
+ <name>RPCConsole</name>
+ <message>
+ <source>N/A</source>
+ <translation>N/A</translation>
+ </message>
+ <message>
+ <source>Client version</source>
+ <translation>Wersyjŏ klijynta</translation>
+ </message>
+ <message>
+ <source>Debug window</source>
+ <translation>Ôkno debugowaniŏ</translation>
+ </message>
+ <message>
+ <source>Using BerkeleyDB version</source>
+ <translation>Używanie wersyje BerkeleyDB</translation>
+ </message>
+ <message>
+ <source>Datadir</source>
+ <translation>Katalog datōw</translation>
+ </message>
+ <message>
+ <source>Startup time</source>
+ <translation>Czŏs sztartniyńciŏ</translation>
+ </message>
+ <message>
+ <source>Network</source>
+ <translation>Nec</translation>
+ </message>
+ <message>
+ <source>Name</source>
+ <translation>Miano</translation>
+ </message>
+ <message>
+ <source>Number of connections</source>
+ <translation>Wielość skuplowań</translation>
+ </message>
+ <message>
+ <source>Block chain</source>
+ <translation>Keta blokōw</translation>
+ </message>
+ <message>
+ <source>Current number of blocks</source>
+ <translation>Terŏźniŏ wielość blokōw</translation>
+ </message>
+ <message>
+ <source>Current number of transactions</source>
+ <translation>Terŏźniŏ wielość transakcyji</translation>
+ </message>
+ <message>
+ <source>Wallet: </source>
+ <translation>Portmanyj: </translation>
+ </message>
+ <message>
+ <source>Received</source>
+ <translation>Ôdebrane</translation>
+ </message>
+ <message>
+ <source>Direction</source>
+ <translation>Richtōng</translation>
+ </message>
+ <message>
+ <source>Version</source>
+ <translation>Wersyjŏ</translation>
+ </message>
+ <message>
+ <source>User Agent</source>
+ <translation>Agynt Używŏcza</translation>
+ </message>
+ <message>
+ <source>Services</source>
+ <translation>Usugi</translation>
+ </message>
+ <message>
+ <source>Connection Time</source>
+ <translation>Czŏs Skuplowaniŏ</translation>
+ </message>
+ <message>
+ <source>Last block time</source>
+ <translation>Czas ôstatnigo bloku</translation>
+ </message>
+ <message>
+ <source>&amp;Open</source>
+ <translation>Ô&amp;dewrzij</translation>
+ </message>
+ <message>
+ <source>&amp;Network Traffic</source>
+ <translation>&amp;Ruch necowy</translation>
+ </message>
+ <message>
+ <source>In:</source>
+ <translation>Wchōd:</translation>
+ </message>
+ <message>
+ <source>Out:</source>
+ <translation>Wychōd:</translation>
+ </message>
+ <message>
+ <source>1 &amp;day</source>
+ <translation>1 &amp;dziyń</translation>
+ </message>
+ <message>
+ <source>&amp;Disconnect</source>
+ <translation>Ô&amp;dkupluj</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>wychodny portmanyj</translation>
+ </message>
+ <message>
+ <source>Welcome to the %1 RPC console.</source>
+ <translation>Witej w %1 kōnsoli RPC.</translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation>POZŌR: Machlyrze namŏwiajōm do wpisowaniŏ tukej roztōmajtych nakŏzań coby porwać portmanyj. Niy używej tyj kōnsole bez połnego zrozumiyniŏ wpisowanych nakŏzań.</translation>
+ </message>
+ <message>
+ <source>Network activity disabled</source>
+ <translation>Aktywność necowŏ zastawiōnŏ</translation>
+ </message>
+ <message>
+ <source>never</source>
+ <translation>nikej</translation>
+ </message>
+ <message>
+ <source>Inbound</source>
+ <translation>Wchodowy</translation>
+ </message>
+ <message>
+ <source>Outbound</source>
+ <translation>Wychodowy</translation>
+ </message>
+ <message>
+ <source>Yes</source>
+ <translation>Ja</translation>
+ </message>
+ <message>
+ <source>No</source>
+ <translation>Niy</translation>
+ </message>
+ </context>
+<context>
+ <name>ReceiveCoinsDialog</name>
+ <message>
+ <source>&amp;Label:</source>
+ <translation>&amp;Etyketa:</translation>
+ </message>
+ <message>
+ <source>An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Bitcoin network.</source>
+ <translation>Ôpcyjōnalnŏ wiadōmość do prziwstōniŏ do żōndaniŏ płatu, kerŏ bydzie wyświytlanŏ, kej żōndanie ôstanie ôdewrzōne. Napōmniynie: wiadōmość ta niy ôstanie wysłanŏ z płatym w nec Bitcoin.</translation>
+ </message>
+ <message>
+ <source>Clear</source>
+ <translation>Wypucuj</translation>
+ </message>
+ <message>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <translation>Natywne adresy segwit (aka Bech32 abo BIP-173) zmyńszajōm niyskorzij twoje ôpłŏcki za transakcyje i dadzōm lepsze zabezpieczynie przed chybami, ale stare portmanyje jejich niy podpiyrajōm. Jeźli ôdznaczōne, zrychtowanŏ ôstanie adresa kōmpatybilnŏ ze starszymi portmanyjami.</translation>
+ </message>
+ <message>
+ <source>Show</source>
+ <translation>Pokŏż</translation>
+ </message>
+ <message>
+ <source>Remove</source>
+ <translation>Wychrōń</translation>
+ </message>
+ <message>
+ <source>Copy URI</source>
+ <translation>Kopiyruj URI</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Kopiyruj etyketã</translation>
+ </message>
+ <message>
+ <source>Copy message</source>
+ <translation>Kopiyruj wiadōmość</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Kopiyruj kwotã</translation>
+ </message>
+</context>
+<context>
+ <name>ReceiveRequestDialog</name>
+ <message>
+ <source>QR Code</source>
+ <translation>Kod QR</translation>
+ </message>
+ <message>
+ <source>Copy &amp;URI</source>
+ <translation>Kopiyruj &amp;URI</translation>
+ </message>
+ <message>
+ <source>Copy &amp;Address</source>
+ <translation>Kopiyruj &amp;Adresã</translation>
+ </message>
+ <message>
+ <source>&amp;Save Image...</source>
+ <translation>&amp;Spamiyntej Ôbrozek</translation>
+ </message>
+ <message>
+ <source>Payment information</source>
+ <translation>Informacyje ô płacie</translation>
+ </message>
+ <message>
+ <source>URI</source>
+ <translation>URI</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Adresa</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Kwota</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Etyketa</translation>
+ </message>
+ <message>
+ <source>Message</source>
+ <translation>Wiadōmość</translation>
+ </message>
+ <message>
+ <source>Wallet</source>
+ <translation>Portmanyj</translation>
+ </message>
+ </context>
+<context>
+ <name>RecentRequestsTableModel</name>
+ <message>
+ <source>Date</source>
+ <translation>Datōm</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Etyketa</translation>
+ </message>
+ <message>
+ <source>Message</source>
+ <translation>Wiadōmość</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(chyba etykety)</translation>
+ </message>
+ </context>
+<context>
+ <name>SendCoinsDialog</name>
+ <message>
+ <source>Send Coins</source>
+ <translation>Poślij mōnety</translation>
+ </message>
+ <message>
+ <source>Quantity:</source>
+ <translation>Wielość:</translation>
+ </message>
+ <message>
+ <source>Bytes:</source>
+ <translation>Bajtōw:</translation>
+ </message>
+ <message>
+ <source>Amount:</source>
+ <translation>Kwota:</translation>
+ </message>
+ <message>
+ <source>Fee:</source>
+ <translation>Ôpłŏcka:</translation>
+ </message>
+ <message>
+ <source>After Fee:</source>
+ <translation>Po ôpłŏcce:</translation>
+ </message>
+ <message>
+ <source>Change:</source>
+ <translation>Wydŏwka:</translation>
+ </message>
+ <message>
+ <source>Choose...</source>
+ <translation>Ôbier...</translation>
+ </message>
+ <message>
+ <source>Warning: Fee estimation is currently not possible.</source>
+ <translation>Pozōr: Ôszacowanie ôpłŏcki za transakcyje je aktualnie niymożebne.</translation>
+ </message>
+ <message>
+ <source>per kilobyte</source>
+ <translation>na kilobajt</translation>
+ </message>
+ <message>
+ <source>Hide</source>
+ <translation>Skryj</translation>
+ </message>
+ <message>
+ <source>Recommended:</source>
+ <translation>Doradzane:</translation>
+ </message>
+ <message>
+ <source>Custom:</source>
+ <translation>Włŏsne:</translation>
+ </message>
+ <message>
+ <source>Dust:</source>
+ <translation>Sztaub:</translation>
+ </message>
+ <message>
+ <source>Balance:</source>
+ <translation>Saldo:</translation>
+ </message>
+ <message>
+ <source>Copy quantity</source>
+ <translation>Kopiyruj wielość</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Kopiyruj kwotã</translation>
+ </message>
+ <message>
+ <source>Copy fee</source>
+ <translation>Kopiyruj ôpłŏckã</translation>
+ </message>
+ <message>
+ <source>Copy after fee</source>
+ <translation>Kopiyruj wielość po ôpłŏcce</translation>
+ </message>
+ <message>
+ <source>Copy bytes</source>
+ <translation>Kopiyruj wielość bajtōw</translation>
+ </message>
+ <message>
+ <source>Copy dust</source>
+ <translation>Kopiyruj sztaub</translation>
+ </message>
+ <message>
+ <source>Copy change</source>
+ <translation>Kopiyruj wydŏwkã</translation>
+ </message>
+ <message>
+ <source>%1 (%2 blocks)</source>
+ <translation>%1 (%2 blokōw)</translation>
+ </message>
+ <message>
+ <source>or</source>
+ <translation>abo</translation>
+ </message>
+ <message>
+ <source>Transaction creation failed!</source>
+ <translation>Utworzynie transakcyje niy podarziło sie!</translation>
+ </message>
+ <message>
+ <source>Warning: Invalid Bitcoin address</source>
+ <translation>Pozōr: niynŏleżnŏ adresa Bitcoin</translation>
+ </message>
+ <message>
+ <source>Warning: Unknown change address</source>
+ <translation>Pozōr: Niyznōmŏ adresa wydŏwki</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(chyba etykety)</translation>
+ </message>
+</context>
+<context>
+ <name>SendCoinsEntry</name>
+ <message>
+ <source>&amp;Label:</source>
+ <translation>&amp;Etyketa:</translation>
+ </message>
+ <message>
+ <source>This is a normal payment.</source>
+ <translation>To je normalny płat.</translation>
+ </message>
+ <message>
+ <source>Alt+A</source>
+ <translation>Alt+A</translation>
+ </message>
+ <message>
+ <source>Alt+P</source>
+ <translation>Alt+P</translation>
+ </message>
+ <message>
+ <source>Use available balance</source>
+ <translation>Użyj dostympnego salda</translation>
+ </message>
+ <message>
+ <source>Message:</source>
+ <translation>Wiadōmość:</translation>
+ </message>
+ <message>
+ <source>A message that was attached to the bitcoin: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Bitcoin network.</source>
+ <translation>Wiadōmość, kerŏ ôstała prziwstōnŏ do URI bitcoin:, kerŏ bydzie przechowowanŏ z transakcyjōm w cylach informacyjnych. Napōmniynie: Ta wiadōmość niy bydzie rozszyrzowanŏ w necu Bitcoin.</translation>
+ </message>
+ </context>
+<context>
+ <name>SendConfirmationDialog</name>
+ <message>
+ <source>Yes</source>
+ <translation>Ja</translation>
+ </message>
+</context>
+<context>
+ <name>ShutdownWindow</name>
+ </context>
+<context>
+ <name>SignVerifyMessageDialog</name>
+ <message>
+ <source>Signatures - Sign / Verify a Message</source>
+ <translation>Szkryfki - Podpisz / Zweryfikuj Wiadōmość</translation>
+ </message>
+ <message>
+ <source>&amp;Sign Message</source>
+ <translation>&amp;Szkryftnij Wiadōmość</translation>
+ </message>
+ <message>
+ <source>Alt+A</source>
+ <translation>Alt+A</translation>
+ </message>
+ <message>
+ <source>Alt+P</source>
+ <translation>Alt+P</translation>
+ </message>
+ <message>
+ <source>Sign &amp;Message</source>
+ <translation>Szkryftnij &amp;Wiadōmość</translation>
+ </message>
+ <message>
+ <source>&amp;Verify Message</source>
+ <translation>&amp;Weryfikuj Wiadōmość</translation>
+ </message>
+ <message>
+ <source>Message signing failed.</source>
+ <translation>Szkryftniyńcie wiadōmości niy podarziło sie.</translation>
+ </message>
+ <message>
+ <source>Message signed.</source>
+ <translation>Wiadōmość szkryftniyntŏ.</translation>
+ </message>
+ <message>
+ <source>Message verification failed.</source>
+ <translation>Weryfikacyjŏ wiadōmości niy podarziła sie.</translation>
+ </message>
+ </context>
+<context>
+ <name>SplashScreen</name>
+ <message>
+ <source>[testnet]</source>
+ <translation>[testnet]</translation>
+ </message>
+</context>
+<context>
+ <name>TrafficGraphWidget</name>
+ <message>
+ <source>KB/s</source>
+ <translation>KB/s</translation>
+ </message>
+</context>
+<context>
+ <name>TransactionDesc</name>
+ <message>
+ <source>Status</source>
+ <translation>Sztatus</translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>Datōm</translation>
+ </message>
+ <message>
+ <source>Source</source>
+ <translation>Źrōdło</translation>
+ </message>
+ <message>
+ <source>From</source>
+ <translation>Z</translation>
+ </message>
+ <message>
+ <source>unknown</source>
+ <translation>niyznōme</translation>
+ </message>
+ <message>
+ <source>To</source>
+ <translation>Do</translation>
+ </message>
+ <message>
+ <source>label</source>
+ <translation>etyketa</translation>
+ </message>
+ <message>
+ <source>Message</source>
+ <translation>Wiadōmość</translation>
+ </message>
+ <message>
+ <source>Comment</source>
+ <translation>Kōmyntŏrz</translation>
+ </message>
+ <message>
+ <source>Transaction</source>
+ <translation>Transakcyjŏ</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Kwota</translation>
+ </message>
+ </context>
+<context>
+ <name>TransactionDescDialog</name>
+ </context>
+<context>
+ <name>TransactionTableModel</name>
+ <message>
+ <source>Date</source>
+ <translation>Datōm</translation>
+ </message>
+ <message>
+ <source>Type</source>
+ <translation>Zorta</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Etyketa</translation>
+ </message>
+ <message>
+ <source>Received with</source>
+ <translation>Ôdebrane z</translation>
+ </message>
+ <message>
+ <source>Received from</source>
+ <translation>Ôdebrane ôd</translation>
+ </message>
+ <message>
+ <source>Payment to yourself</source>
+ <translation>Płat do siebie</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(chyba etykety)</translation>
+ </message>
+ </context>
+<context>
+ <name>TransactionView</name>
+ <message>
+ <source>All</source>
+ <translation>Wszyjske</translation>
+ </message>
+ <message>
+ <source>Today</source>
+ <translation>Dzisiej</translation>
+ </message>
+ <message>
+ <source>Received with</source>
+ <translation>Ôdebrane z</translation>
+ </message>
+ <message>
+ <source>Other</source>
+ <translation>Inksze</translation>
+ </message>
+ <message>
+ <source>Enter address, transaction id, or label to search</source>
+ <translation>Wkludź adresa, idyntyfikatōr transakcyje abo etyketã coby wyszukać</translation>
+ </message>
+ <message>
+ <source>Copy address</source>
+ <translation>Kopiyruj adresã</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Kopiyruj etyketã</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Kopiyruj kwotã</translation>
+ </message>
+ <message>
+ <source>Copy transaction ID</source>
+ <translation>Kopiyruj ID transakcyje</translation>
+ </message>
+ <message>
+ <source>Edit label</source>
+ <translation>Edytuj etyketa</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>Zbiōr *.CSV (dane rozdzielane kōmami)</translation>
+ </message>
+ <message>
+ <source>Confirmed</source>
+ <translation>Przituplowany</translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>Datōm</translation>
+ </message>
+ <message>
+ <source>Type</source>
+ <translation>Zorta</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Etyketa</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Adresa</translation>
+ </message>
+ <message>
+ <source>ID</source>
+ <translation>ID</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Eksportowanie niy podarziło sie</translation>
+ </message>
+ <message>
+ <source>to</source>
+ <translation>do</translation>
+ </message>
+</context>
+<context>
+ <name>UnitDisplayStatusBarControl</name>
+ </context>
+<context>
+ <name>WalletFrame</name>
+ </context>
+<context>
+ <name>WalletModel</name>
+ <message>
+ <source>Send Coins</source>
+ <translation>Poślij mōnety</translation>
+ </message>
+ <message>
+ <source>New fee:</source>
+ <translation>Nowŏ ôpłŏcka:</translation>
+ </message>
+ </context>
+<context>
+ <name>WalletView</name>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>Eksportuj dane z aktywnyj szkarty do zbioru</translation>
+ </message>
+ <message>
+ <source>Backup Failed</source>
+ <translation>Backup niy podarził sie</translation>
+ </message>
+ <message>
+ <source>Cancel</source>
+ <translation>Pociep</translation>
+ </message>
+</context>
+<context>
+ <name>bitcoin-core</name>
+ <message>
+ <source>Bitcoin Core</source>
+ <translation>Bitcoin Core</translation>
+ </message>
+ <message>
+ <source>The %s developers</source>
+ <translation>Twōrcy %s</translation>
+ </message>
+ <message>
+ <source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source>
+ <translation>Pozōr: Nec niy wydŏwŏ sie w połni zgodliwy! Niykerzi grubiŏrze wydajōm sie doświadczać niyprzileżytości.</translation>
+ </message>
+ <message>
+ <source>Copyright (C) %i-%i</source>
+ <translation>Copyright (C) %i-%i</translation>
+ </message>
+ <message>
+ <source>Error loading %s</source>
+ <translation>Feler wgrŏwaniŏ %s</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>Feler wgrŏwaniŏ %s: Klucze prywatne mogōm być zastawiōne ino w czasie tworzyniŏ</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Wallet corrupted</source>
+ <translation>Feler wgrŏwaniŏ %s: Portmanyj poprzniōny</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Wallet requires newer version of %s</source>
+ <translation>Feler wgrŏwaniŏ %s: Portmanyj wymŏgŏ nowszyj wersyje %s</translation>
+ </message>
+ <message>
+ <source>Error loading block database</source>
+ <translation>Feler wgrŏwaniŏ bazy blokōw</translation>
+ </message>
+ <message>
+ <source>Error: Disk space is low!</source>
+ <translation>Feler: Za mało wolnego placu na dysku!</translation>
+ </message>
+ <message>
+ <source>Loading P2P addresses...</source>
+ <translation>Wgrŏwanie adres P2P...</translation>
+ </message>
+ <message>
+ <source>Loading banlist...</source>
+ <translation>Wgrŏwanie wykŏzu zaszperowanych...</translation>
+ </message>
+ <message>
+ <source>Unsupported argument -benchmark ignored, use -debug=bench.</source>
+ <translation>Niypodpiyrany argumynt -benchmark zignorowany, użyj -debug=bench.</translation>
+ </message>
+ <message>
+ <source>Unsupported argument -debugnet ignored, use -debug=net.</source>
+ <translation>Niypodpiyrany argumynt -debugnet zignorowany, użyj -debug=net.</translation>
+ </message>
+ <message>
+ <source>Unsupported argument -tor found, use -onion.</source>
+ <translation>Znŏdniynto było niypodpiyrany argumynt -tor, użyj -onion.</translation>
+ </message>
+ <message>
+ <source>Unsupported logging category %s=%s.</source>
+ <translation>Niypodpiyranŏ kategoryjŏ registrowaniŏ %s=%s.</translation>
+ </message>
+ <message>
+ <source>Verifying blocks...</source>
+ <translation>Weryfikacyjŏ blokōw...</translation>
+ </message>
+ <message>
+ <source>Error loading %s: You can't disable HD on an already existing HD wallet</source>
+ <translation>Feler w czasie wgrŏwaniŏ %s: Niy idzie zastawić HD w już bydōncym portmanyju HD</translation>
+ </message>
+ <message>
+ <source>Information</source>
+ <translation>Informacyjŏ</translation>
+ </message>
+ <message>
+ <source>Signing transaction failed</source>
+ <translation>Szkryftniyńcie transakcyji niy podarziło sie</translation>
+ </message>
+ <message>
+ <source>This is experimental software.</source>
+ <translation>To je eksperymyntalny softwer.</translation>
+ </message>
+ <message>
+ <source>Transaction too large</source>
+ <translation>Transakcyjŏ za srogŏ</translation>
+ </message>
+ <message>
+ <source>Verifying wallet(s)...</source>
+ <translation>Weryfikacyjŏ portmanyja(ōw)...</translation>
+ </message>
+ <message>
+ <source>Warning</source>
+ <translation>Pozōr</translation>
+ </message>
+ <message>
+ <source>Warning: unknown new rules activated (versionbit %i)</source>
+ <translation>Pozōr: aktywowano było niyznōme nowe prawidła (versionbit %i)</translation>
+ </message>
+ <message>
+ <source>Error loading %s: You can't enable HD on an already existing non-HD wallet</source>
+ <translation>Feler w czasie wgrŏwaniŏ %s: Niy idzie zapuścić HD w już bydōncym portmanyju niy-HD</translation>
+ </message>
+ <message>
+ <source>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source>
+ <translation>Imyntnŏ dugość kety wersyje (%i) przekrŏczŏ maksymalnõ dopuszczalnõ dugość (%i). Zmyńsz wielość abo miara parametra uacomment.</translation>
+ </message>
+ <message>
+ <source>Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.</source>
+ <translation>Znŏdniynto było niypodpiyrany argumynt -socks. Ôbiyranie wersyje SOCKS je już niymożebne, podpiyrane sōm ino proxy SOCKS5.</translation>
+ </message>
+ <message>
+ <source>Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay.</source>
+ <translation>Niypodpiyrany argumynt -whitelistalwaysrelay zignorowany, użyj -whitelistrelay i/abo -whitelistforcerelay.</translation>
+ </message>
+ <message>
+ <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source>
+ <translation>Pozōr: wydobywane sōm niyznōme wersyje blokōw! Możliwe, iże ôbowiōnzujōm niyznōme prawidła.</translation>
+ </message>
+ <message>
+ <source>Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup.</source>
+ <translation>Pozōr: Ôdtworzōno było dane z poprzniōnego zbioru portmanyja! Ôryginalny %s ôstoł zapisany za %s w %s; jeźli twoje saldo abo transakcyje sōm niynŏleżne winiyn żeś prziwrōcić kopijõ ibrycznõ.</translation>
+ </message>
+ <message>
+ <source>Error loading wallet %s. Duplicate -wallet filename specified.</source>
+ <translation>Feler w czasie wgrŏwaniŏ portmanyja %s. Podanŏ tuplowane miano zbioru w -wallet.</translation>
+ </message>
+ <message>
+ <source>Starting network threads...</source>
+ <translation>Sztartowanie wōntkōw necowych...</translation>
+ </message>
+ <message>
+ <source>Unknown network specified in -onlynet: '%s'</source>
+ <translation>Niyznōmy nec ôkryślōny w -onlynet: '%s'</translation>
+ </message>
+ <message>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <translation>Pozōr: Wykryto było klucze prywatne w portmanyju {%s} kery mŏ zastawiōne klucze prywatne</translation>
+ </message>
+ <message>
+ <source>Loading block index...</source>
+ <translation>Wgrŏwanie indeksu blokōw...</translation>
+ </message>
+ <message>
+ <source>Loading wallet...</source>
+ <translation>Wgrŏwanie portmanyja...</translation>
+ </message>
+ <message>
+ <source>Rescanning...</source>
+ <translation>Pōnowne skanowanie</translation>
+ </message>
+ <message>
+ <source>Done loading</source>
+ <translation>Wgrŏwanie zakōńczōne</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Feler</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/qt/locale/bitcoin_tr.ts b/src/qt/locale/bitcoin_tr.ts
index b092ee060e..db30aa04c2 100644
--- a/src/qt/locale/bitcoin_tr.ts
+++ b/src/qt/locale/bitcoin_tr.ts
@@ -326,6 +326,14 @@
<translation>&amp;URI Aç...</translation>
</message>
<message>
+ <source>Wallet:</source>
+ <translation>Cüzdan:</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>varsayılan cüzdan</translation>
+ </message>
+ <message>
<source>Click to disable network activity.</source>
<translation>Ağ etkinliğini devre dışı bırakmak için tıklayın.</translation>
</message>
@@ -346,6 +354,10 @@
<translation>Diskteki bloklar yeniden indeksleniyor...</translation>
</message>
<message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>Tünelleme &lt;b&gt;etkin&lt;/b&gt;: %1</translation>
+ </message>
+ <message>
<source>Send coins to a Bitcoin address</source>
<translation>Bir bitcoin adresine bitcoin gönder</translation>
</message>
@@ -514,6 +526,12 @@
</translation>
</message>
<message>
+ <source>Wallet: %1
+</source>
+ <translation>Cüzdan: %1
+</translation>
+ </message>
+ <message>
<source>Type: %1
</source>
<translation>Tür: %1
@@ -750,6 +768,10 @@
<translation>Girilen "%1" adresi geçerli bir Bitcoin adresi değildir.</translation>
</message>
<message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Adres "%1" adres "%2" etiketiyle alım adresiniz olarak mevcut ve bu sebepten gönderen adres olarak eklenemiyor.</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>Cüzdan kilidi açılamadı.</translation>
</message>
@@ -1479,6 +1501,10 @@
<translation>Hata: Belirtilen "%1" veri klasörü yoktur.</translation>
</message>
<message>
+ <source>Error: Cannot parse configuration file: %1.</source>
+ <translation>Hata: %1 yapılandırma dosyası ayrıştırılamadı. </translation>
+ </message>
+ <message>
<source>Error: %1</source>
<translation>Hata: %1</translation>
</message>
@@ -1569,6 +1595,14 @@
<translation>Bellek kullanımı</translation>
</message>
<message>
+ <source>Wallet: </source>
+ <translation>Cüzdan:</translation>
+ </message>
+ <message>
+ <source>(none)</source>
+ <translation>(boş)</translation>
+ </message>
+ <message>
<source>&amp;Reset</source>
<translation>&amp;Sıfırla</translation>
</message>
@@ -1737,6 +1771,10 @@
<translation>&amp;Yasaklamayı Kaldır</translation>
</message>
<message>
+ <source>default wallet</source>
+ <translation>varsayılan cüzdan</translation>
+ </message>
+ <message>
<source>Welcome to the %1 RPC console.</source>
<translation>%1 RPC konsoluna hoş geldiniz.</translation>
</message>
@@ -1761,6 +1799,14 @@
<translation>Ağ etkinliği devre dışı bırakıldı</translation>
</message>
<message>
+ <source>Executing command without any wallet</source>
+ <translation>Komut bir cüzdan olmadan çalıştırılıyor</translation>
+ </message>
+ <message>
+ <source>Executing command using "%1" wallet</source>
+ <translation>Komut "%1" cüzdanı kullanılarak çalıştırılıyor</translation>
+ </message>
+ <message>
<source>(node id: %1)</source>
<translation>(düğüm kimliği: %1)</translation>
</message>
@@ -1832,6 +1878,10 @@
<translation>Temizle</translation>
</message>
<message>
+ <source>Generate native segwit (Bech32) address</source>
+ <translation>Yerli segwit (Bech32) adresi oluştur</translation>
+ </message>
+ <message>
<source>Requested payments history</source>
<translation>Talep edilen ödemelerin tarihçesi</translation>
</message>
@@ -2141,10 +2191,22 @@
<translation>veya</translation>
</message>
<message>
+ <source>from wallet %1</source>
+ <translation>cüzdan %1'den</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction.</source>
+ <translation>Lütfen, işleminizi gözden geçirin.</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>İşlem ücreti</translation>
</message>
<message>
+ <source>Total Amount</source>
+ <translation>Toplam Tutar</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>Bitcoin gönderimini onaylayın</translation>
</message>
@@ -2990,7 +3052,11 @@
<source>The wallet data was successfully saved to %1.</source>
<translation>Cüzdan verileri %1 konumuna başarıyla kaydedildi.</translation>
</message>
- </context>
+ <message>
+ <source>Cancel</source>
+ <translation>İptal</translation>
+ </message>
+</context>
<context>
<name>bitcoin-core</name>
<message>
@@ -3162,6 +3228,10 @@
<translation> -fallbackfee=&lt;tutar&gt; için geçersiz tutar: '%s'</translation>
</message>
<message>
+ <source>Upgrading txindex database</source>
+ <translation>txindex veritabanı yükseltiliyor</translation>
+ </message>
+ <message>
<source>Loading P2P addresses...</source>
<translation>P2P adresleri yükleniyor...</translation>
</message>
@@ -3202,6 +3272,10 @@
<translation>Bu bilgisayarda %s unsuruna bağlanılamadı. %s muhtemelen hâlihazırda çalışmaktadır.</translation>
</message>
<message>
+ <source>Unable to generate keys</source>
+ <translation>Anahtar üretilemiyor</translation>
+ </message>
+ <message>
<source>Unsupported argument -benchmark ignored, use -debug=bench.</source>
<translation>Desteklenmeyen -benchmark argümanı görmezden gelindi, -debug=bench kullanınız.</translation>
</message>
@@ -3338,6 +3412,10 @@
<translation>Cüzdan(lar) kontrol ediliyor...</translation>
</message>
<message>
+ <source>Wallet %s resides outside wallet directory %s</source>
+ <translation>Cüzdan %s, %s cüzdan klasörünün dışında bulunuyor</translation>
+ </message>
+ <message>
<source>Warning</source>
<translation>Uyarı</translation>
</message>
@@ -3434,6 +3512,14 @@
<translation>Yetersiz bakiye</translation>
</message>
<message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>İşlem ücreti hesaplama başarısız. Fallbackfee özelliği devre dışı. Lütfen bir kaç blok için bekleyiniz yada -fallbackfee özelliğini aktif ediniz.</translation>
+ </message>
+ <message>
+ <source>Cannot write to data directory '%s'; check permissions.</source>
+ <translation>Veriler klasöre yazılamıyor '%s'; yetkilendirmeyi kontrol edin.</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>Blok indeksi yükleniyor...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_tr_TR.ts b/src/qt/locale/bitcoin_tr_TR.ts
index cf119c65c2..bf2dae99dc 100644
--- a/src/qt/locale/bitcoin_tr_TR.ts
+++ b/src/qt/locale/bitcoin_tr_TR.ts
@@ -30,6 +30,10 @@
<translation>Seçili adresi listeden sil</translation>
</message>
<message>
+ <source>Enter address or label to search</source>
+ <translation>Aramak için adres veya etiket girin</translation>
+ </message>
+ <message>
<source>Export the data in the current tab to a file</source>
<translation>Seçili sekmedeki veriyi dosya olarak dışa aktar</translation>
</message>
@@ -132,6 +136,10 @@
<translation>Yeni parolayı tekrarla</translation>
</message>
<message>
+ <source>Show password</source>
+ <translation>Şifreyi göster</translation>
+ </message>
+ <message>
<source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
<translation>Yeni parolayı cüzdana girin.&lt;br/&gt;Lütfen &lt;b&gt;on yada daha fazla karakter&lt;/b&gt; veya &lt;b&gt;sekiz yada daha fazla kelime&lt;/b&gt;içeren bir parola kullanın. </translation>
</message>
@@ -192,6 +200,10 @@
<translation>Cüzdan şifreleme başarısız oldu</translation>
</message>
<message>
+ <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <translation>Cüzdan şifreleme dahili bir hata nedeniyle başarısız oldu. Cüzdanınız şifrelenemedi.</translation>
+ </message>
+ <message>
<source>The supplied passphrases do not match.</source>
<translation>Girilen parolalar eşleşmiyor.</translation>
</message>
diff --git a/src/qt/locale/bitcoin_uk.ts b/src/qt/locale/bitcoin_uk.ts
index 6698aa4235..1865a84834 100644
--- a/src/qt/locale/bitcoin_uk.ts
+++ b/src/qt/locale/bitcoin_uk.ts
@@ -3478,12 +3478,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>Вказаний шлях -walletdir "%s" не є каталогом</translation>
</message>
<message>
- <source>Specified blocks directory "%s" does not exist.
-</source>
- <translation>Вказаний шлях до блоків "%s" не існує
-</translation>
- </message>
- <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>Неможливо сплатити комісію із-за малої суми транзакції</translation>
</message>
diff --git a/src/qt/locale/bitcoin_ur_PK.ts b/src/qt/locale/bitcoin_ur_PK.ts
index eadd059f7d..03502be8a8 100644
--- a/src/qt/locale/bitcoin_ur_PK.ts
+++ b/src/qt/locale/bitcoin_ur_PK.ts
@@ -53,10 +53,46 @@
<source>C&amp;hoose</source>
<translation>چننا</translation>
</message>
+ <message>
+ <source>Sending addresses</source>
+ <translation>پتے ارسال کیے جارہے ہیں</translation>
+ </message>
+ <message>
+ <source>Receiving addresses</source>
+ <translation>پتے موصول ہورہے ہیں</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Address</source>
+ <translation>&amp;پتا نقل کریں</translation>
+ </message>
+ <message>
+ <source>Copy &amp;Label</source>
+ <translation>&amp;لیبل نقل کریں</translation>
+ </message>
+ <message>
+ <source>&amp;Edit</source>
+ <translation>&amp;تدوین</translation>
+ </message>
+ <message>
+ <source>Export Address List</source>
+ <translation>پتا فہرست ایکسپورٹ کریں</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>کاما سے جدا فائلیں (*.csv)</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>ایکسپورٹ ناکام ہوا</translation>
+ </message>
</context>
<context>
<name>AddressTableModel</name>
<message>
+ <source>Label</source>
+ <translation>لیبل</translation>
+ </message>
+ <message>
<source>Address</source>
<translation> پتہ</translation>
</message>
@@ -176,9 +212,17 @@
<source>Address</source>
<translation> پتہ</translation>
</message>
+ <message>
+ <source>Label</source>
+ <translation>لیبل</translation>
+ </message>
</context>
<context>
<name>RecentRequestsTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>لیبل</translation>
+ </message>
</context>
<context>
<name>SendCoinsDialog</name>
@@ -221,13 +265,29 @@
</context>
<context>
<name>TransactionTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>لیبل</translation>
+ </message>
</context>
<context>
<name>TransactionView</name>
<message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>کاما سے جدا فائلیں (*.csv)</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>لیبل</translation>
+ </message>
+ <message>
<source>Address</source>
<translation> پتہ</translation>
</message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>ایکسپورٹ ناکام ہوا</translation>
+ </message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
diff --git a/src/qt/locale/bitcoin_vi.ts b/src/qt/locale/bitcoin_vi.ts
index 563d047ff7..768ad38912 100644
--- a/src/qt/locale/bitcoin_vi.ts
+++ b/src/qt/locale/bitcoin_vi.ts
@@ -3,15 +3,15 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>Phải chuột để edit address hoặc label</translation>
+ <translation>Phải chuột để sửa địa chỉ hoặc nhãn</translation>
</message>
<message>
<source>Create a new address</source>
- <translation>Create một address mới</translation>
+ <translation>Tạo một địa chỉ mới</translation>
</message>
<message>
<source>&amp;New</source>
- <translation>&amp;Tạo mới</translation>
+ <translation>&amp;Mới</translation>
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
@@ -318,6 +318,10 @@
<translation>Mở &amp;URI...</translation>
</message>
<message>
+ <source>Wallet:</source>
+ <translation>Ví tiền</translation>
+ </message>
+ <message>
<source>Click to disable network activity.</source>
<translation>Click để vô hiệu hoạt động mạng.</translation>
</message>
diff --git a/src/qt/locale/bitcoin_zh_CN.ts b/src/qt/locale/bitcoin_zh_CN.ts
index 6678641ad3..ed9f6c2d7d 100644
--- a/src/qt/locale/bitcoin_zh_CN.ts
+++ b/src/qt/locale/bitcoin_zh_CN.ts
@@ -47,15 +47,15 @@
</message>
<message>
<source>Choose the address to send coins to</source>
- <translation>选择要付钱过去的地址</translation>
+ <translation>选择要发币给哪些地址</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
- <translation>选择要收钱进来的地址</translation>
+ <translation>选择要用哪些地址收币</translation>
</message>
<message>
<source>C&amp;hoose</source>
- <translation>选择</translation>
+ <translation>选择(&amp;C)</translation>
</message>
<message>
<source>Sending addresses</source>
@@ -67,23 +67,23 @@
</message>
<message>
<source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
- <translation>这些是你要付款过去的比特币地址。在付钱之前,务必要检查金额和收款地址是否正确。</translation>
+ <translation>您可以给这些比特币地址付款。在付款之前,务必要检查金额和收款地址是否正确。</translation>
</message>
<message>
<source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
- <translation>这些是你用来收款的比特币地址。建议在每次交易时,都使用一个新的收款地址。</translation>
+ <translation>您可以用这些比特币地址收款。建议在每次交易时,都使用一个新的收款地址。</translation>
</message>
<message>
<source>&amp;Copy Address</source>
- <translation>复制地址</translation>
+ <translation>复制地址(&amp;C)</translation>
</message>
<message>
<source>Copy &amp;Label</source>
- <translation>复制标签</translation>
+ <translation>复制标签(&amp;L)</translation>
</message>
<message>
<source>&amp;Edit</source>
- <translation>编辑</translation>
+ <translation>编辑(&amp;E)</translation>
</message>
<message>
<source>Export Address List</source>
@@ -189,7 +189,7 @@
</message>
<message>
<source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
- <translation>%1 现在要关闭,以完成加密过程。请注意,加密钱包不能完全防止入侵你的电脑的恶意程序偷取钱币。</translation>
+ <translation>%1 现在要关闭,以完成加密过程。请注意,加密钱包不能完全防止入侵您电脑的恶意程序偷取您的比特币。</translation>
</message>
<message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
@@ -315,17 +315,25 @@
</message>
<message>
<source>&amp;Sending addresses...</source>
- <translation>正在发送地址(&amp;S)...</translation>
+ <translation>付款地址(&amp;S)...</translation>
</message>
<message>
<source>&amp;Receiving addresses...</source>
- <translation>正在接收地址(&amp;R)...</translation>
+ <translation>收款地址(&amp;R)...</translation>
</message>
<message>
<source>Open &amp;URI...</source>
<translation>打开 &amp;URI...</translation>
</message>
<message>
+ <source>Wallet:</source>
+ <translation>钱包:</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>默认钱包</translation>
+ </message>
+ <message>
<source>Click to disable network activity.</source>
<translation>点击禁用网络活动。</translation>
</message>
@@ -343,7 +351,11 @@
</message>
<message>
<source>Reindexing blocks on disk...</source>
- <translation>正在为数据块重建索引...</translation>
+ <translation>正在为区块数据重建索引...</translation>
+ </message>
+ <message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>代理已被&lt;b&gt;启用&lt;/b&gt;:%1</translation>
</message>
<message>
<source>Send coins to a Bitcoin address</source>
@@ -441,13 +453,21 @@
<source>&amp;Command-line options</source>
<translation>命令行选项(&amp;C)</translation>
</message>
+ <message numerus="yes">
+ <source>%n active connection(s) to Bitcoin network</source>
+ <translation><numerusform>%n 个有效连接至比特币网络</numerusform></translation>
+ </message>
<message>
<source>Indexing blocks on disk...</source>
- <translation>正在为数据块建立索引...</translation>
+ <translation>正在为区块数据建立索引...</translation>
</message>
<message>
<source>Processing blocks on disk...</source>
- <translation>正在处理数据块...</translation>
+ <translation>正在处理区块数据...</translation>
+ </message>
+ <message numerus="yes">
+ <source>Processed %n block(s) of transaction history.</source>
+ <translation><numerusform>已处理 %n 个区块的交易历史</numerusform></translation>
</message>
<message>
<source>%1 behind</source>
@@ -506,6 +526,11 @@
</translation>
</message>
<message>
+ <source>Wallet: %1
+</source>
+ <translation>钱包:%1</translation>
+ </message>
+ <message>
<source>Type: %1
</source>
<translation>类型: %1
@@ -556,7 +581,7 @@
<name>CoinControlDialog</name>
<message>
<source>Coin Selection</source>
- <translation>选择钱币</translation>
+ <translation>币源选择(Coin Selection)</translation>
</message>
<message>
<source>Quantity:</source>
@@ -576,7 +601,7 @@
</message>
<message>
<source>Dust:</source>
- <translation>小额:</translation>
+ <translation>粉尘:</translation>
</message>
<message>
<source>After Fee:</source>
@@ -584,11 +609,11 @@
</message>
<message>
<source>Change:</source>
- <translation>变更 : </translation>
+ <translation>找零 : </translation>
</message>
<message>
<source>(un)select all</source>
- <translation>(不)全选</translation>
+ <translation>全(不)选</translation>
</message>
<message>
<source>Tree mode</source>
@@ -664,7 +689,7 @@
</message>
<message>
<source>Copy dust</source>
- <translation>复制零散金额</translation>
+ <translation>复制粉尘金额</translation>
</message>
<message>
<source>Copy change</source>
@@ -684,7 +709,7 @@
</message>
<message>
<source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
- <translation>当任何一个收款金额小于目前的零散金额上限时,文字会变红色。</translation>
+ <translation>当任何一个收款金额小于目前的粉尘金额上限时,文字会变红色。</translation>
</message>
<message>
<source>Can vary +/- %1 satoshi(s) per input.</source>
@@ -742,12 +767,20 @@
<translation>输入的地址 %1 并不是有效的比特币地址。</translation>
</message>
<message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>地址“%1”已经存在,它是一个接受地址,标签为“%2”,所以它不能被添加为一个发送地址。</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>输入地址“%1”已经存在于地址簿中,标签为“%2”。</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>无法将钱包解锁。</translation>
</message>
<message>
<source>New key generation failed.</source>
- <translation>产生新的密钥失败了。</translation>
+ <translation>生成新密钥失败。</translation>
</message>
</context>
<context>
@@ -785,7 +818,7 @@
</message>
<message>
<source>About %1</source>
- <translation>關於 %1</translation>
+ <translation>关于 %1</translation>
</message>
<message>
<source>Command-line options</source>
@@ -850,7 +883,15 @@
<source>Error</source>
<translation>错误</translation>
</message>
- </context>
+ <message numerus="yes">
+ <source>%n GB of free space available</source>
+ <translation><numerusform>%n GB 可用空闲空间</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>(of %n GB needed)</source>
+ <translation><numerusform>(%n GB 需要)</numerusform></translation>
+ </message>
+</context>
<context>
<name>ModalOverlay</name>
<message>
@@ -875,7 +916,7 @@
</message>
<message>
<source>Last block time</source>
- <translation>上一数据块时间</translation>
+ <translation>上一区块时间</translation>
</message>
<message>
<source>Progress</source>
@@ -977,11 +1018,11 @@
</message>
<message>
<source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source>
- <translation>窗口被关闭时最小化而不是退出应用程序。当此选项启用时,应用程序只会在菜单中选择退出时退出。</translation>
+ <translation>窗口被关闭时最小化而不是退出应用程序。当此选项启用时,应用程序只会在菜单中选择“退出”时退出。</translation>
</message>
<message>
<source>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 |.</source>
- <translation>出现在交易的选项卡的上下文菜单项的第三方网址 (例如:区块链接查询) 。 %s的URL被替换为交易哈希。多个的URL需要竖线 | 分隔。</translation>
+ <translation>出现在交易的选项卡的上下文菜单项的第三方网址 (例如:区块浏览器) 。 %s的URL被替换为交易哈希。多个的URL需要竖线 | 分隔。</translation>
</message>
<message>
<source>Active command-line options that override above options:</source>
@@ -1008,6 +1049,22 @@
<translation>网络(&amp;N)</translation>
</message>
<message>
+ <source>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</source>
+ <translation>禁用一些高级特性,但是仍然会对所有区块数据进行完整验证。必须重新下载整个区块链数据才能撤销此设置。实际的磁盘空间占用可能会略高。</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>将区块存储修剪至(&amp;B)</translation>
+ </message>
+ <message>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>警告:还原此设置需要重新下载整个区块链。</translation>
+ </message>
+ <message>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
<translation>(0 = 自动, &lt;0 = 保持指定数量的CPU核心空闲)</translation>
</message>
@@ -1025,11 +1082,11 @@
</message>
<message>
<source>If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed.</source>
- <translation>如果禁用未确认的零钱,则零钱至少需要1个确认才能使用。同时账户余额计算会受到影响。</translation>
+ <translation>如果您禁止动用尚未确认的找零资金,则一笔交易的找零资金至少需要有1个确认后才能动用。这同时也会影响账户余额的计算。</translation>
</message>
<message>
<source>&amp;Spend unconfirmed change</source>
- <translation>使用未经确认的零钱(&amp;S)</translation>
+ <translation>动用尚未确认的找零资金(&amp;S)</translation>
</message>
<message>
<source>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source>
@@ -1101,7 +1158,7 @@
</message>
<message>
<source>M&amp;inimize on close</source>
- <translation>单击关闭按钮最小化(&amp;I)</translation>
+ <translation>单击关闭按钮时最小化(&amp;I)</translation>
</message>
<message>
<source>&amp;Display</source>
@@ -1275,6 +1332,10 @@
<translation>URI 处理</translation>
</message>
<message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>‘bitcoin://’不是合法的URI。请使用'bitcoin:'作为替代。</translation>
+ </message>
+ <message>
<source>Payment request fetch URL is invalid: %1</source>
<translation>取得付款请求的 URL 无效: %1</translation>
</message>
@@ -1412,10 +1473,34 @@
<source>%1 ms</source>
<translation>%1 毫秒</translation>
</message>
+ <message numerus="yes">
+ <source>%n second(s)</source>
+ <translation><numerusform>%n 秒</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n minute(s)</source>
+ <translation><numerusform>%n 分</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n hour(s)</source>
+ <translation><numerusform>%n 时</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n day(s)</source>
+ <translation><numerusform>%n 天</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n week(s)</source>
+ <translation><numerusform>%n 周</numerusform></translation>
+ </message>
<message>
<source>%1 and %2</source>
<translation>%1 和 %2</translation>
</message>
+ <message numerus="yes">
+ <source>%n year(s)</source>
+ <translation><numerusform>%n 年</numerusform></translation>
+ </message>
<message>
<source>%1 B</source>
<translation>%1 字节</translation>
@@ -1444,10 +1529,18 @@
<context>
<name>QObject::QObject</name>
<message>
+ <source>Error parsing command line arguments: %1.</source>
+ <translation>解析命令行参数错误:%1</translation>
+ </message>
+ <message>
<source>Error: Specified data directory "%1" does not exist.</source>
<translation>错误:指定的数据目录“%1”不存在。</translation>
</message>
<message>
+ <source>Error: Cannot parse configuration file: %1.</source>
+ <translation>错误:无法解析配置文件:%1</translation>
+ </message>
+ <message>
<source>Error: %1</source>
<translation>错误:%1</translation>
</message>
@@ -1519,15 +1612,15 @@
</message>
<message>
<source>Block chain</source>
- <translation>数据链</translation>
+ <translation>区块链</translation>
</message>
<message>
<source>Current number of blocks</source>
- <translation>当前数据块数量</translation>
+ <translation>当前区块数量</translation>
</message>
<message>
<source>Memory Pool</source>
- <translation>资金池</translation>
+ <translation>内存池</translation>
</message>
<message>
<source>Current number of transactions</source>
@@ -1538,6 +1631,14 @@
<translation>内存使用</translation>
</message>
<message>
+ <source>Wallet: </source>
+ <translation>钱包:</translation>
+ </message>
+ <message>
+ <source>(none)</source>
+ <translation>(无)</translation>
+ </message>
+ <message>
<source>&amp;Reset</source>
<translation>&amp;重启</translation>
</message>
@@ -1575,7 +1676,7 @@
</message>
<message>
<source>Starting Block</source>
- <translation>正在启动数据块</translation>
+ <translation>起步区块</translation>
</message>
<message>
<source>Synced Headers</source>
@@ -1583,7 +1684,7 @@
</message>
<message>
<source>Synced Blocks</source>
- <translation>同步区块链</translation>
+ <translation>已同步区块</translation>
</message>
<message>
<source>User Agent</source>
@@ -1639,7 +1740,7 @@
</message>
<message>
<source>Last block time</source>
- <translation>上一数据块时间</translation>
+ <translation>上一区块时间</translation>
</message>
<message>
<source>&amp;Open</source>
@@ -1702,6 +1803,10 @@
<translation>重新允许</translation>
</message>
<message>
+ <source>default wallet</source>
+ <translation>默认钱包</translation>
+ </message>
+ <message>
<source>Welcome to the %1 RPC console.</source>
<translation>欢迎使用 %1 的 RPC 控制台。</translation>
</message>
@@ -1726,6 +1831,14 @@
<translation>网络活动已禁用</translation>
</message>
<message>
+ <source>Executing command without any wallet</source>
+ <translation>不使用任何钱包执行命令</translation>
+ </message>
+ <message>
+ <source>Executing command using "%1" wallet</source>
+ <translation>使用“%1”钱包执行命令</translation>
+ </message>
+ <message>
<source>(node id: %1)</source>
<translation>(节点ID: %1)</translation>
</message>
@@ -1797,8 +1910,12 @@
<translation>清除</translation>
</message>
<message>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <translation>原生隔离见证地址(又称为Bech32或者BIP-173)可减少您日后的交易费用,并且可以更好的防范打字错误,但旧版本的钱包不能识别这种地址。如果取消勾选,则会生成一个与旧版本钱包兼容的地址。</translation>
+ </message>
+ <message>
<source>Generate native segwit (Bech32) address</source>
- <translation>生成本地分离见证 (Bech32)地址</translation>
+ <translation>生成原生隔离见证 (Bech32)地址</translation>
</message>
<message>
<source>Requested payments history</source>
@@ -1975,15 +2092,15 @@
</message>
<message>
<source>Change:</source>
- <translation>变更 : </translation>
+ <translation>找零 : </translation>
</message>
<message>
<source>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source>
- <translation>如果激活该选项,但是零钱地址用光或者非法,将会新生成零钱地址,转入零钱。</translation>
+ <translation>如果该选项已被激活,但是找零地址却被用光了,或者是非法的,找零资金将会被转入新生成的地址。</translation>
</message>
<message>
<source>Custom change address</source>
- <translation>自定义零钱地址</translation>
+ <translation>自定义找零地址</translation>
</message>
<message>
<source>Transaction Fee:</source>
@@ -2006,6 +2123,14 @@
<translation>收起 费用设置 </translation>
</message>
<message>
+ <source>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</source>
+ <translation>按照交易的虚拟大小自定义每kB ( 1,000 字节 )要交多少手续费。
+
+注意:手续费是按照字节数计算的,对于一笔大小为500字节(1kB的一半)的交易来说,"每kB付100聪手续费"就意味着手续费一共只付了50聪。</translation>
+ </message>
+ <message>
<source>per kilobyte</source>
<translation>每kb</translation>
</message>
@@ -2031,7 +2156,7 @@
</message>
<message>
<source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
- <translation>(智能交易费用 尚未初始化。 需要再下载一些数据块...)</translation>
+ <translation>(智能交易费用 尚未初始化。 需要再下载一些区块数据...)</translation>
</message>
<message>
<source>Send to multiple recipients at once</source>
@@ -2126,6 +2251,14 @@
<translation>你可以之后再提高手续费(有BIP-125手续费追加的标记)</translation>
</message>
<message>
+ <source>from wallet %1</source>
+ <translation>从钱包%1</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction.</source>
+ <translation>请检查您的交易。</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>交易费用</translation>
</message>
@@ -2134,6 +2267,10 @@
<translation>没有BIP-125手续费追加的标记。</translation>
</message>
<message>
+ <source>Total Amount</source>
+ <translation>总额</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>确认发送</translation>
</message>
@@ -2177,13 +2314,17 @@
<source>Pay only the required fee of %1</source>
<translation>只支付必要费用 %1</translation>
</message>
+ <message numerus="yes">
+ <source>Estimated to begin confirmation within %n block(s).</source>
+ <translation><numerusform>预计 %n 个区块后开始被确认。</numerusform></translation>
+ </message>
<message>
<source>Warning: Invalid Bitcoin address</source>
<translation>警告: 比特币地址无效</translation>
</message>
<message>
<source>Warning: Unknown change address</source>
- <translation>警告:未知的更改地址</translation>
+ <translation>警告:未知的找零地址</translation>
</message>
<message>
<source>Confirm custom change address</source>
@@ -2458,6 +2599,10 @@
</context>
<context>
<name>TransactionDesc</name>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>在进一步同步完%n个区块前状态待定</numerusform></translation>
+ </message>
<message>
<source>Open until %1</source>
<translation>至 %1 个数据块时开启</translation>
@@ -2534,6 +2679,10 @@
<source>Credit</source>
<translation>收入</translation>
</message>
+ <message numerus="yes">
+ <source>matures in %n more block(s)</source>
+ <translation><numerusform>还差 %n 个区块成熟</numerusform></translation>
+ </message>
<message>
<source>not accepted</source>
<translation>未被接受</translation>
@@ -2575,6 +2724,10 @@
<translation>交易总大小</translation>
</message>
<message>
+ <source>Transaction virtual size</source>
+ <translation>交易虚拟大小</translation>
+ </message>
+ <message>
<source>Output index</source>
<translation>输出索引</translation>
</message>
@@ -2584,7 +2737,7 @@
</message>
<message>
<source>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
- <translation>生成的比特币在可以使用前必须有 %1 个成熟的区块。当您生成了此区块后,它将被广播到网络中以加入区块链。如果它未成功进入区块链,其状态将变更为“不接受”并且不可使用。这可能偶尔会发生,如果另一个节点比你早几秒钟成功生成一个区块。</translation>
+ <translation>新挖出的比特币在可以使用前必须经过 %1 个区块确认的成熟过程。当您挖出此区块后,它将被广播到网络中以加入区块链。如果它未成功进入区块链,其状态将变更为“不接受”并且不可使用。这可能偶尔会发生,在另一个节点比你早几秒钟成功挖出一个区块时就会这样。</translation>
</message>
<message>
<source>Debug information</source>
@@ -2636,6 +2789,10 @@
<source>Label</source>
<translation>标签</translation>
</message>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>在进一步同步完%n个区块前状态待定</numerusform></translation>
+ </message>
<message>
<source>Open until %1</source>
<translation>至 %1 个数据块时开启</translation>
@@ -2975,7 +3132,11 @@
<source>The wallet data was successfully saved to %1.</source>
<translation>钱包数据成功保存至 %1。</translation>
</message>
- </context>
+ <message>
+ <source>Cancel</source>
+ <translation>取消</translation>
+ </message>
+</context>
<context>
<name>bitcoin-core</name>
<message>
@@ -2992,7 +3153,7 @@
</message>
<message>
<source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source>
- <translation>无法在开启修剪的状态下重扫描,请使用 -reindex重新下载完整的区块链。</translation>
+ <translation>无法在开启修剪的状态下进行重扫描,请使用 -reindex重新下载完整的区块链。</translation>
</message>
<message>
<source>Error: A fatal internal error occurred, see debug.log for details</source>
@@ -3044,7 +3205,7 @@
</message>
<message>
<source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
- <translation>如果对你的交易量来说,消耗的手续费微乎其微,那么这笔手续费你或许可以忽略它。</translation>
+ <translation>如果找零金额低于粉尘水平,你或许可以忽略这笔手续费。</translation>
</message>
<message>
<source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
@@ -3080,7 +3241,7 @@
</message>
<message>
<source>Change index out of range</source>
- <translation>修改索引超过范围</translation>
+ <translation>找零超过索引范围</translation>
</message>
<message>
<source>Copyright (C) %i-%i</source>
@@ -3088,11 +3249,11 @@
</message>
<message>
<source>Corrupted block database detected</source>
- <translation>检测发现数据块数据库损坏。请使用 -reindex参数重启客户端。</translation>
+ <translation>检测到区块数据库损坏</translation>
</message>
<message>
<source>Do you want to rebuild the block database now?</source>
- <translation>你想现在就重建块数据库吗?</translation>
+ <translation>你想现在就重建区块数据库吗?</translation>
</message>
<message>
<source>Error creating %s: You can't create non-HD wallets with this version.</source>
@@ -3100,7 +3261,7 @@
</message>
<message>
<source>Error initializing block database</source>
- <translation>初始化数据块数据库出错</translation>
+ <translation>初始化区块数据库出错</translation>
</message>
<message>
<source>Error initializing wallet database environment %s!</source>
@@ -3120,11 +3281,11 @@
</message>
<message>
<source>Error loading block database</source>
- <translation>导入数据块数据库出错</translation>
+ <translation>导入区块数据库出错</translation>
</message>
<message>
<source>Error opening block database</source>
- <translation>导入数据块数据库出错</translation>
+ <translation>导入区块数据库出错</translation>
</message>
<message>
<source>Error: Disk space is low!</source>
@@ -3159,6 +3320,14 @@
<translation>-fallbackfee 的无效数额=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>指定的区块目录"%s"不存在。</translation>
+ </message>
+ <message>
+ <source>Upgrading txindex database</source>
+ <translation>正在升级交易索引数据库</translation>
+ </message>
+ <message>
<source>Loading P2P addresses...</source>
<translation>正在加载P2P地址...</translation>
</message>
@@ -3184,7 +3353,7 @@
</message>
<message>
<source>Rewinding blocks...</source>
- <translation>回退区块</translation>
+ <translation>回退区块...</translation>
</message>
<message>
<source>The source code is available from %s.</source>
@@ -3192,13 +3361,17 @@
</message>
<message>
<source>Transaction fee and change calculation failed</source>
- <translation>计算交易手续费和找零失败了</translation>
+ <translation>计算交易手续费和找零失败</translation>
</message>
<message>
<source>Unable to bind to %s on this computer. %s is probably already running.</source>
<translation>无法在本机绑定 %s 端口。%s 可能已经在运行。</translation>
</message>
<message>
+ <source>Unable to generate keys</source>
+ <translation>无法生成密钥</translation>
+ </message>
+ <message>
<source>Unsupported argument -benchmark ignored, use -debug=bench.</source>
<translation>忽略不支持的选项 -benchmark,使用 -debug=bench</translation>
</message>
@@ -3420,7 +3593,7 @@
</message>
<message>
<source>Transaction has too long of a mempool chain</source>
- <translation>交易造成内存池中的交易链太长</translation>
+ <translation>此交易在内存池中的交易链太长</translation>
</message>
<message>
<source>Transaction must have at least one recipient</source>
@@ -3435,6 +3608,14 @@
<translation>金额不足</translation>
</message>
<message>
+ <source>Can't generate a change-address key. Private keys are disabled for this wallet.</source>
+ <translation>无法生成一个找零地址密钥。对于此钱包,私钥已被禁用。</translation>
+ </message>
+ <message>
+ <source>Cannot write to data directory '%s'; check permissions.</source>
+ <translation>不能写入到数据目录'%s';请检查文件权限。</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>正在加载区块索引...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_zh_TW.ts b/src/qt/locale/bitcoin_zh_TW.ts
index 896dd58246..635151bcc9 100644
--- a/src/qt/locale/bitcoin_zh_TW.ts
+++ b/src/qt/locale/bitcoin_zh_TW.ts
@@ -3336,6 +3336,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>設定 -fallbackfee=&lt;金額&gt; 的金額無效: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>指定的區塊目錄 "%s" 不存在。</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>正在升級 txindex 資料庫</translation>
</message>
@@ -3488,12 +3492,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>以 -walletdir 指定的路徑 "%s" 不是個目錄</translation>
</message>
<message>
- <source>Specified blocks directory "%s" does not exist.
-</source>
- <translation>指定的區塊目錄 "%s" 不存在。
-</translation>
- </message>
- <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>交易金額太少而付不起手續費</translation>
</message>
diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp
index c5bedf007a..8ecc33da84 100644
--- a/src/qt/modaloverlay.cpp
+++ b/src/qt/modaloverlay.cpp
@@ -71,6 +71,7 @@ void ModalOverlay::setKnownBestHeight(int count, const QDateTime& blockDate)
if (count > bestHeaderHeight) {
bestHeaderHeight = count;
bestHeaderDate = blockDate;
+ UpdateHeaderSyncLabel();
}
}
@@ -136,11 +137,16 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri
if (estimateNumHeadersLeft < HEADER_HEIGHT_DELTA_SYNC && hasBestHeader) {
ui->numberOfBlocksLeft->setText(QString::number(bestHeaderHeight - count));
} else {
- ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1)...").arg(bestHeaderHeight));
+ UpdateHeaderSyncLabel();
ui->expectedTimeLeft->setText(tr("Unknown..."));
}
}
+void ModalOverlay::UpdateHeaderSyncLabel() {
+ int est_headers_left = bestHeaderDate.secsTo(QDateTime::currentDateTime()) / Params().GetConsensus().nPowTargetSpacing;
+ ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1, %2%)...").arg(bestHeaderHeight).arg(QString::number(100.0 / (bestHeaderHeight + est_headers_left) * bestHeaderHeight, 'f', 1)));
+}
+
void ModalOverlay::toggleVisibility()
{
showHide(layerIsVisible, true);
diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h
index 66a6ad1e02..cf8b53f2b3 100644
--- a/src/qt/modaloverlay.h
+++ b/src/qt/modaloverlay.h
@@ -45,6 +45,7 @@ private:
QVector<QPair<qint64, double> > blockProcessTime;
bool layerIsVisible;
bool userClosed;
+ void UpdateHeaderSyncLabel();
};
#endif // BITCOIN_QT_MODALOVERLAY_H
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 9094aeff56..40dc7bf400 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -154,6 +154,10 @@ void OptionsDialog::setModel(OptionsModel *_model)
if (_model->isRestartRequired())
showRestartWarning(true);
+ // Prune values are in GB to be consistent with intro.cpp
+ static constexpr uint64_t nMinDiskSpace = (MIN_DISK_SPACE_FOR_BLOCK_FILES / GB_BYTES) + (MIN_DISK_SPACE_FOR_BLOCK_FILES % GB_BYTES) ? 1 : 0;
+ ui->pruneSize->setRange(nMinDiskSpace, std::numeric_limits<int>::max());
+
QString strLabel = _model->getOverriddenByCommandLine();
if (strLabel.isEmpty())
strLabel = tr("none");
@@ -164,10 +168,6 @@ void OptionsDialog::setModel(OptionsModel *_model)
mapper->toFirst();
updateDefaultProxyNets();
-
- // Prune values are in GB to be consistent with intro.cpp
- static constexpr uint64_t nMinDiskSpace = (MIN_DISK_SPACE_FOR_BLOCK_FILES / GB_BYTES) + (MIN_DISK_SPACE_FOR_BLOCK_FILES % GB_BYTES) ? 1 : 0;
- ui->pruneSize->setRange(nMinDiskSpace, _model->node().getAssumedBlockchainSize());
}
/* warn when one of the following settings changes by user action (placed here so init via mapper doesn't trigger them) */
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 62496a57f4..5b4fb4cc18 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -17,7 +17,6 @@
#include <net.h>
#include <netbase.h>
#include <txdb.h> // for -dbcache defaults
-#include <qt/intro.h>
#include <QNetworkProxy>
#include <QSettings>
@@ -110,7 +109,7 @@ void OptionsModel::Init(bool resetSettings)
addOverriddenOption("-par");
if (!settings.contains("strDataDir"))
- settings.setValue("strDataDir", Intro::getDefaultDataDirectory());
+ settings.setValue("strDataDir", GUIUtil::getDefaultDataDirectory());
// Wallet
#ifdef ENABLE_WALLET
@@ -187,7 +186,7 @@ void OptionsModel::Reset()
BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings);
// Save the strDataDir setting
- QString dataDir = Intro::getDefaultDataDirectory();
+ QString dataDir = GUIUtil::getDefaultDataDirectory();
dataDir = settings.value("strDataDir", dataDir).toString();
// Remove all entries from our QSettings object
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index b1ec90ad36..43dccec4ea 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -292,9 +292,9 @@ void PaymentServer::handleURIOrFile(const QString& s)
else if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
{
QUrlQuery uri((QUrl(s)));
+#ifdef ENABLE_BIP70
if (uri.hasQueryItem("r")) // payment request URI
{
-#ifdef ENABLE_BIP70
Q_EMIT message(tr("URI handling"),
tr("You are using a BIP70 URL which will be unsupported in the future."),
CClientUIInterface::ICON_WARNING);
@@ -315,19 +315,23 @@ void PaymentServer::handleURIOrFile(const QString& s)
tr("Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()),
CClientUIInterface::ICON_WARNING);
}
-#else
- Q_EMIT message(tr("URI handling"),
- tr("Cannot process payment request because BIP70 support was not compiled in."),
- CClientUIInterface::ICON_WARNING);
-#endif
return;
}
- else // normal URI
+ else
+#endif
+ // normal URI
{
SendCoinsRecipient recipient;
if (GUIUtil::parseBitcoinURI(s, &recipient))
{
if (!IsValidDestinationString(recipient.address.toStdString())) {
+#ifndef ENABLE_BIP70
+ if (uri.hasQueryItem("r")) { // payment request
+ Q_EMIT message(tr("URI handling"),
+ tr("Cannot process payment request because BIP70 support was not compiled in."),
+ CClientUIInterface::ICON_WARNING);
+ }
+#endif
Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address),
CClientUIInterface::MSG_ERROR);
}
@@ -343,9 +347,9 @@ void PaymentServer::handleURIOrFile(const QString& s)
}
}
-#ifdef ENABLE_BIP70
if (QFile::exists(s)) // payment request file
{
+#ifdef ENABLE_BIP70
PaymentRequestPlus request;
SendCoinsRecipient recipient;
if (!readPaymentRequestFromFile(s, request))
@@ -358,8 +362,12 @@ void PaymentServer::handleURIOrFile(const QString& s)
Q_EMIT receivedPaymentRequest(recipient);
return;
- }
+#else
+ Q_EMIT message(tr("Payment request file handling"),
+ tr("Cannot process payment request because BIP70 support was not compiled in."),
+ CClientUIInterface::ICON_WARNING);
#endif
+ }
}
void PaymentServer::handleURIConnection()
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 496aeebf7d..85b691c470 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -9,7 +9,6 @@
#include <qt/guiutil.h>
#include <interfaces/node.h>
-#include <validation.h> // for cs_main
#include <sync.h>
#include <QDebug>
diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp
index d537d759de..fca2a4e8c5 100644
--- a/src/qt/platformstyle.cpp
+++ b/src/qt/platformstyle.cpp
@@ -4,8 +4,6 @@
#include <qt/platformstyle.h>
-#include <qt/guiconstants.h>
-
#include <QApplication>
#include <QColor>
#include <QImage>
diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp
new file mode 100644
index 0000000000..bf1baf5470
--- /dev/null
+++ b/src/qt/qrimagewidget.cpp
@@ -0,0 +1,141 @@
+// Copyright (c) 2011-2018 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 <qt/qrimagewidget.h>
+
+#include <qt/guiutil.h>
+
+#include <QApplication>
+#include <QClipboard>
+#include <QDrag>
+#include <QMenu>
+#include <QMimeData>
+#include <QMouseEvent>
+#include <QPainter>
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h> /* for USE_QRCODE */
+#endif
+
+#ifdef USE_QRCODE
+#include <qrencode.h>
+#endif
+
+QRImageWidget::QRImageWidget(QWidget *parent):
+ QLabel(parent), contextMenu(nullptr)
+{
+ contextMenu = new QMenu(this);
+ QAction *saveImageAction = new QAction(tr("&Save Image..."), this);
+ connect(saveImageAction, &QAction::triggered, this, &QRImageWidget::saveImage);
+ contextMenu->addAction(saveImageAction);
+ QAction *copyImageAction = new QAction(tr("&Copy Image"), this);
+ connect(copyImageAction, &QAction::triggered, this, &QRImageWidget::copyImage);
+ contextMenu->addAction(copyImageAction);
+}
+
+bool QRImageWidget::setQR(const QString& data, const QString& text)
+{
+#ifdef USE_QRCODE
+ setText("");
+ if (data.isEmpty()) return false;
+
+ // limit length
+ if (data.length() > MAX_URI_LENGTH) {
+ setText(tr("Resulting URI too long, try to reduce the text for label / message."));
+ return false;
+ }
+
+ QRcode *code = QRcode_encodeString(data.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1);
+
+ if (!code) {
+ setText(tr("Error encoding URI into QR Code."));
+ return false;
+ }
+
+ QImage qrImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32);
+ qrImage.fill(0xffffff);
+ unsigned char *p = code->data;
+ for (int y = 0; y < code->width; ++y) {
+ for (int x = 0; x < code->width; ++x) {
+ qrImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff));
+ ++p;
+ }
+ }
+ QRcode_free(code);
+
+ QImage qrAddrImage = QImage(QR_IMAGE_SIZE, QR_IMAGE_SIZE + (text.isEmpty() ? 0 : 20), QImage::Format_RGB32);
+ qrAddrImage.fill(0xffffff);
+ QPainter painter(&qrAddrImage);
+ painter.drawImage(0, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE));
+
+ if (!text.isEmpty()) {
+ QFont font = GUIUtil::fixedPitchFont();
+ QRect paddedRect = qrAddrImage.rect();
+
+ // calculate ideal font size
+ qreal font_size = GUIUtil::calculateIdealFontSize(paddedRect.width() - 20, text, font);
+ font.setPointSizeF(font_size);
+
+ painter.setFont(font);
+ paddedRect.setHeight(QR_IMAGE_SIZE+12);
+ painter.drawText(paddedRect, Qt::AlignBottom|Qt::AlignCenter, text);
+ }
+
+ painter.end();
+ setPixmap(QPixmap::fromImage(qrAddrImage));
+
+ return true;
+#else
+ setText(tr("QR code support not available."));
+ return false;
+#endif
+}
+
+QImage QRImageWidget::exportImage()
+{
+ if(!pixmap())
+ return QImage();
+ return pixmap()->toImage();
+}
+
+void QRImageWidget::mousePressEvent(QMouseEvent *event)
+{
+ if(event->button() == Qt::LeftButton && pixmap())
+ {
+ event->accept();
+ QMimeData *mimeData = new QMimeData;
+ mimeData->setImageData(exportImage());
+
+ QDrag *drag = new QDrag(this);
+ drag->setMimeData(mimeData);
+ drag->exec();
+ } else {
+ QLabel::mousePressEvent(event);
+ }
+}
+
+void QRImageWidget::saveImage()
+{
+ if(!pixmap())
+ return;
+ QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Image (*.png)"), nullptr);
+ if (!fn.isEmpty())
+ {
+ exportImage().save(fn);
+ }
+}
+
+void QRImageWidget::copyImage()
+{
+ if(!pixmap())
+ return;
+ QApplication::clipboard()->setImage(exportImage());
+}
+
+void QRImageWidget::contextMenuEvent(QContextMenuEvent *event)
+{
+ if(!pixmap())
+ return;
+ contextMenu->exec(event->globalPos());
+}
diff --git a/src/qt/qrimagewidget.h b/src/qt/qrimagewidget.h
new file mode 100644
index 0000000000..2a219ac101
--- /dev/null
+++ b/src/qt/qrimagewidget.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_QRIMAGEWIDGET_H
+#define BITCOIN_QT_QRIMAGEWIDGET_H
+
+#include <QImage>
+#include <QLabel>
+
+/* Maximum allowed URI length */
+static const int MAX_URI_LENGTH = 255;
+
+/* Size of exported QR Code image */
+static const int QR_IMAGE_SIZE = 300;
+
+QT_BEGIN_NAMESPACE
+class QMenu;
+QT_END_NAMESPACE
+
+/* Label widget for QR code. This image can be dragged, dropped, copied and saved
+ * to disk.
+ */
+class QRImageWidget : public QLabel
+{
+ Q_OBJECT
+
+public:
+ explicit QRImageWidget(QWidget *parent = nullptr);
+ bool setQR(const QString& data, const QString& text = "");
+ QImage exportImage();
+
+public Q_SLOTS:
+ void saveImage();
+ void copyImage();
+
+protected:
+ virtual void mousePressEvent(QMouseEvent *event);
+ virtual void contextMenuEvent(QContextMenuEvent *event);
+
+private:
+ QMenu *contextMenu;
+};
+
+#endif // BITCOIN_QT_QRIMAGEWIDGET_H
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 22a79a12bb..c58717e21e 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -7,9 +7,7 @@
#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>
@@ -95,9 +93,9 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model)
columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH, this);
if (model->wallet().getDefaultAddressType() == OutputType::BECH32) {
- ui->useBech32->setCheckState(Qt::Checked);
+ ui->useLegacyAddress->setCheckState(Qt::Unchecked);
} else {
- ui->useBech32->setCheckState(Qt::Unchecked);
+ ui->useLegacyAddress->setCheckState(Qt::Checked);
}
// Set the button to be enabled or disabled based on whether the wallet can give out new addresses.
@@ -150,7 +148,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked()
QString label = ui->reqLabel->text();
/* Generate new receiving address */
OutputType address_type;
- if (ui->useBech32->isChecked()) {
+ if (!ui->useLegacyAddress->isChecked()) {
address_type = OutputType::BECH32;
} else {
address_type = model->wallet().getDefaultAddressType();
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index f5b30cf6d2..e492502002 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -6,85 +6,16 @@
#include <qt/forms/ui_receiverequestdialog.h>
#include <qt/bitcoinunits.h>
-#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <QClipboard>
-#include <QDrag>
-#include <QMenu>
-#include <QMimeData>
-#include <QMouseEvent>
#include <QPixmap>
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h> /* for USE_QRCODE */
#endif
-#ifdef USE_QRCODE
-#include <qrencode.h>
-#endif
-
-QRImageWidget::QRImageWidget(QWidget *parent):
- QLabel(parent), contextMenu(nullptr)
-{
- contextMenu = new QMenu(this);
- QAction *saveImageAction = new QAction(tr("&Save Image..."), this);
- connect(saveImageAction, &QAction::triggered, this, &QRImageWidget::saveImage);
- contextMenu->addAction(saveImageAction);
- QAction *copyImageAction = new QAction(tr("&Copy Image"), this);
- connect(copyImageAction, &QAction::triggered, this, &QRImageWidget::copyImage);
- contextMenu->addAction(copyImageAction);
-}
-
-QImage QRImageWidget::exportImage()
-{
- if(!pixmap())
- return QImage();
- return pixmap()->toImage();
-}
-
-void QRImageWidget::mousePressEvent(QMouseEvent *event)
-{
- if(event->button() == Qt::LeftButton && pixmap())
- {
- event->accept();
- QMimeData *mimeData = new QMimeData;
- mimeData->setImageData(exportImage());
-
- QDrag *drag = new QDrag(this);
- drag->setMimeData(mimeData);
- drag->exec();
- } else {
- QLabel::mousePressEvent(event);
- }
-}
-
-void QRImageWidget::saveImage()
-{
- if(!pixmap())
- return;
- QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Image (*.png)"), nullptr);
- if (!fn.isEmpty())
- {
- exportImage().save(fn);
- }
-}
-
-void QRImageWidget::copyImage()
-{
- if(!pixmap())
- return;
- QApplication::clipboard()->setImage(exportImage());
-}
-
-void QRImageWidget::contextMenuEvent(QContextMenuEvent *event)
-{
- if(!pixmap())
- return;
- contextMenu->exec(event->globalPos());
-}
-
ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ReceiveRequestDialog),
@@ -150,55 +81,9 @@ void ReceiveRequestDialog::update()
}
ui->outUri->setText(html);
-#ifdef USE_QRCODE
- ui->lblQRCode->setText("");
- if(!uri.isEmpty())
- {
- // limit URI length
- if (uri.length() > MAX_URI_LENGTH)
- {
- ui->lblQRCode->setText(tr("Resulting URI too long, try to reduce the text for label / message."));
- } else {
- QRcode *code = QRcode_encodeString(uri.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1);
- if (!code)
- {
- ui->lblQRCode->setText(tr("Error encoding URI into QR Code."));
- return;
- }
- QImage qrImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32);
- qrImage.fill(0xffffff);
- unsigned char *p = code->data;
- for (int y = 0; y < code->width; y++)
- {
- for (int x = 0; x < code->width; x++)
- {
- qrImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff));
- p++;
- }
- }
- QRcode_free(code);
-
- QImage qrAddrImage = QImage(QR_IMAGE_SIZE, QR_IMAGE_SIZE+20, QImage::Format_RGB32);
- qrAddrImage.fill(0xffffff);
- QPainter painter(&qrAddrImage);
- painter.drawImage(0, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE));
- QFont font = GUIUtil::fixedPitchFont();
- QRect paddedRect = qrAddrImage.rect();
-
- // calculate ideal font size
- qreal font_size = GUIUtil::calculateIdealFontSize(paddedRect.width() - 20, info.address, font);
- font.setPointSizeF(font_size);
-
- painter.setFont(font);
- paddedRect.setHeight(QR_IMAGE_SIZE+12);
- painter.drawText(paddedRect, Qt::AlignBottom|Qt::AlignCenter, info.address);
- painter.end();
-
- ui->lblQRCode->setPixmap(QPixmap::fromImage(qrAddrImage));
- ui->btnSaveAs->setEnabled(true);
- }
+ if (ui->lblQRCode->setQR(uri, info.address)) {
+ ui->btnSaveAs->setEnabled(true);
}
-#endif
}
void ReceiveRequestDialog::on_btnCopyURI_clicked()
diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h
index dd28fd73c8..a6e1a2af16 100644
--- a/src/qt/receiverequestdialog.h
+++ b/src/qt/receiverequestdialog.h
@@ -8,41 +8,11 @@
#include <qt/walletmodel.h>
#include <QDialog>
-#include <QImage>
-#include <QLabel>
-#include <QPainter>
namespace Ui {
class ReceiveRequestDialog;
}
-QT_BEGIN_NAMESPACE
-class QMenu;
-QT_END_NAMESPACE
-
-/* Label widget for QR code. This image can be dragged, dropped, copied and saved
- * to disk.
- */
-class QRImageWidget : public QLabel
-{
- Q_OBJECT
-
-public:
- explicit QRImageWidget(QWidget *parent = nullptr);
- QImage exportImage();
-
-public Q_SLOTS:
- void saveImage();
- void copyImage();
-
-protected:
- virtual void mousePressEvent(QMouseEvent *event);
- virtual void contextMenuEvent(QContextMenuEvent *event);
-
-private:
- QMenu *contextMenu;
-};
-
class ReceiveRequestDialog : public QDialog
{
Q_OBJECT
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index fc1e14b031..071a197c3c 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -21,8 +21,6 @@
#include <util/strencodings.h>
#include <util/system.h>
-#include <openssl/crypto.h>
-
#include <univalue.h>
#ifdef ENABLE_WALLET
@@ -69,7 +67,6 @@ const QStringList historyFilter = QStringList()
<< "importmulti"
<< "sethdseed"
<< "signmessagewithprivkey"
- << "signrawtransaction"
<< "signrawtransactionwithkey"
<< "walletpassphrase"
<< "walletpassphrasechange"
@@ -680,6 +677,9 @@ void RPCConsole::setClientModel(ClientModel *model)
wordList.sort();
autoCompleter = new QCompleter(wordList, this);
autoCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel);
+ // ui->lineEdit is initially disabled because running commands is only
+ // possible from now on.
+ ui->lineEdit->setEnabled(true);
ui->lineEdit->setCompleter(autoCompleter);
autoCompleter->popup()->installEventFilter(this);
// Start thread to execute RPC commands.
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 6e00ab755c..cb9efe9319 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -279,18 +279,16 @@ void SendCoinsDialog::on_sendButton_clicked()
QStringList formatted;
for (const SendCoinsRecipient &rcp : currentTransaction.getRecipients())
{
- // generate bold amount string with wallet name in case of multiwallet
- QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
+ // generate amount string with wallet name in case of multiwallet
+ QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
if (model->isMultiwallet()) {
- amount.append(" <u>"+tr("from wallet %1").arg(GUIUtil::HtmlEscape(model->getWalletName()))+"</u> ");
+ amount.append(tr(" from wallet '%1'").arg(model->getWalletName()));
}
- amount.append("</b>");
- // generate monospace address string
- QString address = "<span style='font-family: monospace;'>" + rcp.address;
- address.append("</span>");
+
+ // generate address string
+ QString address = rcp.address;
QString recipientElement;
- recipientElement = "<br />";
#ifdef ENABLE_BIP70
if (!rcp.paymentRequest.IsInitialized()) // normal payment
@@ -298,7 +296,7 @@ void SendCoinsDialog::on_sendButton_clicked()
{
if(rcp.label.length() > 0) // label with address
{
- recipientElement.append(tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label)));
+ recipientElement.append(tr("%1 to '%2'").arg(amount, rcp.label));
recipientElement.append(QString(" (%1)").arg(address));
}
else // just address
@@ -309,7 +307,7 @@ void SendCoinsDialog::on_sendButton_clicked()
#ifdef ENABLE_BIP70
else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request
{
- recipientElement.append(tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)));
+ recipientElement.append(tr("%1 to '%2'").arg(amount, rcp.authenticatedMerchant));
}
else // unauthenticated payment request
{
@@ -323,7 +321,7 @@ void SendCoinsDialog::on_sendButton_clicked()
QString questionString = tr("Are you sure you want to send?");
questionString.append("<br /><span style='font-size:10pt;'>");
questionString.append(tr("Please, review your transaction."));
- questionString.append("</span><br />%1");
+ questionString.append("</span>%1");
if(txFee > 0)
{
@@ -364,8 +362,17 @@ void SendCoinsDialog::on_sendButton_clicked()
questionString.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
.arg(alternativeUnits.join(" " + tr("or") + " ")));
- SendConfirmationDialog confirmationDialog(tr("Confirm send coins"),
- questionString.arg(formatted.join("<br />")), SEND_CONFIRM_DELAY, this);
+ QString informative_text;
+ QString detailed_text;
+ if (formatted.size() > 1) {
+ questionString = questionString.arg("");
+ informative_text = tr("To review recipient list click \"Show Details...\"");
+ detailed_text = formatted.join("\n\n");
+ } else {
+ questionString = questionString.arg("<br /><br />" + formatted.at(0));
+ }
+
+ SendConfirmationDialog confirmationDialog(tr("Confirm send coins"), questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, this);
confirmationDialog.exec();
QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
@@ -578,7 +585,7 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
case WalletModel::AbsurdFee:
- msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->node().getMaxTxFee()));
+ msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getDefaultMaxTxFee()));
break;
case WalletModel::PaymentRequestExpired:
msgParams.first = tr("Payment request expired.");
@@ -881,10 +888,15 @@ void SendCoinsDialog::coinControlUpdateLabels()
}
}
-SendConfirmationDialog::SendConfirmationDialog(const QString &title, const QString &text, int _secDelay,
- QWidget *parent) :
- QMessageBox(QMessageBox::Question, title, text, QMessageBox::Yes | QMessageBox::Cancel, parent), secDelay(_secDelay)
+SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, QWidget* parent)
+ : QMessageBox(parent), secDelay(_secDelay)
{
+ setIcon(QMessageBox::Question);
+ setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
+ setText(text);
+ setInformativeText(informative_text);
+ setDetailedText(detailed_text);
+ setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
setDefaultButton(QMessageBox::Cancel);
yesButton = button(QMessageBox::Yes);
updateYesButton();
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 337a72b878..c6c1816877 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -108,7 +108,7 @@ class SendConfirmationDialog : public QMessageBox
Q_OBJECT
public:
- SendConfirmationDialog(const QString &title, const QString &text, int secDelay = SEND_CONFIRM_DELAY, QWidget *parent = nullptr);
+ SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, QWidget* parent = nullptr);
int exec();
private Q_SLOTS:
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index d37a78fa8c..71f5f2ae75 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -11,7 +11,7 @@
#include <qt/walletmodel.h>
#include <key_io.h>
-#include <validation.h> // For strMessageMagic
+#include <util/validation.h> // For strMessageMagic
#include <wallet/wallet.h>
#include <string>
@@ -120,8 +120,8 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
return;
}
- const CKeyID* keyID = boost::get<CKeyID>(&destination);
- if (!keyID) {
+ const PKHash* pkhash = boost::get<PKHash>(&destination);
+ if (!pkhash) {
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."));
@@ -137,7 +137,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
}
CKey key;
- if (!model->wallet().getPrivKey(*keyID, key))
+ if (!model->wallet().getPrivKey(CKeyID(*pkhash), key))
{
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("Private key for the entered address is not available."));
@@ -198,7 +198,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
return;
}
- if (!boost::get<CKeyID>(&destination)) {
+ if (!boost::get<PKHash>(&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."));
@@ -229,7 +229,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
return;
}
- if (!(CTxDestination(pubkey.GetID()) == destination)) {
+ if (!(CTxDestination(PKHash(pubkey)) == 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/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 3e414df1f0..ea1019ad1d 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -1,11 +1,9 @@
#include <qt/test/addressbooktests.h>
#include <qt/test/util.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <interfaces/chain.h>
#include <interfaces/node.h>
-#include <qt/addressbookpage.h>
-#include <qt/addresstablemodel.h>
#include <qt/editaddressdialog.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
@@ -58,7 +56,7 @@ void TestAddAddressesToSendBook()
{
TestChain100Setup test;
auto chain = interfaces::MakeChain();
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateMock());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);
@@ -95,6 +93,7 @@ void TestAddAddressesToSendBook()
}
auto check_addbook_size = [&wallet](int expected_size) {
+ LOCK(wallet->cs_wallet);
QCOMPARE(static_cast<int>(wallet->mapAddressBook.size()), expected_size);
};
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index 2c477a2e98..b9bf933ee5 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -5,7 +5,6 @@
#include <qt/test/apptests.h>
#include <chainparams.h>
-#include <init.h>
#include <qt/bitcoin.h>
#include <qt/bitcoingui.h>
#include <qt/networkstyle.h>
@@ -16,9 +15,6 @@
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
-#ifdef ENABLE_WALLET
-#include <wallet/db.h>
-#endif
#include <QAction>
#include <QEventLoop>
@@ -27,9 +23,7 @@
#include <QTest>
#include <QTextEdit>
#include <QtGlobal>
-#if QT_VERSION >= 0x050000
#include <QtTest/QtTestWidgets>
-#endif
#include <QtTest/QtTestGui>
#include <new>
#include <string>
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index 173c814f1e..7a10ada438 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -4,15 +4,11 @@
#include <qt/test/rpcnestedtests.h>
-#include <chainparams.h>
-#include <consensus/validation.h>
#include <fs.h>
#include <interfaces/node.h>
-#include <validation.h>
-#include <rpc/register.h>
#include <rpc/server.h>
#include <qt/rpcconsole.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <univalue.h>
#include <util/system.h>
@@ -120,7 +116,6 @@ void RPCNestedTests::rpcNestedTests()
RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest( abc , cba )");
QVERIFY(result == "[\"abc\",\"cba\"]");
-#if QT_VERSION >= 0x050300
// do the QVERIFY_EXCEPTION_THROWN checks only with Qt5.3 and higher (QVERIFY_EXCEPTION_THROWN was introduced in Qt5.3)
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo() .\n"), std::runtime_error); //invalid syntax
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); //invalid syntax
@@ -131,5 +126,4 @@ void RPCNestedTests::rpcNestedTests()
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tollerate empty arguments when using ,
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using ,
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using ,
-#endif
}
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index ee84da0cdf..e54915ec75 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -1,10 +1,8 @@
#include <qt/test/wallettests.h>
#include <qt/test/util.h>
-#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/node.h>
-#include <base58.h>
#include <qt/bitcoinamountfield.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
@@ -15,7 +13,7 @@
#include <qt/transactionview.h>
#include <qt/walletmodel.h>
#include <key_io.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <validation.h>
#include <wallet/wallet.h>
#include <qt/overviewpage.h>
@@ -135,7 +133,7 @@ void TestGUI()
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
}
auto chain = interfaces::MakeChain();
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateMock());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);
{
@@ -145,12 +143,14 @@ void TestGUI()
}
{
auto locked_chain = wallet->chain().lock();
+ LockAssertion lock(::cs_main);
+
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
CWallet::ScanResult result = wallet->ScanForWalletTransactions(locked_chain->getBlockHash(0), {} /* stop_block */, reserver, true /* fUpdate */);
QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
- QCOMPARE(result.stop_block, chainActive.Tip()->GetBlockHash());
- QVERIFY(result.failed_block.IsNull());
+ QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
+ QVERIFY(result.last_failed_block.IsNull());
}
wallet->SetBroadcastTransactions(true);
@@ -169,8 +169,8 @@ void TestGUI()
// Send two transactions, and verify they are added to transaction list.
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
QCOMPARE(transactionTableModel->rowCount({}), 105);
- uint256 txid1 = SendCoins(*wallet.get(), sendCoinsDialog, CKeyID(), 5 * COIN, false /* rbf */);
- uint256 txid2 = SendCoins(*wallet.get(), sendCoinsDialog, CKeyID(), 10 * COIN, true /* rbf */);
+ uint256 txid1 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 5 * COIN, false /* rbf */);
+ uint256 txid2 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 10 * COIN, true /* rbf */);
QCOMPARE(transactionTableModel->rowCount({}), 107);
QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
QVERIFY(FindTx(*transactionTableModel, txid2).isValid());
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 0d070d9e87..aabe9dfb58 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -20,8 +20,6 @@
#include <script/script.h>
#include <timedata.h>
#include <util/system.h>
-#include <wallet/db.h>
-#include <wallet/wallet.h>
#include <policy/policy.h>
#include <stdint.h>
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index aa785553c8..7d6d84aa7b 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -5,11 +5,8 @@
#include <qt/transactionrecord.h>
#include <chain.h>
-#include <consensus/consensus.h>
#include <interfaces/wallet.h>
#include <key_io.h>
-#include <timedata.h>
-#include <validation.h>
#include <stdint.h>
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 631a9b891d..6fe35b13cf 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -15,11 +15,7 @@
#include <core_io.h>
#include <interfaces/handler.h>
-#include <interfaces/node.h>
-#include <sync.h>
#include <uint256.h>
-#include <util/system.h>
-#include <validation.h>
#include <QColor>
#include <QDateTime>
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index eb6437eb31..17e174e57a 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -10,7 +10,6 @@
#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>
@@ -229,8 +228,8 @@ void TransactionView::setModel(WalletModel *_model)
transactionView->setAlternatingRowColors(true);
transactionView->setSelectionBehavior(QAbstractItemView::SelectRows);
transactionView->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ transactionView->horizontalHeader()->setSortIndicator(TransactionTableModel::Date, Qt::DescendingOrder);
transactionView->setSortingEnabled(true);
- transactionView->sortByColumn(TransactionTableModel::Date, Qt::DescendingOrder);
transactionView->verticalHeader()->hide();
transactionView->setColumnWidth(TransactionTableModel::Status, STATUS_COLUMN_WIDTH);
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index b051dd159b..aa810690c9 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -11,17 +11,12 @@
#include <qt/forms/ui_helpmessagedialog.h>
#include <qt/bitcoingui.h>
-#include <qt/clientmodel.h>
-#include <qt/guiconstants.h>
-#include <qt/intro.h>
#ifdef ENABLE_BIP70
#include <qt/paymentrequestplus.h>
#endif
-#include <qt/guiutil.h>
#include <clientversion.h>
#include <init.h>
-#include <interfaces/node.h>
#include <util/system.h>
#include <util/strencodings.h>
@@ -129,7 +124,7 @@ HelpMessageDialog::~HelpMessageDialog()
void HelpMessageDialog::printToConsole()
{
// On other operating systems, the expected action is to print the message to the console.
- fprintf(stdout, "%s\n", qPrintable(text));
+ tfm::format(std::cout, "%s\n", qPrintable(text));
}
void HelpMessageDialog::showOrPrint()
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index df2b7a3f9b..019bd65823 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -9,8 +9,11 @@
#include <algorithm>
+#include <QApplication>
+#include <QMessageBox>
#include <QMutexLocker>
#include <QThread>
+#include <QWindow>
WalletController::WalletController(interfaces::Node& node, const PlatformStyle* platform_style, OptionsModel* options_model, QObject* parent)
: QObject(parent)
@@ -25,11 +28,17 @@ WalletController::WalletController(interfaces::Node& node, const PlatformStyle*
for (std::unique_ptr<interfaces::Wallet>& wallet : m_node.getWallets()) {
getOrCreateWallet(std::move(wallet));
}
+
+ m_activity_thread.start();
}
// Not using the default destructor because not all member types definitions are
// available in the header, just forward declared.
-WalletController::~WalletController() {}
+WalletController::~WalletController()
+{
+ m_activity_thread.quit();
+ m_activity_thread.wait();
+}
std::vector<WalletModel*> WalletController::getWallets() const
{
@@ -37,6 +46,40 @@ std::vector<WalletModel*> WalletController::getWallets() const
return m_wallets;
}
+std::vector<std::string> WalletController::getWalletsAvailableToOpen() const
+{
+ QMutexLocker locker(&m_mutex);
+ std::vector<std::string> wallets = m_node.listWalletDir();
+ for (WalletModel* wallet_model : m_wallets) {
+ auto it = std::remove(wallets.begin(), wallets.end(), wallet_model->wallet().getWalletName());
+ if (it != wallets.end()) wallets.erase(it);
+ }
+ return wallets;
+}
+
+OpenWalletActivity* WalletController::openWallet(const std::string& name, QWidget* parent)
+{
+ OpenWalletActivity* activity = new OpenWalletActivity(this, name);
+ activity->moveToThread(&m_activity_thread);
+ return activity;
+}
+
+void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)
+{
+ QMessageBox box(parent);
+ box.setWindowTitle(tr("Close wallet"));
+ box.setText(tr("Are you sure you wish to close wallet <i>%1</i>?").arg(wallet_model->getDisplayName()));
+ box.setInformativeText(tr("Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled."));
+ box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
+ box.setDefaultButton(QMessageBox::Yes);
+ if (box.exec() != QMessageBox::Yes) return;
+
+ // First remove wallet from node.
+ wallet_model->wallet().remove();
+ // Now release the model.
+ removeAndDeleteWallet(wallet_model);
+}
+
WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet)
{
QMutexLocker locker(&m_mutex);
@@ -56,7 +99,17 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal
m_wallets.push_back(wallet_model);
connect(wallet_model, &WalletModel::unload, [this, wallet_model] {
- removeAndDeleteWallet(wallet_model);
+ // Defer removeAndDeleteWallet when no modal widget is active.
+ // TODO: remove this workaround by removing usage of QDiallog::exec.
+ if (QApplication::activeModalWidget()) {
+ connect(qApp, &QApplication::focusWindowChanged, wallet_model, [this, wallet_model]() {
+ if (!QApplication::activeModalWidget()) {
+ removeAndDeleteWallet(wallet_model);
+ }
+ }, Qt::QueuedConnection);
+ } else {
+ removeAndDeleteWallet(wallet_model);
+ }
});
// Re-emit coinsSent signal from wallet model.
@@ -93,3 +146,24 @@ void WalletController::removeAndDeleteWallet(WalletModel* wallet_model)
// CWallet shared pointer.
delete wallet_model;
}
+
+
+OpenWalletActivity::OpenWalletActivity(WalletController* wallet_controller, const std::string& name)
+ : m_wallet_controller(wallet_controller)
+ , m_name(name)
+{}
+
+void OpenWalletActivity::open()
+{
+ std::string error, warning;
+ std::unique_ptr<interfaces::Wallet> wallet = m_wallet_controller->m_node.loadWallet(m_name, error, warning);
+ if (!warning.empty()) {
+ Q_EMIT message(QMessageBox::Warning, QString::fromStdString(warning));
+ }
+ if (wallet) {
+ Q_EMIT opened(m_wallet_controller->getOrCreateWallet(std::move(wallet)));
+ } else {
+ Q_EMIT message(QMessageBox::Critical, QString::fromStdString(error));
+ }
+ Q_EMIT finished();
+}
diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h
index 22b71b07ff..19b3a82253 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -12,7 +12,9 @@
#include <memory>
#include <vector>
+#include <QMessageBox>
#include <QMutex>
+#include <QThread>
class OptionsModel;
class PlatformStyle;
@@ -22,6 +24,8 @@ class Handler;
class Node;
} // namespace interfaces
+class OpenWalletActivity;
+
/**
* Controller between interfaces::Node, WalletModel instances and the GUI.
*/
@@ -37,6 +41,10 @@ public:
~WalletController();
std::vector<WalletModel*> getWallets() const;
+ std::vector<std::string> getWalletsAvailableToOpen() const;
+
+ OpenWalletActivity* openWallet(const std::string& name, QWidget* parent = nullptr);
+ void closeWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
private Q_SLOTS:
void addWallet(WalletModel* wallet_model);
@@ -48,12 +56,35 @@ Q_SIGNALS:
void coinsSent(WalletModel* wallet_model, SendCoinsRecipient recipient, QByteArray transaction);
private:
+ QThread m_activity_thread;
interfaces::Node& m_node;
const PlatformStyle* const m_platform_style;
OptionsModel* const m_options_model;
mutable QMutex m_mutex;
std::vector<WalletModel*> m_wallets;
std::unique_ptr<interfaces::Handler> m_handler_load_wallet;
+
+ friend class OpenWalletActivity;
+};
+
+class OpenWalletActivity : public QObject
+{
+ Q_OBJECT
+
+public:
+ OpenWalletActivity(WalletController* wallet_controller, const std::string& name);
+
+public Q_SLOTS:
+ void open();
+
+Q_SIGNALS:
+ void message(QMessageBox::Icon icon, const QString text);
+ void finished();
+ void opened(WalletModel* wallet_model);
+
+private:
+ WalletController* const m_wallet_controller;
+ std::string const m_name;
};
#endif // BITCOIN_QT_WALLETCONTROLLER_H
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 466f2278eb..94413547d4 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -40,15 +40,11 @@ void WalletFrame::setClientModel(ClientModel *_clientModel)
this->clientModel = _clientModel;
}
-bool WalletFrame::addWallet(WalletModel *walletModel)
+void WalletFrame::addWallet(WalletModel *walletModel)
{
- if (!gui || !clientModel || !walletModel) {
- return false;
- }
+ if (!gui || !clientModel || !walletModel) return;
- if (mapWalletViews.count(walletModel) > 0) {
- return false;
- }
+ if (mapWalletViews.count(walletModel) > 0) return;
WalletView *walletView = new WalletView(platformStyle, this);
walletView->setBitcoinGUI(gui);
@@ -72,31 +68,25 @@ bool WalletFrame::addWallet(WalletModel *walletModel)
});
connect(walletView, &WalletView::outOfSyncWarningClicked, this, &WalletFrame::outOfSyncWarningClicked);
-
- return true;
}
-bool WalletFrame::setCurrentWallet(WalletModel* wallet_model)
+void WalletFrame::setCurrentWallet(WalletModel* wallet_model)
{
- if (mapWalletViews.count(wallet_model) == 0)
- return false;
+ if (mapWalletViews.count(wallet_model) == 0) return;
WalletView *walletView = mapWalletViews.value(wallet_model);
walletStack->setCurrentWidget(walletView);
assert(walletView);
walletView->updateEncryptionStatus();
- return true;
}
-bool WalletFrame::removeWallet(WalletModel* wallet_model)
+void WalletFrame::removeWallet(WalletModel* wallet_model)
{
- if (mapWalletViews.count(wallet_model) == 0)
- return false;
+ if (mapWalletViews.count(wallet_model) == 0) return;
WalletView *walletView = mapWalletViews.take(wallet_model);
walletStack->removeWidget(walletView);
delete walletView;
- return true;
}
void WalletFrame::removeAllWallets()
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index 6a74fde9fd..156653f47d 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -36,9 +36,9 @@ public:
void setClientModel(ClientModel *clientModel);
- bool addWallet(WalletModel *walletModel);
- bool setCurrentWallet(WalletModel* wallet_model);
- bool removeWallet(WalletModel* wallet_model);
+ void addWallet(WalletModel *walletModel);
+ void setCurrentWallet(WalletModel* wallet_model);
+ void removeWallet(WalletModel* wallet_model);
void removeAllWallets();
bool handlePaymentRequest(const SendCoinsRecipient& recipient);
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 2a9144bec9..a2b295df21 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -222,9 +222,9 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
}
// reject absurdly high fee. (This can never happen because the
- // wallet caps the fee at maxTxFee. This merely serves as a
+ // wallet caps the fee at m_default_max_tx_fee. This merely serves as a
// belt-and-suspenders check)
- if (nFeeRequired > m_node.getMaxTxFee())
+ if (nFeeRequired > m_wallet->getDefaultMaxTxFee())
return AbsurdFee;
}
@@ -482,7 +482,7 @@ WalletModel::UnlockContext::~UnlockContext()
}
}
-void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs)
+void WalletModel::UnlockContext::CopyFrom(UnlockContext&& rhs)
{
// Transfer context; old object no longer relocks wallet
*this = rhs;
@@ -580,12 +580,7 @@ bool WalletModel::privateKeysDisabled() const
bool WalletModel::canGetAddresses() const
{
- // The wallet can provide a fresh address if:
- // * hdEnabled(): an HD seed is present; or
- // * it is a legacy wallet, because:
- // * !hdEnabled(): an HD seed is not present; and
- // * !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS): private keys have not been disabled (which results in hdEnabled() == true)
- return m_wallet->hdEnabled() || (!m_wallet->hdEnabled() && !m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
+ return m_wallet->canGetAddresses();
}
QString WalletModel::getWalletName() const
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index b123befbb4..54428aec08 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -194,15 +194,18 @@ public:
bool isValid() const { return valid; }
- // Copy operator and constructor transfer the context
- UnlockContext(const UnlockContext& obj) { CopyFrom(obj); }
- UnlockContext& operator=(const UnlockContext& rhs) { CopyFrom(rhs); return *this; }
+ // Copy constructor is disabled.
+ UnlockContext(const UnlockContext&) = delete;
+ // Move operator and constructor transfer the context
+ UnlockContext(UnlockContext&& obj) { CopyFrom(std::move(obj)); }
+ UnlockContext& operator=(UnlockContext&& rhs) { CopyFrom(std::move(rhs)); return *this; }
private:
WalletModel *wallet;
bool valid;
mutable bool relock; // mutable, as it can be set to false by copying
- void CopyFrom(const UnlockContext& rhs);
+ UnlockContext& operator=(const UnlockContext&) = default;
+ void CopyFrom(UnlockContext&& rhs);
};
UnlockContext requestUnlock();
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index eb3b0baf08..8c0dc276b0 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -8,7 +8,6 @@
#include <qt/walletmodeltransaction.h>
-#include <interfaces/node.h>
#include <policy/policy.h>
WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &_recipients) :
@@ -29,7 +28,7 @@ std::unique_ptr<interfaces::PendingWalletTx>& WalletModelTransaction::getWtx()
unsigned int WalletModelTransaction::getTransactionSize()
{
- return wtx ? wtx->getVirtualSize() : 0;
+ return wtx ? GetVirtualTransactionSize(wtx->get()) : 0;
}
CAmount WalletModelTransaction::getTransactionFee() const
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 5f6f93d948..be47f67f95 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -316,6 +316,7 @@ void WalletView::showProgress(const QString &title, int nProgress)
if (progressDialog) {
progressDialog->close();
progressDialog->deleteLater();
+ progressDialog = nullptr;
}
} else if (progressDialog) {
if (progressDialog->wasCanceled()) {
diff --git a/src/random.cpp b/src/random.cpp
index 3b7f7910b0..de26e6de1a 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -78,25 +78,119 @@ static inline int64_t GetPerformanceCounter() noexcept
}
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
-static bool rdrand_supported = false;
+static bool g_rdrand_supported = false;
+static bool g_rdseed_supported = false;
static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000;
+static constexpr uint32_t CPUID_F7_EBX_RDSEED = 0x00040000;
+#ifdef bit_RDRND
+static_assert(CPUID_F1_ECX_RDRAND == bit_RDRND, "Unexpected value for bit_RDRND");
+#endif
+#ifdef bit_RDSEED
+static_assert(CPUID_F7_EBX_RDSEED == bit_RDSEED, "Unexpected value for bit_RDSEED");
+#endif
+static void inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d)
+{
+ // We can't use __get_cpuid as it doesn't support subleafs.
+#ifdef __GNUC__
+ __cpuid_count(leaf, subleaf, a, b, c, d);
+#else
+ __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf));
+#endif
+}
+
static void InitHardwareRand()
{
uint32_t eax, ebx, ecx, edx;
- if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) {
- rdrand_supported = true;
+ GetCPUID(1, 0, eax, ebx, ecx, edx);
+ if (ecx & CPUID_F1_ECX_RDRAND) {
+ g_rdrand_supported = true;
+ }
+ GetCPUID(7, 0, eax, ebx, ecx, edx);
+ if (ebx & CPUID_F7_EBX_RDSEED) {
+ g_rdseed_supported = true;
}
}
static void ReportHardwareRand()
{
- if (rdrand_supported) {
- // This must be done in a separate function, as HWRandInit() may be indirectly called
- // from global constructors, before logging is initialized.
+ // This must be done in a separate function, as HWRandInit() may be indirectly called
+ // from global constructors, before logging is initialized.
+ if (g_rdseed_supported) {
+ LogPrintf("Using RdSeed as additional entropy source\n");
+ }
+ if (g_rdrand_supported) {
LogPrintf("Using RdRand as an additional entropy source\n");
}
}
+/** Read 64 bits of entropy using rdrand.
+ *
+ * Must only be called when RdRand is supported.
+ */
+static uint64_t GetRdRand() noexcept
+{
+ // RdRand may very rarely fail. Invoke it up to 10 times in a loop to reduce this risk.
+#ifdef __i386__
+ uint8_t ok;
+ uint32_t r1, r2;
+ for (int i = 0; i < 10; ++i) {
+ __asm__ volatile (".byte 0x0f, 0xc7, 0xf0; setc %1" : "=a"(r1), "=q"(ok) :: "cc"); // rdrand %eax
+ if (ok) break;
+ }
+ for (int i = 0; i < 10; ++i) {
+ __asm__ volatile (".byte 0x0f, 0xc7, 0xf0; setc %1" : "=a"(r2), "=q"(ok) :: "cc"); // rdrand %eax
+ if (ok) break;
+ }
+ return (((uint64_t)r2) << 32) | r1;
+#elif defined(__x86_64__) || defined(__amd64__)
+ uint8_t ok;
+ uint64_t r1;
+ for (int i = 0; i < 10; ++i) {
+ __asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf0; setc %1" : "=a"(r1), "=q"(ok) :: "cc"); // rdrand %rax
+ if (ok) break;
+ }
+ return r1;
+#else
+#error "RdRand is only supported on x86 and x86_64"
+#endif
+}
+
+/** Read 64 bits of entropy using rdseed.
+ *
+ * Must only be called when RdSeed is supported.
+ */
+static uint64_t GetRdSeed() noexcept
+{
+ // RdSeed may fail when the HW RNG is overloaded. Loop indefinitely until enough entropy is gathered,
+ // but pause after every failure.
+#ifdef __i386__
+ uint8_t ok;
+ uint32_t r1, r2;
+ do {
+ __asm__ volatile (".byte 0x0f, 0xc7, 0xf8; setc %1" : "=a"(r1), "=q"(ok) :: "cc"); // rdseed %eax
+ if (ok) break;
+ __asm__ volatile ("pause");
+ } while(true);
+ do {
+ __asm__ volatile (".byte 0x0f, 0xc7, 0xf8; setc %1" : "=a"(r2), "=q"(ok) :: "cc"); // rdseed %eax
+ if (ok) break;
+ __asm__ volatile ("pause");
+ } while(true);
+ return (((uint64_t)r2) << 32) | r1;
+#elif defined(__x86_64__) || defined(__amd64__)
+ uint8_t ok;
+ uint64_t r1;
+ do {
+ __asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf8; setc %1" : "=a"(r1), "=q"(ok) :: "cc"); // rdseed %rax
+ if (ok) break;
+ __asm__ volatile ("pause");
+ } while(true);
+ return r1;
+#else
+#error "RdSeed is only supported on x86 and x86_64"
+#endif
+}
+
#else
/* Access to other hardware random number generators could be added here later,
* assuming it is sufficiently fast (in the order of a few hundred CPU cycles).
@@ -107,40 +201,68 @@ static void InitHardwareRand() {}
static void ReportHardwareRand() {}
#endif
-static bool GetHardwareRand(unsigned char* ent32) noexcept {
+/** Add 64 bits of entropy gathered from hardware to hasher. Do nothing if not supported. */
+static void SeedHardwareFast(CSHA512& hasher) noexcept {
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
- if (rdrand_supported) {
- uint8_t ok;
- // Not all assemblers support the rdrand instruction, write it in hex.
-#ifdef __i386__
- for (int iter = 0; iter < 4; ++iter) {
- uint32_t r1, r2;
- __asm__ volatile (".byte 0x0f, 0xc7, 0xf0;" // rdrand %eax
- ".byte 0x0f, 0xc7, 0xf2;" // rdrand %edx
- "setc %2" :
- "=a"(r1), "=d"(r2), "=q"(ok) :: "cc");
- if (!ok) return false;
- WriteLE32(ent32 + 8 * iter, r1);
- WriteLE32(ent32 + 8 * iter + 4, r2);
- }
-#else
- uint64_t r1, r2, r3, r4;
- __asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf0, " // rdrand %rax
- "0x48, 0x0f, 0xc7, 0xf3, " // rdrand %rbx
- "0x48, 0x0f, 0xc7, 0xf1, " // rdrand %rcx
- "0x48, 0x0f, 0xc7, 0xf2; " // rdrand %rdx
- "setc %4" :
- "=a"(r1), "=b"(r2), "=c"(r3), "=d"(r4), "=q"(ok) :: "cc");
- if (!ok) return false;
- WriteLE64(ent32, r1);
- WriteLE64(ent32 + 8, r2);
- WriteLE64(ent32 + 16, r3);
- WriteLE64(ent32 + 24, r4);
+ if (g_rdrand_supported) {
+ uint64_t out = GetRdRand();
+ hasher.Write((const unsigned char*)&out, sizeof(out));
+ return;
+ }
#endif
- return true;
+}
+
+/** Add 256 bits of entropy gathered from hardware to hasher. Do nothing if not supported. */
+static void SeedHardwareSlow(CSHA512& hasher) noexcept {
+#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
+ // When we want 256 bits of entropy, prefer RdSeed over RdRand, as it's
+ // guaranteed to produce independent randomness on every call.
+ if (g_rdseed_supported) {
+ for (int i = 0; i < 4; ++i) {
+ uint64_t out = GetRdSeed();
+ hasher.Write((const unsigned char*)&out, sizeof(out));
+ }
+ return;
+ }
+ // When falling back to RdRand, XOR the result of 1024 results.
+ // This guarantees a reseeding occurs between each.
+ if (g_rdrand_supported) {
+ for (int i = 0; i < 4; ++i) {
+ uint64_t out = 0;
+ for (int j = 0; j < 1024; ++j) out ^= GetRdRand();
+ hasher.Write((const unsigned char*)&out, sizeof(out));
+ }
+ return;
}
#endif
- return false;
+}
+
+/** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */
+static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA512& hasher) noexcept
+{
+ CSHA512 inner_hasher;
+ inner_hasher.Write(seed, sizeof(seed));
+
+ // Hash loop
+ unsigned char buffer[64];
+ int64_t stop = GetTimeMicros() + microseconds;
+ do {
+ for (int i = 0; i < 1000; ++i) {
+ inner_hasher.Finalize(buffer);
+ inner_hasher.Reset();
+ inner_hasher.Write(buffer, sizeof(buffer));
+ }
+ // Benchmark operation and feed it into outer hasher.
+ int64_t perf = GetPerformanceCounter();
+ hasher.Write((const unsigned char*)&perf, sizeof(perf));
+ } while (GetTimeMicros() < stop);
+
+ // Produce output from inner state and feed it to outer hasher.
+ inner_hasher.Finalize(buffer);
+ hasher.Write(buffer, sizeof(buffer));
+ // Try to clean up.
+ inner_hasher.Reset();
+ memory_cleanse(buffer, sizeof(buffer));
}
static void RandAddSeedPerfmon(CSHA512& hasher)
@@ -407,8 +529,7 @@ static void SeedFast(CSHA512& hasher) noexcept
hasher.Write((const unsigned char*)&ptr, sizeof(ptr));
// Hardware randomness is very fast when available; use it always.
- bool have_hw_rand = GetHardwareRand(buffer);
- if (have_hw_rand) hasher.Write(buffer, sizeof(buffer));
+ SeedHardwareFast(hasher);
// High-precision timestamp
SeedTimestamp(hasher);
@@ -436,7 +557,23 @@ static void SeedSlow(CSHA512& hasher) noexcept
SeedTimestamp(hasher);
}
-static void SeedSleep(CSHA512& hasher)
+/** Extract entropy from rng, strengthen it, and feed it into hasher. */
+static void SeedStrengthen(CSHA512& hasher, RNGState& rng) noexcept
+{
+ static std::atomic<int64_t> last_strengthen{0};
+ int64_t last_time = last_strengthen.load();
+ int64_t current_time = GetTimeMicros();
+ if (current_time > last_time + 60000000) { // Only run once a minute
+ // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
+ unsigned char strengthen_seed[32];
+ rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
+ // Strengthen it for 10ms (100ms on first run), and feed it into hasher.
+ Strengthen(strengthen_seed, last_time == 0 ? 100000 : 10000, hasher);
+ last_strengthen = current_time;
+ }
+}
+
+static void SeedSleep(CSHA512& hasher, RNGState& rng)
{
// Everything that the 'fast' seeder includes
SeedFast(hasher);
@@ -452,19 +589,28 @@ static void SeedSleep(CSHA512& hasher)
// Windows performance monitor data (once every 10 minutes)
RandAddSeedPerfmon(hasher);
+
+ // Strengthen every minute
+ SeedStrengthen(hasher, rng);
}
-static void SeedStartup(CSHA512& hasher) noexcept
+static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
{
#ifdef WIN32
RAND_screen();
#endif
+ // Gather 256 bits of hardware randomness, if available
+ SeedHardwareSlow(hasher);
+
// Everything that the 'slow' seeder includes.
SeedSlow(hasher);
// Windows performance monitor data.
RandAddSeedPerfmon(hasher);
+
+ // Strengthen
+ SeedStrengthen(hasher, rng);
}
enum class RNGLevel {
@@ -489,7 +635,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
SeedSlow(hasher);
break;
case RNGLevel::SLEEP:
- SeedSleep(hasher);
+ SeedSleep(hasher, rng);
break;
}
@@ -497,7 +643,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
if (!rng.MixExtract(out, num, std::move(hasher), false)) {
// On the first invocation, also seed with SeedStartup().
CSHA512 startup_hasher;
- SeedStartup(startup_hasher);
+ SeedStartup(startup_hasher, rng);
rng.MixExtract(out, num, std::move(startup_hasher), true);
}
@@ -514,9 +660,11 @@ void GetRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNG
void GetStrongRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::SLOW); }
void RandAddSeedSleep() { ProcRand(nullptr, 0, RNGLevel::SLEEP); }
+bool g_mock_deterministic_tests{false};
+
uint64_t GetRand(uint64_t nMax) noexcept
{
- return FastRandomContext().randrange(nMax);
+ return FastRandomContext(g_mock_deterministic_tests).randrange(nMax);
}
int GetRandInt(int nMax) noexcept
@@ -554,7 +702,7 @@ std::vector<unsigned char> FastRandomContext::randbytes(size_t len)
if (requires_seed) RandomSeed();
std::vector<unsigned char> ret(len);
if (len > 0) {
- rng.Output(&ret[0], len);
+ rng.Keystream(&ret[0], len);
}
return ret;
}
diff --git a/src/random.h b/src/random.h
index 4c73f3822a..75d037738d 100644
--- a/src/random.h
+++ b/src/random.h
@@ -24,7 +24,7 @@
* perform 'fast' seeding, consisting of mixing in:
* - A stack pointer (indirectly committing to calling thread and call stack)
* - A high-precision timestamp (rdtsc when available, c++ high_resolution_clock otherwise)
- * - Hardware RNG (rdrand) when available.
+ * - 64 bits from the hardware RNG (rdrand) when available.
* These entropy sources are very fast, and only designed to protect against situations
* where a VM state restore/copy results in multiple systems with the same randomness.
* FastRandomContext on the other hand does not protect against this once created, but
@@ -43,13 +43,16 @@
* - RandAddSeedSleep() seeds everything that fast seeding includes, but additionally:
* - A high-precision timestamp before and after sleeping 1ms.
* - (On Windows) Once every 10 minutes, performance monitoring data from the OS.
+ - - Once every minute, strengthen the entropy for 10 ms using repeated SHA512.
* These just exploit the fact the system is idle to improve the quality of the RNG
* slightly.
*
* On first use of the RNG (regardless of what function is called first), all entropy
* sources used in the 'slow' seeder are included, but also:
+ * - 256 bits from the hardware RNG (rdseed or rdrand) when available.
* - (On Windows) Performance monitoring data from the OS.
* - (On Windows) Through OpenSSL, the screen contents.
+ * - Strengthen the entropy for 100 ms using repeated SHA512.
*
* When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and
* (up to) the first 32 bytes of H are produced as output, while the last 32 bytes
@@ -110,7 +113,7 @@ private:
if (requires_seed) {
RandomSeed();
}
- rng.Output(bytebuf, sizeof(bytebuf));
+ rng.Keystream(bytebuf, sizeof(bytebuf));
bytebuf_size = sizeof(bytebuf);
}
diff --git a/src/rest.cpp b/src/rest.cpp
index 326f7ae1d2..ab409947d3 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -141,13 +141,13 @@ static bool rest_headers(HTTPRequest* req,
headers.reserve(count);
{
LOCK(cs_main);
- tip = chainActive.Tip();
+ tip = ::ChainActive().Tip();
const CBlockIndex* pindex = LookupBlockIndex(hash);
- while (pindex != nullptr && chainActive.Contains(pindex)) {
+ while (pindex != nullptr && ::ChainActive().Contains(pindex)) {
headers.push_back(pindex);
if (headers.size() == (unsigned long)count)
break;
- pindex = chainActive.Next(pindex);
+ pindex = ::ChainActive().Next(pindex);
}
}
@@ -209,7 +209,7 @@ static bool rest_block(HTTPRequest* req,
CBlockIndex* tip = nullptr;
{
LOCK(cs_main);
- tip = chainActive.Tip();
+ tip = ::ChainActive().Tip();
pblockindex = LookupBlockIndex(hash);
if (!pblockindex) {
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
@@ -300,7 +300,7 @@ static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart)
switch (rf) {
case RetFormat::JSON: {
- UniValue mempoolInfoObject = mempoolInfoToJSON();
+ UniValue mempoolInfoObject = MempoolInfoToJSON(::mempool);
std::string strJSON = mempoolInfoObject.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
@@ -322,7 +322,7 @@ static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPar
switch (rf) {
case RetFormat::JSON: {
- UniValue mempoolObject = mempoolToJSON(true);
+ UniValue mempoolObject = MempoolToJSON(::mempool, true);
std::string strJSON = mempoolObject.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
@@ -522,7 +522,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
// serialize data
// use exact same output as mentioned in Bip64
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
- ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs;
+ ssGetUTXOResponse << ::ChainActive().Height() << ::ChainActive().Tip()->GetBlockHash() << bitmap << outs;
std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
req->WriteHeader("Content-Type", "application/octet-stream");
@@ -532,7 +532,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
case RetFormat::HEX: {
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
- ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs;
+ ssGetUTXOResponse << ::ChainActive().Height() << ::ChainActive().Tip()->GetBlockHash() << bitmap << outs;
std::string strHex = HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + "\n";
req->WriteHeader("Content-Type", "text/plain");
@@ -545,8 +545,8 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
// pack in some essentials
// use more or less the same output as mentioned in Bip64
- objGetUTXOResponse.pushKV("chainHeight", chainActive.Height());
- objGetUTXOResponse.pushKV("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex());
+ objGetUTXOResponse.pushKV("chainHeight", ::ChainActive().Height());
+ objGetUTXOResponse.pushKV("chaintipHash", ::ChainActive().Tip()->GetBlockHash().GetHex());
objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
UniValue utxos(UniValue::VARR);
@@ -590,10 +590,10 @@ static bool rest_blockhash_by_height(HTTPRequest* req,
CBlockIndex* pblockindex = nullptr;
{
LOCK(cs_main);
- if (blockheight > chainActive.Height()) {
+ if (blockheight > ::ChainActive().Height()) {
return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
}
- pblockindex = chainActive[blockheight];
+ pblockindex = ::ChainActive()[blockheight];
}
switch (rf) {
case RetFormat::BINARY: {
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index fa0b62ec48..50c4589d9f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1,20 +1,19 @@
// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 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 <rpc/blockchain.h>
#include <amount.h>
-#include <base58.h>
+#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
-#include <checkpoints.h>
#include <coins.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <hash.h>
-#include <index/txindex.h>
+#include <index/blockfilterindex.h>
#include <key_io.h>
#include <policy/feerate.h>
#include <policy/policy.h>
@@ -27,8 +26,10 @@
#include <sync.h>
#include <txdb.h>
#include <txmempool.h>
+#include <undo.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/validation.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
@@ -91,6 +92,9 @@ static int ComputeNextBlockAndDepth(const CBlockIndex* tip, const CBlockIndex* b
UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex)
{
+ // Serialize passed information without accessing chain state of the active chain!
+ AssertLockNotHeld(cs_main); // For performance reasons
+
UniValue result(UniValue::VOBJ);
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
const CBlockIndex* pnext;
@@ -117,6 +121,9 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails)
{
+ // Serialize passed information without accessing chain state of the active chain!
+ AssertLockNotHeld(cs_main); // For performance reasons
+
UniValue result(UniValue::VOBJ);
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
const CBlockIndex* pnext;
@@ -174,7 +181,7 @@ static UniValue getblockcount(const JSONRPCRequest& request)
}.ToString());
LOCK(cs_main);
- return chainActive.Height();
+ return ::ChainActive().Height();
}
static UniValue getbestblockhash(const JSONRPCRequest& request)
@@ -194,7 +201,7 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
}.ToString());
LOCK(cs_main);
- return chainActive.Tip()->GetBlockHash().GetHex();
+ return ::ChainActive().Tip()->GetBlockHash().GetHex();
}
void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
@@ -215,7 +222,7 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
"\nWaits for a specific new block and returns useful info about it.\n"
"\nReturns the current block on timeout or exit.\n",
{
- {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
+ {"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
},
RPCResult{
"{ (json object)\n"
@@ -256,8 +263,8 @@ static UniValue waitforblock(const JSONRPCRequest& request)
"\nWaits for a specific new block and returns useful info about it.\n"
"\nReturns the current block on timeout or exit.\n",
{
- {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "Block hash to wait for."},
- {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Block hash to wait for."},
+ {"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
},
RPCResult{
"{ (json object)\n"
@@ -302,8 +309,8 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
"of the current tip.\n"
"\nReturns the current block on timeout or exit.\n",
{
- {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Block height to wait for."},
- {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
+ {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "Block height to wait for."},
+ {"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
},
RPCResult{
"{ (json object)\n"
@@ -373,12 +380,14 @@ static UniValue getdifficulty(const JSONRPCRequest& request)
}.ToString());
LOCK(cs_main);
- return GetDifficulty(chainActive.Tip());
+ return GetDifficulty(::ChainActive().Tip());
}
static std::string EntryDescriptionString()
{
- return " \"size\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\n"
+ return " \"vsize\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\n"
+ " \"size\" : n, (numeric) (DEPRECATED) same as vsize. Only returned if bitcoind is started with -deprecatedrpc=size\n"
+ " size will be completely removed in v0.20.\n"
" \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)\n"
" \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority (DEPRECATED)\n"
" \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n"
@@ -405,9 +414,9 @@ static std::string EntryDescriptionString()
" \"bip125-replaceable\" : true|false, (boolean) Whether this transaction could be replaced due to BIP125 (replace-by-fee)\n";
}
-static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCKS_REQUIRED(::mempool.cs)
+static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
{
- AssertLockHeld(mempool.cs);
+ AssertLockHeld(pool.cs);
UniValue fees(UniValue::VOBJ);
fees.pushKV("base", ValueFromAmount(e.GetFee()));
@@ -416,7 +425,8 @@ static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCK
fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants()));
info.pushKV("fees", fees);
- info.pushKV("size", (int)e.GetTxSize());
+ info.pushKV("vsize", (int)e.GetTxSize());
+ if (IsDeprecatedRPCEnabled("size")) info.pushKV("size", (int)e.GetTxSize());
info.pushKV("fee", ValueFromAmount(e.GetFee()));
info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee()));
info.pushKV("time", e.GetTime());
@@ -427,12 +437,12 @@ static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCK
info.pushKV("ancestorcount", e.GetCountWithAncestors());
info.pushKV("ancestorsize", e.GetSizeWithAncestors());
info.pushKV("ancestorfees", e.GetModFeesWithAncestors());
- info.pushKV("wtxid", mempool.vTxHashes[e.vTxHashesIdx].first.ToString());
+ info.pushKV("wtxid", pool.vTxHashes[e.vTxHashesIdx].first.ToString());
const CTransaction& tx = e.GetTx();
std::set<std::string> setDepends;
for (const CTxIn& txin : tx.vin)
{
- if (mempool.exists(txin.prevout.hash))
+ if (pool.exists(txin.prevout.hash))
setDepends.insert(txin.prevout.hash.ToString());
}
@@ -445,8 +455,8 @@ static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCK
info.pushKV("depends", depends);
UniValue spent(UniValue::VARR);
- const CTxMemPool::txiter &it = mempool.mapTx.find(tx.GetHash());
- const CTxMemPool::setEntries &setChildren = mempool.GetMemPoolChildren(it);
+ const CTxMemPool::txiter& it = pool.mapTx.find(tx.GetHash());
+ const CTxMemPool::setEntries& setChildren = pool.GetMemPoolChildren(it);
for (CTxMemPool::txiter childiter : setChildren) {
spent.push_back(childiter->GetTx().GetHash().ToString());
}
@@ -455,7 +465,7 @@ static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCK
// Add opt-in RBF status
bool rbfStatus = false;
- RBFTransactionState rbfState = IsRBFOptIn(tx, mempool);
+ RBFTransactionState rbfState = IsRBFOptIn(tx, pool);
if (rbfState == RBFTransactionState::UNKNOWN) {
throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool");
} else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) {
@@ -465,25 +475,24 @@ static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCK
info.pushKV("bip125-replaceable", rbfStatus);
}
-UniValue mempoolToJSON(bool fVerbose)
+UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose)
{
- if (fVerbose)
- {
- LOCK(mempool.cs);
+ if (verbose) {
+ LOCK(pool.cs);
UniValue o(UniValue::VOBJ);
- for (const CTxMemPoolEntry& e : mempool.mapTx)
- {
+ for (const CTxMemPoolEntry& e : pool.mapTx) {
const uint256& hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ);
- entryToJSON(info, e);
- o.pushKV(hash.ToString(), info);
+ entryToJSON(pool, info, e);
+ // Mempool has unique entries so there is no advantage in using
+ // UniValue::pushKV, which checks if the key already exists in O(N).
+ // UniValue::__pushKV is used instead which currently is O(1).
+ o.__pushKV(hash.ToString(), info);
}
return o;
- }
- else
- {
+ } else {
std::vector<uint256> vtxid;
- mempool.queryHashes(vtxid);
+ pool.queryHashes(vtxid);
UniValue a(UniValue::VARR);
for (const uint256& hash : vtxid)
@@ -501,7 +510,7 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
"\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
{
- {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"},
+ {"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"},
},
RPCResult{"for verbose = false",
"[ (json array of string)\n"
@@ -525,7 +534,7 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
if (!request.params[0].isNull())
fVerbose = request.params[0].get_bool();
- return mempoolToJSON(fVerbose);
+ return MempoolToJSON(::mempool, fVerbose);
}
static UniValue getmempoolancestors(const JSONRPCRequest& request)
@@ -535,8 +544,8 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
RPCHelpMan{"getmempoolancestors",
"\nIf txid is in the mempool, returns all in-mempool ancestors.\n",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"},
- {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
+ {"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"},
},
{
RPCResult{"for verbose = false",
@@ -591,7 +600,7 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
const CTxMemPoolEntry &e = *ancestorIt;
const uint256& _hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ);
- entryToJSON(info, e);
+ entryToJSON(::mempool, info, e);
o.pushKV(_hash.ToString(), info);
}
return o;
@@ -605,8 +614,8 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
RPCHelpMan{"getmempooldescendants",
"\nIf txid is in the mempool, returns all in-mempool descendants.\n",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"},
- {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
+ {"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"},
},
{
RPCResult{"for verbose = false",
@@ -661,7 +670,7 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
const CTxMemPoolEntry &e = *descendantIt;
const uint256& _hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ);
- entryToJSON(info, e);
+ entryToJSON(::mempool, info, e);
o.pushKV(_hash.ToString(), info);
}
return o;
@@ -675,7 +684,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
RPCHelpMan{"getmempoolentry",
"\nReturns mempool data for given transaction\n",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
},
RPCResult{
"{ (json object)\n"
@@ -700,7 +709,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
const CTxMemPoolEntry &e = *it;
UniValue info(UniValue::VOBJ);
- entryToJSON(info, e);
+ entryToJSON(::mempool, info, e);
return info;
}
@@ -711,7 +720,7 @@ static UniValue getblockhash(const JSONRPCRequest& request)
RPCHelpMan{"getblockhash",
"\nReturns hash of block in best-block-chain at height provided.\n",
{
- {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The height index"},
+ {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The height index"},
},
RPCResult{
"\"hash\" (string) The block hash\n"
@@ -725,10 +734,10 @@ static UniValue getblockhash(const JSONRPCRequest& request)
LOCK(cs_main);
int nHeight = request.params[0].get_int();
- if (nHeight < 0 || nHeight > chainActive.Height())
+ if (nHeight < 0 || nHeight > ::ChainActive().Height())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
- CBlockIndex* pblockindex = chainActive[nHeight];
+ CBlockIndex* pblockindex = ::ChainActive()[nHeight];
return pblockindex->GetBlockHash().GetHex();
}
@@ -740,8 +749,8 @@ static UniValue getblockheader(const JSONRPCRequest& request)
"\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
"If verbose is true, returns an Object with information about blockheader <hash>.\n",
{
- {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The block hash"},
- {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "true for a json object, false for the hex-encoded data"},
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
+ {"verbose", RPCArg::Type::BOOL, /* default */ "true", "true for a json object, false for the hex-encoded data"},
},
{
RPCResult{"for verbose = true",
@@ -784,7 +793,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
{
LOCK(cs_main);
pblockindex = LookupBlockIndex(hash);
- tip = chainActive.Tip();
+ tip = ::ChainActive().Tip();
}
if (!pblockindex) {
@@ -821,17 +830,29 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
return block;
}
+static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
+{
+ CBlockUndo blockUndo;
+ if (IsBlockPruned(pblockindex)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
+ }
+
+ if (!UndoReadFromDisk(blockUndo, pblockindex)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Can't read undo data from disk");
+ }
+
+ return blockUndo;
+}
+
static UniValue getblock(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
- RPCHelpMan{"getblock",
+ const RPCHelpMan help{"getblock",
"\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
"If verbosity is 1, returns an Object with information about block <hash>.\n"
"If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n",
{
- {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The block hash"},
- {"verbosity", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"},
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
+ {"verbosity", RPCArg::Type::NUM, /* default */ "1", "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"},
},
{
RPCResult{"for verbosity = 0",
@@ -877,9 +898,11 @@ static UniValue getblock(const JSONRPCRequest& request)
HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
},
- }.ToString());
+ };
- LOCK(cs_main);
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
uint256 hash(ParseHashV(request.params[0], "blockhash"));
@@ -891,12 +914,20 @@ static UniValue getblock(const JSONRPCRequest& request)
verbosity = request.params[1].get_bool() ? 1 : 0;
}
- const CBlockIndex* pblockindex = LookupBlockIndex(hash);
- if (!pblockindex) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
- }
+ CBlock block;
+ const CBlockIndex* pblockindex;
+ const CBlockIndex* tip;
+ {
+ LOCK(cs_main);
+ pblockindex = LookupBlockIndex(hash);
+ tip = ::ChainActive().Tip();
- const CBlock block = GetBlockChecked(pblockindex);
+ if (!pblockindex) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+
+ block = GetBlockChecked(pblockindex);
+ }
if (verbosity <= 0)
{
@@ -906,7 +937,7 @@ static UniValue getblock(const JSONRPCRequest& request)
return strHex;
}
- return blockToJSON(block, chainActive.Tip(), pblockindex, verbosity >= 2);
+ return blockToJSON(block, tip, pblockindex, verbosity >= 2);
}
struct CCoinsStats
@@ -986,7 +1017,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
throw std::runtime_error(
RPCHelpMan{"pruneblockchain", "",
{
- {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The block height to prune up to. May be set to a discrete height, or a unix timestamp\n"
+ {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block height to prune up to. May be set to a discrete height, or a unix timestamp\n"
" to prune blocks whose block time is at least 2 hours older than the provided timestamp."},
},
RPCResult{
@@ -1011,7 +1042,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
// too low to be a block time (corresponds to timestamp from Sep 2001).
if (heightParam > 1000000000) {
// Add a 2 hour buffer to include blocks which might have had old timestamps
- CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW);
+ CBlockIndex* pindex = ::ChainActive().FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
}
@@ -1019,7 +1050,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
}
unsigned int height = (unsigned int) heightParam;
- unsigned int chainHeight = (unsigned int) chainActive.Height();
+ unsigned int chainHeight = (unsigned int) ::ChainActive().Height();
if (chainHeight < Params().PruneAfterHeight())
throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning.");
else if (height > chainHeight)
@@ -1030,7 +1061,12 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
}
PruneBlockFilesManual(height);
- return uint64_t(height);
+ const CBlockIndex* block = ::ChainActive().Tip();
+ assert(block);
+ while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
+ block = block->pprev;
+ }
+ return uint64_t(block->nHeight);
}
static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
@@ -1062,7 +1098,7 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
UniValue ret(UniValue::VOBJ);
CCoinsStats stats;
- FlushStateToDisk();
+ ::ChainstateActive().ForceFlushStateToDisk();
if (GetUTXOStats(pcoinsdbview.get(), stats)) {
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
@@ -1085,9 +1121,9 @@ UniValue gettxout(const JSONRPCRequest& request)
RPCHelpMan{"gettxout",
"\nReturns details about an unspent transaction output.\n",
{
- {"txid", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction id"},
- {"n", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "vout number"},
- {"include_mempool", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."},
+ {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
+ {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"},
+ {"include_mempool", RPCArg::Type::BOOL, /* default */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."},
},
RPCResult{
"{\n"
@@ -1166,8 +1202,8 @@ static UniValue verifychain(const JSONRPCRequest& request)
RPCHelpMan{"verifychain",
"\nVerifies blockchain database.\n",
{
- {"checklevel", RPCArg::Type::NUM, /* opt */ true, /* default_val */ strprintf("%d, range=0-4", nCheckLevel), "How thorough the block verification is."},
- {"nblocks", RPCArg::Type::NUM, /* opt */ true, /* default_val */ strprintf("%d, 0=all", nCheckDepth), "The number of blocks to check."},
+ {"checklevel", RPCArg::Type::NUM, /* default */ strprintf("%d, range=0-4", nCheckLevel), "How thorough the block verification is."},
+ {"nblocks", RPCArg::Type::NUM, /* default */ strprintf("%d, 0=all", nCheckDepth), "The number of blocks to check."},
},
RPCResult{
"true|false (boolean) Verified or not\n"
@@ -1318,16 +1354,16 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
LOCK(cs_main);
- const CBlockIndex* tip = chainActive.Tip();
+ const CBlockIndex* tip = ::ChainActive().Tip();
UniValue obj(UniValue::VOBJ);
obj.pushKV("chain", Params().NetworkIDString());
- obj.pushKV("blocks", (int)chainActive.Height());
+ obj.pushKV("blocks", (int)::ChainActive().Height());
obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
obj.pushKV("difficulty", (double)GetDifficulty(tip));
obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
- obj.pushKV("initialblockdownload", IsInitialBlockDownload());
+ obj.pushKV("initialblockdownload", ::ChainstateActive().IsInitialBlockDownload());
obj.pushKV("chainwork", tip->nChainWork.GetHex());
obj.pushKV("size_on_disk", CalculateCurrentUsage());
obj.pushKV("pruned", fPruneMode);
@@ -1418,11 +1454,11 @@ static UniValue getchaintips(const JSONRPCRequest& request)
LOCK(cs_main);
/*
- * Idea: the set of chain tips is chainActive.tip, plus orphan blocks which do not have another orphan building off of them.
+ * Idea: the set of chain tips is ::ChainActive().tip, plus orphan blocks which do not have another orphan building off of them.
* Algorithm:
* - Make one pass through mapBlockIndex, picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
* - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip.
- * - add chainActive.Tip()
+ * - add ::ChainActive().Tip()
*/
std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
std::set<const CBlockIndex*> setOrphans;
@@ -1430,7 +1466,7 @@ static UniValue getchaintips(const JSONRPCRequest& request)
for (const std::pair<const uint256, CBlockIndex*>& item : mapBlockIndex)
{
- if (!chainActive.Contains(item.second)) {
+ if (!::ChainActive().Contains(item.second)) {
setOrphans.insert(item.second);
setPrevs.insert(item.second->pprev);
}
@@ -1444,7 +1480,7 @@ static UniValue getchaintips(const JSONRPCRequest& request)
}
// Always report the currently active tip.
- setTips.insert(chainActive.Tip());
+ setTips.insert(::ChainActive().Tip());
/* Construct the output array. */
UniValue res(UniValue::VARR);
@@ -1454,11 +1490,11 @@ static UniValue getchaintips(const JSONRPCRequest& request)
obj.pushKV("height", block->nHeight);
obj.pushKV("hash", block->phashBlock->GetHex());
- const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight;
+ const int branchLen = block->nHeight - ::ChainActive().FindFork(block)->nHeight;
obj.pushKV("branchlen", branchLen);
std::string status;
- if (chainActive.Contains(block)) {
+ if (::ChainActive().Contains(block)) {
// This block is part of the currently active chain.
status = "active";
} else if (block->nStatus & BLOCK_FAILED_MASK) {
@@ -1485,15 +1521,18 @@ static UniValue getchaintips(const JSONRPCRequest& request)
return res;
}
-UniValue mempoolInfoToJSON()
+UniValue MempoolInfoToJSON(const CTxMemPool& pool)
{
+ // Make sure this call is atomic in the pool.
+ LOCK(pool.cs);
UniValue ret(UniValue::VOBJ);
- ret.pushKV("size", (int64_t) mempool.size());
- ret.pushKV("bytes", (int64_t) mempool.GetTotalTxSize());
- ret.pushKV("usage", (int64_t) mempool.DynamicMemoryUsage());
+ ret.pushKV("loaded", pool.IsLoaded());
+ ret.pushKV("size", (int64_t)pool.size());
+ ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
+ ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
ret.pushKV("maxmempool", (int64_t) maxmempool);
- ret.pushKV("mempoolminfee", ValueFromAmount(std::max(mempool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
+ ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
return ret;
@@ -1508,6 +1547,7 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
{},
RPCResult{
"{\n"
+ " \"loaded\": true|false (boolean) True if the mempool is fully loaded\n"
" \"size\": xxxxx, (numeric) Current tx count\n"
" \"bytes\": xxxxx, (numeric) Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\n"
" \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n"
@@ -1522,7 +1562,7 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
},
}.ToString());
- return mempoolInfoToJSON();
+ return MempoolInfoToJSON(::mempool);
}
static UniValue preciousblock(const JSONRPCRequest& request)
@@ -1534,7 +1574,7 @@ static UniValue preciousblock(const JSONRPCRequest& request)
"\nA later preciousblock call can override the effect of an earlier one.\n"
"\nThe effects of preciousblock are not retained across restarts.\n",
{
- {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to mark as precious"},
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to mark as precious"},
},
RPCResults{},
RPCExamples{
@@ -1571,7 +1611,7 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
RPCHelpMan{"invalidateblock",
"\nPermanently marks a block as invalid, as if it violated a consensus rule.\n",
{
- {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to mark as invalid"},
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to mark as invalid"},
},
RPCResults{},
RPCExamples{
@@ -1583,15 +1623,15 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
uint256 hash(ParseHashV(request.params[0], "blockhash"));
CValidationState state;
+ CBlockIndex* pblockindex;
{
LOCK(cs_main);
- CBlockIndex* pblockindex = LookupBlockIndex(hash);
+ pblockindex = LookupBlockIndex(hash);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
-
- InvalidateBlock(state, Params(), pblockindex);
}
+ InvalidateBlock(state, Params(), pblockindex);
if (state.IsValid()) {
ActivateBestChain(state, Params());
@@ -1612,7 +1652,7 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
"\nRemoves invalidity status of a block, its ancestors and its descendants, reconsider them for activation.\n"
"This can be used to undo the effects of invalidateblock.\n",
{
- {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to reconsider"},
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to reconsider"},
},
RPCResults{},
RPCExamples{
@@ -1650,8 +1690,8 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
RPCHelpMan{"getchaintxstats",
"\nCompute statistics about the total number and rate of transactions in the chain.\n",
{
- {"nblocks", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "one month", "Size of the window in number of blocks"},
- {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "chain tip", "The hash of the block that ends the window."},
+ {"nblocks", RPCArg::Type::NUM, /* default */ "one month", "Size of the window in number of blocks"},
+ {"blockhash", RPCArg::Type::STR_HEX, /* default */ "chain tip", "The hash of the block that ends the window."},
},
RPCResult{
"{\n"
@@ -1675,7 +1715,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
if (request.params[1].isNull()) {
LOCK(cs_main);
- pindex = chainActive.Tip();
+ pindex = ::ChainActive().Tip();
} else {
uint256 hash(ParseHashV(request.params[1], "blockhash"));
LOCK(cs_main);
@@ -1683,7 +1723,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- if (!chainActive.Contains(pindex)) {
+ if (!::ChainActive().Contains(pindex)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain");
}
}
@@ -1778,18 +1818,15 @@ static constexpr size_t PER_UTXO_OVERHEAD = sizeof(COutPoint) + sizeof(uint32_t)
static UniValue getblockstats(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) {
- throw std::runtime_error(
- RPCHelpMan{"getblockstats",
+ const RPCHelpMan help{"getblockstats",
"\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
- "It won't work for some heights with pruning.\n"
- "It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n",
+ "It won't work for some heights with pruning.\n",
{
- {"hash_or_height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The block hash or height of the target block", "", {"", "string or numeric"}},
- {"stats", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "all values", "Values to plot (see result below)",
+ {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", "", {"", "string or numeric"}},
+ {"stats", RPCArg::Type::ARR, /* default */ "all values", "Values to plot (see result below)",
{
- {"height", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "Selected statistic"},
- {"time", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "Selected statistic"},
+ {"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
+ {"time", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
},
"stats"},
},
@@ -1836,7 +1873,9 @@ static UniValue getblockstats(const JSONRPCRequest& request)
HelpExampleCli("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'")
+ HelpExampleRpc("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'")
},
- }.ToString());
+ };
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
}
LOCK(cs_main);
@@ -1844,7 +1883,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
CBlockIndex* pindex;
if (request.params[0].isNum()) {
const int height = request.params[0].get_int();
- const int current_tip = chainActive.Height();
+ const int current_tip = ::ChainActive().Height();
if (height < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height));
}
@@ -1852,14 +1891,14 @@ static UniValue getblockstats(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", height, current_tip));
}
- pindex = chainActive[height];
+ pindex = ::ChainActive()[height];
} else {
const uint256 hash(ParseHashV(request.params[0], "hash_or_height"));
pindex = LookupBlockIndex(hash);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- if (!chainActive.Contains(pindex)) {
+ if (!::ChainActive().Contains(pindex)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Block is not in chain %s", Params().NetworkIDString()));
}
}
@@ -1876,6 +1915,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
}
const CBlock block = GetBlockChecked(pindex);
+ const CBlockUndo blockUndo = GetUndoChecked(pindex);
const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
@@ -1889,10 +1929,6 @@ static UniValue getblockstats(const JSONRPCRequest& request)
const bool do_calculate_weight = do_all || SetHasKeys(stats, "total_weight", "avgfeerate", "swtotal_weight", "avgfeerate", "feerate_percentiles", "minfeerate", "maxfeerate");
const bool do_calculate_sw = do_all || SetHasKeys(stats, "swtxs", "swtotal_size", "swtotal_weight");
- if (loop_inputs && !g_txindex) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled");
- }
-
CAmount maxfee = 0;
CAmount maxfeerate = 0;
CAmount minfee = MAX_MONEY;
@@ -1913,7 +1949,8 @@ static UniValue getblockstats(const JSONRPCRequest& request)
std::vector<std::pair<CAmount, int64_t>> feerate_array;
std::vector<int64_t> txsize_array;
- for (const auto& tx : block.vtx) {
+ for (size_t i = 0; i < block.vtx.size(); ++i) {
+ const auto& tx = block.vtx.at(i);
outputs += tx->vout.size();
CAmount tx_total_out = 0;
@@ -1957,14 +1994,9 @@ static UniValue getblockstats(const JSONRPCRequest& request)
if (loop_inputs) {
CAmount tx_total_in = 0;
- for (const CTxIn& in : tx->vin) {
- CTransactionRef tx_in;
- uint256 hashBlock;
- if (!GetTransaction(in.prevout.hash, tx_in, Params().GetConsensus(), hashBlock)) {
- throw JSONRPCError(RPC_INTERNAL_ERROR, std::string("Unexpected internal error (tx index seems corrupt)"));
- }
-
- CTxOut prevoutput = tx_in->vout[in.prevout.n];
+ const auto& txundo = blockUndo.vtxundo.at(i - 1);
+ for (const Coin& coin: txundo.vprevout) {
+ const CTxOut& prevoutput = coin.out;
tx_total_in += prevoutput.nValue;
utxo_size_inc -= GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
@@ -2058,11 +2090,11 @@ static UniValue savemempool(const JSONRPCRequest& request)
}.ToString());
}
- if (!g_is_mempool_loaded) {
+ if (!::mempool.IsLoaded()) {
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
}
- if (!DumpMempool()) {
+ if (!DumpMempool(::mempool)) {
throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
}
@@ -2148,18 +2180,18 @@ UniValue scantxoutset(const JSONRPCRequest& request)
"In the latter case, a range needs to be specified by below if different from 1000.\n"
"For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n",
{
- {"action", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The action to execute\n"
+ {"action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
" \"start\" for starting a scan\n"
" \"abort\" for aborting the current scan (returns true when abort was successful)\n"
" \"status\" for progress report (in %) of the current scan"},
- {"scanobjects", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "Array of scan objects\n"
+ {"scanobjects", RPCArg::Type::ARR, RPCArg::Optional::NO, "Array of scan objects\n"
" Every scan object is either a string descriptor or an object:",
{
- {"descriptor", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "An output descriptor"},
- {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "An object with output descriptor and metadata",
+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
{
- {"desc", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "An output descriptor"},
- {"range", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1000", "Up to what child index HD chains should be explored"},
+ {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
+ {"range", RPCArg::Type::RANGE, /* default */ "1000", "The range of HD chain indexes to explore (either end or [begin,end])"},
},
},
},
@@ -2216,7 +2248,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
// loop through the scan objects
for (const UniValue& scanobject : request.params[1].get_array().getValues()) {
std::string desc_str;
- int range = 1000;
+ std::pair<int64_t, int64_t> range = {0, 1000};
if (scanobject.isStr()) {
desc_str = scanobject.get_str();
} else if (scanobject.isObject()) {
@@ -2225,8 +2257,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
desc_str = desc_uni.get_str();
UniValue range_uni = find_value(scanobject, "range");
if (!range_uni.isNull()) {
- range = range_uni.get_int();
- if (range < 0 || range > 1000000) throw JSONRPCError(RPC_INVALID_PARAMETER, "range out of range");
+ range = ParseDescriptorRange(range_uni);
}
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
@@ -2237,8 +2268,11 @@ UniValue scantxoutset(const JSONRPCRequest& request)
if (!desc) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor '%s'", desc_str));
}
- if (!desc->IsRange()) range = 0;
- for (int i = 0; i <= range; ++i) {
+ if (!desc->IsRange()) {
+ range.first = 0;
+ range.second = 0;
+ }
+ for (int i = range.first; i <= range.second; ++i) {
std::vector<CScript> scripts;
if (!desc->Expand(i, provider, scripts, provider)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
@@ -2261,7 +2295,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
std::unique_ptr<CCoinsViewCursor> pcursor;
{
LOCK(cs_main);
- FlushStateToDisk();
+ ::ChainstateActive().ForceFlushStateToDisk();
pcursor = std::unique_ptr<CCoinsViewCursor>(pcoinsdbview->Cursor());
assert(pcursor);
}
@@ -2294,6 +2328,85 @@ UniValue scantxoutset(const JSONRPCRequest& request)
return result;
}
+static UniValue getblockfilter(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
+ throw std::runtime_error(
+ RPCHelpMan{"getblockfilter",
+ "\nRetrieve a BIP 157 content filter for a particular block.\n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hash of the block"},
+ {"filtertype", RPCArg::Type::STR, /*default*/ "basic", "The type name of the filter"},
+ },
+ RPCResult{
+ "{\n"
+ " \"filter\" : (string) the hex-encoded filter data\n"
+ " \"header\" : (string) the hex-encoded filter header\n"
+ "}\n"
+ },
+ RPCExamples{
+ HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"")
+ }
+ }.ToString()
+ );
+ }
+
+ uint256 block_hash = ParseHashV(request.params[0], "blockhash");
+ std::string filtertype_name = "basic";
+ if (!request.params[1].isNull()) {
+ filtertype_name = request.params[1].get_str();
+ }
+
+ BlockFilterType filtertype;
+ if (!BlockFilterTypeByName(filtertype_name, filtertype)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown filtertype");
+ }
+
+ BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
+ if (!index) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Index is not enabled for filtertype " + filtertype_name);
+ }
+
+ const CBlockIndex* block_index;
+ bool block_was_connected;
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(block_hash);
+ if (!block_index) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+ block_was_connected = block_index->IsValid(BLOCK_VALID_SCRIPTS);
+ }
+
+ bool index_ready = index->BlockUntilSyncedToCurrentChain();
+
+ BlockFilter filter;
+ uint256 filter_header;
+ if (!index->LookupFilter(block_index, filter) ||
+ !index->LookupFilterHeader(block_index, filter_header)) {
+ int err_code;
+ std::string errmsg = "Filter not found.";
+
+ if (!block_was_connected) {
+ err_code = RPC_INVALID_ADDRESS_OR_KEY;
+ errmsg += " Block was not connected to active chain.";
+ } else if (!index_ready) {
+ err_code = RPC_MISC_ERROR;
+ errmsg += " Block filters are still in the process of being indexed.";
+ } else {
+ err_code = RPC_INTERNAL_ERROR;
+ errmsg += " This error is unexpected and indicates index corruption.";
+ }
+
+ throw JSONRPCError(err_code, errmsg);
+ }
+
+ UniValue ret(UniValue::VOBJ);
+ ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
+ ret.pushKV("header", filter_header.GetHex());
+ return ret;
+}
+
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -2321,6 +2434,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
{ "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} },
+ { "blockchain", "getblockfilter", &getblockfilter, {"blockhash", "filtertype"} },
/* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, {"blockhash"} },
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index 529132d033..ff461fbcbc 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -5,12 +5,17 @@
#ifndef BITCOIN_RPC_BLOCKCHAIN_H
#define BITCOIN_RPC_BLOCKCHAIN_H
-#include <vector>
-#include <stdint.h>
#include <amount.h>
+#include <sync.h>
+
+#include <stdint.h>
+#include <vector>
+
+extern RecursiveMutex cs_main;
class CBlock;
class CBlockIndex;
+class CTxMemPool;
class UniValue;
static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5;
@@ -27,16 +32,16 @@ double GetDifficulty(const CBlockIndex* blockindex);
void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
/** Block description to JSON */
-UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails = false);
+UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails = false) LOCKS_EXCLUDED(cs_main);
/** Mempool information to JSON */
-UniValue mempoolInfoToJSON();
+UniValue MempoolInfoToJSON(const CTxMemPool& pool);
/** Mempool to JSON */
-UniValue mempoolToJSON(bool fVerbose = false);
+UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose = false);
/** Block header to JSON */
-UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex);
+UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex) LOCKS_EXCLUDED(cs_main);
/** Used by getblockstats to get feerates at different percentiles by weight */
void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight);
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 6f1bfb03d1..4144a17bc3 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -28,8 +28,6 @@ public:
static const CRPCConvertParam vRPCConvertParams[] =
{
{ "setmocktime", 0, "timestamp" },
- { "generate", 0, "nblocks" },
- { "generate", 1, "maxtries" },
{ "generatetoaddress", 0, "nblocks" },
{ "generatetoaddress", 2, "maxtries" },
{ "getnetworkhashps", 0, "nblocks" },
@@ -68,6 +66,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendmany", 4, "subtractfeefrom" },
{ "sendmany", 5 , "replaceable" },
{ "sendmany", 6 , "conf_target" },
+ { "deriveaddresses", 1, "range" },
{ "scantxoutset", 1, "scanobjects" },
{ "addmultisigaddress", 0, "nrequired" },
{ "addmultisigaddress", 1, "keys" },
@@ -89,14 +88,14 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createrawtransaction", 2, "locktime" },
{ "createrawtransaction", 3, "replaceable" },
{ "decoderawtransaction", 1, "iswitness" },
- { "signrawtransaction", 1, "prevtxs" },
- { "signrawtransaction", 2, "privkeys" },
{ "signrawtransactionwithkey", 1, "privkeys" },
{ "signrawtransactionwithkey", 2, "prevtxs" },
{ "signrawtransactionwithwallet", 1, "prevtxs" },
{ "sendrawtransaction", 1, "allowhighfees" },
+ { "sendrawtransaction", 1, "maxfeerate" },
{ "testmempoolaccept", 0, "rawtxs" },
{ "testmempoolaccept", 1, "allowhighfees" },
+ { "testmempoolaccept", 1, "maxfeerate" },
{ "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 2, "iswitness" },
@@ -112,6 +111,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createpsbt", 2, "locktime" },
{ "createpsbt", 3, "replaceable" },
{ "combinepsbt", 0, "txs"},
+ { "joinpsbts", 0, "txs"},
{ "finalizepsbt", 1, "extract"},
{ "converttopsbt", 1, "permitsigdata"},
{ "converttopsbt", 2, "iswitness"},
@@ -161,6 +161,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "rescanblockchain", 0, "start_height"},
{ "rescanblockchain", 1, "stop_height"},
{ "createwallet", 1, "disable_private_keys"},
+ { "createwallet", 2, "blank"},
{ "getnodeaddresses", 0, "count"},
{ "stop", 0, "wait" },
};
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 200dfa107b..477f05f46c 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -16,13 +16,16 @@
#include <policy/fees.h>
#include <pow.h>
#include <rpc/blockchain.h>
-#include <rpc/mining.h>
#include <rpc/server.h>
#include <rpc/util.h>
+#include <script/script.h>
#include <shutdown.h>
#include <txmempool.h>
+#include <univalue.h>
+#include <util/fees.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/validation.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
@@ -31,26 +34,16 @@
#include <memory>
#include <stdint.h>
-unsigned int ParseConfirmTarget(const UniValue& value)
-{
- int target = value.get_int();
- unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
- if (target < 1 || (unsigned int)target > max_target) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u - %u", 1, max_target));
- }
- return (unsigned int)target;
-}
-
/**
* Return average network hashes per second based on the last 'lookup' blocks,
* or from the last difficulty change if 'lookup' is nonpositive.
* If 'height' is nonnegative, compute the estimate at the time when a given block was found.
*/
static UniValue GetNetworkHashPS(int lookup, int height) {
- CBlockIndex *pb = chainActive.Tip();
+ CBlockIndex *pb = ::ChainActive().Tip();
- if (height >= 0 && height < chainActive.Height())
- pb = chainActive[height];
+ if (height >= 0 && height < ::ChainActive().Height())
+ pb = ::ChainActive()[height];
if (pb == nullptr || !pb->nHeight)
return 0;
@@ -92,8 +85,8 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
"Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n"
"Pass in [height] to estimate the network speed at the time when a certain block was found.\n",
{
- {"nblocks", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "120", "The number of blocks, or -1 for blocks since last difficulty change."},
- {"height", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "-1", "To estimate at the time of the given height."},
+ {"nblocks", RPCArg::Type::NUM, /* default */ "120", "The number of blocks, or -1 for blocks since last difficulty change."},
+ {"height", RPCArg::Type::NUM, /* default */ "-1", "To estimate at the time of the given height."},
},
RPCResult{
"x (numeric) Hashes per second estimated\n"
@@ -108,7 +101,7 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1);
}
-UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
+static UniValue generateBlocks(const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
{
static const int nInnerLoopCount = 0x10000;
int nHeightEnd = 0;
@@ -116,20 +109,20 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
{ // Don't keep cs_main locked
LOCK(cs_main);
- nHeight = chainActive.Height();
+ nHeight = ::ChainActive().Height();
nHeightEnd = nHeight+nGenerate;
}
unsigned int nExtraNonce = 0;
UniValue blockHashes(UniValue::VARR);
while (nHeight < nHeightEnd && !ShutdownRequested())
{
- std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript));
+ std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbase_script));
if (!pblocktemplate.get())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
CBlock *pblock = &pblocktemplate->block;
{
LOCK(cs_main);
- IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
+ IncrementExtraNonce(pblock, ::ChainActive().Tip(), nExtraNonce);
}
while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
++pblock->nNonce;
@@ -146,12 +139,6 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
++nHeight;
blockHashes.push_back(pblock->GetHash().GetHex());
-
- //mark script as important because it was used at least for one coinbase output if the script came from the wallet
- if (keepScript)
- {
- coinbaseScript->KeepScript();
- }
}
return blockHashes;
}
@@ -163,9 +150,9 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
RPCHelpMan{"generatetoaddress",
"\nMine blocks immediately to a specified address (before the RPC call returns)\n",
{
- {"nblocks", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "How many blocks are generated immediately."},
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The address to send the newly generated bitcoin to."},
- {"maxtries", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1000000", "How many iterations to try."},
+ {"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address to send the newly generated bitcoin to."},
+ {"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."},
},
RPCResult{
"[ blockhashes ] (array) hashes of blocks generated\n"
@@ -189,45 +176,44 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
}
- std::shared_ptr<CReserveScript> coinbaseScript = std::make_shared<CReserveScript>();
- coinbaseScript->reserveScript = GetScriptForDestination(destination);
+ CScript coinbase_script = GetScriptForDestination(destination);
- return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false);
+ return generateBlocks(coinbase_script, nGenerate, nMaxTries);
}
static UniValue getmininginfo(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
+ if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
RPCHelpMan{"getmininginfo",
"\nReturns a json object containing mining-related information.",
{},
RPCResult{
- "{\n"
- " \"blocks\": nnn, (numeric) The current block\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"
- " \"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"
- "}\n"
+ "{\n"
+ " \"blocks\": nnn, (numeric) The current block\n"
+ " \"currentblockweight\": nnn, (numeric, optional) The block weight of the last assembled block (only present if a block was ever assembled)\n"
+ " \"currentblocktx\": nnn, (numeric, optional) The number of block transactions of the last assembled block (only present if a block was ever assembled)\n"
+ " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\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"
+ "}\n"
},
RPCExamples{
HelpExampleCli("getmininginfo", "")
+ HelpExampleRpc("getmininginfo", "")
},
}.ToString());
-
+ }
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
- obj.pushKV("blocks", (int)chainActive.Height());
- obj.pushKV("currentblockweight", (uint64_t)nLastBlockWeight);
- obj.pushKV("currentblocktx", (uint64_t)nLastBlockTx);
- obj.pushKV("difficulty", (double)GetDifficulty(chainActive.Tip()));
+ obj.pushKV("blocks", (int)::ChainActive().Height());
+ if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
+ if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
+ obj.pushKV("difficulty", (double)GetDifficulty(::ChainActive().Tip()));
obj.pushKV("networkhashps", getnetworkhashps(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
obj.pushKV("chain", Params().NetworkIDString());
@@ -244,10 +230,10 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
RPCHelpMan{"prioritisetransaction",
"Accepts the transaction into mined blocks at a higher (or lower) priority\n",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id."},
- {"dummy", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "null", "API-Compatibility for previous API. Must be zero or null.\n"
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id."},
+ {"dummy", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "API-Compatibility for previous API. Must be zero or null.\n"
" DEPRECATED. For forward compatibility use named arguments and omit this parameter."},
- {"fee_delta", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The fee value (in satoshis) to add (or subtract, if negative).\n"
+ {"fee_delta", RPCArg::Type::NUM, RPCArg::Optional::NO, "The fee value (in satoshis) to add (or subtract, if negative).\n"
" Note, that this value is not a fee rate. It is a value to modify absolute fee of the TX.\n"
" The fee is not actually paid, only the algorithm for selecting transactions into a block\n"
" considers the transaction as it would have paid a higher (or lower) fee."},
@@ -316,17 +302,17 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
" https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
" https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n",
{
- {"template_request", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "A json object in the following spec",
+ {"template_request", RPCArg::Type::OBJ, RPCArg::Optional::NO, "A json object in the following spec",
{
- {"mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
- {"capabilities", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A list of strings",
+ {"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
+ {"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "A list of strings",
{
- {"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
+ {"support", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
},
},
- {"rules", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A list of strings",
+ {"rules", RPCArg::Type::ARR, RPCArg::Optional::NO, "A list of strings",
{
- {"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported softfork deployment"},
+ {"support", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported softfork deployment"},
},
},
},
@@ -423,7 +409,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
return "duplicate-inconclusive";
}
- CBlockIndex* const pindexPrev = chainActive.Tip();
+ CBlockIndex* const pindexPrev = ::ChainActive().Tip();
// TestBlockValidity only supports blocks built on the current Tip
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
return "inconclusive-not-best-prevblk";
@@ -454,10 +440,10 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0)
- throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Bitcoin is not connected!");
+ throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
- if (IsInitialBlockDownload())
- throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks...");
+ if (::ChainstateActive().IsInitialBlockDownload())
+ throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks...");
static unsigned int nTransactionsUpdatedLast;
@@ -479,7 +465,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
else
{
// NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
- hashWatchedChain = chainActive.Tip()->GetBlockHash();
+ hashWatchedChain = ::ChainActive().Tip()->GetBlockHash();
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
}
@@ -517,7 +503,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
static CBlockIndex* pindexPrev;
static int64_t nStart;
static std::unique_ptr<CBlockTemplate> pblocktemplate;
- if (pindexPrev != chainActive.Tip() ||
+ if (pindexPrev != ::ChainActive().Tip() ||
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
{
// Clear pindexPrev so future calls make a new block, despite any failures from here on
@@ -525,7 +511,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
// Store the pindexBest used before CreateNewBlock, to avoid races
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
- CBlockIndex* pindexPrevNew = chainActive.Tip();
+ CBlockIndex* pindexPrevNew = ::ChainActive().Tip();
nStart = GetTime();
// Create new block
@@ -660,7 +646,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
result.pushKV("transactions", transactions);
result.pushKV("coinbaseaux", aux);
result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue);
- result.pushKV("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast));
+ result.pushKV("longpollid", ::ChainActive().Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast));
result.pushKV("target", hashTarget.GetHex());
result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1);
result.pushKV("mutable", aMutable);
@@ -716,8 +702,8 @@ static UniValue submitblock(const JSONRPCRequest& request)
"\nAttempts to submit new block to network.\n"
"See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n",
{
- {"hexdata", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded block data to submit"},
- {"dummy", RPCArg::Type::STR, /* opt */ true, /* default_val */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."},
+ {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block data to submit"},
+ {"dummy", RPCArg::Type::STR, /* default */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."},
},
RPCResults{},
RPCExamples{
@@ -781,7 +767,7 @@ static UniValue submitheader(const JSONRPCRequest& request)
"\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid."
"\nThrows when the header is invalid.\n",
{
- {"hexdata", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded block header data"},
+ {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block header data"},
},
RPCResult{
"None"
@@ -823,8 +809,8 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
"for which the estimate is valid. Uses virtual transaction size as defined\n"
"in BIP 141 (witness data is discounted).\n",
{
- {"conf_target", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Confirmation target in blocks (1 - 1008)"},
- {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "CONSERVATIVE", "The fee estimate mode.\n"
+ {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "CONSERVATIVE", "The fee estimate mode.\n"
" Whether to return a more conservative estimate which also satisfies\n"
" a longer history. A conservative estimate potentially returns a\n"
" higher feerate and is more likely to be sufficient for the desired\n"
@@ -853,7 +839,8 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
- unsigned int conf_target = ParseConfirmTarget(request.params[0]);
+ unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
+ unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
bool conservative = true;
if (!request.params[1].isNull()) {
FeeEstimateMode fee_mode;
@@ -890,8 +877,8 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
"confirmation within conf_target blocks if possible. Uses virtual transaction size as\n"
"defined in BIP 141 (witness data is discounted).\n",
{
- {"conf_target", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Confirmation target in blocks (1 - 1008)"},
- {"threshold", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0.95", "The proportion of transactions in a given feerate range that must have been\n"
+ {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
+ {"threshold", RPCArg::Type::NUM, /* default */ "0.95", "The proportion of transactions in a given feerate range that must have been\n"
" confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n"
" lower buckets."},
},
@@ -925,7 +912,8 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
- unsigned int conf_target = ParseConfirmTarget(request.params[0]);
+ unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
+ unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
double threshold = 0.95;
if (!request.params[1].isNull()) {
threshold = request.params[1].get_real();
diff --git a/src/rpc/mining.h b/src/rpc/mining.h
deleted file mode 100644
index 8d46273159..0000000000
--- a/src/rpc/mining.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// 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_RPC_MINING_H
-#define BITCOIN_RPC_MINING_H
-
-#include <script/script.h>
-
-#include <univalue.h>
-
-/** Generate blocks (mine) */
-UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript);
-
-/** Check bounds on a command line confirm target */
-unsigned int ParseConfirmTarget(const UniValue& value);
-
-#endif
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 9d17a2fc4e..2b29fd543b 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -3,25 +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 <clientversion.h>
-#include <core_io.h>
#include <crypto/ripemd160.h>
#include <key_io.h>
-#include <validation.h>
#include <httpserver.h>
-#include <net.h>
-#include <netbase.h>
#include <outputtype.h>
#include <rpc/blockchain.h>
#include <rpc/server.h>
#include <rpc/util.h>
-#include <timedata.h>
+#include <script/descriptor.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <warnings.h>
+#include <util/validation.h>
#include <stdint.h>
+#include <tuple>
#ifdef HAVE_MALLOC_INFO
#include <malloc.h>
#endif
@@ -33,13 +28,9 @@ static UniValue validateaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
RPCHelpMan{"validateaddress",
- "\nReturn information about the given bitcoin address.\n"
- "DEPRECATION WARNING: Parts of this command have been deprecated and moved to getaddressinfo. Clients must\n"
- "transition to using getaddressinfo to access this information before upgrading to v0.18. The following deprecated\n"
- "fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n"
- "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n",
+ "\nReturn information about the given bitcoin address.\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to validate"},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"},
},
RPCResult{
"{\n"
@@ -86,12 +77,12 @@ static UniValue createmultisig(const JSONRPCRequest& request)
"\nCreates a multi-signature address with n signature of m keys required.\n"
"It returns a json object with the address and redeemScript.\n",
{
- {"nrequired", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The number of required signatures out of the n keys."},
- {"keys", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of hex-encoded public keys.",
+ {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys."},
+ {"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of hex-encoded public keys.",
{
- {"key", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded public key"},
+ {"key", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The hex-encoded public key"},
}},
- {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "legacy", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"address_type", RPCArg::Type::STR, /* default */ "legacy", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
"{\n"
@@ -142,6 +133,125 @@ static UniValue createmultisig(const JSONRPCRequest& request)
return result;
}
+UniValue getdescriptorinfo(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw std::runtime_error(
+ RPCHelpMan{"getdescriptorinfo",
+ {"\nAnalyses a descriptor.\n"},
+ {
+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
+ },
+ RPCResult{
+ "{\n"
+ " \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\n"
+ " \"isrange\" : true|false, (boolean) Whether the descriptor is ranged\n"
+ " \"issolvable\" : true|false, (boolean) Whether the descriptor is solvable\n"
+ " \"hasprivatekeys\" : true|false, (boolean) Whether the input descriptor contained at least one private key\n"
+ "}\n"
+ },
+ RPCExamples{
+ "Analyse a descriptor\n" +
+ HelpExampleCli("getdescriptorinfo", "\"wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)\"")
+ }}.ToString()
+ );
+ }
+
+ RPCTypeCheck(request.params, {UniValue::VSTR});
+
+ FlatSigningProvider provider;
+ auto desc = Parse(request.params[0].get_str(), provider);
+ if (!desc) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor"));
+ }
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("descriptor", desc->ToString());
+ result.pushKV("isrange", desc->IsRange());
+ result.pushKV("issolvable", desc->IsSolvable());
+ result.pushKV("hasprivatekeys", provider.keys.size() > 0);
+ return result;
+}
+
+UniValue deriveaddresses(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.empty() || request.params.size() > 2) {
+ throw std::runtime_error(
+ RPCHelpMan{"deriveaddresses",
+ {"\nDerives one or more addresses corresponding to an output descriptor.\n"
+ "Examples of output descriptors are:\n"
+ " pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
+ " wpkh(<pubkey>) Native segwit P2PKH outputs for the given pubkey\n"
+ " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
+ " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
+ "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
+ "or more path elements separated by \"/\", where \"h\" represents a hardened child key.\n"
+ "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"},
+ {
+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
+ {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."},
+ },
+ RPCResult{
+ "[ address ] (array) the derived addresses\n"
+ },
+ RPCExamples{
+ "First three native segwit receive addresses\n" +
+ HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu\" \"[0,2]\"")
+ }}.ToString()
+ );
+ }
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()}); // Range argument is checked later
+ const std::string desc_str = request.params[0].get_str();
+
+ int64_t range_begin = 0;
+ int64_t range_end = 0;
+
+ if (request.params.size() >= 2 && !request.params[1].isNull()) {
+ std::tie(range_begin, range_end) = ParseDescriptorRange(request.params[1]);
+ }
+
+ FlatSigningProvider key_provider;
+ auto desc = Parse(desc_str, key_provider, /* require_checksum = */ true);
+ if (!desc) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor"));
+ }
+
+ if (!desc->IsRange() && request.params.size() > 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
+ }
+
+ if (desc->IsRange() && request.params.size() == 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified for a ranged descriptor");
+ }
+
+ UniValue addresses(UniValue::VARR);
+
+ for (int i = range_begin; i <= range_end; ++i) {
+ FlatSigningProvider provider;
+ std::vector<CScript> scripts;
+ if (!desc->Expand(i, key_provider, scripts, provider)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys"));
+ }
+
+ for (const CScript &script : scripts) {
+ CTxDestination dest;
+ if (!ExtractDestination(script, dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Descriptor does not have a corresponding address"));
+ }
+
+ addresses.push_back(EncodeDestination(dest));
+ }
+ }
+
+ // This should not be possible, but an assert seems overkill:
+ if (addresses.empty()) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result");
+ }
+
+ return addresses;
+}
+
static UniValue verifymessage(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3)
@@ -149,9 +259,9 @@ static UniValue verifymessage(const JSONRPCRequest& request)
RPCHelpMan{"verifymessage",
"\nVerify a signed message\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to use for the signature."},
- {"signature", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The signature provided by the signer in base 64 encoding (see signmessage)."},
- {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message that was signed."},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to use for the signature."},
+ {"signature", RPCArg::Type::STR, RPCArg::Optional::NO, "The signature provided by the signer in base 64 encoding (see signmessage)."},
+ {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message that was signed."},
},
RPCResult{
"true|false (boolean) If the signature is verified or not.\n"
@@ -179,8 +289,8 @@ static UniValue verifymessage(const JSONRPCRequest& request)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
}
- const CKeyID *keyID = boost::get<CKeyID>(&destination);
- if (!keyID) {
+ const PKHash *pkhash = boost::get<PKHash>(&destination);
+ if (!pkhash) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
@@ -198,7 +308,7 @@ static UniValue verifymessage(const JSONRPCRequest& request)
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
return false;
- return (pubkey.GetID() == *keyID);
+ return (pubkey.GetID() == *pkhash);
}
static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
@@ -208,8 +318,8 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
RPCHelpMan{"signmessagewithprivkey",
"\nSign a message with the private key of an address\n",
{
- {"privkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The private key to sign the message with."},
- {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message to create a signature of."},
+ {"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key to sign the message with."},
+ {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."},
},
RPCResult{
"\"signature\" (string) The signature of the message encoded in base 64\n"
@@ -250,7 +360,7 @@ static UniValue setmocktime(const JSONRPCRequest& request)
RPCHelpMan{"setmocktime",
"\nSet the local time to given timestamp (-regtest only)\n",
{
- {"timestamp", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Unix seconds-since-epoch timestamp\n"
+ {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Unix seconds-since-epoch timestamp\n"
" Pass 0 to go back to using the system time."},
},
RPCResults{},
@@ -316,7 +426,7 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request)
RPCHelpMan{"getmemoryinfo",
"Returns an object containing information about memory usage.\n",
{
- {"mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"stats\"", "determines what kind of information is returned.\n"
+ {"mode", RPCArg::Type::STR, /* default */ "\"stats\"", "determines what kind of information is returned.\n"
" - \"stats\" returns general statistics about memory usage in the daemon.\n"
" - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc 2.10+)."},
},
@@ -366,9 +476,9 @@ static void EnableOrDisableLogCategories(UniValue cats, bool enable) {
bool success;
if (enable) {
- success = g_logger->EnableCategory(cat);
+ success = LogInstance().EnableCategory(cat);
} else {
- success = g_logger->DisableCategory(cat);
+ success = LogInstance().DisableCategory(cat);
}
if (!success) {
@@ -393,13 +503,13 @@ UniValue logging(const JSONRPCRequest& request)
" - \"none\", \"0\" : even if other logging categories are specified, ignore all of them.\n"
,
{
- {"include", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of categories to add debug logging",
+ {"include", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of categories to add debug logging",
{
- {"include_category", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "the valid logging category"},
+ {"include_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"},
}},
- {"exclude", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of categories to remove debug logging",
+ {"exclude", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of categories to remove debug logging",
{
- {"exclude_category", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "the valid logging category"},
+ {"exclude_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"},
}},
},
RPCResult{
@@ -415,14 +525,14 @@ UniValue logging(const JSONRPCRequest& request)
}.ToString());
}
- uint32_t original_log_categories = g_logger->GetCategoryMask();
+ uint32_t original_log_categories = LogInstance().GetCategoryMask();
if (request.params[0].isArray()) {
EnableOrDisableLogCategories(request.params[0], true);
}
if (request.params[1].isArray()) {
EnableOrDisableLogCategories(request.params[1], false);
}
- uint32_t updated_log_categories = g_logger->GetCategoryMask();
+ uint32_t updated_log_categories = LogInstance().GetCategoryMask();
uint32_t changed_log_categories = original_log_categories ^ updated_log_categories;
// Update libevent logging if BCLog::LIBEVENT has changed.
@@ -431,8 +541,8 @@ UniValue logging(const JSONRPCRequest& request)
// Throw an error if the user has explicitly asked to change only the libevent
// flag and it failed.
if (changed_log_categories & BCLog::LIBEVENT) {
- if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) {
- g_logger->DisableCategory(BCLog::LIBEVENT);
+ if (!UpdateHTTPServerLogging(LogInstance().WillLogCategory(BCLog::LIBEVENT))) {
+ LogInstance().DisableCategory(BCLog::LIBEVENT);
if (changed_log_categories == BCLog::LIBEVENT) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1.");
}
@@ -473,6 +583,8 @@ static const CRPCCommand commands[] =
{ "control", "logging", &logging, {"include", "exclude"}},
{ "util", "validateaddress", &validateaddress, {"address"} },
{ "util", "createmultisig", &createmultisig, {"nrequired","keys","address_type"} },
+ { "util", "deriveaddresses", &deriveaddresses, {"descriptor", "range"} },
+ { "util", "getdescriptorinfo", &getdescriptorinfo, {"descriptor"} },
{ "util", "verifymessage", &verifymessage, {"address","signature","message"} },
{ "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} },
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index b50038b085..d993a88458 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -5,18 +5,17 @@
#include <rpc/server.h>
#include <banman.h>
-#include <chainparams.h>
#include <clientversion.h>
#include <core_io.h>
#include <net.h>
#include <net_processing.h>
#include <netbase.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <rpc/protocol.h>
#include <rpc/util.h>
#include <sync.h>
#include <timedata.h>
-#include <ui_interface.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <validation.h>
@@ -221,8 +220,8 @@ static UniValue addnode(const JSONRPCRequest& request)
"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",
{
- {"node", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The node (see getpeerinfo for nodes)"},
- {"command", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "'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"},
+ {"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The node (see getpeerinfo for nodes)"},
+ {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'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"},
},
RPCResults{},
RPCExamples{
@@ -266,8 +265,8 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
"\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
"\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n",
{
- {"address", RPCArg::Type::STR, /* opt */ true, /* default_val */ "fallback to nodeid", "The IP address/port of the node"},
- {"nodeid", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to address", "The node ID (see getpeerinfo for node IDs)"},
+ {"address", RPCArg::Type::STR, /* default */ "fallback to nodeid", "The IP address/port of the node"},
+ {"nodeid", RPCArg::Type::NUM, /* default */ "fallback to address", "The node ID (see getpeerinfo for node IDs)"},
},
RPCResults{},
RPCExamples{
@@ -311,7 +310,7 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
"\nReturns information about the given added node, or all added nodes\n"
"(note that onetry addnodes are not listed here)\n",
{
- {"node", RPCArg::Type::STR, /* opt */ true, /* default_val */ "all nodes", "If provided, return information about this specific node, otherwise all nodes are returned."},
+ {"node", RPCArg::Type::STR, /* default */ "all nodes", "If provided, return information about this specific node, otherwise all nodes are returned."},
},
RPCResult{
"[\n"
@@ -495,7 +494,7 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
obj.pushKV("protocolversion",PROTOCOL_VERSION);
if(g_connman)
obj.pushKV("localservices", strprintf("%016x", g_connman->GetLocalServices()));
- obj.pushKV("localrelay", fRelayTxes);
+ obj.pushKV("localrelay", g_relay_txes);
obj.pushKV("timeoffset", GetTimeOffset());
if (g_connman) {
obj.pushKV("networkactive", g_connman->GetNetworkActive());
@@ -523,19 +522,13 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
static UniValue setban(const JSONRPCRequest& request)
{
- std::string strCommand;
- if (!request.params[1].isNull())
- strCommand = request.params[1].get_str();
- if (request.fHelp || request.params.size() < 2 ||
- (strCommand != "add" && strCommand != "remove"))
- throw std::runtime_error(
- RPCHelpMan{"setban",
+ const RPCHelpMan help{"setban",
"\nAttempts to add or remove an IP/Subnet from the banned list.\n",
{
- {"subnet", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)"},
- {"command", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list"},
- {"bantime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)"},
- {"absolute", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If set, the bantime must be an absolute timestamp in seconds since epoch (Jan 1 1970 GMT)"},
+ {"subnet", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)"},
+ {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list"},
+ {"bantime", RPCArg::Type::NUM, /* default */ "0", "time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)"},
+ {"absolute", RPCArg::Type::BOOL, /* default */ "false", "If set, the bantime must be an absolute timestamp in seconds since epoch (Jan 1 1970 GMT)"},
},
RPCResults{},
RPCExamples{
@@ -543,7 +536,13 @@ static UniValue setban(const JSONRPCRequest& request)
+ HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
+ HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")
},
- }.ToString());
+ };
+ std::string strCommand;
+ if (!request.params[1].isNull())
+ strCommand = request.params[1].get_str();
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size()) || (strCommand != "add" && strCommand != "remove")) {
+ throw std::runtime_error(help.ToString());
+ }
if (!g_banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
}
@@ -667,7 +666,7 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
RPCHelpMan{"setnetworkactive",
"\nDisable/enable all p2p network activity.\n",
{
- {"state", RPCArg::Type::BOOL, /* opt */ false, /* default_val */ "", "true to enable networking, false to disable"},
+ {"state", RPCArg::Type::BOOL, RPCArg::Optional::NO, "true to enable networking, false to disable"},
},
RPCResults{},
RPCExamples{""},
@@ -691,7 +690,7 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
RPCHelpMan{"getnodeaddresses",
"\nReturn known addresses which can potentially be used to find new nodes in the network\n",
{
- {"count", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "How many addresses to return. Limited to the smaller of " + std::to_string(ADDRMAN_GETADDR_MAX) + " or " + std::to_string(ADDRMAN_GETADDR_MAX_PCT) + "% of all known addresses."},
+ {"count", RPCArg::Type::NUM, /* default */ "1", "How many addresses to return. Limited to the smaller of " + std::to_string(ADDRMAN_GETADDR_MAX) + " or " + std::to_string(ADDRMAN_GETADDR_MAX_PCT) + "% of all known addresses."},
},
RPCResult{
"[\n"
diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp
index 23999b305a..33b0130a94 100644
--- a/src/rpc/protocol.cpp
+++ b/src/rpc/protocol.cpp
@@ -10,7 +10,6 @@
#include <util/system.h>
#include <util/strencodings.h>
#include <util/time.h>
-#include <version.h>
/**
* JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index ac2e0ff4ee..9da24afe79 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,32 +9,39 @@
#include <consensus/validation.h>
#include <core_io.h>
#include <index/txindex.h>
-#include <init.h>
#include <key_io.h>
#include <keystore.h>
#include <merkleblock.h>
-#include <net.h>
-#include <policy/policy.h>
+#include <node/coin.h>
+#include <node/psbt.h>
+#include <node/transaction.h>
#include <policy/rbf.h>
#include <primitives/transaction.h>
-#include <rpc/rawtransaction.h>
+#include <psbt.h>
+#include <rpc/rawtransaction_util.h>
#include <rpc/server.h>
#include <rpc/util.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 <util/moneystr.h>
#include <util/strencodings.h>
#include <validation.h>
#include <validationinterface.h>
-#include <future>
+
+#include <numeric>
#include <stdint.h>
#include <univalue.h>
+/** High fee for sendrawtransaction and testmempoolaccept.
+ * By default, transaction with a fee higher than this will be rejected by the
+ * RPCs. This can be overridden with the maxfeerate argument.
+ */
+constexpr static CAmount DEFAULT_MAX_RAW_TX_FEE{COIN / 10};
static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
{
@@ -51,8 +58,8 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
entry.pushKV("blockhash", hashBlock.GetHex());
CBlockIndex* pindex = LookupBlockIndex(hashBlock);
if (pindex) {
- if (chainActive.Contains(pindex)) {
- entry.pushKV("confirmations", 1 + chainActive.Height() - pindex->nHeight);
+ if (::ChainActive().Contains(pindex)) {
+ entry.pushKV("confirmations", 1 + ::ChainActive().Height() - pindex->nHeight);
entry.pushKV("time", pindex->GetBlockTime());
entry.pushKV("blocktime", pindex->GetBlockTime());
}
@@ -64,23 +71,24 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
static UniValue getrawtransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
- throw std::runtime_error(
- RPCHelpMan{"getrawtransaction",
+ const RPCHelpMan help{
+ "getrawtransaction",
+ "\nReturn the raw transaction data.\n"
+
"\nBy default this function only works for mempool transactions. When called with a blockhash\n"
"argument, getrawtransaction will return the transaction if the specified block is available and\n"
"the transaction is found in that block. When called without a blockhash argument, getrawtransaction\n"
"will return the transaction if it is in the mempool, or if -txindex is enabled and the transaction\n"
"is in a block in the blockchain.\n"
- "\nReturn the raw transaction data.\n"
- "\nIf verbose is 'true', returns an Object with information about 'txid'.\n"
- "If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n"
- ,
+ "\nHint: Use gettransaction for wallet transactions.\n"
+
+ "\nIf verbose is 'true', returns an Object with information about 'txid'.\n"
+ "If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
- {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If false, return a string, otherwise return a json object"},
- {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "null", "The block in which to look for the transaction"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"verbose", RPCArg::Type::BOOL, /* default */ "false", "If false, return a string, otherwise return a json object"},
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "The block in which to look for the transaction"},
},
{
RPCResult{"if verbose is not set or set to false",
@@ -141,7 +149,11 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("getrawtransaction", "\"mytxid\" false \"myblockhash\"")
+ HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
bool in_active_chain = true;
uint256 hash = ParseHashV(request.params[0], "parameter 1");
@@ -166,7 +178,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
if (!blockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found");
}
- in_active_chain = chainActive.Contains(blockindex);
+ in_active_chain = ::ChainActive().Contains(blockindex);
}
bool f_txindex_ready = false;
@@ -184,7 +196,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
}
errmsg = "No such transaction found in the provided block";
} else if (!g_txindex) {
- errmsg = "No such mempool transaction. Use -txindex to enable blockchain transaction queries";
+ errmsg = "No such mempool transaction. Use -txindex or provide a block hash to enable blockchain transaction queries";
} else if (!f_txindex_ready) {
errmsg = "No such mempool transaction. Blockchain transactions are still in the process of being indexed";
} else {
@@ -214,12 +226,12 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
"you need to maintain a transaction index, using the -txindex command line option or\n"
"specify the block in which the transaction is included manually (by blockhash).\n",
{
- {"txids", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of txids to filter",
+ {"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of txids to filter",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A transaction hash"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"},
},
},
- {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "null", "If specified, looks for txid in the block with this hash"},
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "If specified, looks for txid in the block with this hash"},
},
RPCResult{
"\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n"
@@ -256,7 +268,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
for (const auto& tx : setTxids) {
const Coin& coin = AccessByTxid(*pcoinsTip, tx);
if (!coin.IsSpent()) {
- pblockindex = chainActive[coin.nHeight];
+ pblockindex = ::ChainActive()[coin.nHeight];
break;
}
}
@@ -307,7 +319,7 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
"\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n"
"and throwing an RPC error if the block is not in our best chain\n",
{
- {"proof", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded proof generated by gettxoutproof"},
+ {"proof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded proof generated by gettxoutproof"},
},
RPCResult{
"[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n"
@@ -330,7 +342,7 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
LOCK(cs_main);
const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash());
- if (!pindex || !chainActive.Contains(pindex) || pindex->nTx == 0) {
+ if (!pindex || !::ChainActive().Contains(pindex) || pindex->nTx == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
}
@@ -344,119 +356,6 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
return res;
}
-CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
-{
- if (inputs_in.isNull() || outputs_in.isNull())
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
-
- UniValue inputs = inputs_in.get_array();
- const bool outputs_is_obj = outputs_in.isObject();
- UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
-
- CMutableTransaction rawTx;
-
- if (!locktime.isNull()) {
- int64_t nLockTime = locktime.get_int64();
- if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
- rawTx.nLockTime = nLockTime;
- }
-
- bool rbfOptIn = rbf.isTrue();
-
- for (unsigned int idx = 0; idx < inputs.size(); idx++) {
- const UniValue& input = inputs[idx];
- const UniValue& o = input.get_obj();
-
- uint256 txid = ParseHashO(o, "txid");
-
- const UniValue& vout_v = find_value(o, "vout");
- if (!vout_v.isNum())
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
- int nOutput = vout_v.get_int();
- if (nOutput < 0)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
-
- uint32_t nSequence;
- if (rbfOptIn) {
- nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
- } else if (rawTx.nLockTime) {
- nSequence = CTxIn::SEQUENCE_FINAL - 1;
- } else {
- nSequence = CTxIn::SEQUENCE_FINAL;
- }
-
- // set the sequence number if passed in the parameters object
- const UniValue& sequenceObj = find_value(o, "sequence");
- if (sequenceObj.isNum()) {
- int64_t seqNr64 = sequenceObj.get_int64();
- if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
- } else {
- nSequence = (uint32_t)seqNr64;
- }
- }
-
- CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
-
- rawTx.vin.push_back(in);
- }
-
- if (!outputs_is_obj) {
- // Translate array of key-value pairs into dict
- UniValue outputs_dict = UniValue(UniValue::VOBJ);
- for (size_t i = 0; i < outputs.size(); ++i) {
- const UniValue& output = outputs[i];
- if (!output.isObject()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected");
- }
- if (output.size() != 1) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key");
- }
- outputs_dict.pushKVs(output);
- }
- outputs = std::move(outputs_dict);
- }
-
- // Duplicate checking
- std::set<CTxDestination> destinations;
- bool has_data{false};
-
- for (const std::string& name_ : outputs.getKeys()) {
- if (name_ == "data") {
- if (has_data) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");
- }
- has_data = true;
- std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
-
- CTxOut out(0, CScript() << OP_RETURN << data);
- rawTx.vout.push_back(out);
- } else {
- CTxDestination destination = DecodeDestination(name_);
- if (!IsValidDestination(destination)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
- }
-
- if (!destinations.insert(destination).second) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
- }
-
- CScript scriptPubKey = GetScriptForDestination(destination);
- CAmount nAmount = AmountFromValue(outputs[name_]);
-
- CTxOut out(nAmount, scriptPubKey);
- rawTx.vout.push_back(out);
- }
- }
-
- if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
- }
-
- return rawTx;
-}
-
static UniValue createrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
@@ -468,36 +367,36 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
"Note that the transaction's inputs are not signed, and\n"
"it is not stored in the wallet or transmitted to the network.\n",
{
- {"inputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of json objects",
+ {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of json objects",
{
- {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
- {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
- {"sequence", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
+ {"sequence", RPCArg::Type::NUM, /* default */ "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"},
},
},
},
},
- {"outputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
+ {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
"That is, each address can only appear once and there can only be one 'data' object.\n"
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.",
{
- {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"address", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT},
+ {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT},
},
},
- {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"data", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
+ {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
},
},
},
},
- {"locktime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
- {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Marks this transaction as BIP125-replaceable.\n"
+ {"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "false", "Marks this transaction as BIP125-replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."},
},
RPCResult{
@@ -527,14 +426,17 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
static UniValue decoderawtransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
- RPCHelpMan{"decoderawtransaction",
+ const RPCHelpMan help{"decoderawtransaction",
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n",
{
- {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction hex string"},
- {"iswitness", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction\n"
- " If iswitness is not present, heuristic tests will be used in decoding"},
+ {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction hex string"},
+ {"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
+ "If iswitness is not present, heuristic tests will be used in decoding.\n"
+ "If true, only witness deserialization will be tried.\n"
+ "If false, only non-witness deserialization will be tried.\n"
+ "This boolean should reflect whether the transaction has inputs\n"
+ "(e.g. fully valid, or on-chain transactions), if known by the caller."
+ },
},
RPCResult{
"{\n"
@@ -581,7 +483,11 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
HelpExampleCli("decoderawtransaction", "\"hexstring\"")
+ HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
@@ -600,33 +506,54 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
return result;
}
+static std::string GetAllOutputTypes()
+{
+ std::string ret;
+ for (int i = TX_NONSTANDARD; i <= TX_WITNESS_UNKNOWN; ++i) {
+ if (i != TX_NONSTANDARD) ret += ", ";
+ ret += GetTxnOutputType(static_cast<txnouttype>(i));
+ }
+ return ret;
+}
+
static UniValue decodescript(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
- RPCHelpMan{"decodescript",
+ const RPCHelpMan help{"decodescript",
"\nDecode a hex-encoded script.\n",
{
- {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded script"},
+ {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded script"},
},
RPCResult{
"{\n"
- " \"asm\":\"asm\", (string) Script public key\n"
- " \"hex\":\"hex\", (string) hex-encoded public key\n"
- " \"type\":\"type\", (string) The output type\n"
- " \"reqSigs\": n, (numeric) The required signatures\n"
- " \"addresses\": [ (json array of string)\n"
- " \"address\" (string) bitcoin address\n"
+ " \"asm\":\"asm\", (string) Script public key\n"
+ " \"type\":\"type\", (string) The output type (e.g. "+GetAllOutputTypes()+")\n"
+ " \"reqSigs\": n, (numeric) The required signatures\n"
+ " \"addresses\": [ (json array of string)\n"
+ " \"address\" (string) bitcoin address\n"
" ,...\n"
" ],\n"
- " \"p2sh\",\"address\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n"
+ " \"p2sh\":\"str\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n"
+ " \"segwit\": { (json object) Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness).\n"
+ " \"asm\":\"str\", (string) String representation of the script public key\n"
+ " \"hex\":\"hexstr\", (string) Hex string of the script public key\n"
+ " \"type\":\"str\", (string) The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)\n"
+ " \"reqSigs\": n, (numeric) The required signatures (always 1)\n"
+ " \"addresses\": [ (json array of string) (always length 1)\n"
+ " \"address\" (string) segwit address\n"
+ " ,...\n"
+ " ],\n"
+ " \"p2sh-segwit\":\"str\" (string) address of the P2SH script wrapping this witness redeem script.\n"
"}\n"
},
RPCExamples{
HelpExampleCli("decodescript", "\"hexstring\"")
+ HelpExampleRpc("decodescript", "\"hexstring\"")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
RPCTypeCheck(request.params, {UniValue::VSTR});
@@ -638,7 +565,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
} else {
// Empty scripts are valid
}
- ScriptPubKeyToUniv(script, r, false);
+ ScriptPubKeyToUniv(script, r, /* fIncludeHex */ false);
UniValue type;
type = find_value(r, "type");
@@ -646,7 +573,7 @@ static 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.pushKV("p2sh", EncodeDestination(CScriptID(script)));
+ r.pushKV("p2sh", EncodeDestination(ScriptHash(script)));
// P2SH and witness programs cannot be wrapped in P2WSH, if this script
// is a witness program, don't return addresses for a segwit programs.
if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") {
@@ -672,8 +599,8 @@ static UniValue decodescript(const JSONRPCRequest& request)
// Newer segwit program versions should be considered when then become available.
segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script));
}
- ScriptPubKeyToUniv(segwitScr, sr, true);
- sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr)));
+ ScriptPubKeyToUniv(segwitScr, sr, /* fIncludeHex */ true);
+ sr.pushKV("p2sh-segwit", EncodeDestination(ScriptHash(segwitScr)));
r.pushKV("segwit", sr);
}
}
@@ -681,23 +608,6 @@ static UniValue decodescript(const JSONRPCRequest& request)
return r;
}
-/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */
-static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage)
-{
- UniValue entry(UniValue::VOBJ);
- entry.pushKV("txid", txin.prevout.hash.ToString());
- entry.pushKV("vout", (uint64_t)txin.prevout.n);
- UniValue witness(UniValue::VARR);
- for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) {
- witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end()));
- }
- entry.pushKV("witness", witness);
- entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
- entry.pushKV("sequence", (uint64_t)txin.nSequence);
- entry.pushKV("error", strMessage);
- vErrorsRet.push_back(entry);
-}
-
static UniValue combinerawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
@@ -707,9 +617,9 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
"The combined transaction may be another partially signed transaction or a \n"
"fully signed transaction.",
{
- {"txs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of hex strings of partially signed transactions",
+ {"txs", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of hex strings of partially signed transactions",
{
- {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A transaction hash"},
+ {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"},
},
},
},
@@ -782,145 +692,6 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
return EncodeHexTx(CTransaction(mergedTx));
}
-UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType)
-{
- // Fetch previous transactions (inputs):
- CCoinsView viewDummy;
- CCoinsViewCache view(&viewDummy);
- {
- LOCK2(cs_main, mempool.cs);
- CCoinsViewCache &viewChain = *pcoinsTip;
- CCoinsViewMemPool viewMempool(&viewChain, mempool);
- view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
-
- for (const CTxIn& txin : mtx.vin) {
- view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
- }
-
- view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
- }
-
- // Add previous txouts given in the RPC call:
- if (!prevTxsUnival.isNull()) {
- UniValue prevTxs = prevTxsUnival.get_array();
- for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) {
- const UniValue& p = prevTxs[idx];
- if (!p.isObject()) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
- }
-
- UniValue prevOut = p.get_obj();
-
- RPCTypeCheckObj(prevOut,
- {
- {"txid", UniValueType(UniValue::VSTR)},
- {"vout", UniValueType(UniValue::VNUM)},
- {"scriptPubKey", UniValueType(UniValue::VSTR)},
- });
-
- uint256 txid = ParseHashO(prevOut, "txid");
-
- int nOut = find_value(prevOut, "vout").get_int();
- if (nOut < 0) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
- }
-
- COutPoint out(txid, nOut);
- std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
- CScript scriptPubKey(pkData.begin(), pkData.end());
-
- {
- const Coin& coin = view.AccessCoin(out);
- if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) {
- std::string err("Previous output scriptPubKey mismatch:\n");
- err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
- ScriptToAsmStr(scriptPubKey);
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
- }
- Coin newcoin;
- newcoin.out.scriptPubKey = scriptPubKey;
- newcoin.out.nValue = MAX_MONEY;
- if (prevOut.exists("amount")) {
- newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
- }
- newcoin.nHeight = 1;
- view.AddCoin(out, std::move(newcoin), true);
- }
-
- // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
- if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
- RPCTypeCheckObj(prevOut,
- {
- {"redeemScript", UniValueType(UniValue::VSTR)},
- });
- UniValue v = find_value(prevOut, "redeemScript");
- if (!v.isNull()) {
- std::vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
- CScript redeemScript(rsData.begin(), rsData.end());
- keystore->AddCScript(redeemScript);
- // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
- keystore->AddCScript(GetScriptForWitness(redeemScript));
- }
- }
- }
- }
-
- int nHashType = ParseSighashString(hashType);
-
- bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
-
- // Script verification errors
- UniValue vErrors(UniValue::VARR);
-
- // Use CTransaction for the constant parts of the
- // transaction to avoid rehashing.
- const CTransaction txConst(mtx);
- // Sign what we can:
- for (unsigned int i = 0; i < mtx.vin.size(); i++) {
- CTxIn& txin = mtx.vin[i];
- const Coin& coin = view.AccessCoin(txin.prevout);
- if (coin.IsSpent()) {
- TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
- continue;
- }
- const CScript& prevPubKey = coin.out.scriptPubKey;
- const CAmount& amount = coin.out.nValue;
-
- SignatureData sigdata = DataFromTransaction(mtx, i, coin.out);
- // Only sign SIGHASH_SINGLE if there's a corresponding output:
- if (!fHashSingle || (i < mtx.vout.size())) {
- ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
- }
-
- UpdateInput(txin, sigdata);
-
- // amount must be specified for valid segwit signature
- if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
- throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin.out.ToString()));
- }
-
- ScriptError serror = SCRIPT_ERR_OK;
- if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &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();
-
- UniValue result(UniValue::VOBJ);
- result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
- result.pushKV("complete", fComplete);
- if (!vErrors.empty()) {
- result.pushKV("errors", vErrors);
- }
-
- return result;
-}
-
static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
@@ -932,26 +703,27 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
"The third optional argument (may be null) is an array of previous transaction outputs that\n"
"this transaction depends on but may not yet be in the block chain.\n",
{
- {"hexstring", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction hex string"},
- {"privkeys", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of base58-encoded private keys for signing",
+ {"hexstring", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction hex string"},
+ {"privkeys", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of base58-encoded private keys for signing",
{
- {"privatekey", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "private key in base58-encoding"},
+ {"privatekey", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "private key in base58-encoding"},
},
},
- {"prevtxs", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of previous dependent transaction outputs",
+ {"prevtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of previous dependent transaction outputs",
{
- {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
- {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
- {"scriptPubKey", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "script key"},
- {"redeemScript", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "omitted", "(required for P2SH or P2WSH) redeem script"},
- {"amount", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The amount spent"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
+ {"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
+ {"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
+ {"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
+ {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "(required for Segwit inputs) the amount spent"},
},
},
},
},
- {"sighashtype", RPCArg::Type::STR, /* opt */ true, /* default_val */ "ALL", "The signature hash type. Must be one of:\n"
+ {"sighashtype", RPCArg::Type::STR, /* default */ "ALL", "The signature hash type. Must be one of:\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -977,8 +749,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
"}\n"
},
RPCExamples{
- HelpExampleCli("signrawtransactionwithkey", "\"myhex\"")
- + HelpExampleRpc("signrawtransactionwithkey", "\"myhex\"")
+ HelpExampleCli("signrawtransactionwithkey", "\"myhex\" \"[\\\"key1\\\",\\\"key2\\\"]\"")
+ + HelpExampleRpc("signrawtransactionwithkey", "\"myhex\", \"[\\\"key1\\\",\\\"key2\\\"]\"")
},
}.ToString());
@@ -1000,27 +772,26 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
keystore.AddKey(key);
}
- return SignTransaction(*g_rpc_interfaces->chain, mtx, request.params[2], &keystore, true, request.params[3]);
-}
+ // Fetch previous transactions (inputs):
+ std::map<COutPoint, Coin> coins;
+ for (const CTxIn& txin : mtx.vin) {
+ coins[txin.prevout]; // Create empty map entry keyed by prevout.
+ }
+ FindCoins(coins);
-UniValue signrawtransaction(const JSONRPCRequest& request)
-{
- // This method should be removed entirely in V0.19, along with the entries in the
- // CRPCCommand table and rpc/client.cpp.
- throw JSONRPCError(RPC_METHOD_DEPRECATED, "signrawtransaction was removed in v0.18.\n"
- "Clients should transition to using signrawtransactionwithkey and signrawtransactionwithwallet");
+ return SignTransaction(mtx, request.params[2], &keystore, coins, true, request.params[3]);
}
static UniValue sendrawtransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
- RPCHelpMan{"sendrawtransaction",
+ const RPCHelpMan help{"sendrawtransaction",
"\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
"\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n",
{
- {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex string of the raw transaction"},
- {"allowhighfees", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Allow high fees"},
+ {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
+ {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE),
+ "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
+ "/kB.\nSet to 0 to accept any fee rate.\n"},
},
RPCResult{
"\"hex\" (string) The transaction hash in hex\n"
@@ -1035,96 +806,60 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
},
- }.ToString());
+ };
- std::promise<void> promise;
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
- RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
+ RPCTypeCheck(request.params, {
+ UniValue::VSTR,
+ UniValueType(), // NUM or BOOL, checked later
+ });
// parse hex string from parameter
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
- const uint256& hashTx = tx->GetHash();
-
- CAmount nMaxRawTxFee = maxTxFee;
- 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++) {
- const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
- fHaveChain = !existingCoin.IsSpent();
- }
- bool fHaveMempool = mempool.exists(hashTx);
- if (!fHaveMempool && !fHaveChain) {
- // push to local node and sync with wallets
- CValidationState state;
- bool fMissingInputs;
- if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
- nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) {
- if (state.IsInvalid()) {
- throw JSONRPCError(RPC_TRANSACTION_REJECTED, FormatStateMessage(state));
- } else {
- if (fMissingInputs) {
- throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
- }
- throw JSONRPCError(RPC_TRANSACTION_ERROR, FormatStateMessage(state));
- }
- } 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();
+ CAmount max_raw_tx_fee = DEFAULT_MAX_RAW_TX_FEE;
+ // TODO: temporary migration code for old clients. Remove in v0.20
+ if (request.params[1].isBool()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0.");
+ } else if (!request.params[1].isNull()) {
+ size_t weight = GetTransactionWeight(*tx);
+ CFeeRate fr(AmountFromValue(request.params[1]));
+ // the +3/4 part rounds the value up, and is the same formula used when
+ // calculating the fee for a transaction
+ // (see GetVirtualTransactionSize)
+ max_raw_tx_fee = fr.GetFee((weight+3)/4);
}
- } // cs_main
-
- promise.get_future().wait();
-
- if(!g_connman)
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
-
- CInv inv(MSG_TX, hashTx);
- g_connman->ForEachNode([&inv](CNode* pnode)
- {
- pnode->PushInventory(inv);
- });
+ uint256 txid;
+ std::string err_string;
+ const TransactionError err = BroadcastTransaction(tx, txid, err_string, max_raw_tx_fee);
+ if (TransactionError::OK != err) {
+ throw JSONRPCTransactionError(err, err_string);
+ }
- return hashTx.GetHex();
+ return txid.GetHex();
}
static UniValue testmempoolaccept(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
- throw std::runtime_error(
- RPCHelpMan{"testmempoolaccept",
+ const RPCHelpMan help{"testmempoolaccept",
"\nReturns result of mempool acceptance tests indicating if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
"\nThis checks if the transaction violates the consensus or policy rules.\n"
"\nSee sendrawtransaction call.\n",
{
- {"rawtxs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "An array of hex strings of raw transactions.\n"
+ {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.\n"
" Length must be one for now.",
{
- {"rawtx", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", ""},
+ {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
},
},
- {"allowhighfees", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Allow high fees"},
+ {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
},
RPCResult{
"[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n"
@@ -1146,10 +881,17 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
}
- RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VBOOL});
+ RPCTypeCheck(request.params, {
+ UniValue::VARR,
+ UniValueType(), // NUM or BOOL, checked later
+ });
+
if (request.params[0].get_array().size() != 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Array must contain exactly one raw transaction for now");
}
@@ -1161,9 +903,17 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
const uint256& tx_hash = tx->GetHash();
- CAmount max_raw_tx_fee = ::maxTxFee;
- if (!request.params[1].isNull() && request.params[1].get_bool()) {
- max_raw_tx_fee = 0;
+ CAmount max_raw_tx_fee = DEFAULT_MAX_RAW_TX_FEE;
+ // TODO: temporary migration code for old clients. Remove in v0.20
+ if (request.params[1].isBool()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0.");
+ } else if (!request.params[1].isNull()) {
+ size_t weight = GetTransactionWeight(*tx);
+ CFeeRate fr(AmountFromValue(request.params[1]));
+ // the +3/4 part rounds the value up, and is the same formula used when
+ // calculating the fee for a transaction
+ // (see GetVirtualTransactionSize)
+ max_raw_tx_fee = fr.GetFee((weight+3)/4);
}
UniValue result(UniValue::VARR);
@@ -1219,7 +969,7 @@ UniValue decodepsbt(const JSONRPCRequest& request)
RPCHelpMan{"decodepsbt",
"\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n",
{
- {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The PSBT base64 string"},
+ {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The PSBT base64 string"},
},
RPCResult{
"{\n"
@@ -1319,7 +1069,7 @@ UniValue decodepsbt(const JSONRPCRequest& request)
// Unserialize the transactions
PartiallySignedTransaction psbtx;
std::string error;
- if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) {
+ if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
}
@@ -1495,9 +1245,9 @@ UniValue combinepsbt(const JSONRPCRequest& request)
"\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
"Implements the Combiner role.\n",
{
- {"txs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of base64 strings of partially signed transactions",
+ {"txs", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of base64 strings of partially signed transactions",
{
- {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "A base64 string of a PSBT"},
+ {"psbt", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "A base64 string of a PSBT"},
},
},
},
@@ -1514,29 +1264,24 @@ UniValue combinepsbt(const JSONRPCRequest& request)
// Unserialize the transactions
std::vector<PartiallySignedTransaction> psbtxs;
UniValue txs = request.params[0].get_array();
+ if (txs.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter 'txs' cannot be empty");
+ }
for (unsigned int i = 0; i < txs.size(); ++i) {
PartiallySignedTransaction psbtx;
std::string error;
- if (!DecodePSBT(psbtx, txs[i].get_str(), error)) {
+ if (!DecodeBase64PSBT(psbtx, txs[i].get_str(), error)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
}
psbtxs.push_back(psbtx);
}
- PartiallySignedTransaction merged_psbt(psbtxs[0]); // Copy the first one
-
- // Merge
- for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) {
- if (*it != merged_psbt) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "PSBTs do not refer to the same transactions.");
- }
- merged_psbt.Merge(*it);
- }
- if (!merged_psbt.IsSane()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Merged PSBT is inconsistent");
+ PartiallySignedTransaction merged_psbt;
+ const TransactionError error = CombinePSBTs(merged_psbt, psbtxs);
+ if (error != TransactionError::OK) {
+ throw JSONRPCTransactionError(error);
}
- UniValue result(UniValue::VOBJ);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << merged_psbt;
return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
@@ -1552,8 +1297,8 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
"created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n"
"Implements the Finalizer and Extractor roles.\n",
{
- {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "A base64 string of a PSBT"},
- {"extract", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "If true and the transaction is complete,\n"
+ {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
+ {"extract", RPCArg::Type::BOOL, /* default */ "true", "If true and the transaction is complete,\n"
" extract and return the complete transaction in normal network serialization instead of the PSBT."},
},
RPCResult{
@@ -1574,33 +1319,27 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
// Unserialize the transactions
PartiallySignedTransaction psbtx;
std::string error;
- if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) {
+ if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
}
- // Finalize input signatures -- in case we have partial signatures that add up to a complete
- // signature, but have not combined them yet (e.g. because the combiner that created this
- // PartiallySignedTransaction did not understand them), this will combine them into a final
- // script.
- bool complete = true;
- for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL);
- }
+ bool extract = request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool());
+
+ CMutableTransaction mtx;
+ bool complete = FinalizeAndExtractPSBT(psbtx, mtx);
UniValue result(UniValue::VOBJ);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- bool extract = request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool());
+ std::string result_str;
+
if (complete && extract) {
- CMutableTransaction mtx(*psbtx.tx);
- for (unsigned int i = 0; i < mtx.vin.size(); ++i) {
- mtx.vin[i].scriptSig = psbtx.inputs[i].final_script_sig;
- mtx.vin[i].scriptWitness = psbtx.inputs[i].final_script_witness;
- }
ssTx << mtx;
- result.pushKV("hex", HexStr(ssTx.str()));
+ result_str = HexStr(ssTx.str());
+ result.pushKV("hex", result_str);
} else {
ssTx << psbtx;
- result.pushKV("psbt", EncodeBase64(ssTx.str()));
+ result_str = EncodeBase64(ssTx.str());
+ result.pushKV("psbt", result_str);
}
result.pushKV("complete", complete);
@@ -1615,36 +1354,36 @@ UniValue createpsbt(const JSONRPCRequest& request)
"\nCreates a transaction in the Partially Signed Transaction format.\n"
"Implements the Creator role.\n",
{
- {"inputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of json objects",
+ {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of json objects",
{
- {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
- {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
- {"sequence", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
+ {"sequence", RPCArg::Type::NUM, /* default */ "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"},
},
},
},
},
- {"outputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
+ {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
"That is, each address can only appear once and there can only be one 'data' object.\n"
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.",
{
- {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"address", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT},
+ {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT},
},
},
- {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"data", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
+ {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
},
},
},
},
- {"locktime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
- {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Marks this transaction as BIP125 replaceable.\n"
+ {"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "false", "Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."},
},
RPCResult{
@@ -1685,19 +1424,20 @@ UniValue createpsbt(const JSONRPCRequest& request)
UniValue converttopsbt(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
- throw std::runtime_error(
- RPCHelpMan{"converttopsbt",
+ const RPCHelpMan help{"converttopsbt",
"\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n"
"createpsbt and walletcreatefundedpsbt should be used for new applications.\n",
{
- {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex string of a raw transaction"},
- {"permitsigdata", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If true, any signatures in the input will be discarded and conversion.\n"
+ {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of a raw transaction"},
+ {"permitsigdata", RPCArg::Type::BOOL, /* default */ "false", "If true, any signatures in the input will be discarded and conversion\n"
" will continue. If false, RPC will fail if any signatures are present."},
- {"iswitness", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
- " If iswitness is not present, heuristic tests will be used in decoding. If true, only witness deserializaion\n"
- " will be tried. If false, only non-witness deserialization will be tried. Only has an effect if\n"
- " permitsigdata is true."},
+ {"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
+ "If iswitness is not present, heuristic tests will be used in decoding.\n"
+ "If true, only witness deserialization will be tried.\n"
+ "If false, only non-witness deserialization will be tried.\n"
+ "This boolean should reflect whether the transaction has inputs\n"
+ "(e.g. fully valid, or on-chain transactions), if known by the caller."
+ },
},
RPCResult{
" \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
@@ -1708,8 +1448,11 @@ UniValue converttopsbt(const JSONRPCRequest& request)
"\nConvert the transaction to a PSBT\n"
+ HelpExampleCli("converttopsbt", "\"rawtransaction\"")
},
- }.ToString());
+ };
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VBOOL}, true);
@@ -1718,8 +1461,8 @@ UniValue converttopsbt(const JSONRPCRequest& request)
bool permitsigdata = request.params[1].isNull() ? false : request.params[1].get_bool();
bool witness_specified = !request.params[2].isNull();
bool iswitness = witness_specified ? request.params[2].get_bool() : false;
- bool try_witness = permitsigdata ? (witness_specified ? iswitness : true) : false;
- bool try_no_witness = permitsigdata ? (witness_specified ? !iswitness : true) : true;
+ const bool try_witness = witness_specified ? iswitness : true;
+ const bool try_no_witness = witness_specified ? !iswitness : true;
if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
@@ -1750,6 +1493,246 @@ UniValue converttopsbt(const JSONRPCRequest& request)
return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
}
+UniValue utxoupdatepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw std::runtime_error(
+ RPCHelpMan{"utxoupdatepsbt",
+ "\nUpdates a PSBT with witness UTXOs retrieved from the UTXO set or the mempool.\n",
+ {
+ {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
+ },
+ RPCResult {
+ " \"psbt\" (string) The base64-encoded partially signed transaction with inputs updated\n"
+ },
+ RPCExamples {
+ HelpExampleCli("utxoupdatepsbt", "\"psbt\"")
+ }}.ToString());
+ }
+
+ RPCTypeCheck(request.params, {UniValue::VSTR}, true);
+
+ // Unserialize the transactions
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ // Fetch previous transactions (inputs):
+ CCoinsView viewDummy;
+ CCoinsViewCache view(&viewDummy);
+ {
+ LOCK2(cs_main, mempool.cs);
+ CCoinsViewCache &viewChain = *pcoinsTip;
+ CCoinsViewMemPool viewMempool(&viewChain, mempool);
+ view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
+
+ for (const CTxIn& txin : psbtx.tx->vin) {
+ view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
+ }
+
+ view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
+ }
+
+ // Fill the inputs
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ if (input.non_witness_utxo || !input.witness_utxo.IsNull()) {
+ continue;
+ }
+
+ const Coin& coin = view.AccessCoin(psbtx.tx->vin[i].prevout);
+
+ std::vector<std::vector<unsigned char>> solutions_data;
+ txnouttype which_type = Solver(coin.out.scriptPubKey, solutions_data);
+ if (which_type == TX_WITNESS_V0_SCRIPTHASH || which_type == TX_WITNESS_V0_KEYHASH || which_type == TX_WITNESS_UNKNOWN) {
+ input.witness_utxo = coin.out;
+ }
+ }
+
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
+UniValue joinpsbts(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw std::runtime_error(
+ RPCHelpMan{"joinpsbts",
+ "\nJoins multiple distinct PSBTs with different inputs and outputs into one PSBT with inputs and outputs from all of the PSBTs\n"
+ "No input in any of the PSBTs can be in more than one of the PSBTs.\n",
+ {
+ {"txs", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of base64 strings of partially signed transactions",
+ {
+ {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
+ }}
+ },
+ RPCResult {
+ " \"psbt\" (string) The base64-encoded partially signed transaction\n"
+ },
+ RPCExamples {
+ HelpExampleCli("joinpsbts", "\"psbt\"")
+ }}.ToString());
+ }
+
+ RPCTypeCheck(request.params, {UniValue::VARR}, true);
+
+ // Unserialize the transactions
+ std::vector<PartiallySignedTransaction> psbtxs;
+ UniValue txs = request.params[0].get_array();
+
+ if (txs.size() <= 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "At least two PSBTs are required to join PSBTs.");
+ }
+
+ int32_t best_version = 1;
+ uint32_t best_locktime = 0xffffffff;
+ for (unsigned int i = 0; i < txs.size(); ++i) {
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodeBase64PSBT(psbtx, txs[i].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+ psbtxs.push_back(psbtx);
+ // Choose the highest version number
+ if (psbtx.tx->nVersion > best_version) {
+ best_version = psbtx.tx->nVersion;
+ }
+ // Choose the lowest lock time
+ if (psbtx.tx->nLockTime < best_locktime) {
+ best_locktime = psbtx.tx->nLockTime;
+ }
+ }
+
+ // Create a blank psbt where everything will be added
+ PartiallySignedTransaction merged_psbt;
+ merged_psbt.tx = CMutableTransaction();
+ merged_psbt.tx->nVersion = best_version;
+ merged_psbt.tx->nLockTime = best_locktime;
+
+ // Merge
+ for (auto& psbt : psbtxs) {
+ for (unsigned int i = 0; i < psbt.tx->vin.size(); ++i) {
+ if (!merged_psbt.AddInput(psbt.tx->vin[i], psbt.inputs[i])) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input %s:%d exists in multiple PSBTs", psbt.tx->vin[i].prevout.hash.ToString().c_str(), psbt.tx->vin[i].prevout.n));
+ }
+ }
+ for (unsigned int i = 0; i < psbt.tx->vout.size(); ++i) {
+ merged_psbt.AddOutput(psbt.tx->vout[i], psbt.outputs[i]);
+ }
+ merged_psbt.unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
+ }
+
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << merged_psbt;
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
+UniValue analyzepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw std::runtime_error(
+ RPCHelpMan{"analyzepsbt",
+ "\nAnalyzes and provides information about the current status of a PSBT and its inputs\n",
+ {
+ {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
+ },
+ RPCResult {
+ "{\n"
+ " \"inputs\" : [ (array of json objects)\n"
+ " {\n"
+ " \"has_utxo\" : true|false (boolean) Whether a UTXO is provided\n"
+ " \"is_final\" : true|false (boolean) Whether the input is finalized\n"
+ " \"missing\" : { (json object, optional) Things that are missing that are required to complete this input\n"
+ " \"pubkeys\" : [ (array, optional)\n"
+ " \"keyid\" (string) Public key ID, hash160 of the public key, of a public key whose BIP 32 derivation path is missing\n"
+ " ]\n"
+ " \"signatures\" : [ (array, optional)\n"
+ " \"keyid\" (string) Public key ID, hash160 of the public key, of a public key whose signature is missing\n"
+ " ]\n"
+ " \"redeemscript\" : \"hash\" (string, optional) Hash160 of the redeemScript that is missing\n"
+ " \"witnessscript\" : \"hash\" (string, optional) SHA256 of the witnessScript that is missing\n"
+ " }\n"
+ " \"next\" : \"role\" (string, optional) Role of the next person that this input needs to go to\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ " \"estimated_vsize\" : vsize (numeric, optional) Estimated vsize of the final signed transaction\n"
+ " \"estimated_feerate\" : feerate (numeric, optional) Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kB. Shown only if all UTXO slots in the PSBT have been filled.\n"
+ " \"fee\" : fee (numeric, optional) The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled.\n"
+ " \"next\" : \"role\" (string) Role of the next person that this psbt needs to go to\n"
+ "}\n"
+ },
+ RPCExamples {
+ HelpExampleCli("analyzepsbt", "\"psbt\"")
+ }}.ToString());
+ }
+
+ RPCTypeCheck(request.params, {UniValue::VSTR});
+
+ // Unserialize the transaction
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ PSBTAnalysis psbta = AnalyzePSBT(psbtx);
+
+ UniValue result(UniValue::VOBJ);
+ UniValue inputs_result(UniValue::VARR);
+ for (const auto& input : psbta.inputs) {
+ UniValue input_univ(UniValue::VOBJ);
+ UniValue missing(UniValue::VOBJ);
+
+ input_univ.pushKV("has_utxo", input.has_utxo);
+ input_univ.pushKV("is_final", input.is_final);
+ input_univ.pushKV("next", PSBTRoleName(input.next));
+
+ if (!input.missing_pubkeys.empty()) {
+ UniValue missing_pubkeys_univ(UniValue::VARR);
+ for (const CKeyID& pubkey : input.missing_pubkeys) {
+ missing_pubkeys_univ.push_back(HexStr(pubkey));
+ }
+ missing.pushKV("pubkeys", missing_pubkeys_univ);
+ }
+ if (!input.missing_redeem_script.IsNull()) {
+ missing.pushKV("redeemscript", HexStr(input.missing_redeem_script));
+ }
+ if (!input.missing_witness_script.IsNull()) {
+ missing.pushKV("witnessscript", HexStr(input.missing_witness_script));
+ }
+ if (!input.missing_sigs.empty()) {
+ UniValue missing_sigs_univ(UniValue::VARR);
+ for (const CKeyID& pubkey : input.missing_sigs) {
+ missing_sigs_univ.push_back(HexStr(pubkey));
+ }
+ missing.pushKV("signatures", missing_sigs_univ);
+ }
+ if (!missing.getKeys().empty()) {
+ input_univ.pushKV("missing", missing);
+ }
+ inputs_result.push_back(input_univ);
+ }
+ result.pushKV("inputs", inputs_result);
+
+ if (psbta.estimated_vsize != nullopt) {
+ result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize);
+ }
+ if (psbta.estimated_feerate != nullopt) {
+ result.pushKV("estimated_feerate", ValueFromAmount(psbta.estimated_feerate->GetFeePerK()));
+ }
+ if (psbta.fee != nullopt) {
+ result.pushKV("fee", ValueFromAmount(*psbta.fee));
+ }
+ result.pushKV("next", PSBTRoleName(psbta.next));
+
+ return result;
+}
+
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -1758,16 +1741,18 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
- { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
+ { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees|maxfeerate"} },
{ "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },
- { "hidden", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} },
{ "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
- { "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees"} },
+ { "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees|maxfeerate"} },
{ "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} },
{ "rawtransactions", "combinepsbt", &combinepsbt, {"txs"} },
{ "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
{ "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime","replaceable"} },
{ "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} },
+ { "rawtransactions", "utxoupdatepsbt", &utxoupdatepsbt, {"psbt"} },
+ { "rawtransactions", "joinpsbts", &joinpsbts, {"txs"} },
+ { "rawtransactions", "analyzepsbt", &analyzepsbt, {"psbt"} },
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
diff --git a/src/rpc/rawtransaction.h b/src/rpc/rawtransaction.h
deleted file mode 100644
index 52d701d1c3..0000000000
--- a/src/rpc/rawtransaction.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2017-2018 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_RPC_RAWTRANSACTION_H
-#define BITCOIN_RPC_RAWTRANSACTION_H
-
-class CBasicKeyStore;
-struct CMutableTransaction;
-class UniValue;
-
-namespace interfaces {
-class Chain;
-} // namespace interfaces
-
-/** Sign a transaction with the given keystore and previous transactions */
-UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
-
-/** Create a transaction from univalue parameters */
-CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
-
-#endif // BITCOIN_RPC_RAWTRANSACTION_H
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
new file mode 100644
index 0000000000..9c4cdc3a90
--- /dev/null
+++ b/src/rpc/rawtransaction_util.cpp
@@ -0,0 +1,282 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 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 <rpc/rawtransaction_util.h>
+
+#include <coins.h>
+#include <core_io.h>
+#include <key_io.h>
+#include <keystore.h>
+#include <policy/policy.h>
+#include <primitives/transaction.h>
+#include <rpc/protocol.h>
+#include <rpc/util.h>
+#include <tinyformat.h>
+#include <univalue.h>
+#include <util/rbf.h>
+#include <util/strencodings.h>
+
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
+{
+ if (inputs_in.isNull() || outputs_in.isNull())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
+
+ UniValue inputs = inputs_in.get_array();
+ const bool outputs_is_obj = outputs_in.isObject();
+ UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
+
+ CMutableTransaction rawTx;
+
+ if (!locktime.isNull()) {
+ int64_t nLockTime = locktime.get_int64();
+ if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
+ rawTx.nLockTime = nLockTime;
+ }
+
+ bool rbfOptIn = rbf.isTrue();
+
+ for (unsigned int idx = 0; idx < inputs.size(); idx++) {
+ const UniValue& input = inputs[idx];
+ const UniValue& o = input.get_obj();
+
+ uint256 txid = ParseHashO(o, "txid");
+
+ const UniValue& vout_v = find_value(o, "vout");
+ if (!vout_v.isNum())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
+ int nOutput = vout_v.get_int();
+ if (nOutput < 0)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
+
+ uint32_t nSequence;
+ if (rbfOptIn) {
+ nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
+ } else if (rawTx.nLockTime) {
+ nSequence = CTxIn::SEQUENCE_FINAL - 1;
+ } else {
+ nSequence = CTxIn::SEQUENCE_FINAL;
+ }
+
+ // set the sequence number if passed in the parameters object
+ const UniValue& sequenceObj = find_value(o, "sequence");
+ if (sequenceObj.isNum()) {
+ int64_t seqNr64 = sequenceObj.get_int64();
+ if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
+ } else {
+ nSequence = (uint32_t)seqNr64;
+ }
+ }
+
+ CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
+
+ rawTx.vin.push_back(in);
+ }
+
+ if (!outputs_is_obj) {
+ // Translate array of key-value pairs into dict
+ UniValue outputs_dict = UniValue(UniValue::VOBJ);
+ for (size_t i = 0; i < outputs.size(); ++i) {
+ const UniValue& output = outputs[i];
+ if (!output.isObject()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected");
+ }
+ if (output.size() != 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key");
+ }
+ outputs_dict.pushKVs(output);
+ }
+ outputs = std::move(outputs_dict);
+ }
+
+ // Duplicate checking
+ std::set<CTxDestination> destinations;
+ bool has_data{false};
+
+ for (const std::string& name_ : outputs.getKeys()) {
+ if (name_ == "data") {
+ if (has_data) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");
+ }
+ has_data = true;
+ std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
+
+ CTxOut out(0, CScript() << OP_RETURN << data);
+ rawTx.vout.push_back(out);
+ } else {
+ CTxDestination destination = DecodeDestination(name_);
+ if (!IsValidDestination(destination)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
+ }
+
+ if (!destinations.insert(destination).second) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
+ }
+
+ CScript scriptPubKey = GetScriptForDestination(destination);
+ CAmount nAmount = AmountFromValue(outputs[name_]);
+
+ CTxOut out(nAmount, scriptPubKey);
+ rawTx.vout.push_back(out);
+ }
+ }
+
+ if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
+ }
+
+ return rawTx;
+}
+
+/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */
+static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage)
+{
+ UniValue entry(UniValue::VOBJ);
+ entry.pushKV("txid", txin.prevout.hash.ToString());
+ entry.pushKV("vout", (uint64_t)txin.prevout.n);
+ UniValue witness(UniValue::VARR);
+ for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) {
+ witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end()));
+ }
+ entry.pushKV("witness", witness);
+ entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
+ entry.pushKV("sequence", (uint64_t)txin.nSequence);
+ entry.pushKV("error", strMessage);
+ vErrorsRet.push_back(entry);
+}
+
+UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore* keystore, std::map<COutPoint, Coin>& coins, bool is_temp_keystore, const UniValue& hashType)
+{
+ // Add previous txouts given in the RPC call:
+ if (!prevTxsUnival.isNull()) {
+ UniValue prevTxs = prevTxsUnival.get_array();
+ for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) {
+ const UniValue& p = prevTxs[idx];
+ if (!p.isObject()) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
+ }
+
+ UniValue prevOut = p.get_obj();
+
+ RPCTypeCheckObj(prevOut,
+ {
+ {"txid", UniValueType(UniValue::VSTR)},
+ {"vout", UniValueType(UniValue::VNUM)},
+ {"scriptPubKey", UniValueType(UniValue::VSTR)},
+ });
+
+ uint256 txid = ParseHashO(prevOut, "txid");
+
+ int nOut = find_value(prevOut, "vout").get_int();
+ if (nOut < 0) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
+ }
+
+ COutPoint out(txid, nOut);
+ std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
+ CScript scriptPubKey(pkData.begin(), pkData.end());
+
+ {
+ auto coin = coins.find(out);
+ if (coin != coins.end() && !coin->second.IsSpent() && coin->second.out.scriptPubKey != scriptPubKey) {
+ std::string err("Previous output scriptPubKey mismatch:\n");
+ err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+
+ ScriptToAsmStr(scriptPubKey);
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
+ }
+ Coin newcoin;
+ newcoin.out.scriptPubKey = scriptPubKey;
+ newcoin.out.nValue = MAX_MONEY;
+ if (prevOut.exists("amount")) {
+ newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
+ }
+ newcoin.nHeight = 1;
+ coins[out] = std::move(newcoin);
+ }
+
+ // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
+ if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
+ RPCTypeCheckObj(prevOut,
+ {
+ {"redeemScript", UniValueType(UniValue::VSTR)},
+ {"witnessScript", UniValueType(UniValue::VSTR)},
+ }, true);
+ UniValue rs = find_value(prevOut, "redeemScript");
+ if (!rs.isNull()) {
+ std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript"));
+ CScript redeemScript(rsData.begin(), rsData.end());
+ keystore->AddCScript(redeemScript);
+ // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
+ // This is only for compatibility, it is encouraged to use the explicit witnessScript field instead.
+ keystore->AddCScript(GetScriptForWitness(redeemScript));
+ }
+ UniValue ws = find_value(prevOut, "witnessScript");
+ if (!ws.isNull()) {
+ std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript"));
+ CScript witnessScript(wsData.begin(), wsData.end());
+ keystore->AddCScript(witnessScript);
+ // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
+ keystore->AddCScript(GetScriptForWitness(witnessScript));
+ }
+ }
+ }
+ }
+
+ int nHashType = ParseSighashString(hashType);
+
+ bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
+
+ // Script verification errors
+ UniValue vErrors(UniValue::VARR);
+
+ // Use CTransaction for the constant parts of the
+ // transaction to avoid rehashing.
+ const CTransaction txConst(mtx);
+ // Sign what we can:
+ for (unsigned int i = 0; i < mtx.vin.size(); i++) {
+ CTxIn& txin = mtx.vin[i];
+ auto coin = coins.find(txin.prevout);
+ if (coin == coins.end() || coin->second.IsSpent()) {
+ TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
+ continue;
+ }
+ const CScript& prevPubKey = coin->second.out.scriptPubKey;
+ const CAmount& amount = coin->second.out.nValue;
+
+ SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
+ // Only sign SIGHASH_SINGLE if there's a corresponding output:
+ if (!fHashSingle || (i < mtx.vout.size())) {
+ ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
+ }
+
+ UpdateInput(txin, sigdata);
+
+ // amount must be specified for valid segwit signature
+ if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString()));
+ }
+
+ ScriptError serror = SCRIPT_ERR_OK;
+ if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &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();
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
+ result.pushKV("complete", fComplete);
+ if (!vErrors.empty()) {
+ result.pushKV("errors", vErrors);
+ }
+
+ return result;
+}
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
new file mode 100644
index 0000000000..c115d33a77
--- /dev/null
+++ b/src/rpc/rawtransaction_util.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2017-2018 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_RPC_RAWTRANSACTION_UTIL_H
+#define BITCOIN_RPC_RAWTRANSACTION_UTIL_H
+
+#include <map>
+
+class CBasicKeyStore;
+class UniValue;
+struct CMutableTransaction;
+class Coin;
+class COutPoint;
+
+/**
+ * Sign a transaction with the given keystore and previous transactions
+ *
+ * @param mtx The transaction to-be-signed
+ * @param prevTxs Array of previous txns outputs that tx depends on but may not yet be in the block chain
+ * @param keystore Temporary keystore containing signing keys
+ * @param coins Map of unspent outputs - coins in mempool and current chain UTXO set, may be extended by previous txns outputs after call
+ * @param tempKeystore Whether to use temporary keystore
+ * @param hashType The signature hash type
+ * @returns JSON object with details of signed transaction
+ */
+UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore* keystore, std::map<COutPoint, Coin>& coins, bool tempKeystore, const UniValue& hashType);
+
+/** Create a transaction from univalue parameters */
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
+
+#endif // BITCOIN_RPC_RAWTRANSACTION_UTIL_H
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 2ed74547b9..ca17d379bc 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -7,11 +7,9 @@
#include <fs.h>
#include <key_io.h>
-#include <random.h>
#include <rpc/util.h>
#include <shutdown.h>
#include <sync.h>
-#include <ui_interface.h>
#include <util/strencodings.h>
#include <util/system.h>
@@ -30,6 +28,7 @@ static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server starte
static RPCTimerInterface* timerInterface = nullptr;
/* Map of name to timer. */
static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers;
+static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler);
struct RPCCommandExecutionInfo
{
@@ -76,108 +75,15 @@ void RPCServer::OnStopped(std::function<void ()> slot)
g_rpcSignals.Stopped.connect(slot);
}
-void RPCTypeCheck(const UniValue& params,
- const std::list<UniValueType>& typesExpected,
- bool fAllowNull)
-{
- unsigned int i = 0;
- for (const UniValueType& t : typesExpected) {
- if (params.size() <= i)
- break;
-
- const UniValue& v = params[i];
- if (!(fAllowNull && v.isNull())) {
- RPCTypeCheckArgument(v, t);
- }
- i++;
- }
-}
-
-void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
-{
- if (!typeExpected.typeAny && value.type() != typeExpected.type) {
- throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type())));
- }
-}
-
-void RPCTypeCheckObj(const UniValue& o,
- const std::map<std::string, UniValueType>& typesExpected,
- bool fAllowNull,
- bool fStrict)
-{
- for (const auto& t : typesExpected) {
- const UniValue& v = find_value(o, t.first);
- if (!fAllowNull && v.isNull())
- throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
-
- if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
- std::string err = strprintf("Expected type %s for %s, got %s",
- uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
- throw JSONRPCError(RPC_TYPE_ERROR, err);
- }
- }
-
- if (fStrict)
- {
- for (const std::string& k : o.getKeys())
- {
- if (typesExpected.count(k) == 0)
- {
- std::string err = strprintf("Unexpected key %s", k);
- throw JSONRPCError(RPC_TYPE_ERROR, err);
- }
- }
- }
-}
-
-CAmount AmountFromValue(const UniValue& value)
-{
- if (!value.isNum() && !value.isStr())
- throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
- CAmount amount;
- if (!ParseFixedPoint(value.getValStr(), 8, &amount))
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
- if (!MoneyRange(amount))
- throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
- return amount;
-}
-
-uint256 ParseHashV(const UniValue& v, std::string strName)
-{
- std::string strHex(v.get_str());
- if (64 != strHex.length())
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
- if (!IsHex(strHex)) // Note: IsHex("") is false
- throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
- return uint256S(strHex);
-}
-uint256 ParseHashO(const UniValue& o, std::string strKey)
-{
- return ParseHashV(find_value(o, strKey), strKey);
-}
-std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
-{
- std::string strHex;
- if (v.isStr())
- strHex = v.get_str();
- if (!IsHex(strHex))
- throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
- return ParseHex(strHex);
-}
-std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
-{
- return ParseHexV(find_value(o, strKey), strKey);
-}
-
std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
{
std::string strRet;
std::string category;
- std::set<rpcfn_type> setDone;
+ std::set<intptr_t> setDone;
std::vector<std::pair<std::string, const CRPCCommand*> > vCommands;
for (const auto& entry : mapCommands)
- vCommands.push_back(make_pair(entry.second->category + entry.first, entry.second));
+ vCommands.push_back(make_pair(entry.second.front()->category + entry.first, entry.second.front()));
sort(vCommands.begin(), vCommands.end());
JSONRPCRequest jreq(helpreq);
@@ -193,9 +99,9 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest&
jreq.strMethod = strMethod;
try
{
- rpcfn_type pfn = pcmd->actor;
- if (setDone.insert(pfn).second)
- (*pfn)(jreq);
+ UniValue unused_result;
+ if (setDone.insert(pcmd->unique_id).second)
+ pcmd->actor(jreq, unused_result, true /* last_handler */);
}
catch (const std::exception& e)
{
@@ -230,7 +136,7 @@ UniValue help(const JSONRPCRequest& jsonRequest)
RPCHelpMan{"help",
"\nList all commands, or get help for a specified command.\n",
{
- {"command", RPCArg::Type::STR, /* opt */ true, /* default_val */ "all commands", "The command to get help on"},
+ {"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"},
},
RPCResult{
"\"text\" (string) The help text\n"
@@ -296,8 +202,20 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
RPCHelpMan{"getrpcinfo",
"\nReturns details of the RPC server.\n",
{},
- RPCResults{},
- RPCExamples{""},
+ RPCResult{
+ "{\n"
+ " \"active_commands\" (array) All active commands\n"
+ " [\n"
+ " { (object) Information about an active command\n"
+ " \"method\" (string) The name of the RPC command \n"
+ " \"duration\" (numeric) The running time in microseconds\n"
+ " },...\n"
+ " ]\n"
+ "}\n"
+ },
+ RPCExamples{
+ HelpExampleCli("getrpcinfo", "")
+ + HelpExampleRpc("getrpcinfo", "")},
}.ToString()
);
}
@@ -337,32 +255,32 @@ CRPCTable::CRPCTable()
const CRPCCommand *pcmd;
pcmd = &vRPCCommands[vcidx];
- mapCommands[pcmd->name] = pcmd;
+ mapCommands[pcmd->name].push_back(pcmd);
}
}
-const CRPCCommand *CRPCTable::operator[](const std::string &name) const
-{
- std::map<std::string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
- if (it == mapCommands.end())
- return nullptr;
- return (*it).second;
-}
-
bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
{
if (IsRPCRunning())
return false;
- // don't allow overwriting for now
- std::map<std::string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
- if (it != mapCommands.end())
- return false;
-
- mapCommands[name] = pcmd;
+ mapCommands[name].push_back(pcmd);
return true;
}
+bool CRPCTable::removeCommand(const std::string& name, const CRPCCommand* pcmd)
+{
+ auto it = mapCommands.find(name);
+ if (it != mapCommands.end()) {
+ auto new_end = std::remove(it->second.begin(), it->second.end(), pcmd);
+ if (it->second.end() != new_end) {
+ it->second.erase(new_end, it->second.end());
+ return true;
+ }
+ }
+ return false;
+}
+
void StartRPC()
{
LogPrint(BCLog::RPC, "Starting RPC\n");
@@ -543,18 +461,28 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const
}
// Find method
- const CRPCCommand *pcmd = tableRPC[request.strMethod];
- if (!pcmd)
- throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
+ auto it = mapCommands.find(request.strMethod);
+ if (it != mapCommands.end()) {
+ UniValue result;
+ for (const auto& command : it->second) {
+ if (ExecuteCommand(*command, request, result, &command == &it->second.back())) {
+ return result;
+ }
+ }
+ }
+ throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
+}
+static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler)
+{
try
{
RPCCommandExecution execution(request.strMethod);
// Execute, convert arguments to array if necessary
if (request.params.isObject()) {
- return pcmd->actor(transformNamedArguments(request, pcmd->argNames));
+ return command.actor(transformNamedArguments(request, command.argNames), result, last_handler);
} else {
- return pcmd->actor(request);
+ return command.actor(request, result, last_handler);
}
}
catch (const std::exception& e)
@@ -570,17 +498,6 @@ std::vector<std::string> CRPCTable::listCommands() const
return commandList;
}
-std::string HelpExampleCli(const std::string& methodname, const std::string& args)
-{
- return "> bitcoin-cli " + methodname + " " + args + "\n";
-}
-
-std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
-{
- return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
- "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
-}
-
void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
{
if (!timerInterface)
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 2d62a76f3c..431ff0bb7c 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -27,15 +27,6 @@ namespace RPCServer
void OnStopped(std::function<void ()> slot);
}
-/** Wrapper for UniValue::VType, which includes typeAny:
- * Used to denote don't care type. */
-struct UniValueType {
- UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {}
- UniValueType() : typeAny(true) {}
- bool typeAny;
- UniValue::VType type;
-};
-
class JSONRPCRequest
{
public:
@@ -65,26 +56,6 @@ void SetRPCWarmupFinished();
/* returns the current warmup state. */
bool RPCIsInWarmup(std::string *outStatus);
-/**
- * Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
- * the right number of arguments are passed, just that any passed are the correct type.
- */
-void RPCTypeCheck(const UniValue& params,
- const std::list<UniValueType>& typesExpected, bool fAllowNull=false);
-
-/**
- * Type-check one argument; throws JSONRPCError if wrong type given.
- */
-void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected);
-
-/*
- Check for expected keys/value types in an Object.
-*/
-void RPCTypeCheckObj(const UniValue& o,
- const std::map<std::string, UniValueType>& typesExpected,
- bool fAllowNull = false,
- bool fStrict = false);
-
/** Opaque base class for timers returned by NewTimerFunc.
* This provides no methods at the moment, but makes sure that delete
* cleans up the whole state.
@@ -131,10 +102,31 @@ typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest);
class CRPCCommand
{
public:
+ //! RPC method handler reading request and assigning result. Should return
+ //! true if request is fully handled, false if it should be passed on to
+ //! subsequent handlers.
+ using Actor = std::function<bool(const JSONRPCRequest& request, UniValue& result, bool last_handler)>;
+
+ //! Constructor taking Actor callback supporting multiple handlers.
+ CRPCCommand(std::string category, std::string name, Actor actor, std::vector<std::string> args, intptr_t unique_id)
+ : category(std::move(category)), name(std::move(name)), actor(std::move(actor)), argNames(std::move(args)),
+ unique_id(unique_id)
+ {
+ }
+
+ //! Simplified constructor taking plain rpcfn_type function pointer.
+ CRPCCommand(const char* category, const char* name, rpcfn_type fn, std::initializer_list<const char*> args)
+ : CRPCCommand(category, name,
+ [fn](const JSONRPCRequest& request, UniValue& result, bool) { result = fn(request); return true; },
+ {args.begin(), args.end()}, intptr_t(fn))
+ {
+ }
+
std::string category;
std::string name;
- rpcfn_type actor;
+ Actor actor;
std::vector<std::string> argNames;
+ intptr_t unique_id;
};
/**
@@ -143,10 +135,9 @@ public:
class CRPCTable
{
private:
- std::map<std::string, const CRPCCommand*> mapCommands;
+ std::map<std::string, std::vector<const CRPCCommand*>> mapCommands;
public:
CRPCTable();
- const CRPCCommand* operator[](const std::string& name) const;
std::string help(const std::string& name, const JSONRPCRequest& helpreq) const;
/**
@@ -169,9 +160,7 @@ public:
*
* Returns false if RPC server is already running (dump concurrency protection).
*
- * Commands cannot be overwritten (returns false).
- *
- * Commands with different method names but the same callback function will
+ * Commands with different method names but the same unique_id will
* be considered aliases, and only the first registered method name will
* show up in the help text command listing. Aliased commands do not have
* to have the same behavior. Server and client code can distinguish
@@ -179,25 +168,13 @@ public:
* register different names, types, and numbers of parameters.
*/
bool appendCommand(const std::string& name, const CRPCCommand* pcmd);
+ bool removeCommand(const std::string& name, const CRPCCommand* pcmd);
};
bool IsDeprecatedRPCEnabled(const std::string& method);
extern CRPCTable tableRPC;
-/**
- * Utilities: convert hex-encoded Values
- * (throws error if not hex).
- */
-extern uint256 ParseHashV(const UniValue& v, std::string strName);
-extern uint256 ParseHashO(const UniValue& o, std::string strKey);
-extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
-extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
-
-extern CAmount AmountFromValue(const UniValue& value);
-extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
-extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
-
void StartRPC();
void InterruptRPC();
void StopRPC();
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 4275cc09a8..9cdb22001f 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -1,16 +1,121 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 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_io.h>
#include <keystore.h>
-#include <rpc/protocol.h>
#include <rpc/util.h>
#include <tinyformat.h>
#include <util/strencodings.h>
+#include <tuple>
+
InitInterfaces* g_rpc_interfaces = nullptr;
+void RPCTypeCheck(const UniValue& params,
+ const std::list<UniValueType>& typesExpected,
+ bool fAllowNull)
+{
+ unsigned int i = 0;
+ for (const UniValueType& t : typesExpected) {
+ if (params.size() <= i)
+ break;
+
+ const UniValue& v = params[i];
+ if (!(fAllowNull && v.isNull())) {
+ RPCTypeCheckArgument(v, t);
+ }
+ i++;
+ }
+}
+
+void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
+{
+ if (!typeExpected.typeAny && value.type() != typeExpected.type) {
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type())));
+ }
+}
+
+void RPCTypeCheckObj(const UniValue& o,
+ const std::map<std::string, UniValueType>& typesExpected,
+ bool fAllowNull,
+ bool fStrict)
+{
+ for (const auto& t : typesExpected) {
+ const UniValue& v = find_value(o, t.first);
+ if (!fAllowNull && v.isNull())
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
+
+ if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
+ std::string err = strprintf("Expected type %s for %s, got %s",
+ uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
+ throw JSONRPCError(RPC_TYPE_ERROR, err);
+ }
+ }
+
+ if (fStrict)
+ {
+ for (const std::string& k : o.getKeys())
+ {
+ if (typesExpected.count(k) == 0)
+ {
+ std::string err = strprintf("Unexpected key %s", k);
+ throw JSONRPCError(RPC_TYPE_ERROR, err);
+ }
+ }
+ }
+}
+
+CAmount AmountFromValue(const UniValue& value)
+{
+ if (!value.isNum() && !value.isStr())
+ throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
+ CAmount amount;
+ if (!ParseFixedPoint(value.getValStr(), 8, &amount))
+ throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
+ if (!MoneyRange(amount))
+ throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
+ return amount;
+}
+
+uint256 ParseHashV(const UniValue& v, std::string strName)
+{
+ std::string strHex(v.get_str());
+ if (64 != strHex.length())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
+ if (!IsHex(strHex)) // Note: IsHex("") is false
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
+ return uint256S(strHex);
+}
+uint256 ParseHashO(const UniValue& o, std::string strKey)
+{
+ return ParseHashV(find_value(o, strKey), strKey);
+}
+std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
+{
+ std::string strHex;
+ if (v.isStr())
+ strHex = v.get_str();
+ if (!IsHex(strHex))
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
+ return ParseHex(strHex);
+}
+std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
+{
+ return ParseHexV(find_value(o, strKey), strKey);
+}
+
+std::string HelpExampleCli(const std::string& methodname, const std::string& args)
+{
+ return "> bitcoin-cli " + methodname + " " + args + "\n";
+}
+
+std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
+{
+ return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
+ "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
+}
+
// Converts a hex string to a public key if possible
CPubKey HexToPubKey(const std::string& hex_in)
{
@@ -78,7 +183,7 @@ public:
return UniValue(UniValue::VOBJ);
}
- UniValue operator()(const CKeyID& keyID) const
+ UniValue operator()(const PKHash& keyID) const
{
UniValue obj(UniValue::VOBJ);
obj.pushKV("isscript", false);
@@ -86,7 +191,7 @@ public:
return obj;
}
- UniValue operator()(const CScriptID& scriptID) const
+ UniValue operator()(const ScriptHash& scriptID) const
{
UniValue obj(UniValue::VOBJ);
obj.pushKV("isscript", true);
@@ -129,6 +234,47 @@ UniValue DescribeAddress(const CTxDestination& dest)
return boost::apply_visitor(DescribeAddressVisitor(), dest);
}
+unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target)
+{
+ int target = value.get_int();
+ if (target < 1 || (unsigned int)target > max_target) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u - %u", 1, max_target));
+ }
+ return (unsigned int)target;
+}
+
+RPCErrorCode RPCErrorFromTransactionError(TransactionError terr)
+{
+ switch (terr) {
+ case TransactionError::MEMPOOL_REJECTED:
+ return RPC_TRANSACTION_REJECTED;
+ case TransactionError::ALREADY_IN_CHAIN:
+ return RPC_TRANSACTION_ALREADY_IN_CHAIN;
+ case TransactionError::P2P_DISABLED:
+ return RPC_CLIENT_P2P_DISABLED;
+ case TransactionError::INVALID_PSBT:
+ case TransactionError::PSBT_MISMATCH:
+ return RPC_INVALID_PARAMETER;
+ case TransactionError::SIGHASH_MISMATCH:
+ return RPC_DESERIALIZATION_ERROR;
+ default: break;
+ }
+ return RPC_TRANSACTION_ERROR;
+}
+
+UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string)
+{
+ if (err_string.length() > 0) {
+ return JSONRPCError(RPCErrorFromTransactionError(terr), err_string);
+ } else {
+ return JSONRPCError(RPCErrorFromTransactionError(terr), TransactionErrorString(terr));
+ }
+}
+
+/**
+ * A pair of strings that can be aligned (through padding) with other Sections
+ * later on
+ */
struct Section {
Section(const std::string& left, const std::string& right)
: m_left{left}, m_right{right} {}
@@ -136,6 +282,10 @@ struct Section {
const std::string m_right;
};
+/**
+ * Keeps track of RPCArgs by transforming them into sections for the purpose
+ * of serializing everything to a single string
+ */
struct Sections {
std::vector<Section> m_sections;
size_t m_max_pad{0};
@@ -146,37 +296,48 @@ struct Sections {
m_sections.push_back(s);
}
+ /**
+ * Serializing RPCArgs depends on the outer type. Only arrays and
+ * dictionaries can be nested in json. The top-level outer type is "named
+ * arguments", a mix between a dictionary and arrays.
+ */
enum class OuterType {
ARR,
OBJ,
NAMED_ARG, // Only set on first recursion
};
+ /**
+ * Recursive helper to translate an RPCArg into sections
+ */
void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NAMED_ARG)
{
const auto indent = std::string(current_indent, ' ');
const auto indent_next = std::string(current_indent + 2, ' ');
+ const bool push_name{outer_type == OuterType::OBJ}; // Dictionary keys must have a name
+
switch (arg.m_type) {
case RPCArg::Type::STR_HEX:
case RPCArg::Type::STR:
case RPCArg::Type::NUM:
case RPCArg::Type::AMOUNT:
+ case RPCArg::Type::RANGE:
case RPCArg::Type::BOOL: {
if (outer_type == OuterType::NAMED_ARG) return; // Nothing more to do for non-recursive types on first recursion
auto left = indent;
- if (arg.m_type_str.size() != 0 && outer_type == OuterType::OBJ) {
+ if (arg.m_type_str.size() != 0 && push_name) {
left += "\"" + arg.m_name + "\": " + arg.m_type_str.at(0);
} else {
- left += outer_type == OuterType::OBJ ? arg.ToStringObj(/* oneline */ false) : arg.ToString(/* oneline */ false);
+ left += push_name ? arg.ToStringObj(/* oneline */ false) : arg.ToString(/* oneline */ false);
}
left += ",";
- PushSection({left, arg.ToDescriptionString(/* implicitly_required */ outer_type == OuterType::ARR)});
+ PushSection({left, arg.ToDescriptionString()});
break;
}
case RPCArg::Type::OBJ:
case RPCArg::Type::OBJ_USER_KEYS: {
- const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString(/* implicitly_required */ outer_type == OuterType::ARR);
- PushSection({indent + "{", right});
+ const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString();
+ PushSection({indent + (push_name ? "\"" + arg.m_name + "\": " : "") + "{", right});
for (const auto& arg_inner : arg.m_inner) {
Push(arg_inner, current_indent + 2, OuterType::OBJ);
}
@@ -188,9 +349,9 @@ struct Sections {
}
case RPCArg::Type::ARR: {
auto left = indent;
- left += outer_type == OuterType::OBJ ? "\"" + arg.m_name + "\": " : "";
+ left += push_name ? "\"" + arg.m_name + "\": " : "";
left += "[";
- const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString(/* implicitly_required */ outer_type == OuterType::ARR);
+ const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString();
PushSection({left, right});
for (const auto& arg_inner : arg.m_inner) {
Push(arg_inner, current_indent + 2, OuterType::ARR);
@@ -204,6 +365,9 @@ struct Sections {
}
}
+ /**
+ * Concatenate all sections with proper padding
+ */
std::string ToString() const
{
std::string ret;
@@ -275,6 +439,17 @@ std::string RPCExamples::ToDescriptionString() const
return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples;
}
+bool RPCHelpMan::IsValidNumArgs(size_t num_args) const
+{
+ size_t num_required_args = 0;
+ for (size_t n = m_args.size(); n > 0; --n) {
+ if (!m_args.at(n - 1).IsOptional()) {
+ num_required_args = n;
+ break;
+ }
+ }
+ return num_required_args <= num_args && num_args <= m_args.size();
+}
std::string RPCHelpMan::ToString() const
{
std::string ret;
@@ -283,8 +458,9 @@ std::string RPCHelpMan::ToString() const
ret += m_name;
bool was_optional{false};
for (const auto& arg : m_args) {
+ const bool optional = arg.IsOptional();
ret += " ";
- if (arg.m_optional) {
+ if (optional) {
if (!was_optional) ret += "( ";
was_optional = true;
} else {
@@ -324,7 +500,16 @@ std::string RPCHelpMan::ToString() const
return ret;
}
-std::string RPCArg::ToDescriptionString(const bool implicitly_required) const
+bool RPCArg::IsOptional() const
+{
+ if (m_fallback.which() == 1) {
+ return true;
+ } else {
+ return RPCArg::Optional::NO != boost::get<RPCArg::Optional>(m_fallback);
+ }
+}
+
+std::string RPCArg::ToDescriptionString() const
{
std::string ret;
ret += "(";
@@ -345,6 +530,10 @@ std::string RPCArg::ToDescriptionString(const bool implicitly_required) const
ret += "numeric or string";
break;
}
+ case Type::RANGE: {
+ ret += "numeric or array";
+ break;
+ }
case Type::BOOL: {
ret += "boolean";
break;
@@ -362,19 +551,24 @@ std::string RPCArg::ToDescriptionString(const bool implicitly_required) const
// no default case, so the compiler can warn about missing cases
}
}
- if (!implicitly_required) {
- ret += ", ";
- if (m_optional) {
- ret += "optional";
- if (!m_default_value.empty()) {
- ret += ", default=" + m_default_value;
- } else {
- // TODO enable this assert, when all optional parameters have their default value documented
- //assert(false);
- }
- } else {
- ret += "required";
- assert(m_default_value.empty()); // Default value is ignored, and must not be present
+ if (m_fallback.which() == 1) {
+ ret += ", optional, default=" + boost::get<std::string>(m_fallback);
+ } else {
+ switch (boost::get<RPCArg::Optional>(m_fallback)) {
+ case RPCArg::Optional::OMITTED: {
+ // nothing to do. Element is treated as if not present and has no default value
+ break;
+ }
+ case RPCArg::Optional::OMITTED_NAMED_ARG: {
+ ret += ", optional"; // Default value is "null"
+ break;
+ }
+ case RPCArg::Optional::NO: {
+ ret += ", required";
+ break;
+ }
+
+ // no default case, so the compiler can warn about missing cases
}
}
ret += ")";
@@ -399,6 +593,8 @@ std::string RPCArg::ToStringObj(const bool oneline) const
return res + "\"hex\"";
case Type::NUM:
return res + "n";
+ case Type::RANGE:
+ return res + "n or [n,n]";
case Type::AMOUNT:
return res + "amount";
case Type::BOOL:
@@ -429,6 +625,7 @@ std::string RPCArg::ToString(const bool oneline) const
return "\"" + m_name + "\"";
}
case Type::NUM:
+ case Type::RANGE:
case Type::AMOUNT:
case Type::BOOL: {
return m_name;
@@ -458,3 +655,33 @@ std::string RPCArg::ToString(const bool oneline) const
}
assert(false);
}
+
+static std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
+{
+ if (value.isNum()) {
+ return {0, value.get_int64()};
+ }
+ if (value.isArray() && value.size() == 2 && value[0].isNum() && value[1].isNum()) {
+ int64_t low = value[0].get_int64();
+ int64_t high = value[1].get_int64();
+ if (low > high) throw JSONRPCError(RPC_INVALID_PARAMETER, "Range specified as [begin,end] must not have begin after end");
+ return {low, high};
+ }
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified as end or as [begin,end]");
+}
+
+std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value)
+{
+ int64_t low, high;
+ std::tie(low, high) = ParseRange(value);
+ if (low < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0");
+ }
+ if ((high >> 31) != 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high");
+ }
+ if (high >= low + 1000000) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large");
+ }
+ return {low, high};
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 4a9d4be787..e4fa8fc3d7 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -1,17 +1,21 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 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_RPC_UTIL_H
#define BITCOIN_RPC_UTIL_H
+#include <node/transaction.h>
#include <pubkey.h>
+#include <rpc/protocol.h>
#include <script/standard.h>
#include <univalue.h>
#include <string>
#include <vector>
+#include <boost/variant.hpp>
+
class CKeyStore;
class CPubKey;
class CScript;
@@ -22,12 +26,63 @@ struct InitInterfaces;
//! state to RPC method implementations.
extern InitInterfaces* g_rpc_interfaces;
+/** Wrapper for UniValue::VType, which includes typeAny:
+ * Used to denote don't care type. */
+struct UniValueType {
+ UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {}
+ UniValueType() : typeAny(true) {}
+ bool typeAny;
+ UniValue::VType type;
+};
+
+/**
+ * Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
+ * the right number of arguments are passed, just that any passed are the correct type.
+ */
+void RPCTypeCheck(const UniValue& params,
+ const std::list<UniValueType>& typesExpected, bool fAllowNull=false);
+
+/**
+ * Type-check one argument; throws JSONRPCError if wrong type given.
+ */
+void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected);
+
+/*
+ Check for expected keys/value types in an Object.
+*/
+void RPCTypeCheckObj(const UniValue& o,
+ const std::map<std::string, UniValueType>& typesExpected,
+ bool fAllowNull = false,
+ bool fStrict = false);
+
+/**
+ * Utilities: convert hex-encoded Values
+ * (throws error if not hex).
+ */
+extern uint256 ParseHashV(const UniValue& v, std::string strName);
+extern uint256 ParseHashO(const UniValue& o, std::string strKey);
+extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
+extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
+
+extern CAmount AmountFromValue(const UniValue& value);
+extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
+extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
+
CPubKey HexToPubKey(const std::string& hex_in);
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys);
UniValue DescribeAddress(const CTxDestination& dest);
+//! Parse a confirm target option and raise an RPC error if it is invalid.
+unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target);
+
+RPCErrorCode RPCErrorFromTransactionError(TransactionError terr);
+UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string = "");
+
+//! Parse a JSON range specified as int64, or [int64, int64]
+std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value);
+
struct RPCArg {
enum class Type {
OBJ,
@@ -38,12 +93,30 @@ struct RPCArg {
OBJ_USER_KEYS, //!< Special type where the user must set the keys e.g. to define multiple addresses; as opposed to e.g. an options object where the keys are predefined
AMOUNT, //!< Special type representing a floating point amount (can be either NUM or STR)
STR_HEX, //!< Special type that is a STR with only hex chars
+ RANGE, //!< Special type that is a NUM or [NUM,NUM]
+ };
+
+ enum class Optional {
+ /** Required arg */
+ NO,
+ /**
+ * Optional arg that is a named argument and has a default value of
+ * `null`. When possible, the default value should be specified.
+ */
+ OMITTED_NAMED_ARG,
+ /**
+ * Optional argument with default value omitted because they are
+ * implicitly clear. That is, elements in an array or object may not
+ * exist by default.
+ * When possible, the default value should be specified.
+ */
+ OMITTED,
};
+ using Fallback = boost::variant<Optional, /* default value for optional args */ std::string>;
const std::string m_name; //!< The name of the arg (can be empty for inner args)
const Type m_type;
const std::vector<RPCArg> m_inner; //!< Only used for arrays or dicts
- const bool m_optional;
- const std::string m_default_value; //!< Only used for optional args
+ const Fallback m_fallback;
const std::string m_description;
const std::string m_oneline_description; //!< Should be empty unless it is supposed to override the auto-generated summary line
const std::vector<std::string> m_type_str; //!< Should be empty unless it is supposed to override the auto-generated type strings. Vector length is either 0 or 2, m_type_str.at(0) will override the type of the value in a key-value pair, m_type_str.at(1) will override the type in the argument description.
@@ -51,15 +124,13 @@ struct RPCArg {
RPCArg(
const std::string& name,
const Type& type,
- const bool opt,
- const std::string& default_val,
+ const Fallback& fallback,
const std::string& description,
const std::string& oneline_description = "",
const std::vector<std::string>& type_str = {})
: m_name{name},
m_type{type},
- m_optional{opt},
- m_default_value{default_val},
+ m_fallback{fallback},
m_description{description},
m_oneline_description{oneline_description},
m_type_str{type_str}
@@ -70,8 +141,7 @@ struct RPCArg {
RPCArg(
const std::string& name,
const Type& type,
- const bool opt,
- const std::string& default_val,
+ const Fallback& fallback,
const std::string& description,
const std::vector<RPCArg>& inner,
const std::string& oneline_description = "",
@@ -79,8 +149,7 @@ struct RPCArg {
: m_name{name},
m_type{type},
m_inner{inner},
- m_optional{opt},
- m_default_value{default_val},
+ m_fallback{fallback},
m_description{description},
m_oneline_description{oneline_description},
m_type_str{type_str}
@@ -88,6 +157,8 @@ struct RPCArg {
assert(type == Type::ARR || type == Type::OBJ);
}
+ bool IsOptional() const;
+
/**
* Return the type string of the argument.
* Set oneline to allow it to be overridden by a custom oneline type string (m_oneline_description).
@@ -101,9 +172,8 @@ struct RPCArg {
/**
* Return the description string, including the argument type and whether
* the argument is required.
- * implicitly_required is set for arguments in an array, which are neither optional nor required.
*/
- std::string ToDescriptionString(bool implicitly_required = false) const;
+ std::string ToDescriptionString() const;
};
struct RPCResult {
@@ -164,6 +234,8 @@ public:
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples);
std::string ToString() const;
+ /** If the supplied number of args is neither too small nor too high */
+ bool IsValidNumArgs(size_t num_args) const;
private:
const std::string m_name;
diff --git a/src/scheduler.h b/src/scheduler.h
index 6d7f42cf9f..436f661c59 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -45,13 +45,13 @@ public:
// Call func at/after time t
void schedule(Function f, boost::chrono::system_clock::time_point t=boost::chrono::system_clock::now());
- // Convenience method: call f once deltaSeconds from now
+ // Convenience method: call f once deltaMilliSeconds from now
void scheduleFromNow(Function f, int64_t deltaMilliSeconds);
// Another convenience method: call f approximately
- // every deltaSeconds forever, starting deltaSeconds from now.
+ // every deltaMilliSeconds forever, starting deltaMilliSeconds from now.
// To be more precise: every time f is finished, it
- // is rescheduled to run deltaSeconds later. If you
+ // is rescheduled to run deltaMilliSeconds later. If you
// need more accurate scheduling, don't use this method.
void scheduleEvery(Function f, int64_t deltaMilliSeconds);
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index a702be5b78..50119ba184 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -10,6 +10,7 @@
#include <script/standard.h>
#include <span.h>
+#include <util/bip32.h>
#include <util/system.h>
#include <util/strencodings.h>
@@ -20,21 +21,130 @@
namespace {
////////////////////////////////////////////////////////////////////////////
-// Internal representation //
+// Checksum //
////////////////////////////////////////////////////////////////////////////
-typedef std::vector<uint32_t> KeyPath;
+// This section implements a checksum algorithm for descriptors with the
+// following properties:
+// * Mistakes in a descriptor string are measured in "symbol errors". The higher
+// the number of symbol errors, the harder it is to detect:
+// * An error substituting a character from 0123456789()[],'/*abcdefgh@:$%{} for
+// another in that set always counts as 1 symbol error.
+// * Note that hex encoded keys are covered by these characters. Xprvs and
+// xpubs use other characters too, but already have their own checksum
+// mechanism.
+// * Function names like "multi()" use other characters, but mistakes in
+// these would generally result in an unparseable descriptor.
+// * A case error always counts as 1 symbol error.
+// * Any other 1 character substitution error counts as 1 or 2 symbol errors.
+// * Any 1 symbol error is always detected.
+// * Any 2 or 3 symbol error in a descriptor of up to 49154 characters is always detected.
+// * Any 4 symbol error in a descriptor of up to 507 characters is always detected.
+// * Any 5 symbol error in a descriptor of up to 77 characters is always detected.
+// * Is optimized to minimize the chance a 5 symbol error in a descriptor up to 387 characters is undetected
+// * Random errors have a chance of 1 in 2**40 of being undetected.
+//
+// These properties are achieved by expanding every group of 3 (non checksum) characters into
+// 4 GF(32) symbols, over which a cyclic code is defined.
+
+/*
+ * Interprets c as 8 groups of 5 bits which are the coefficients of a degree 8 polynomial over GF(32),
+ * multiplies that polynomial by x, computes its remainder modulo a generator, and adds the constant term val.
+ *
+ * This generator is G(x) = x^8 + {30}x^7 + {23}x^6 + {15}x^5 + {14}x^4 + {10}x^3 + {6}x^2 + {12}x + {9}.
+ * It is chosen to define an cyclic error detecting code which is selected by:
+ * - Starting from all BCH codes over GF(32) of degree 8 and below, which by construction guarantee detecting
+ * 3 errors in windows up to 19000 symbols.
+ * - Taking all those generators, and for degree 7 ones, extend them to degree 8 by adding all degree-1 factors.
+ * - Selecting just the set of generators that guarantee detecting 4 errors in a window of length 512.
+ * - Selecting one of those with best worst-case behavior for 5 errors in windows of length up to 512.
+ *
+ * The generator and the constants to implement it can be verified using this Sage code:
+ * B = GF(2) # Binary field
+ * BP.<b> = B[] # Polynomials over the binary field
+ * F_mod = b**5 + b**3 + 1
+ * F.<f> = GF(32, modulus=F_mod, repr='int') # GF(32) definition
+ * FP.<x> = F[] # Polynomials over GF(32)
+ * E_mod = x**3 + x + F.fetch_int(8)
+ * E.<e> = F.extension(E_mod) # Extension field definition
+ * alpha = e**2743 # Choice of an element in extension field
+ * for p in divisors(E.order() - 1): # Verify alpha has order 32767.
+ * assert((alpha**p == 1) == (p % 32767 == 0))
+ * G = lcm([(alpha**i).minpoly() for i in [1056,1057,1058]] + [x + 1])
+ * print(G) # Print out the generator
+ * for i in [1,2,4,8,16]: # Print out {1,2,4,8,16}*(G mod x^8), packed in hex integers.
+ * v = 0
+ * for coef in reversed((F.fetch_int(i)*(G % x**8)).coefficients(sparse=True)):
+ * v = v*32 + coef.integer_representation()
+ * print("0x%x" % v)
+ */
+uint64_t PolyMod(uint64_t c, int val)
+{
+ uint8_t c0 = c >> 35;
+ c = ((c & 0x7ffffffff) << 5) ^ val;
+ if (c0 & 1) c ^= 0xf5dee51989;
+ if (c0 & 2) c ^= 0xa9fdca3312;
+ if (c0 & 4) c ^= 0x1bab10e32d;
+ if (c0 & 8) c ^= 0x3706b1677a;
+ if (c0 & 16) c ^= 0x644d626ffd;
+ return c;
+}
-std::string FormatKeyPath(const KeyPath& path)
+std::string DescriptorChecksum(const Span<const char>& span)
{
- std::string ret;
- for (auto i : path) {
- ret += strprintf("/%i", (i << 1) >> 1);
- if (i >> 31) ret += '\'';
+ /** A character set designed such that:
+ * - The most common 'unprotected' descriptor characters (hex, keypaths) are in the first group of 32.
+ * - Case errors cause an offset that's a multiple of 32.
+ * - As many alphabetic characters are in the same group (while following the above restrictions).
+ *
+ * If p(x) gives the position of a character c in this character set, every group of 3 characters
+ * (a,b,c) is encoded as the 4 symbols (p(a) & 31, p(b) & 31, p(c) & 31, (p(a) / 32) + 3 * (p(b) / 32) + 9 * (p(c) / 32).
+ * This means that changes that only affect the lower 5 bits of the position, or only the higher 2 bits, will just
+ * affect a single symbol.
+ *
+ * As a result, within-group-of-32 errors count as 1 symbol, as do cross-group errors that don't affect
+ * the position within the groups.
+ */
+ static std::string INPUT_CHARSET =
+ "0123456789()[],'/*abcdefgh@:$%{}"
+ "IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~"
+ "ijklmnopqrstuvwxyzABCDEFGH`#\"\\ ";
+
+ /** The character set for the checksum itself (same as bech32). */
+ static std::string CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
+
+ uint64_t c = 1;
+ int cls = 0;
+ int clscount = 0;
+ for (auto ch : span) {
+ auto pos = INPUT_CHARSET.find(ch);
+ if (pos == std::string::npos) return "";
+ c = PolyMod(c, pos & 31); // Emit a symbol for the position inside the group, for every character.
+ cls = cls * 3 + (pos >> 5); // Accumulate the group numbers
+ if (++clscount == 3) {
+ // Emit an extra symbol representing the group numbers, for every 3 characters.
+ c = PolyMod(c, cls);
+ cls = 0;
+ clscount = 0;
+ }
}
+ if (clscount > 0) c = PolyMod(c, cls);
+ for (int j = 0; j < 8; ++j) c = PolyMod(c, 0); // Shift further to determine the checksum.
+ c ^= 1; // Prevent appending zeroes from not affecting the checksum.
+
+ std::string ret(8, ' ');
+ for (int j = 0; j < 8; ++j) ret[j] = CHECKSUM_CHARSET[(c >> (5 * (7 - j))) & 31];
return ret;
}
+std::string AddChecksum(const std::string& str) { return str + "#" + DescriptorChecksum(MakeSpan(str)); }
+
+////////////////////////////////////////////////////////////////////////////
+// Internal representation //
+////////////////////////////////////////////////////////////////////////////
+
+typedef std::vector<uint32_t> KeyPath;
+
/** Interface for public key objects in descriptors. */
struct PubkeyProvider
{
@@ -54,6 +164,9 @@ struct PubkeyProvider
/** Get the descriptor string form including private data (if available in arg). */
virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
+
+ /** Derive a private key, if private data is available in arg. */
+ virtual bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const = 0;
};
class OriginPubkeyProvider final : public PubkeyProvider
@@ -63,7 +176,7 @@ class OriginPubkeyProvider final : public PubkeyProvider
std::string OriginString() const
{
- return HexStr(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint)) + FormatKeyPath(m_origin.path);
+ return HexStr(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint)) + FormatHDKeypath(m_origin.path);
}
public:
@@ -85,6 +198,10 @@ public:
ret = "[" + OriginString() + "]" + std::move(sub);
return true;
}
+ bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
+ {
+ return m_provider->GetPrivKey(pos, arg, key);
+ }
};
/** An object representing a parsed constant public key in a descriptor. */
@@ -112,6 +229,10 @@ public:
ret = EncodeSecret(key);
return true;
}
+ bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
+ {
+ return arg.GetKey(m_pubkey.GetID(), key);
+ }
};
enum class DeriveType {
@@ -156,14 +277,9 @@ public:
{
if (key) {
if (IsHardened()) {
- CExtKey extkey;
- if (!GetExtKey(arg, extkey)) return false;
- for (auto entry : m_path) {
- extkey.Derive(extkey, entry);
- }
- if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
- if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
- *key = extkey.Neuter().pubkey;
+ CKey priv_key;
+ if (!GetPrivKey(pos, arg, priv_key)) return false;
+ *key = priv_key.GetPubKey();
} else {
// TODO: optimize by caching
CExtPubKey extkey = m_extkey;
@@ -184,7 +300,7 @@ public:
}
std::string ToString() const override
{
- std::string ret = EncodeExtPubKey(m_extkey) + FormatKeyPath(m_path);
+ std::string ret = EncodeExtPubKey(m_extkey) + FormatHDKeypath(m_path);
if (IsRange()) {
ret += "/*";
if (m_derive == DeriveType::HARDENED) ret += '\'';
@@ -195,13 +311,25 @@ public:
{
CExtKey key;
if (!GetExtKey(arg, key)) return false;
- out = EncodeExtKey(key) + FormatKeyPath(m_path);
+ out = EncodeExtKey(key) + FormatHDKeypath(m_path);
if (IsRange()) {
out += "/*";
if (m_derive == DeriveType::HARDENED) out += '\'';
}
return true;
}
+ bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
+ {
+ CExtKey extkey;
+ if (!GetExtKey(arg, extkey)) return false;
+ for (auto entry : m_path) {
+ extkey.Derive(extkey, entry);
+ }
+ if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
+ if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
+ key = extkey.key;
+ return true;
+ }
};
/** Base class for all Descriptor implementations. */
@@ -226,7 +354,7 @@ protected:
* @param pubkeys The evaluations of the m_pubkey_args field.
* @param script The evaluation of m_script_arg (or nullptr when m_script_arg is nullptr).
* @param out A FlatSigningProvider to put scripts or public keys in that are necessary to the solver.
- * The script and pubkeys argument to this function are automatically added.
+ * The script arguments to this function are automatically added, as is the origin info of the provided pubkeys.
* @return A vector with scriptPubKeys for this descriptor.
*/
virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, const CScript* script, FlatSigningProvider& out) const = 0;
@@ -282,10 +410,15 @@ public:
{
std::string ret;
ToStringHelper(nullptr, ret, false);
- return ret;
+ return AddChecksum(ret);
}
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const override final { return ToStringHelper(&arg, out, true); }
+ bool ToPrivateString(const SigningProvider& arg, std::string& out) const override final
+ {
+ bool ret = ToStringHelper(&arg, out, true);
+ out = AddChecksum(out);
+ return ret;
+ }
bool ExpandHelper(int pos, const SigningProvider& arg, Span<const unsigned char>* cache_read, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache_write) const
{
@@ -321,8 +454,7 @@ public:
pubkeys.reserve(entries.size());
for (auto& entry : entries) {
pubkeys.push_back(entry.first);
- out.origins.emplace(entry.first.GetID(), std::move(entry.second));
- out.pubkeys.emplace(entry.first.GetID(), entry.first);
+ out.origins.emplace(entry.first.GetID(), std::make_pair<CPubKey, KeyOriginInfo>(CPubKey(entry.first), std::move(entry.second)));
}
if (m_script_arg) {
for (const auto& subscript : subscripts) {
@@ -348,6 +480,20 @@ public:
Span<const unsigned char> span = MakeSpan(cache);
return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &span, output_scripts, out, nullptr) && span.size() == 0;
}
+
+ void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const final
+ {
+ for (const auto& p : m_pubkey_args) {
+ CKey key;
+ if (!p->GetPrivKey(pos, provider, key)) continue;
+ out.keys.emplace(key.GetPubKey().GetID(), key);
+ }
+ if (m_script_arg) {
+ FlatSigningProvider subprovider;
+ m_script_arg->ExpandPrivate(pos, provider, subprovider);
+ out = Merge(out, subprovider);
+ }
+ }
};
/** Construct a vector with one element, which is moved into it. */
@@ -396,7 +542,12 @@ public:
class PKHDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(keys[0].GetID())); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override
+ {
+ CKeyID id = keys[0].GetID();
+ out.pubkeys.emplace(id, keys[0]);
+ return Singleton(GetScriptForDestination(PKHash(id)));
+ }
public:
PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "pkh") {}
};
@@ -405,7 +556,12 @@ public:
class WPKHDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(WitnessV0KeyHash(keys[0].GetID()))); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override
+ {
+ CKeyID id = keys[0].GetID();
+ out.pubkeys.emplace(id, keys[0]);
+ return Singleton(GetScriptForDestination(WitnessV0KeyHash(id)));
+ }
public:
WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "wpkh") {}
};
@@ -418,13 +574,14 @@ protected:
{
std::vector<CScript> ret;
CKeyID id = keys[0].GetID();
+ out.pubkeys.emplace(id, keys[0]);
ret.emplace_back(GetScriptForRawPubKey(keys[0])); // P2PK
- ret.emplace_back(GetScriptForDestination(id)); // P2PKH
+ ret.emplace_back(GetScriptForDestination(PKHash(id))); // P2PKH
if (keys[0].IsCompressed()) {
CScript p2wpkh = GetScriptForDestination(WitnessV0KeyHash(id));
out.scripts.emplace(CScriptID(p2wpkh), p2wpkh);
ret.emplace_back(p2wpkh);
- ret.emplace_back(GetScriptForDestination(CScriptID(p2wpkh))); // P2SH-P2WPKH
+ ret.emplace_back(GetScriptForDestination(ScriptHash(p2wpkh))); // P2SH-P2WPKH
}
return ret;
}
@@ -447,7 +604,7 @@ public:
class SHDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(CScriptID(*script))); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(ScriptHash(*script))); }
public:
SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {}
};
@@ -750,11 +907,25 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
return MakeUnique<RawDescriptor>(script);
}
+
} // namespace
-std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out)
+std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum)
{
Span<const char> sp(descriptor.data(), descriptor.size());
+
+ // Checksum checks
+ auto check_split = Split(sp, '#');
+ if (check_split.size() > 2) return nullptr; // Multiple '#' symbols
+ if (check_split.size() == 1 && require_checksum) return nullptr; // Missing checksum
+ if (check_split.size() == 2) {
+ if (check_split[1].size() != 8) return nullptr; // Unexpected length for checksum
+ auto checksum = DescriptorChecksum(check_split[0]);
+ if (checksum.empty()) return nullptr; // Invalid characters in payload
+ if (!std::equal(checksum.begin(), checksum.end(), check_split[1].begin())) return nullptr; // Checksum mismatch
+ }
+ sp = check_split[0];
+
auto ret = ParseScript(sp, ParseScriptContext::TOP, out);
if (sp.size() == 0 && ret) return std::unique_ptr<Descriptor>(std::move(ret));
return nullptr;
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index 44f0efca03..af7ae229ca 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -60,10 +60,25 @@ struct Descriptor {
* out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider).
*/
virtual bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
+
+ /** Expand the private key for a descriptor at a specified position, if possible.
+ *
+ * pos: the position at which to expand the descriptor. If IsRange() is false, this is ignored.
+ * provider: the provider to query for the private keys.
+ * out: any private keys available for the specified pos will be placed here.
+ */
+ virtual void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const = 0;
};
-/** Parse a descriptor string. Included private keys are put in out. Returns nullptr if parsing fails. */
-std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out);
+/** Parse a descriptor string. Included private keys are put in out.
+ *
+ * If the descriptor has a checksum, it must be valid. If require_checksum
+ * is set, the checksum is mandatory - otherwise it is optional.
+ *
+ * If a parse error occurs, or the checksum is missing/invalid, or anything
+ * else is wrong, nullptr is returned.
+ */
+std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum = false);
/** Find a descriptor for the specified script, using information from provider where possible.
*
diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp
index 51bd2d6e9f..75fc2e84f1 100644
--- a/src/script/ismine.cpp
+++ b/src/script/ismine.cpp
@@ -90,7 +90,7 @@ IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey,
// This also applies to the P2WSH case.
break;
}
- ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
+ ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(PKHash(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
break;
}
case TX_PUBKEYHASH:
diff --git a/src/script/ismine.h b/src/script/ismine.h
index 601e70f709..55e28e225a 100644
--- a/src/script/ismine.h
+++ b/src/script/ismine.h
@@ -9,6 +9,7 @@
#include <script/standard.h>
#include <stdint.h>
+#include <bitset>
class CKeyStore;
class CScript;
@@ -16,10 +17,11 @@ class CScript;
/** IsMine() return codes */
enum isminetype
{
- ISMINE_NO = 0,
- ISMINE_WATCH_ONLY = 1,
- ISMINE_SPENDABLE = 2,
- ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE
+ ISMINE_NO = 0,
+ ISMINE_WATCH_ONLY = 1 << 0,
+ ISMINE_SPENDABLE = 1 << 1,
+ ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE,
+ ISMINE_ENUM_ELEMENTS,
};
/** used for bitflags of isminetype */
typedef uint8_t isminefilter;
@@ -27,4 +29,23 @@ typedef uint8_t isminefilter;
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
+/**
+ * Cachable amount subdivided into watchonly and spendable parts.
+ */
+struct CachableAmount
+{
+ // NO and ALL are never (supposed to be) cached
+ std::bitset<ISMINE_ENUM_ELEMENTS> m_cached;
+ CAmount m_value[ISMINE_ENUM_ELEMENTS];
+ inline void Reset()
+ {
+ m_cached.reset();
+ }
+ void Set(isminefilter filter, CAmount value)
+ {
+ m_cached.set(filter);
+ m_value[filter] = value;
+ }
+};
+
#endif // BITCOIN_SCRIPT_ISMINE_H
diff --git a/src/script/script.cpp b/src/script/script.cpp
index 982aa241e7..0666a385d1 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -5,7 +5,6 @@
#include <script/script.h>
-#include <tinyformat.h>
#include <util/strencodings.h>
const char* GetOpName(opcodetype opcode)
diff --git a/src/script/script.h b/src/script/script.h
index 1d8ddba2f2..11e8661a5b 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -581,13 +581,4 @@ struct CScriptWitness
std::string ToString() const;
};
-class CReserveScript
-{
-public:
- CScript reserveScript;
- virtual void KeepScript() {}
- CReserveScript() {}
- virtual ~CReserveScript() {}
-};
-
#endif // BITCOIN_SCRIPT_SCRIPT_H
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index 94005cf6f3..eaf5363bd7 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -5,7 +5,6 @@
#include <script/sigcache.h>
-#include <memusage.h>
#include <pubkey.h>
#include <random.h>
#include <uint256.h>
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 792fb2997f..36dd68a3d8 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -83,6 +83,8 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat
assert(i.second);
return true;
}
+ // Could not make signature or signature not found, add keyid to missing
+ sigdata.missing_sigs.push_back(keyid);
return false;
}
@@ -116,17 +118,24 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
case TX_PUBKEYHASH: {
CKeyID keyID = CKeyID(uint160(vSolutions[0]));
CPubKey pubkey;
- if (!GetPubKey(provider, sigdata, keyID, pubkey)) return false;
+ if (!GetPubKey(provider, sigdata, keyID, pubkey)) {
+ // Pubkey could not be found, add to missing
+ sigdata.missing_pubkeys.push_back(keyID);
+ return false;
+ }
if (!CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) return false;
ret.push_back(std::move(sig));
ret.push_back(ToByteVector(pubkey));
return true;
}
case TX_SCRIPTHASH:
- if (GetCScript(provider, sigdata, uint160(vSolutions[0]), scriptRet)) {
+ h160 = uint160(vSolutions[0]);
+ if (GetCScript(provider, sigdata, h160, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
+ // Could not find redeemScript, add to missing
+ sigdata.missing_redeem_script = h160;
return false;
case TX_MULTISIG: {
@@ -154,6 +163,8 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
+ // Could not find witnessScript, add to missing
+ sigdata.missing_witness_script = uint256(vSolutions[0]);
return false;
default:
@@ -232,67 +243,6 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
return sigdata.complete;
}
-bool PSBTInputSigned(PSBTInput& input)
-{
- return !input.final_script_sig.empty() || !input.final_script_witness.IsNull();
-}
-
-bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash)
-{
- PSBTInput& input = psbt.inputs.at(index);
- const CMutableTransaction& tx = *psbt.tx;
-
- if (PSBTInputSigned(input)) {
- return true;
- }
-
- // Fill SignatureData with input info
- SignatureData sigdata;
- input.FillSignatureData(sigdata);
-
- // Get UTXO
- bool require_witness_sig = false;
- CTxOut utxo;
-
- // Verify input sanity, which checks that at most one of witness or non-witness utxos is provided.
- if (!input.IsSane()) {
- return false;
- }
-
- if (input.non_witness_utxo) {
- // If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
- COutPoint prevout = tx.vin[index].prevout;
- if (input.non_witness_utxo->GetHash() != prevout.hash) {
- return false;
- }
- utxo = input.non_witness_utxo->vout[prevout.n];
- } else if (!input.witness_utxo.IsNull()) {
- utxo = input.witness_utxo;
- // When we're taking our information from a witness UTXO, we can't verify it is actually data from
- // the output being spent. This is safe in case a witness signature is produced (which includes this
- // information directly in the hash), but not for non-witness signatures. Remember that we require
- // a witness signature in this situation.
- require_witness_sig = true;
- } else {
- return false;
- }
-
- MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
- sigdata.witness = false;
- bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
- // Verify that a witness signature was produced in case one was required.
- if (require_witness_sig && !sigdata.witness) return false;
- input.FromSignatureData(sigdata);
-
- // If we have a witness signature, use the smaller witness UTXO.
- if (sigdata.witness) {
- input.witness_utxo = utxo;
- input.non_witness_utxo = nullptr;
- }
-
- return sig_complete;
-}
-
class SignatureExtractorChecker final : public BaseSignatureChecker
{
private:
@@ -509,166 +459,6 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
return false;
}
-PartiallySignedTransaction::PartiallySignedTransaction(const CMutableTransaction& tx) : tx(tx)
-{
- inputs.resize(tx.vin.size());
- outputs.resize(tx.vout.size());
-}
-
-bool PartiallySignedTransaction::IsNull() const
-{
- return !tx && inputs.empty() && outputs.empty() && unknown.empty();
-}
-
-void PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
-{
- for (unsigned int i = 0; i < inputs.size(); ++i) {
- inputs[i].Merge(psbt.inputs[i]);
- }
- for (unsigned int i = 0; i < outputs.size(); ++i) {
- outputs[i].Merge(psbt.outputs[i]);
- }
- unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
-}
-
-bool PartiallySignedTransaction::IsSane() const
-{
- for (PSBTInput input : inputs) {
- if (!input.IsSane()) return false;
- }
- return true;
-}
-
-bool PSBTInput::IsNull() const
-{
- return !non_witness_utxo && witness_utxo.IsNull() && partial_sigs.empty() && unknown.empty() && hd_keypaths.empty() && redeem_script.empty() && witness_script.empty();
-}
-
-void PSBTInput::FillSignatureData(SignatureData& sigdata) const
-{
- if (!final_script_sig.empty()) {
- sigdata.scriptSig = final_script_sig;
- sigdata.complete = true;
- }
- if (!final_script_witness.IsNull()) {
- sigdata.scriptWitness = final_script_witness;
- sigdata.complete = true;
- }
- if (sigdata.complete) {
- return;
- }
-
- sigdata.signatures.insert(partial_sigs.begin(), partial_sigs.end());
- if (!redeem_script.empty()) {
- sigdata.redeem_script = redeem_script;
- }
- if (!witness_script.empty()) {
- sigdata.witness_script = witness_script;
- }
- for (const auto& key_pair : hd_keypaths) {
- sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
- }
-}
-
-void PSBTInput::FromSignatureData(const SignatureData& sigdata)
-{
- if (sigdata.complete) {
- partial_sigs.clear();
- hd_keypaths.clear();
- redeem_script.clear();
- witness_script.clear();
-
- if (!sigdata.scriptSig.empty()) {
- final_script_sig = sigdata.scriptSig;
- }
- if (!sigdata.scriptWitness.IsNull()) {
- final_script_witness = sigdata.scriptWitness;
- }
- return;
- }
-
- partial_sigs.insert(sigdata.signatures.begin(), sigdata.signatures.end());
- if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
- redeem_script = sigdata.redeem_script;
- }
- if (witness_script.empty() && !sigdata.witness_script.empty()) {
- witness_script = sigdata.witness_script;
- }
- for (const auto& entry : sigdata.misc_pubkeys) {
- hd_keypaths.emplace(entry.second);
- }
-}
-
-void PSBTInput::Merge(const PSBTInput& input)
-{
- if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo;
- if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) {
- witness_utxo = input.witness_utxo;
- non_witness_utxo = nullptr; // Clear out any non-witness utxo when we set a witness one.
- }
-
- partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end());
- hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end());
- unknown.insert(input.unknown.begin(), input.unknown.end());
-
- if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script;
- if (witness_script.empty() && !input.witness_script.empty()) witness_script = input.witness_script;
- if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig;
- if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness;
-}
-
-bool PSBTInput::IsSane() const
-{
- // Cannot have both witness and non-witness utxos
- if (!witness_utxo.IsNull() && non_witness_utxo) return false;
-
- // If we have a witness_script or a scriptWitness, we must also have a witness utxo
- if (!witness_script.empty() && witness_utxo.IsNull()) return false;
- if (!final_script_witness.IsNull() && witness_utxo.IsNull()) return false;
-
- return true;
-}
-
-void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
-{
- if (!redeem_script.empty()) {
- sigdata.redeem_script = redeem_script;
- }
- if (!witness_script.empty()) {
- sigdata.witness_script = witness_script;
- }
- for (const auto& key_pair : hd_keypaths) {
- sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
- }
-}
-
-void PSBTOutput::FromSignatureData(const SignatureData& sigdata)
-{
- if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
- redeem_script = sigdata.redeem_script;
- }
- if (witness_script.empty() && !sigdata.witness_script.empty()) {
- witness_script = sigdata.witness_script;
- }
- for (const auto& entry : sigdata.misc_pubkeys) {
- hd_keypaths.emplace(entry.second);
- }
-}
-
-bool PSBTOutput::IsNull() const
-{
- return redeem_script.empty() && witness_script.empty() && hd_keypaths.empty() && unknown.empty();
-}
-
-void PSBTOutput::Merge(const PSBTOutput& output)
-{
- hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end());
- unknown.insert(output.unknown.begin(), output.unknown.end());
-
- if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script;
- if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script;
-}
-
bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const
{
return m_provider->GetCScript(scriptid, script);
@@ -693,7 +483,13 @@ bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& inf
bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
-bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return LookupHelper(origins, keyid, info); }
+bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
+{
+ std::pair<CPubKey, KeyOriginInfo> out;
+ bool ret = LookupHelper(origins, keyid, out);
+ if (ret) info = std::move(out.second);
+ return ret;
+}
bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
diff --git a/src/script/sign.h b/src/script/sign.h
index e884f4c480..f746325b90 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -22,13 +22,27 @@ struct CMutableTransaction;
struct KeyOriginInfo
{
- unsigned char fingerprint[4];
+ unsigned char fingerprint[4]; //!< First 32 bits of the Hash160 of the public key at the root of the path
std::vector<uint32_t> path;
friend bool operator==(const KeyOriginInfo& a, const KeyOriginInfo& b)
{
return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path;
}
+
+ ADD_SERIALIZE_METHODS;
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action)
+ {
+ READWRITE(fingerprint);
+ READWRITE(path);
+ }
+
+ void clear()
+ {
+ memset(fingerprint, 0, 4);
+ path.clear();
+ }
};
/** An interface to be implemented by keystores that support signing. */
@@ -63,7 +77,7 @@ struct FlatSigningProvider final : public SigningProvider
{
std::map<CScriptID, CScript> scripts;
std::map<CKeyID, CPubKey> pubkeys;
- std::map<CKeyID, KeyOriginInfo> origins;
+ std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
std::map<CKeyID, CKey> keys;
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
@@ -117,38 +131,16 @@ struct SignatureData {
CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144.
std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness.
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys;
+ std::vector<CKeyID> missing_pubkeys; ///< KeyIDs of pubkeys which could not be found
+ std::vector<CKeyID> missing_sigs; ///< KeyIDs of pubkeys for signatures which could not be found
+ uint160 missing_redeem_script; ///< ScriptID of the missing redeemScript (if any)
+ uint256 missing_witness_script; ///< SHA256 of the missing witnessScript (if any)
SignatureData() {}
explicit SignatureData(const CScript& script) : scriptSig(script) {}
void MergeSignatureData(SignatureData sigdata);
};
-// Magic bytes
-static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
-
-// Global types
-static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00;
-
-// Input types
-static constexpr uint8_t PSBT_IN_NON_WITNESS_UTXO = 0x00;
-static constexpr uint8_t PSBT_IN_WITNESS_UTXO = 0x01;
-static constexpr uint8_t PSBT_IN_PARTIAL_SIG = 0x02;
-static constexpr uint8_t PSBT_IN_SIGHASH = 0x03;
-static constexpr uint8_t PSBT_IN_REDEEMSCRIPT = 0x04;
-static constexpr uint8_t PSBT_IN_WITNESSSCRIPT = 0x05;
-static constexpr uint8_t PSBT_IN_BIP32_DERIVATION = 0x06;
-static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07;
-static constexpr uint8_t PSBT_IN_SCRIPTWITNESS = 0x08;
-
-// Output types
-static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;
-static constexpr uint8_t PSBT_OUT_WITNESSSCRIPT = 0x01;
-static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;
-
-// The separator is 0x00. Reading this in means that the unserializer can interpret it
-// as a 0 length key which indicates that this is the separator. The separator has no value.
-static constexpr uint8_t PSBT_SEPARATOR = 0x00;
-
// Takes a stream and multiple arguments and serializes them as if first serialized into a vector and then into the stream
// The resulting output into the stream has the total serialized length of all of the objects followed by all objects concatenated with each other.
template<typename Stream, typename... X>
@@ -223,514 +215,6 @@ void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_k
}
}
-/** A structure for PSBTs which contain per-input information */
-struct PSBTInput
-{
- CTransactionRef non_witness_utxo;
- CTxOut witness_utxo;
- CScript redeem_script;
- CScript witness_script;
- CScript final_script_sig;
- CScriptWitness final_script_witness;
- std::map<CPubKey, KeyOriginInfo> hd_keypaths;
- std::map<CKeyID, SigPair> partial_sigs;
- std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
- int sighash_type = 0;
-
- bool IsNull() const;
- void FillSignatureData(SignatureData& sigdata) const;
- void FromSignatureData(const SignatureData& sigdata);
- void Merge(const PSBTInput& input);
- bool IsSane() const;
- PSBTInput() {}
-
- template <typename Stream>
- inline void Serialize(Stream& s) const {
- // Write the utxo
- // If there is a non-witness utxo, then don't add the witness one.
- if (non_witness_utxo) {
- SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO);
- OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
- SerializeToVector(os, non_witness_utxo);
- } else if (!witness_utxo.IsNull()) {
- SerializeToVector(s, PSBT_IN_WITNESS_UTXO);
- SerializeToVector(s, witness_utxo);
- }
-
- if (final_script_sig.empty() && final_script_witness.IsNull()) {
- // Write any partial signatures
- for (auto sig_pair : partial_sigs) {
- SerializeToVector(s, PSBT_IN_PARTIAL_SIG, MakeSpan(sig_pair.second.first));
- s << sig_pair.second.second;
- }
-
- // Write the sighash type
- if (sighash_type > 0) {
- SerializeToVector(s, PSBT_IN_SIGHASH);
- SerializeToVector(s, sighash_type);
- }
-
- // Write the redeem script
- if (!redeem_script.empty()) {
- SerializeToVector(s, PSBT_IN_REDEEMSCRIPT);
- s << redeem_script;
- }
-
- // Write the witness script
- if (!witness_script.empty()) {
- SerializeToVector(s, PSBT_IN_WITNESSSCRIPT);
- s << witness_script;
- }
-
- // Write any hd keypaths
- SerializeHDKeypaths(s, hd_keypaths, PSBT_IN_BIP32_DERIVATION);
- }
-
- // Write script sig
- if (!final_script_sig.empty()) {
- SerializeToVector(s, PSBT_IN_SCRIPTSIG);
- s << final_script_sig;
- }
- // write script witness
- if (!final_script_witness.IsNull()) {
- SerializeToVector(s, PSBT_IN_SCRIPTWITNESS);
- SerializeToVector(s, final_script_witness.stack);
- }
-
- // Write unknown things
- for (auto& entry : unknown) {
- s << entry.first;
- s << entry.second;
- }
-
- s << PSBT_SEPARATOR;
- }
-
-
- template <typename Stream>
- inline void Unserialize(Stream& s) {
- // Read loop
- bool found_sep = false;
- while(!s.empty()) {
- // Read
- std::vector<unsigned char> key;
- s >> key;
-
- // the key is empty if that was actually a separator byte
- // This is a special case for key lengths 0 as those are not allowed (except for separator)
- if (key.empty()) {
- found_sep = true;
- break;
- }
-
- // First byte of key is the type
- unsigned char type = key[0];
-
- // Do stuff based on type
- switch(type) {
- case PSBT_IN_NON_WITNESS_UTXO:
- {
- if (non_witness_utxo) {
- throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided");
- } else if (key.size() != 1) {
- throw std::ios_base::failure("Non-witness utxo key is more than one byte type");
- }
- // Set the stream to unserialize with witness since this is always a valid network transaction
- OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() & ~SERIALIZE_TRANSACTION_NO_WITNESS);
- UnserializeFromVector(os, non_witness_utxo);
- break;
- }
- case PSBT_IN_WITNESS_UTXO:
- if (!witness_utxo.IsNull()) {
- throw std::ios_base::failure("Duplicate Key, input witness utxo already provided");
- } else if (key.size() != 1) {
- throw std::ios_base::failure("Witness utxo key is more than one byte type");
- }
- UnserializeFromVector(s, witness_utxo);
- break;
- case PSBT_IN_PARTIAL_SIG:
- {
- // Make sure that the key is the size of pubkey + 1
- if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) {
- throw std::ios_base::failure("Size of key was not the expected size for the type partial signature pubkey");
- }
- // Read in the pubkey from key
- CPubKey pubkey(key.begin() + 1, key.end());
- if (!pubkey.IsFullyValid()) {
- throw std::ios_base::failure("Invalid pubkey");
- }
- if (partial_sigs.count(pubkey.GetID()) > 0) {
- throw std::ios_base::failure("Duplicate Key, input partial signature for pubkey already provided");
- }
-
- // Read in the signature from value
- std::vector<unsigned char> sig;
- s >> sig;
-
- // Add to list
- partial_sigs.emplace(pubkey.GetID(), SigPair(pubkey, std::move(sig)));
- break;
- }
- case PSBT_IN_SIGHASH:
- if (sighash_type > 0) {
- throw std::ios_base::failure("Duplicate Key, input sighash type already provided");
- } else if (key.size() != 1) {
- throw std::ios_base::failure("Sighash type key is more than one byte type");
- }
- UnserializeFromVector(s, sighash_type);
- break;
- case PSBT_IN_REDEEMSCRIPT:
- {
- if (!redeem_script.empty()) {
- throw std::ios_base::failure("Duplicate Key, input redeemScript already provided");
- } else if (key.size() != 1) {
- throw std::ios_base::failure("Input redeemScript key is more than one byte type");
- }
- s >> redeem_script;
- break;
- }
- case PSBT_IN_WITNESSSCRIPT:
- {
- if (!witness_script.empty()) {
- throw std::ios_base::failure("Duplicate Key, input witnessScript already provided");
- } else if (key.size() != 1) {
- throw std::ios_base::failure("Input witnessScript key is more than one byte type");
- }
- s >> witness_script;
- break;
- }
- case PSBT_IN_BIP32_DERIVATION:
- {
- DeserializeHDKeypaths(s, key, hd_keypaths);
- break;
- }
- case PSBT_IN_SCRIPTSIG:
- {
- if (!final_script_sig.empty()) {
- throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided");
- } else if (key.size() != 1) {
- throw std::ios_base::failure("Final scriptSig key is more than one byte type");
- }
- s >> final_script_sig;
- break;
- }
- case PSBT_IN_SCRIPTWITNESS:
- {
- if (!final_script_witness.IsNull()) {
- throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided");
- } else if (key.size() != 1) {
- throw std::ios_base::failure("Final scriptWitness key is more than one byte type");
- }
- UnserializeFromVector(s, final_script_witness.stack);
- break;
- }
- // Unknown stuff
- default:
- if (unknown.count(key) > 0) {
- throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
- }
- // Read in the value
- std::vector<unsigned char> val_bytes;
- s >> val_bytes;
- unknown.emplace(std::move(key), std::move(val_bytes));
- break;
- }
- }
-
- if (!found_sep) {
- throw std::ios_base::failure("Separator is missing at the end of an input map");
- }
- }
-
- template <typename Stream>
- PSBTInput(deserialize_type, Stream& s) {
- Unserialize(s);
- }
-};
-
-/** A structure for PSBTs which contains per output information */
-struct PSBTOutput
-{
- CScript redeem_script;
- CScript witness_script;
- std::map<CPubKey, KeyOriginInfo> hd_keypaths;
- std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
-
- bool IsNull() const;
- void FillSignatureData(SignatureData& sigdata) const;
- void FromSignatureData(const SignatureData& sigdata);
- void Merge(const PSBTOutput& output);
- bool IsSane() const;
- PSBTOutput() {}
-
- template <typename Stream>
- inline void Serialize(Stream& s) const {
- // Write the redeem script
- if (!redeem_script.empty()) {
- SerializeToVector(s, PSBT_OUT_REDEEMSCRIPT);
- s << redeem_script;
- }
-
- // Write the witness script
- if (!witness_script.empty()) {
- SerializeToVector(s, PSBT_OUT_WITNESSSCRIPT);
- s << witness_script;
- }
-
- // Write any hd keypaths
- SerializeHDKeypaths(s, hd_keypaths, PSBT_OUT_BIP32_DERIVATION);
-
- // Write unknown things
- for (auto& entry : unknown) {
- s << entry.first;
- s << entry.second;
- }
-
- s << PSBT_SEPARATOR;
- }
-
-
- template <typename Stream>
- inline void Unserialize(Stream& s) {
- // Read loop
- bool found_sep = false;
- while(!s.empty()) {
- // Read
- std::vector<unsigned char> key;
- s >> key;
-
- // the key is empty if that was actually a separator byte
- // This is a special case for key lengths 0 as those are not allowed (except for separator)
- if (key.empty()) {
- found_sep = true;
- break;
- }
-
- // First byte of key is the type
- unsigned char type = key[0];
-
- // Do stuff based on type
- switch(type) {
- case PSBT_OUT_REDEEMSCRIPT:
- {
- if (!redeem_script.empty()) {
- throw std::ios_base::failure("Duplicate Key, output redeemScript already provided");
- } else if (key.size() != 1) {
- throw std::ios_base::failure("Output redeemScript key is more than one byte type");
- }
- s >> redeem_script;
- break;
- }
- case PSBT_OUT_WITNESSSCRIPT:
- {
- if (!witness_script.empty()) {
- throw std::ios_base::failure("Duplicate Key, output witnessScript already provided");
- } else if (key.size() != 1) {
- throw std::ios_base::failure("Output witnessScript key is more than one byte type");
- }
- s >> witness_script;
- break;
- }
- case PSBT_OUT_BIP32_DERIVATION:
- {
- DeserializeHDKeypaths(s, key, hd_keypaths);
- break;
- }
- // Unknown stuff
- default: {
- if (unknown.count(key) > 0) {
- throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
- }
- // Read in the value
- std::vector<unsigned char> val_bytes;
- s >> val_bytes;
- unknown.emplace(std::move(key), std::move(val_bytes));
- break;
- }
- }
- }
-
- if (!found_sep) {
- throw std::ios_base::failure("Separator is missing at the end of an output map");
- }
- }
-
- template <typename Stream>
- PSBTOutput(deserialize_type, Stream& s) {
- Unserialize(s);
- }
-};
-
-/** A version of CTransaction with the PSBT format*/
-struct PartiallySignedTransaction
-{
- boost::optional<CMutableTransaction> tx;
- std::vector<PSBTInput> inputs;
- std::vector<PSBTOutput> outputs;
- std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
-
- bool IsNull() const;
- void Merge(const PartiallySignedTransaction& psbt);
- bool IsSane() const;
- PartiallySignedTransaction() {}
- PartiallySignedTransaction(const PartiallySignedTransaction& psbt_in) : tx(psbt_in.tx), inputs(psbt_in.inputs), outputs(psbt_in.outputs), unknown(psbt_in.unknown) {}
- explicit PartiallySignedTransaction(const CMutableTransaction& tx);
-
- // Only checks if they refer to the same transaction
- friend bool operator==(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b)
- {
- return a.tx->GetHash() == b.tx->GetHash();
- }
- friend bool operator!=(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b)
- {
- return !(a == b);
- }
-
- template <typename Stream>
- inline void Serialize(Stream& s) const {
-
- // magic bytes
- s << PSBT_MAGIC_BYTES;
-
- // unsigned tx flag
- SerializeToVector(s, PSBT_GLOBAL_UNSIGNED_TX);
-
- // Write serialized tx to a stream
- OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
- SerializeToVector(os, *tx);
-
- // Write the unknown things
- for (auto& entry : unknown) {
- s << entry.first;
- s << entry.second;
- }
-
- // Separator
- s << PSBT_SEPARATOR;
-
- // Write inputs
- for (const PSBTInput& input : inputs) {
- s << input;
- }
- // Write outputs
- for (const PSBTOutput& output : outputs) {
- s << output;
- }
- }
-
-
- template <typename Stream>
- inline void Unserialize(Stream& s) {
- // Read the magic bytes
- uint8_t magic[5];
- s >> magic;
- if (!std::equal(magic, magic + 5, PSBT_MAGIC_BYTES)) {
- throw std::ios_base::failure("Invalid PSBT magic bytes");
- }
-
- // Read global data
- bool found_sep = false;
- while(!s.empty()) {
- // Read
- std::vector<unsigned char> key;
- s >> key;
-
- // the key is empty if that was actually a separator byte
- // This is a special case for key lengths 0 as those are not allowed (except for separator)
- if (key.empty()) {
- found_sep = true;
- break;
- }
-
- // First byte of key is the type
- unsigned char type = key[0];
-
- // Do stuff based on type
- switch(type) {
- case PSBT_GLOBAL_UNSIGNED_TX:
- {
- if (tx) {
- throw std::ios_base::failure("Duplicate Key, unsigned tx already provided");
- } else if (key.size() != 1) {
- throw std::ios_base::failure("Global unsigned tx key is more than one byte type");
- }
- CMutableTransaction mtx;
- // Set the stream to serialize with non-witness since this should always be non-witness
- OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
- UnserializeFromVector(os, mtx);
- tx = std::move(mtx);
- // Make sure that all scriptSigs and scriptWitnesses are empty
- for (const CTxIn& txin : tx->vin) {
- if (!txin.scriptSig.empty() || !txin.scriptWitness.IsNull()) {
- throw std::ios_base::failure("Unsigned tx does not have empty scriptSigs and scriptWitnesses.");
- }
- }
- break;
- }
- // Unknown stuff
- default: {
- if (unknown.count(key) > 0) {
- throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
- }
- // Read in the value
- std::vector<unsigned char> val_bytes;
- s >> val_bytes;
- unknown.emplace(std::move(key), std::move(val_bytes));
- }
- }
- }
-
- if (!found_sep) {
- throw std::ios_base::failure("Separator is missing at the end of the global map");
- }
-
- // Make sure that we got an unsigned tx
- if (!tx) {
- throw std::ios_base::failure("No unsigned transcation was provided");
- }
-
- // Read input data
- unsigned int i = 0;
- while (!s.empty() && i < tx->vin.size()) {
- PSBTInput input;
- s >> input;
- inputs.push_back(input);
-
- // Make sure the non-witness utxo matches the outpoint
- if (input.non_witness_utxo && input.non_witness_utxo->GetHash() != tx->vin[i].prevout.hash) {
- throw std::ios_base::failure("Non-witness UTXO does not match outpoint hash");
- }
- ++i;
- }
- // Make sure that the number of inputs matches the number of inputs in the transaction
- if (inputs.size() != tx->vin.size()) {
- throw std::ios_base::failure("Inputs provided does not match the number of inputs in transaction.");
- }
-
- // Read output data
- i = 0;
- while (!s.empty() && i < tx->vout.size()) {
- PSBTOutput output;
- s >> output;
- outputs.push_back(output);
- ++i;
- }
- // Make sure that the number of outputs matches the number of outputs in the transaction
- if (outputs.size() != tx->vout.size()) {
- throw std::ios_base::failure("Outputs provided does not match the number of outputs in transaction.");
- }
- // Sanity check
- if (!IsSane()) {
- throw std::ios_base::failure("PSBT is not sane.");
- }
- }
-
- template <typename Stream>
- PartiallySignedTransaction(deserialize_type, Stream& s) {
- Unserialize(s);
- }
-};
-
/** Produce a script signature using a generic signature creator. */
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata);
@@ -738,12 +222,6 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType);
bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType);
-/** Checks whether a PSBTInput is already signed. */
-bool PSBTInputSigned(PSBTInput& input);
-
-/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
-bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL);
-
/** Extract signature data from a transaction input, and insert it. */
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout);
void UpdateInput(CTxIn& input, const SignatureData& data);
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 31bfd04b0f..b7d6cd925c 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -8,8 +8,6 @@
#include <crypto/sha256.h>
#include <pubkey.h>
#include <script/script.h>
-#include <util/system.h>
-#include <util/strencodings.h>
typedef std::vector<unsigned char> valtype;
@@ -19,6 +17,10 @@ unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
+ScriptHash::ScriptHash(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
+
+PKHash::PKHash(const CPubKey& pubkey) : uint160(pubkey.GetID()) {}
+
WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
{
CSHA256().Write(in.data(), in.size()).Finalize(begin());
@@ -162,17 +164,17 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
if (!pubKey.IsValid())
return false;
- addressRet = pubKey.GetID();
+ addressRet = PKHash(pubKey);
return true;
}
else if (whichType == TX_PUBKEYHASH)
{
- addressRet = CKeyID(uint160(vSolutions[0]));
+ addressRet = PKHash(uint160(vSolutions[0]));
return true;
}
else if (whichType == TX_SCRIPTHASH)
{
- addressRet = CScriptID(uint160(vSolutions[0]));
+ addressRet = ScriptHash(uint160(vSolutions[0]));
return true;
} else if (whichType == TX_WITNESS_V0_KEYHASH) {
WitnessV0KeyHash hash;
@@ -217,7 +219,7 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::
if (!pubKey.IsValid())
continue;
- CTxDestination address = pubKey.GetID();
+ CTxDestination address = PKHash(pubKey);
addressRet.push_back(address);
}
@@ -250,13 +252,13 @@ public:
return false;
}
- bool operator()(const CKeyID &keyID) const {
+ bool operator()(const PKHash &keyID) const {
script->clear();
*script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
return true;
}
- bool operator()(const CScriptID &scriptID) const {
+ bool operator()(const ScriptHash &scriptID) const {
script->clear();
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
return true;
diff --git a/src/script/standard.h b/src/script/standard.h
index fc20fb6a08..e45e2d92cc 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -73,6 +73,22 @@ public:
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
};
+struct PKHash : public uint160
+{
+ PKHash() : uint160() {}
+ explicit PKHash(const uint160& hash) : uint160(hash) {}
+ explicit PKHash(const CPubKey& pubkey);
+ using uint160::uint160;
+};
+
+struct ScriptHash : public uint160
+{
+ ScriptHash() : uint160() {}
+ explicit ScriptHash(const uint160& hash) : uint160(hash) {}
+ explicit ScriptHash(const CScript& script);
+ using uint160::uint160;
+};
+
struct WitnessV0ScriptHash : public uint256
{
WitnessV0ScriptHash() : uint256() {}
@@ -113,14 +129,14 @@ struct WitnessUnknown
/**
* A txout script template with a specific destination. It is either:
* * CNoDestination: no destination set
- * * CKeyID: TX_PUBKEYHASH destination (P2PKH)
- * * CScriptID: TX_SCRIPTHASH destination (P2SH)
+ * * PKHash: TX_PUBKEYHASH destination (P2PKH)
+ * * ScriptHash: 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, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
+typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
/** Check whether a CTxDestination is a CNoDestination. */
bool IsValidDestination(const CTxDestination& dest);
@@ -153,8 +169,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
* multisig scripts, this populates the addressRet vector with the pubkey IDs
* and nRequiredRet with the n required to spend. For other destinations,
* addressRet is populated with a single value and nRequiredRet is set to 1.
- * Returns true if successful. Currently does not extract address from
- * pay-to-witness scripts.
+ * Returns true if successful.
*
* Note: this function confuses destinations (a subset of CScripts that are
* encodable as an address) with key identifiers (of keys involved in a
diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore
index 87fea161ba..55d325aeef 100644
--- a/src/secp256k1/.gitignore
+++ b/src/secp256k1/.gitignore
@@ -1,5 +1,6 @@
bench_inv
bench_ecdh
+bench_ecmult
bench_sign
bench_verify
bench_schnorr_verify
diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml
index 2439529242..74f658f4d1 100644
--- a/src/secp256k1/.travis.yml
+++ b/src/secp256k1/.travis.yml
@@ -1,5 +1,5 @@
language: c
-sudo: false
+os: linux
addons:
apt:
packages: libgmp-dev
@@ -11,7 +11,7 @@ cache:
- src/java/guava/
env:
global:
- - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no
+ - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no JNI=no
- GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar
matrix:
- SCALAR=32bit RECOVERY=yes
@@ -29,7 +29,7 @@ env:
- BUILD=distcheck
- EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
- EXTRAFLAGS=CFLAGS=-O0
- - BUILD=check-java ECDH=yes EXPERIMENTAL=yes
+ - BUILD=check-java JNI=yes ECDH=yes EXPERIMENTAL=yes
matrix:
fast_finish: true
include:
@@ -65,5 +65,4 @@ before_script: ./autogen.sh
script:
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
- - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
-os: linux
+ - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am
index c071fbe275..9e5b7dcce0 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -42,6 +42,8 @@ noinst_HEADERS += src/field_5x52_asm_impl.h
noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h
noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h
noinst_HEADERS += src/util.h
+noinst_HEADERS += src/scratch.h
+noinst_HEADERS += src/scratch_impl.h
noinst_HEADERS += src/testrand.h
noinst_HEADERS += src/testrand_impl.h
noinst_HEADERS += src/hash.h
@@ -79,7 +81,7 @@ libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES)
noinst_PROGRAMS =
if USE_BENCHMARK
-noinst_PROGRAMS += bench_verify bench_sign bench_internal
+noinst_PROGRAMS += bench_verify bench_sign bench_internal bench_ecmult
bench_verify_SOURCES = src/bench_verify.c
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_sign_SOURCES = src/bench_sign.c
@@ -87,6 +89,9 @@ bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_internal_SOURCES = src/bench_internal.c
bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB)
bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
+bench_ecmult_SOURCES = src/bench_ecmult.c
+bench_ecmult_LDADD = $(SECP_LIBS) $(COMMON_LIB)
+bench_ecmult_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
endif
TESTS =
@@ -109,7 +114,7 @@ exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src $(SECP_INCLUDE
if !ENABLE_COVERAGE
exhaustive_tests_CPPFLAGS += -DVERIFY
endif
-exhaustive_tests_LDADD = $(SECP_LIBS)
+exhaustive_tests_LDADD = $(SECP_LIBS) $(COMMON_LIB)
exhaustive_tests_LDFLAGS = -static
TESTS += exhaustive_tests
endif
@@ -146,7 +151,6 @@ endif
if USE_ECMULT_STATIC_PRECOMPUTATION
CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)
-CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function
gen_context_OBJECTS = gen_context.o
gen_context_BIN = gen_context$(BUILD_EXEEXT)
@@ -154,11 +158,12 @@ gen_%.o: src/gen_%.c
$(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@
$(gen_context_BIN): $(gen_context_OBJECTS)
- $(CC_FOR_BUILD) $^ -o $@
+ $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $^ -o $@
$(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h
$(tests_OBJECTS): src/ecmult_static_context.h
$(bench_internal_OBJECTS): src/ecmult_static_context.h
+$(bench_ecmult_OBJECTS): src/ecmult_static_context.h
src/ecmult_static_context.h: $(gen_context_BIN)
./$(gen_context_BIN)
diff --git a/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
index 1fc3627614..cdc78d87d4 100644
--- a/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
+++ b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
@@ -1,5 +1,5 @@
# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
+# https://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
# ===========================================================================
#
# SYNOPSIS
@@ -44,7 +44,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 10
+#serial 14
AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR])
AC_DEFUN([AX_JNI_INCLUDE_DIR],[
@@ -66,9 +66,17 @@ else
fi
case "$host_os" in
- darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
- _JINC="$_JTOPDIR/Headers";;
- *) _JINC="$_JTOPDIR/include";;
+ darwin*) # Apple Java headers are inside the Xcode bundle.
+ macos_version=$(sw_vers -productVersion | sed -n -e 's/^@<:@0-9@:>@*.\(@<:@0-9@:>@*\).@<:@0-9@:>@*/\1/p')
+ if @<:@ "$macos_version" -gt "7" @:>@; then
+ _JTOPDIR="$(xcrun --show-sdk-path)/System/Library/Frameworks/JavaVM.framework"
+ _JINC="$_JTOPDIR/Headers"
+ else
+ _JTOPDIR="/System/Library/Frameworks/JavaVM.framework"
+ _JINC="$_JTOPDIR/Headers"
+ fi
+ ;;
+ *) _JINC="$_JTOPDIR/include";;
esac
_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR])
_AS_ECHO_LOG([_JINC=$_JINC])
@@ -76,30 +84,27 @@ _AS_ECHO_LOG([_JINC=$_JINC])
# On Mac OS X 10.6.4, jni.h is a symlink:
# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h
# -> ../../CurrentJDK/Headers/jni.h.
-
AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path,
[
-if test -f "$_JINC/jni.h"; then
- ac_cv_jni_header_path="$_JINC"
- JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
-else
- _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
- if test -f "$_JTOPDIR/include/jni.h"; then
- ac_cv_jni_header_path="$_JTOPDIR/include"
+ if test -f "$_JINC/jni.h"; then
+ ac_cv_jni_header_path="$_JINC"
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
else
- ac_cv_jni_header_path=none
+ _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
+ if test -f "$_JTOPDIR/include/jni.h"; then
+ ac_cv_jni_header_path="$_JTOPDIR/include"
+ JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
+ else
+ ac_cv_jni_header_path=none
+ fi
fi
-fi
])
-
-
# get the likely subdirectories for system specific java includes
case "$host_os" in
bsdi*) _JNI_INC_SUBDIRS="bsdos";;
-darwin*) _JNI_INC_SUBDIRS="darwin";;
freebsd*) _JNI_INC_SUBDIRS="freebsd";;
+darwin*) _JNI_INC_SUBDIRS="darwin";;
linux*) _JNI_INC_SUBDIRS="linux genunix";;
osf*) _JNI_INC_SUBDIRS="alpha";;
solaris*) _JNI_INC_SUBDIRS="solaris";;
@@ -112,9 +117,9 @@ if test "x$ac_cv_jni_header_path" != "xnone"; then
# add any subdirectories that are present
for JINCSUBDIR in $_JNI_INC_SUBDIRS
do
- if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
- JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
- fi
+ if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
+ JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
+ fi
done
fi
])
diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
index b74acb8c13..3b3975cbdd 100644
--- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4
+++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
@@ -48,7 +48,6 @@ if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
EC_KEY_free(eckey);
ECDSA_SIG *sig_openssl;
sig_openssl = ECDSA_SIG_new();
- (void)sig_openssl->r;
ECDSA_SIG_free(sig_openssl);
]])],[has_openssl_ec=yes],[has_openssl_ec=no])
AC_MSG_RESULT([$has_openssl_ec])
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index e5fcbcb4ed..3b7a328c8a 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -85,9 +85,9 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
])
AC_ARG_ENABLE(benchmark,
- AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is no)]),
+ AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is yes)]),
[use_benchmark=$enableval],
- [use_benchmark=no])
+ [use_benchmark=yes])
AC_ARG_ENABLE(coverage,
AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]),
@@ -135,9 +135,9 @@ AC_ARG_ENABLE(module_recovery,
[enable_module_recovery=no])
AC_ARG_ENABLE(jni,
- AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]),
+ AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is no)]),
[use_jni=$enableval],
- [use_jni=auto])
+ [use_jni=no])
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto])
@@ -153,12 +153,6 @@ AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto]
AC_CHECK_TYPES([__int128])
-AC_MSG_CHECKING([for __builtin_expect])
-AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])],
- [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_EXPECT,1,[Define this symbol if __builtin_expect is available]) ],
- [ AC_MSG_RESULT([no])
- ])
-
if test x"$enable_coverage" = x"yes"; then
AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code])
CFLAGS="$CFLAGS -O0 --coverage"
@@ -168,27 +162,54 @@ else
fi
if test x"$use_ecmult_static_precomputation" != x"no"; then
+ # Temporarily switch to an environment for the native compiler
save_cross_compiling=$cross_compiling
cross_compiling=no
- TEMP_CC="$CC"
+ SAVE_CC="$CC"
CC="$CC_FOR_BUILD"
- AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}])
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS_FOR_BUILD"
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS_FOR_BUILD"
+ SAVE_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS_FOR_BUILD"
+
+ warn_CFLAGS_FOR_BUILD="-Wall -Wextra -Wno-unused-function"
+ saved_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $warn_CFLAGS_FOR_BUILD"
+ AC_MSG_CHECKING([if native ${CC_FOR_BUILD} supports ${warn_CFLAGS_FOR_BUILD}])
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
+ [ AC_MSG_RESULT([yes]) ],
+ [ AC_MSG_RESULT([no])
+ CFLAGS="$saved_CFLAGS"
+ ])
+
+ AC_MSG_CHECKING([for working native compiler: ${CC_FOR_BUILD}])
AC_RUN_IFELSE(
- [AC_LANG_PROGRAM([], [return 0])],
+ [AC_LANG_PROGRAM([], [])],
[working_native_cc=yes],
[working_native_cc=no],[dnl])
- CC="$TEMP_CC"
+
+ CFLAGS_FOR_BUILD="$CFLAGS"
+
+ # Restore the environment
cross_compiling=$save_cross_compiling
+ CC="$SAVE_CC"
+ CFLAGS="$SAVE_CFLAGS"
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
if test x"$working_native_cc" = x"no"; then
+ AC_MSG_RESULT([no])
set_precomp=no
+ m4_define([please_set_for_build], [Please set CC_FOR_BUILD, CFLAGS_FOR_BUILD, CPPFLAGS_FOR_BUILD, and/or LDFLAGS_FOR_BUILD.])
if test x"$use_ecmult_static_precomputation" = x"yes"; then
- AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
+ AC_MSG_ERROR([native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
else
- AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
+ AC_MSG_WARN([Disabling statically generated ecmult table because the native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
fi
else
- AC_MSG_RESULT([ok])
+ AC_MSG_RESULT([yes])
set_precomp=yes
fi
else
@@ -441,17 +462,6 @@ if test x"$use_external_asm" = x"yes"; then
AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
fi
-AC_MSG_NOTICE([Using static precomputation: $set_precomp])
-AC_MSG_NOTICE([Using assembly optimizations: $set_asm])
-AC_MSG_NOTICE([Using field implementation: $set_field])
-AC_MSG_NOTICE([Using bignum implementation: $set_bignum])
-AC_MSG_NOTICE([Using scalar implementation: $set_scalar])
-AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism])
-AC_MSG_NOTICE([Building for coverage analysis: $enable_coverage])
-AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
-AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery])
-AC_MSG_NOTICE([Using jni: $use_jni])
-
if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([******])
AC_MSG_NOTICE([WARNING: experimental build])
@@ -481,7 +491,7 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
-AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
+AM_CONDITIONAL([USE_JNI], [test x"$use_jni" = x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
@@ -491,3 +501,24 @@ unset PKG_CONFIG_PATH
PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP"
AC_OUTPUT
+
+echo
+echo "Build Options:"
+echo " with endomorphism = $use_endomorphism"
+echo " with ecmult precomp = $set_precomp"
+echo " with jni = $use_jni"
+echo " with benchmarks = $use_benchmark"
+echo " with coverage = $enable_coverage"
+echo " module ecdh = $enable_module_ecdh"
+echo " module recovery = $enable_module_recovery"
+echo
+echo " asm = $set_asm"
+echo " bignum = $set_bignum"
+echo " field = $set_field"
+echo " scalar = $set_scalar"
+echo
+echo " CC = $CC"
+echo " CFLAGS = $CFLAGS"
+echo " CPPFLAGS = $CPPFLAGS"
+echo " LDFLAGS = $LDFLAGS"
+echo
diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h
index 3e9c098d19..43af09c330 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -42,6 +42,19 @@ extern "C" {
*/
typedef struct secp256k1_context_struct secp256k1_context;
+/** Opaque data structure that holds rewriteable "scratch space"
+ *
+ * The purpose of this structure is to replace dynamic memory allocations,
+ * because we target architectures where this may not be available. It is
+ * essentially a resizable (within specified parameters) block of bytes,
+ * which is initially created either by memory allocation or TODO as a pointer
+ * into some fixed rewritable space.
+ *
+ * Unlike the context object, this cannot safely be shared between threads
+ * without additional synchronization logic.
+ */
+typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space;
+
/** Opaque data structure that holds a parsed and valid public key.
*
* The exact representation of data inside is implementation defined and not
@@ -166,6 +179,13 @@ typedef int (*secp256k1_nonce_function)(
#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06
#define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07
+/** A simple secp256k1 context object with no precomputed tables. These are useful for
+ * type serialization/parsing functions which require a context object to maintain
+ * API consistency, but currently do not require expensive precomputations or dynamic
+ * allocations.
+ */
+SECP256K1_API extern const secp256k1_context *secp256k1_context_no_precomp;
+
/** Create a secp256k1 context object.
*
* Returns: a newly created context object.
@@ -243,6 +263,26 @@ SECP256K1_API void secp256k1_context_set_error_callback(
const void* data
) SECP256K1_ARG_NONNULL(1);
+/** Create a secp256k1 scratch space object.
+ *
+ * Returns: a newly created scratch space.
+ * Args: ctx: an existing context object (cannot be NULL)
+ * In: max_size: maximum amount of memory to allocate
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create(
+ const secp256k1_context* ctx,
+ size_t max_size
+) SECP256K1_ARG_NONNULL(1);
+
+/** Destroy a secp256k1 scratch space.
+ *
+ * The pointer may not be used afterwards.
+ * Args: scratch: space to destroy
+ */
+SECP256K1_API void secp256k1_scratch_space_destroy(
+ secp256k1_scratch_space* scratch
+);
+
/** Parse a variable-length public key into the pubkey object.
*
* Returns: 1 if the public key was fully valid.
@@ -498,7 +538,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
*
* Returns: 1 always
* Args: ctx: pointer to a context object
- * In/Out: pubkey: pointer to the public key to be negated (cannot be NULL)
+ * In/Out: seckey: pointer to the 32-byte private key to be negated (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_negate(
const secp256k1_context* ctx,
@@ -575,7 +615,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Updates the context randomization to protect against side-channel leakage.
- * Returns: 1: randomization successfully updated
+ * Returns: 1: randomization successfully updated or nothing to randomize
* 0: error
* Args: ctx: pointer to a context object (cannot be NULL)
* In: seed32: pointer to a 32-byte random seed (NULL resets to initial state)
@@ -590,6 +630,11 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
* that it does not affect function results, but shields against attacks which
* rely on any input-dependent behaviour.
*
+ * This function has currently an effect only on contexts initialized for signing
+ * because randomization is currently used only for signing. However, this is not
+ * guaranteed and may change in the future. It is safe to call this function on
+ * contexts not initialized for signing; then it will have no effect and return 1.
+ *
* You should call this after secp256k1_context_create or
* secp256k1_context_clone, and may call this repeatedly afterwards.
*/
diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h
index 88492dc1a4..df5fde235c 100644
--- a/src/secp256k1/include/secp256k1_ecdh.h
+++ b/src/secp256k1/include/secp256k1_ecdh.h
@@ -7,21 +7,45 @@
extern "C" {
#endif
+/** A pointer to a function that applies hash function to a point
+ *
+ * Returns: 1 if a point was successfully hashed. 0 will cause ecdh to fail
+ * Out: output: pointer to an array to be filled by the function
+ * In: x: pointer to a 32-byte x coordinate
+ * y: pointer to a 32-byte y coordinate
+ * data: Arbitrary data pointer that is passed through
+ */
+typedef int (*secp256k1_ecdh_hash_function)(
+ unsigned char *output,
+ const unsigned char *x,
+ const unsigned char *y,
+ void *data
+);
+
+/** An implementation of SHA256 hash function that applies to compressed public key. */
+SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256;
+
+/** A default ecdh hash function (currently equal to secp256k1_ecdh_hash_function_sha256). */
+SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default;
+
/** Compute an EC Diffie-Hellman secret in constant time
* Returns: 1: exponentiation was successful
* 0: scalar was invalid (zero or overflow)
* Args: ctx: pointer to a context object (cannot be NULL)
- * Out: result: a 32-byte array which will be populated by an ECDH
- * secret computed from the point and scalar
+ * Out: output: pointer to an array to be filled by the function
* In: pubkey: a pointer to a secp256k1_pubkey containing an
* initialized public key
* privkey: a 32-byte scalar with which to multiply the point
+ * hashfp: pointer to a hash function. If NULL, secp256k1_ecdh_hash_function_sha256 is used
+ * data: Arbitrary data pointer that is passed through
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh(
const secp256k1_context* ctx,
- unsigned char *result,
+ unsigned char *output,
const secp256k1_pubkey *pubkey,
- const unsigned char *privkey
+ const unsigned char *privkey,
+ secp256k1_ecdh_hash_function hashfp,
+ void *data
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
#ifdef __cplusplus
diff --git a/src/secp256k1/libsecp256k1.pc.in b/src/secp256k1/libsecp256k1.pc.in
index a0d006f113..694e98eef5 100644
--- a/src/secp256k1/libsecp256k1.pc.in
+++ b/src/secp256k1/libsecp256k1.pc.in
@@ -8,6 +8,6 @@ Description: Optimized C library for EC operations on curve secp256k1
URL: https://github.com/bitcoin-core/secp256k1
Version: @PACKAGE_VERSION@
Cflags: -I${includedir}
-Libs.private: @SECP_LIBS@
Libs: -L${libdir} -lsecp256k1
+Libs.private: @SECP_LIBS@
diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h
index d5ebe01301..5b59783f68 100644
--- a/src/secp256k1/src/bench.h
+++ b/src/secp256k1/src/bench.h
@@ -8,6 +8,7 @@
#define SECP256K1_BENCH_H
#include <stdio.h>
+#include <string.h>
#include <math.h>
#include "sys/time.h"
@@ -63,4 +64,19 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v
printf("us\n");
}
+int have_flag(int argc, char** argv, char *flag) {
+ char** argm = argv + argc;
+ argv++;
+ if (argv == argm) {
+ return 1;
+ }
+ while (argv != NULL && argv != argm) {
+ if (strcmp(*argv, flag) == 0) {
+ return 1;
+ }
+ argv++;
+ }
+ return 0;
+}
+
#endif /* SECP256K1_BENCH_H */
diff --git a/src/secp256k1/src/bench_ecdh.c b/src/secp256k1/src/bench_ecdh.c
index cde5e2dbb4..c1dd5a6ac9 100644
--- a/src/secp256k1/src/bench_ecdh.c
+++ b/src/secp256k1/src/bench_ecdh.c
@@ -15,11 +15,11 @@ typedef struct {
secp256k1_context *ctx;
secp256k1_pubkey point;
unsigned char scalar[32];
-} bench_ecdh_t;
+} bench_ecdh_data;
static void bench_ecdh_setup(void* arg) {
int i;
- bench_ecdh_t *data = (bench_ecdh_t*)arg;
+ bench_ecdh_data *data = (bench_ecdh_data*)arg;
const unsigned char point[] = {
0x03,
0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06,
@@ -39,15 +39,15 @@ static void bench_ecdh_setup(void* arg) {
static void bench_ecdh(void* arg) {
int i;
unsigned char res[32];
- bench_ecdh_t *data = (bench_ecdh_t*)arg;
+ bench_ecdh_data *data = (bench_ecdh_data*)arg;
for (i = 0; i < 20000; i++) {
- CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar) == 1);
+ CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar, NULL, NULL) == 1);
}
}
int main(void) {
- bench_ecdh_t data;
+ bench_ecdh_data data;
run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, 20000);
return 0;
diff --git a/src/secp256k1/src/bench_ecmult.c b/src/secp256k1/src/bench_ecmult.c
new file mode 100644
index 0000000000..6d0ed1f436
--- /dev/null
+++ b/src/secp256k1/src/bench_ecmult.c
@@ -0,0 +1,207 @@
+/**********************************************************************
+ * 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 <stdio.h>
+
+#include "include/secp256k1.h"
+
+#include "util.h"
+#include "hash_impl.h"
+#include "num_impl.h"
+#include "field_impl.h"
+#include "group_impl.h"
+#include "scalar_impl.h"
+#include "ecmult_impl.h"
+#include "bench.h"
+#include "secp256k1.c"
+
+#define POINTS 32768
+#define ITERS 10000
+
+typedef struct {
+ /* Setup once in advance */
+ secp256k1_context* ctx;
+ secp256k1_scratch_space* scratch;
+ secp256k1_scalar* scalars;
+ secp256k1_ge* pubkeys;
+ secp256k1_scalar* seckeys;
+ secp256k1_gej* expected_output;
+ secp256k1_ecmult_multi_func ecmult_multi;
+
+ /* Changes per test */
+ size_t count;
+ int includes_g;
+
+ /* Changes per test iteration */
+ size_t offset1;
+ size_t offset2;
+
+ /* Test output. */
+ secp256k1_gej* output;
+} bench_data;
+
+static int bench_callback(secp256k1_scalar* sc, secp256k1_ge* ge, size_t idx, void* arg) {
+ bench_data* data = (bench_data*)arg;
+ if (data->includes_g) ++idx;
+ if (idx == 0) {
+ *sc = data->scalars[data->offset1];
+ *ge = secp256k1_ge_const_g;
+ } else {
+ *sc = data->scalars[(data->offset1 + idx) % POINTS];
+ *ge = data->pubkeys[(data->offset2 + idx - 1) % POINTS];
+ }
+ return 1;
+}
+
+static void bench_ecmult(void* arg) {
+ bench_data* data = (bench_data*)arg;
+
+ size_t count = data->count;
+ int includes_g = data->includes_g;
+ size_t iters = 1 + ITERS / count;
+ size_t iter;
+
+ for (iter = 0; iter < iters; ++iter) {
+ data->ecmult_multi(&data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g);
+ data->offset1 = (data->offset1 + count) % POINTS;
+ data->offset2 = (data->offset2 + count - 1) % POINTS;
+ }
+}
+
+static void bench_ecmult_setup(void* arg) {
+ bench_data* data = (bench_data*)arg;
+ data->offset1 = (data->count * 0x537b7f6f + 0x8f66a481) % POINTS;
+ data->offset2 = (data->count * 0x7f6f537b + 0x6a1a8f49) % POINTS;
+}
+
+static void bench_ecmult_teardown(void* arg) {
+ bench_data* data = (bench_data*)arg;
+ size_t iters = 1 + ITERS / data->count;
+ size_t iter;
+ /* Verify the results in teardown, to avoid doing comparisons while benchmarking. */
+ for (iter = 0; iter < iters; ++iter) {
+ secp256k1_gej tmp;
+ secp256k1_gej_add_var(&tmp, &data->output[iter], &data->expected_output[iter], NULL);
+ CHECK(secp256k1_gej_is_infinity(&tmp));
+ }
+}
+
+static void generate_scalar(uint32_t num, secp256k1_scalar* scalar) {
+ secp256k1_sha256 sha256;
+ unsigned char c[11] = {'e', 'c', 'm', 'u', 'l', 't', 0, 0, 0, 0};
+ unsigned char buf[32];
+ int overflow = 0;
+ c[6] = num;
+ c[7] = num >> 8;
+ c[8] = num >> 16;
+ c[9] = num >> 24;
+ secp256k1_sha256_initialize(&sha256);
+ secp256k1_sha256_write(&sha256, c, sizeof(c));
+ secp256k1_sha256_finalize(&sha256, buf);
+ secp256k1_scalar_set_b32(scalar, buf, &overflow);
+ CHECK(!overflow);
+}
+
+static void run_test(bench_data* data, size_t count, int includes_g) {
+ char str[32];
+ static const secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
+ size_t iters = 1 + ITERS / count;
+ size_t iter;
+
+ data->count = count;
+ data->includes_g = includes_g;
+
+ /* Compute (the negation of) the expected results directly. */
+ data->offset1 = (data->count * 0x537b7f6f + 0x8f66a481) % POINTS;
+ data->offset2 = (data->count * 0x7f6f537b + 0x6a1a8f49) % POINTS;
+ for (iter = 0; iter < iters; ++iter) {
+ secp256k1_scalar tmp;
+ secp256k1_scalar total = data->scalars[(data->offset1++) % POINTS];
+ size_t i = 0;
+ for (i = 0; i + 1 < count; ++i) {
+ secp256k1_scalar_mul(&tmp, &data->seckeys[(data->offset2++) % POINTS], &data->scalars[(data->offset1++) % POINTS]);
+ secp256k1_scalar_add(&total, &total, &tmp);
+ }
+ secp256k1_scalar_negate(&total, &total);
+ secp256k1_ecmult(&data->ctx->ecmult_ctx, &data->expected_output[iter], NULL, &zero, &total);
+ }
+
+ /* Run the benchmark. */
+ sprintf(str, includes_g ? "ecmult_%ig" : "ecmult_%i", (int)count);
+ run_benchmark(str, bench_ecmult, bench_ecmult_setup, bench_ecmult_teardown, data, 10, count * (1 + ITERS / count));
+}
+
+int main(int argc, char **argv) {
+ bench_data data;
+ int i, p;
+ secp256k1_gej* pubkeys_gej;
+ size_t scratch_size;
+
+ data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16;
+ data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size);
+ data.ecmult_multi = secp256k1_ecmult_multi_var;
+
+ if (argc > 1) {
+ if(have_flag(argc, argv, "pippenger_wnaf")) {
+ printf("Using pippenger_wnaf:\n");
+ data.ecmult_multi = secp256k1_ecmult_pippenger_batch_single;
+ } else if(have_flag(argc, argv, "strauss_wnaf")) {
+ printf("Using strauss_wnaf:\n");
+ data.ecmult_multi = secp256k1_ecmult_strauss_batch_single;
+ } else if(have_flag(argc, argv, "simple")) {
+ printf("Using simple algorithm:\n");
+ data.ecmult_multi = secp256k1_ecmult_multi_var;
+ secp256k1_scratch_space_destroy(data.scratch);
+ data.scratch = NULL;
+ } else {
+ fprintf(stderr, "%s: unrecognized argument '%s'.\n", argv[0], argv[1]);
+ fprintf(stderr, "Use 'pippenger_wnaf', 'strauss_wnaf', 'simple' or no argument to benchmark a combined algorithm.\n");
+ return 1;
+ }
+ }
+
+ /* Allocate stuff */
+ data.scalars = malloc(sizeof(secp256k1_scalar) * POINTS);
+ data.seckeys = malloc(sizeof(secp256k1_scalar) * POINTS);
+ data.pubkeys = malloc(sizeof(secp256k1_ge) * POINTS);
+ data.expected_output = malloc(sizeof(secp256k1_gej) * (ITERS + 1));
+ data.output = malloc(sizeof(secp256k1_gej) * (ITERS + 1));
+
+ /* Generate a set of scalars, and private/public keypairs. */
+ pubkeys_gej = malloc(sizeof(secp256k1_gej) * POINTS);
+ secp256k1_gej_set_ge(&pubkeys_gej[0], &secp256k1_ge_const_g);
+ secp256k1_scalar_set_int(&data.seckeys[0], 1);
+ for (i = 0; i < POINTS; ++i) {
+ generate_scalar(i, &data.scalars[i]);
+ if (i) {
+ secp256k1_gej_double_var(&pubkeys_gej[i], &pubkeys_gej[i - 1], NULL);
+ secp256k1_scalar_add(&data.seckeys[i], &data.seckeys[i - 1], &data.seckeys[i - 1]);
+ }
+ }
+ secp256k1_ge_set_all_gej_var(data.pubkeys, pubkeys_gej, POINTS);
+ free(pubkeys_gej);
+
+ for (i = 1; i <= 8; ++i) {
+ run_test(&data, i, 1);
+ }
+
+ for (p = 0; p <= 11; ++p) {
+ for (i = 9; i <= 16; ++i) {
+ run_test(&data, i << p, 1);
+ }
+ }
+ secp256k1_context_destroy(data.ctx);
+ if (data.scratch != NULL) {
+ secp256k1_scratch_space_destroy(data.scratch);
+ }
+ free(data.scalars);
+ free(data.pubkeys);
+ free(data.seckeys);
+ free(data.output);
+ free(data.expected_output);
+
+ return(0);
+}
diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c
index 0809f77bda..9071724331 100644
--- a/src/secp256k1/src/bench_internal.c
+++ b/src/secp256k1/src/bench_internal.c
@@ -25,10 +25,10 @@ typedef struct {
secp256k1_gej gej_x, gej_y;
unsigned char data[64];
int wnaf[256];
-} bench_inv_t;
+} bench_inv;
void bench_setup(void* arg) {
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
static const unsigned char init_x[32] = {
0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13,
@@ -58,7 +58,7 @@ void bench_setup(void* arg) {
void bench_scalar_add(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 2000000; i++) {
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
@@ -67,7 +67,7 @@ void bench_scalar_add(void* arg) {
void bench_scalar_negate(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 2000000; i++) {
secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x);
@@ -76,7 +76,7 @@ void bench_scalar_negate(void* arg) {
void bench_scalar_sqr(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x);
@@ -85,7 +85,7 @@ void bench_scalar_sqr(void* arg) {
void bench_scalar_mul(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y);
@@ -95,7 +95,7 @@ void bench_scalar_mul(void* arg) {
#ifdef USE_ENDOMORPHISM
void bench_scalar_split(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_scalar l, r;
@@ -107,7 +107,7 @@ void bench_scalar_split(void* arg) {
void bench_scalar_inverse(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 2000; i++) {
secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x);
@@ -117,7 +117,7 @@ void bench_scalar_inverse(void* arg) {
void bench_scalar_inverse_var(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 2000; i++) {
secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x);
@@ -127,7 +127,7 @@ void bench_scalar_inverse_var(void* arg) {
void bench_field_normalize(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 2000000; i++) {
secp256k1_fe_normalize(&data->fe_x);
@@ -136,7 +136,7 @@ void bench_field_normalize(void* arg) {
void bench_field_normalize_weak(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 2000000; i++) {
secp256k1_fe_normalize_weak(&data->fe_x);
@@ -145,7 +145,7 @@ void bench_field_normalize_weak(void* arg) {
void bench_field_mul(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y);
@@ -154,7 +154,7 @@ void bench_field_mul(void* arg) {
void bench_field_sqr(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_fe_sqr(&data->fe_x, &data->fe_x);
@@ -163,7 +163,7 @@ void bench_field_sqr(void* arg) {
void bench_field_inverse(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_fe_inv(&data->fe_x, &data->fe_x);
@@ -173,7 +173,7 @@ void bench_field_inverse(void* arg) {
void bench_field_inverse_var(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_fe_inv_var(&data->fe_x, &data->fe_x);
@@ -183,17 +183,19 @@ void bench_field_inverse_var(void* arg) {
void bench_field_sqrt(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
+ secp256k1_fe t;
for (i = 0; i < 20000; i++) {
- secp256k1_fe_sqrt(&data->fe_x, &data->fe_x);
+ t = data->fe_x;
+ secp256k1_fe_sqrt(&data->fe_x, &t);
secp256k1_fe_add(&data->fe_x, &data->fe_y);
}
}
void bench_group_double_var(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL);
@@ -202,7 +204,7 @@ void bench_group_double_var(void* arg) {
void bench_group_add_var(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL);
@@ -211,7 +213,7 @@ void bench_group_add_var(void* arg) {
void bench_group_add_affine(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y);
@@ -220,7 +222,7 @@ void bench_group_add_affine(void* arg) {
void bench_group_add_affine_var(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL);
@@ -229,7 +231,7 @@ void bench_group_add_affine_var(void* arg) {
void bench_group_jacobi_var(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_gej_has_quad_y_var(&data->gej_x);
@@ -238,7 +240,7 @@ void bench_group_jacobi_var(void* arg) {
void bench_ecmult_wnaf(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A);
@@ -248,10 +250,10 @@ void bench_ecmult_wnaf(void* arg) {
void bench_wnaf_const(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) {
- secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A);
+ secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A, 256);
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
}
@@ -259,8 +261,8 @@ void bench_wnaf_const(void* arg) {
void bench_sha256(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
- secp256k1_sha256_t sha;
+ bench_inv *data = (bench_inv*)arg;
+ secp256k1_sha256 sha;
for (i = 0; i < 20000; i++) {
secp256k1_sha256_initialize(&sha);
@@ -271,8 +273,8 @@ void bench_sha256(void* arg) {
void bench_hmac_sha256(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
- secp256k1_hmac_sha256_t hmac;
+ bench_inv *data = (bench_inv*)arg;
+ secp256k1_hmac_sha256 hmac;
for (i = 0; i < 20000; i++) {
secp256k1_hmac_sha256_initialize(&hmac, data->data, 32);
@@ -283,8 +285,8 @@ void bench_hmac_sha256(void* arg) {
void bench_rfc6979_hmac_sha256(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
- secp256k1_rfc6979_hmac_sha256_t rng;
+ bench_inv *data = (bench_inv*)arg;
+ secp256k1_rfc6979_hmac_sha256 rng;
for (i = 0; i < 20000; i++) {
secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64);
@@ -311,7 +313,7 @@ void bench_context_sign(void* arg) {
#ifndef USE_NUM_NONE
void bench_num_jacobi(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
secp256k1_num nx, norder;
secp256k1_scalar_get_num(&nx, &data->scalar_x);
@@ -324,23 +326,8 @@ void bench_num_jacobi(void* arg) {
}
#endif
-int have_flag(int argc, char** argv, char *flag) {
- char** argm = argv + argc;
- argv++;
- if (argv == argm) {
- return 1;
- }
- while (argv != NULL && argv != argm) {
- if (strcmp(*argv, flag) == 0) {
- return 1;
- }
- argv++;
- }
- return 0;
-}
-
int main(int argc, char **argv) {
- bench_inv_t data;
+ bench_inv data;
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, 2000000);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, 2000000);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, 200000);
diff --git a/src/secp256k1/src/bench_recover.c b/src/secp256k1/src/bench_recover.c
index 6489378cc6..b806eed94e 100644
--- a/src/secp256k1/src/bench_recover.c
+++ b/src/secp256k1/src/bench_recover.c
@@ -13,11 +13,11 @@ typedef struct {
secp256k1_context *ctx;
unsigned char msg[32];
unsigned char sig[64];
-} bench_recover_t;
+} bench_recover_data;
void bench_recover(void* arg) {
int i;
- bench_recover_t *data = (bench_recover_t*)arg;
+ bench_recover_data *data = (bench_recover_data*)arg;
secp256k1_pubkey pubkey;
unsigned char pubkeyc[33];
@@ -38,7 +38,7 @@ void bench_recover(void* arg) {
void bench_recover_setup(void* arg) {
int i;
- bench_recover_t *data = (bench_recover_t*)arg;
+ bench_recover_data *data = (bench_recover_data*)arg;
for (i = 0; i < 32; i++) {
data->msg[i] = 1 + i;
@@ -49,7 +49,7 @@ void bench_recover_setup(void* arg) {
}
int main(void) {
- bench_recover_t data;
+ bench_recover_data data;
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
diff --git a/src/secp256k1/src/bench_sign.c b/src/secp256k1/src/bench_sign.c
index ed7224d757..544b43963c 100644
--- a/src/secp256k1/src/bench_sign.c
+++ b/src/secp256k1/src/bench_sign.c
@@ -12,11 +12,11 @@ typedef struct {
secp256k1_context* ctx;
unsigned char msg[32];
unsigned char key[32];
-} bench_sign_t;
+} bench_sign;
static void bench_sign_setup(void* arg) {
int i;
- bench_sign_t *data = (bench_sign_t*)arg;
+ bench_sign *data = (bench_sign*)arg;
for (i = 0; i < 32; i++) {
data->msg[i] = i + 1;
@@ -26,9 +26,9 @@ static void bench_sign_setup(void* arg) {
}
}
-static void bench_sign(void* arg) {
+static void bench_sign_run(void* arg) {
int i;
- bench_sign_t *data = (bench_sign_t*)arg;
+ bench_sign *data = (bench_sign*)arg;
unsigned char sig[74];
for (i = 0; i < 20000; i++) {
@@ -45,11 +45,11 @@ static void bench_sign(void* arg) {
}
int main(void) {
- bench_sign_t data;
+ bench_sign data;
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
- run_benchmark("ecdsa_sign", bench_sign, bench_sign_setup, NULL, &data, 10, 20000);
+ run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, 20000);
secp256k1_context_destroy(data.ctx);
return 0;
diff --git a/src/secp256k1/src/eckey_impl.h b/src/secp256k1/src/eckey_impl.h
index 1ab9a68ec0..7c5b789325 100644
--- a/src/secp256k1/src/eckey_impl.h
+++ b/src/secp256k1/src/eckey_impl.h
@@ -18,7 +18,7 @@ static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char
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] == SECP256K1_TAG_PUBKEY_ODD);
- } else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) {
+ } else if (size == 65 && (pub[0] == SECP256K1_TAG_PUBKEY_UNCOMPRESSED || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) {
secp256k1_fe x, y;
if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) {
return 0;
diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h
index 6d44aba60b..3d75a960f4 100644
--- a/src/secp256k1/src/ecmult.h
+++ b/src/secp256k1/src/ecmult.h
@@ -1,5 +1,5 @@
/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
@@ -9,6 +9,8 @@
#include "num.h"
#include "group.h"
+#include "scalar.h"
+#include "scratch.h"
typedef struct {
/* For accelerating the computation of a*P + b*G: */
@@ -28,4 +30,19 @@ 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);
+typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data);
+
+/**
+ * Multi-multiply: R = inp_g_sc * G + sum_i ni * Ai.
+ * Chooses the right algorithm for a given number of points and scratch space
+ * size. Resets and overwrites the given scratch space. If the points do not
+ * fit in the scratch space the algorithm is repeatedly run with batches of
+ * points. If no scratch space is given then a simple algorithm is used that
+ * simply multiplies the points with the corresponding scalars and adds them up.
+ * Returns: 1 on success (including when inp_g_sc is NULL and n is 0)
+ * 0 if there is not enough scratch space for a single point or
+ * callback returns 0
+ */
+static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n);
+
#endif /* SECP256K1_ECMULT_H */
diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h
index 72bf7d7582..d4804b8b68 100644
--- a/src/secp256k1/src/ecmult_const.h
+++ b/src/secp256k1/src/ecmult_const.h
@@ -10,6 +10,8 @@
#include "scalar.h"
#include "group.h"
-static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q);
+/* Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus
+ * one because we internally sometimes add 2 to the number during the WNAF conversion. */
+static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits);
#endif /* SECP256K1_ECMULT_CONST_H */
diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h
index 7d7a172b7b..8411752eb0 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -12,13 +12,6 @@
#include "ecmult_const.h"
#include "ecmult_impl.h"
-#ifdef USE_ENDOMORPHISM
- #define WNAF_BITS 128
-#else
- #define WNAF_BITS 256
-#endif
-#define WNAF_SIZE(w) ((WNAF_BITS + (w) - 1) / (w))
-
/* This is like `ECMULT_TABLE_GET_GE` but is constant time */
#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \
int m; \
@@ -55,7 +48,7 @@
*
* Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335
*/
-static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
+static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size) {
int global_sign;
int skew = 0;
int word = 0;
@@ -74,9 +67,14 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
* and we'd lose any performance benefit. Instead, we use a technique from
* Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even)
* or 2 (for odd) to the number we are encoding, returning a skew value indicating
- * this, and having the caller compensate after doing the multiplication. */
-
- /* Negative numbers will be negated to keep their bit representation below the maximum width */
+ * this, and having the caller compensate after doing the multiplication.
+ *
+ * In fact, we _do_ want to negate numbers to minimize their bit-lengths (and in
+ * particular, to ensure that the outputs from the endomorphism-split fit into
+ * 128 bits). If we negate, the parity of our number flips, inverting which of
+ * {1, 2} we want to add to the scalar when ensuring that it's odd. Further
+ * complicating things, -1 interacts badly with `secp256k1_scalar_cadd_bit` and
+ * we need to special-case it in this logic. */
flip = secp256k1_scalar_is_high(&s);
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
bit = flip ^ !secp256k1_scalar_is_even(&s);
@@ -95,7 +93,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
/* 4 */
u_last = secp256k1_scalar_shr_int(&s, w);
- while (word * w < WNAF_BITS) {
+ while (word * w < size) {
int sign;
int even;
@@ -115,37 +113,44 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
wnaf[word] = u * global_sign;
VERIFY_CHECK(secp256k1_scalar_is_zero(&s));
- VERIFY_CHECK(word == WNAF_SIZE(w));
+ VERIFY_CHECK(word == WNAF_SIZE_BITS(size, w));
return skew;
}
-
-static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar) {
+static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar, int size) {
secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)];
secp256k1_ge tmpa;
secp256k1_fe Z;
int skew_1;
- int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
#ifdef USE_ENDOMORPHISM
secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)];
int skew_lam;
secp256k1_scalar q_1, q_lam;
#endif
+ int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
int i;
secp256k1_scalar sc = *scalar;
/* build wnaf representation for q. */
+ int rsize = size;
+#ifdef USE_ENDOMORPHISM
+ if (size > 128) {
+ rsize = 128;
+ /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */
+ secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc);
+ skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1, 128);
+ skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1, 128);
+ } else
+#endif
+ {
+ skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1, size);
#ifdef USE_ENDOMORPHISM
- /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */
- secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc);
- skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1);
- skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1);
-#else
- skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1);
+ skew_lam = 0;
#endif
+ }
/* Calculate odd multiples of a.
* All multiples are brought to the same Z 'denominator', which is stored
@@ -159,26 +164,30 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
secp256k1_fe_normalize_weak(&pre_a[i].y);
}
#ifdef USE_ENDOMORPHISM
- for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
- secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
+ if (size > 128) {
+ for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
+ secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
+ }
}
#endif
/* first loop iteration (separated out so we can directly set r, rather
* than having it start at infinity, get doubled several times, then have
* its new value added to it) */
- i = wnaf_1[WNAF_SIZE(WINDOW_A - 1)];
+ i = wnaf_1[WNAF_SIZE_BITS(rsize, WINDOW_A - 1)];
VERIFY_CHECK(i != 0);
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A);
secp256k1_gej_set_ge(r, &tmpa);
#ifdef USE_ENDOMORPHISM
- i = wnaf_lam[WNAF_SIZE(WINDOW_A - 1)];
- VERIFY_CHECK(i != 0);
- ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A);
- secp256k1_gej_add_ge(r, r, &tmpa);
+ if (size > 128) {
+ i = wnaf_lam[WNAF_SIZE_BITS(rsize, WINDOW_A - 1)];
+ VERIFY_CHECK(i != 0);
+ ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A);
+ secp256k1_gej_add_ge(r, r, &tmpa);
+ }
#endif
/* remaining loop iterations */
- for (i = WNAF_SIZE(WINDOW_A - 1) - 1; i >= 0; i--) {
+ for (i = WNAF_SIZE_BITS(rsize, WINDOW_A - 1) - 1; i >= 0; i--) {
int n;
int j;
for (j = 0; j < WINDOW_A - 1; ++j) {
@@ -190,10 +199,12 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
VERIFY_CHECK(n != 0);
secp256k1_gej_add_ge(r, r, &tmpa);
#ifdef USE_ENDOMORPHISM
- n = wnaf_lam[i];
- ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
- VERIFY_CHECK(n != 0);
- secp256k1_gej_add_ge(r, r, &tmpa);
+ if (size > 128) {
+ n = wnaf_lam[i];
+ ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
+ VERIFY_CHECK(n != 0);
+ secp256k1_gej_add_ge(r, r, &tmpa);
+ }
#endif
}
@@ -213,14 +224,18 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
secp256k1_ge_set_gej(&correction, &tmpj);
secp256k1_ge_to_storage(&correction_1_stor, a);
#ifdef USE_ENDOMORPHISM
- secp256k1_ge_to_storage(&correction_lam_stor, a);
+ if (size > 128) {
+ secp256k1_ge_to_storage(&correction_lam_stor, a);
+ }
#endif
secp256k1_ge_to_storage(&a2_stor, &correction);
/* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */
secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2);
#ifdef USE_ENDOMORPHISM
- secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2);
+ if (size > 128) {
+ secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2);
+ }
#endif
/* Apply the correction */
@@ -229,10 +244,12 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
secp256k1_gej_add_ge(r, r, &correction);
#ifdef USE_ENDOMORPHISM
- secp256k1_ge_from_storage(&correction, &correction_lam_stor);
- secp256k1_ge_neg(&correction, &correction);
- secp256k1_ge_mul_lambda(&correction, &correction);
- secp256k1_gej_add_ge(r, r, &correction);
+ if (size > 128) {
+ secp256k1_ge_from_storage(&correction, &correction_lam_stor);
+ secp256k1_ge_neg(&correction, &correction);
+ secp256k1_ge_mul_lambda(&correction, &correction);
+ secp256k1_gej_add_ge(r, r, &correction);
+ }
#endif
}
}
diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h
index 9615b932dd..d64505dc00 100644
--- a/src/secp256k1/src/ecmult_gen_impl.h
+++ b/src/secp256k1/src/ecmult_gen_impl.h
@@ -77,7 +77,7 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL);
}
}
- secp256k1_ge_set_all_gej_var(prec, precj, 1024, cb);
+ secp256k1_ge_set_all_gej_var(prec, precj, 1024);
}
for (j = 0; j < 64; j++) {
for (i = 0; i < 16; i++) {
@@ -161,7 +161,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
secp256k1_gej gb;
secp256k1_fe s;
unsigned char nonce32[32];
- secp256k1_rfc6979_hmac_sha256_t rng;
+ secp256k1_rfc6979_hmac_sha256 rng;
int retry;
unsigned char keydata[64] = {0};
if (seed32 == NULL) {
diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h
index 93d3794cb4..1986914a4f 100644
--- a/src/secp256k1/src/ecmult_impl.h
+++ b/src/secp256k1/src/ecmult_impl.h
@@ -1,13 +1,14 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/*****************************************************************************
+ * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php. *
+ *****************************************************************************/
#ifndef SECP256K1_ECMULT_IMPL_H
#define SECP256K1_ECMULT_IMPL_H
#include <string.h>
+#include <stdint.h>
#include "group.h"
#include "scalar.h"
@@ -41,9 +42,36 @@
#endif
#endif
+#ifdef USE_ENDOMORPHISM
+ #define WNAF_BITS 128
+#else
+ #define WNAF_BITS 256
+#endif
+#define WNAF_SIZE_BITS(bits, w) (((bits) + (w) - 1) / (w))
+#define WNAF_SIZE(w) WNAF_SIZE_BITS(WNAF_BITS, w)
+
/** The number of entries a table with precomputed multiples needs to have. */
#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2))
+/* The number of objects allocated on the scratch space for ecmult_multi algorithms */
+#define PIPPENGER_SCRATCH_OBJECTS 6
+#define STRAUSS_SCRATCH_OBJECTS 6
+
+#define PIPPENGER_MAX_BUCKET_WINDOW 12
+
+/* Minimum number of points for which pippenger_wnaf is faster than strauss wnaf */
+#ifdef USE_ENDOMORPHISM
+ #define ECMULT_PIPPENGER_THRESHOLD 88
+#else
+ #define ECMULT_PIPPENGER_THRESHOLD 160
+#endif
+
+#ifdef USE_ENDOMORPHISM
+ #define ECMULT_MAX_POINTS_PER_BATCH 5000000
+#else
+ #define ECMULT_MAX_POINTS_PER_BATCH 10000000
+#endif
+
/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain
* the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will
* contain prej[0].z / a.z. The other zr[i] values = prej[i].z / prej[i-1].z.
@@ -109,24 +137,135 @@ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *p
secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr);
}
-static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge_storage *pre, const secp256k1_gej *a, const secp256k1_callback *cb) {
- secp256k1_gej *prej = (secp256k1_gej*)checked_malloc(cb, sizeof(secp256k1_gej) * n);
- secp256k1_ge *prea = (secp256k1_ge*)checked_malloc(cb, sizeof(secp256k1_ge) * n);
- secp256k1_fe *zr = (secp256k1_fe*)checked_malloc(cb, sizeof(secp256k1_fe) * n);
+static void secp256k1_ecmult_odd_multiples_table_storage_var(const int n, secp256k1_ge_storage *pre, const secp256k1_gej *a) {
+ secp256k1_gej d;
+ secp256k1_ge d_ge, p_ge;
+ secp256k1_gej pj;
+ secp256k1_fe zi;
+ secp256k1_fe zr;
+ secp256k1_fe dx_over_dz_squared;
int i;
- /* Compute the odd multiples in Jacobian form. */
- secp256k1_ecmult_odd_multiples_table(n, prej, zr, a);
- /* Convert them in batch to affine coordinates. */
- secp256k1_ge_set_table_gej_var(prea, prej, zr, n);
- /* Convert them to compact storage form. */
- for (i = 0; i < n; i++) {
- secp256k1_ge_to_storage(&pre[i], &prea[i]);
+ VERIFY_CHECK(!a->infinity);
+
+ secp256k1_gej_double_var(&d, a, NULL);
+
+ /* First, we perform all the additions in an isomorphic curve obtained by multiplying
+ * all `z` coordinates by 1/`d.z`. In these coordinates `d` is affine so we can use
+ * `secp256k1_gej_add_ge_var` to perform the additions. For each addition, we store
+ * the resulting y-coordinate and the z-ratio, since we only have enough memory to
+ * store two field elements. These are sufficient to efficiently undo the isomorphism
+ * and recompute all the `x`s.
+ */
+ d_ge.x = d.x;
+ d_ge.y = d.y;
+ d_ge.infinity = 0;
+
+ secp256k1_ge_set_gej_zinv(&p_ge, a, &d.z);
+ pj.x = p_ge.x;
+ pj.y = p_ge.y;
+ pj.z = a->z;
+ pj.infinity = 0;
+
+ for (i = 0; i < (n - 1); i++) {
+ secp256k1_fe_normalize_var(&pj.y);
+ secp256k1_fe_to_storage(&pre[i].y, &pj.y);
+ secp256k1_gej_add_ge_var(&pj, &pj, &d_ge, &zr);
+ secp256k1_fe_normalize_var(&zr);
+ secp256k1_fe_to_storage(&pre[i].x, &zr);
}
- free(prea);
- free(prej);
- free(zr);
+ /* Invert d.z in the same batch, preserving pj.z so we can extract 1/d.z */
+ secp256k1_fe_mul(&zi, &pj.z, &d.z);
+ secp256k1_fe_inv_var(&zi, &zi);
+
+ /* Directly set `pre[n - 1]` to `pj`, saving the inverted z-coordinate so
+ * that we can combine it with the saved z-ratios to compute the other zs
+ * without any more inversions. */
+ secp256k1_ge_set_gej_zinv(&p_ge, &pj, &zi);
+ secp256k1_ge_to_storage(&pre[n - 1], &p_ge);
+
+ /* Compute the actual x-coordinate of D, which will be needed below. */
+ secp256k1_fe_mul(&d.z, &zi, &pj.z); /* d.z = 1/d.z */
+ secp256k1_fe_sqr(&dx_over_dz_squared, &d.z);
+ secp256k1_fe_mul(&dx_over_dz_squared, &dx_over_dz_squared, &d.x);
+
+ /* Going into the second loop, we have set `pre[n-1]` to its final affine
+ * form, but still need to set `pre[i]` for `i` in 0 through `n-2`. We
+ * have `zi = (p.z * d.z)^-1`, where
+ *
+ * `p.z` is the z-coordinate of the point on the isomorphic curve
+ * which was ultimately assigned to `pre[n-1]`.
+ * `d.z` is the multiplier that must be applied to all z-coordinates
+ * to move from our isomorphic curve back to secp256k1; so the
+ * product `p.z * d.z` is the z-coordinate of the secp256k1
+ * point assigned to `pre[n-1]`.
+ *
+ * All subsequent inverse-z-coordinates can be obtained by multiplying this
+ * factor by successive z-ratios, which is much more efficient than directly
+ * computing each one.
+ *
+ * Importantly, these inverse-zs will be coordinates of points on secp256k1,
+ * while our other stored values come from computations on the isomorphic
+ * curve. So in the below loop, we will take care not to actually use `zi`
+ * or any derived values until we're back on secp256k1.
+ */
+ i = n - 1;
+ while (i > 0) {
+ secp256k1_fe zi2, zi3;
+ const secp256k1_fe *rzr;
+ i--;
+
+ secp256k1_ge_from_storage(&p_ge, &pre[i]);
+
+ /* For each remaining point, we extract the z-ratio from the stored
+ * x-coordinate, compute its z^-1 from that, and compute the full
+ * point from that. */
+ rzr = &p_ge.x;
+ secp256k1_fe_mul(&zi, &zi, rzr);
+ secp256k1_fe_sqr(&zi2, &zi);
+ secp256k1_fe_mul(&zi3, &zi2, &zi);
+ /* To compute the actual x-coordinate, we use the stored z ratio and
+ * y-coordinate, which we obtained from `secp256k1_gej_add_ge_var`
+ * in the loop above, as well as the inverse of the square of its
+ * z-coordinate. We store the latter in the `zi2` variable, which is
+ * computed iteratively starting from the overall Z inverse then
+ * multiplying by each z-ratio in turn.
+ *
+ * Denoting the z-ratio as `rzr`, we observe that it is equal to `h`
+ * from the inside of the above `gej_add_ge_var` call. This satisfies
+ *
+ * rzr = d_x * z^2 - x * d_z^2
+ *
+ * where (`d_x`, `d_z`) are Jacobian coordinates of `D` and `(x, z)`
+ * are Jacobian coordinates of our desired point -- except both are on
+ * the isomorphic curve that we were using when we called `gej_add_ge_var`.
+ * To get back to secp256k1, we must multiply both `z`s by `d_z`, or
+ * equivalently divide both `x`s by `d_z^2`. Our equation then becomes
+ *
+ * rzr = d_x * z^2 / d_z^2 - x
+ *
+ * (The left-hand-side, being a ratio of z-coordinates, is unaffected
+ * by the isomorphism.)
+ *
+ * Rearranging to solve for `x`, we have
+ *
+ * x = d_x * z^2 / d_z^2 - rzr
+ *
+ * But what we actually want is the affine coordinate `X = x/z^2`,
+ * which will satisfy
+ *
+ * X = d_x / d_z^2 - rzr / z^2
+ * = dx_over_dz_squared - rzr * zi2
+ */
+ secp256k1_fe_mul(&p_ge.x, rzr, &zi2);
+ secp256k1_fe_negate(&p_ge.x, &p_ge.x, 1);
+ secp256k1_fe_add(&p_ge.x, &dx_over_dz_squared);
+ /* y is stored_y/z^3, as we expect */
+ secp256k1_fe_mul(&p_ge.y, &p_ge.y, &zi3);
+ /* Store */
+ secp256k1_ge_to_storage(&pre[i], &p_ge);
+ }
}
/** The following two macro retrieves a particular odd multiple from a table
@@ -138,7 +277,8 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge
if ((n) > 0) { \
*(r) = (pre)[((n)-1)/2]; \
} else { \
- secp256k1_ge_neg((r), &(pre)[(-(n)-1)/2]); \
+ *(r) = (pre)[(-(n)-1)/2]; \
+ secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \
} \
} while(0)
@@ -150,7 +290,7 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge
secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \
} else { \
secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \
- secp256k1_ge_neg((r), (r)); \
+ secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \
} \
} while(0)
@@ -174,7 +314,7 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G));
/* precompute the tables with odd multiples */
- secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj, cb);
+ secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj);
#ifdef USE_ENDOMORPHISM
{
@@ -188,7 +328,7 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
for (i = 0; i < 128; i++) {
secp256k1_gej_double_var(&g_128j, &g_128j, NULL);
}
- secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j, cb);
+ secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j);
}
#endif
}
@@ -283,50 +423,78 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a,
return last_set_bit + 1;
}
-static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) {
- secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)];
- secp256k1_ge tmpa;
- secp256k1_fe Z;
+struct secp256k1_strauss_point_state {
#ifdef USE_ENDOMORPHISM
- secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
secp256k1_scalar na_1, na_lam;
- /* Splitted G factors. */
- secp256k1_scalar ng_1, ng_128;
int wnaf_na_1[130];
int wnaf_na_lam[130];
int bits_na_1;
int bits_na_lam;
- int wnaf_ng_1[129];
- int bits_ng_1;
- int wnaf_ng_128[129];
- int bits_ng_128;
#else
int wnaf_na[256];
int bits_na;
+#endif
+ size_t input_pos;
+};
+
+struct secp256k1_strauss_state {
+ secp256k1_gej* prej;
+ secp256k1_fe* zr;
+ secp256k1_ge* pre_a;
+#ifdef USE_ENDOMORPHISM
+ secp256k1_ge* pre_a_lam;
+#endif
+ struct secp256k1_strauss_point_state* ps;
+};
+
+static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, const struct secp256k1_strauss_state *state, secp256k1_gej *r, int num, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) {
+ secp256k1_ge tmpa;
+ secp256k1_fe Z;
+#ifdef USE_ENDOMORPHISM
+ /* Splitted G factors. */
+ secp256k1_scalar ng_1, ng_128;
+ int wnaf_ng_1[129];
+ int bits_ng_1 = 0;
+ int wnaf_ng_128[129];
+ int bits_ng_128 = 0;
+#else
int wnaf_ng[256];
- int bits_ng;
+ int bits_ng = 0;
#endif
int i;
- int bits;
+ int bits = 0;
+ int np;
+ int no = 0;
+ for (np = 0; np < num; ++np) {
+ if (secp256k1_scalar_is_zero(&na[np]) || secp256k1_gej_is_infinity(&a[np])) {
+ continue;
+ }
+ state->ps[no].input_pos = np;
#ifdef USE_ENDOMORPHISM
- /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */
- secp256k1_scalar_split_lambda(&na_1, &na_lam, na);
-
- /* build wnaf representation for na_1 and na_lam. */
- bits_na_1 = secp256k1_ecmult_wnaf(wnaf_na_1, 130, &na_1, WINDOW_A);
- bits_na_lam = secp256k1_ecmult_wnaf(wnaf_na_lam, 130, &na_lam, WINDOW_A);
- VERIFY_CHECK(bits_na_1 <= 130);
- VERIFY_CHECK(bits_na_lam <= 130);
- bits = bits_na_1;
- if (bits_na_lam > bits) {
- bits = bits_na_lam;
- }
+ /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */
+ secp256k1_scalar_split_lambda(&state->ps[no].na_1, &state->ps[no].na_lam, &na[np]);
+
+ /* build wnaf representation for na_1 and na_lam. */
+ state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1, 130, &state->ps[no].na_1, WINDOW_A);
+ state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 130, &state->ps[no].na_lam, WINDOW_A);
+ VERIFY_CHECK(state->ps[no].bits_na_1 <= 130);
+ VERIFY_CHECK(state->ps[no].bits_na_lam <= 130);
+ if (state->ps[no].bits_na_1 > bits) {
+ bits = state->ps[no].bits_na_1;
+ }
+ if (state->ps[no].bits_na_lam > bits) {
+ bits = state->ps[no].bits_na_lam;
+ }
#else
- /* build wnaf representation for na. */
- bits_na = secp256k1_ecmult_wnaf(wnaf_na, 256, na, WINDOW_A);
- bits = bits_na;
+ /* build wnaf representation for na. */
+ state->ps[no].bits_na = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na, 256, &na[np], WINDOW_A);
+ if (state->ps[no].bits_na > bits) {
+ bits = state->ps[no].bits_na;
+ }
#endif
+ ++no;
+ }
/* Calculate odd multiples of a.
* All multiples are brought to the same Z 'denominator', which is stored
@@ -338,29 +506,51 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
* of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same
* isomorphism to efficiently add with a known Z inverse.
*/
- secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, a);
+ if (no > 0) {
+ /* Compute the odd multiples in Jacobian form. */
+ secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej, state->zr, &a[state->ps[0].input_pos]);
+ for (np = 1; np < no; ++np) {
+ secp256k1_gej tmp = a[state->ps[np].input_pos];
+#ifdef VERIFY
+ secp256k1_fe_normalize_var(&(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z));
+#endif
+ secp256k1_gej_rescale(&tmp, &(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z));
+ secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &tmp);
+ secp256k1_fe_mul(state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &(a[state->ps[np].input_pos].z));
+ }
+ /* Bring them to the same Z denominator. */
+ secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, &Z, state->prej, state->zr);
+ } else {
+ secp256k1_fe_set_int(&Z, 1);
+ }
#ifdef USE_ENDOMORPHISM
- for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
- secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
+ for (np = 0; np < no; ++np) {
+ for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
+ secp256k1_ge_mul_lambda(&state->pre_a_lam[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i]);
+ }
}
- /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */
- secp256k1_scalar_split_128(&ng_1, &ng_128, ng);
+ if (ng) {
+ /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */
+ secp256k1_scalar_split_128(&ng_1, &ng_128, ng);
- /* Build wnaf representation for ng_1 and ng_128 */
- bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G);
- bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G);
- if (bits_ng_1 > bits) {
- bits = bits_ng_1;
- }
- if (bits_ng_128 > bits) {
- bits = bits_ng_128;
+ /* Build wnaf representation for ng_1 and ng_128 */
+ bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G);
+ bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G);
+ if (bits_ng_1 > bits) {
+ bits = bits_ng_1;
+ }
+ if (bits_ng_128 > bits) {
+ bits = bits_ng_128;
+ }
}
#else
- bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G);
- if (bits_ng > bits) {
- bits = bits_ng;
+ if (ng) {
+ bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G);
+ if (bits_ng > bits) {
+ bits = bits_ng;
+ }
}
#endif
@@ -370,13 +560,15 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
int n;
secp256k1_gej_double_var(r, r, NULL);
#ifdef USE_ENDOMORPHISM
- if (i < bits_na_1 && (n = wnaf_na_1[i])) {
- ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A);
- secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
- }
- if (i < bits_na_lam && (n = wnaf_na_lam[i])) {
- ECMULT_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
- secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
+ for (np = 0; np < no; ++np) {
+ if (i < state->ps[np].bits_na_1 && (n = state->ps[np].wnaf_na_1[i])) {
+ ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
+ secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
+ }
+ if (i < state->ps[np].bits_na_lam && (n = state->ps[np].wnaf_na_lam[i])) {
+ ECMULT_TABLE_GET_GE(&tmpa, state->pre_a_lam + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
+ secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
+ }
}
if (i < bits_ng_1 && (n = wnaf_ng_1[i])) {
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G);
@@ -387,9 +579,11 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
}
#else
- if (i < bits_na && (n = wnaf_na[i])) {
- ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A);
- secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
+ for (np = 0; np < no; ++np) {
+ if (i < state->ps[np].bits_na && (n = state->ps[np].wnaf_na[i])) {
+ ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
+ secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
+ }
}
if (i < bits_ng && (n = wnaf_ng[i])) {
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G);
@@ -403,4 +597,585 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
}
}
+static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) {
+ secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)];
+ secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)];
+ secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)];
+ struct secp256k1_strauss_point_state ps[1];
+#ifdef USE_ENDOMORPHISM
+ secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
+#endif
+ struct secp256k1_strauss_state state;
+
+ state.prej = prej;
+ state.zr = zr;
+ state.pre_a = pre_a;
+#ifdef USE_ENDOMORPHISM
+ state.pre_a_lam = pre_a_lam;
+#endif
+ state.ps = ps;
+ secp256k1_ecmult_strauss_wnaf(ctx, &state, r, 1, a, na, ng);
+}
+
+static size_t secp256k1_strauss_scratch_size(size_t n_points) {
+#ifdef USE_ENDOMORPHISM
+ static const size_t point_size = (2 * sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
+#else
+ static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
+#endif
+ return n_points*point_size;
+}
+
+static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
+ secp256k1_gej* points;
+ secp256k1_scalar* scalars;
+ struct secp256k1_strauss_state state;
+ size_t i;
+
+ secp256k1_gej_set_infinity(r);
+ if (inp_g_sc == NULL && n_points == 0) {
+ return 1;
+ }
+
+ if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) {
+ return 0;
+ }
+ points = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_gej));
+ scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_scalar));
+ state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
+ state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
+#ifdef USE_ENDOMORPHISM
+ state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
+ state.pre_a_lam = state.pre_a + n_points * ECMULT_TABLE_SIZE(WINDOW_A);
+#else
+ state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
+#endif
+ state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
+
+ for (i = 0; i < n_points; i++) {
+ secp256k1_ge point;
+ if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) {
+ secp256k1_scratch_deallocate_frame(scratch);
+ return 0;
+ }
+ secp256k1_gej_set_ge(&points[i], &point);
+ }
+ secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc);
+ secp256k1_scratch_deallocate_frame(scratch);
+ return 1;
+}
+
+/* Wrapper for secp256k1_ecmult_multi_func interface */
+static int secp256k1_ecmult_strauss_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
+ return secp256k1_ecmult_strauss_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
+}
+
+static size_t secp256k1_strauss_max_points(secp256k1_scratch *scratch) {
+ return secp256k1_scratch_max_allocation(scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
+}
+
+/** 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] is either 0 or an odd integer between -(1 << w) and (1 << w)
+ * - the number of words set is always WNAF_SIZE(w)
+ * - the returned skew is 0 or 1
+ */
+static int secp256k1_wnaf_fixed(int *wnaf, const secp256k1_scalar *s, int w) {
+ int skew = 0;
+ int pos;
+ int max_pos;
+ int last_w;
+ const secp256k1_scalar *work = s;
+
+ if (secp256k1_scalar_is_zero(s)) {
+ for (pos = 0; pos < WNAF_SIZE(w); pos++) {
+ wnaf[pos] = 0;
+ }
+ return 0;
+ }
+
+ if (secp256k1_scalar_is_even(s)) {
+ skew = 1;
+ }
+
+ wnaf[0] = secp256k1_scalar_get_bits_var(work, 0, w) + skew;
+ /* Compute last window size. Relevant when window size doesn't divide the
+ * number of bits in the scalar */
+ last_w = WNAF_BITS - (WNAF_SIZE(w) - 1) * w;
+
+ /* Store the position of the first nonzero word in max_pos to allow
+ * skipping leading zeros when calculating the wnaf. */
+ for (pos = WNAF_SIZE(w) - 1; pos > 0; pos--) {
+ int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w);
+ if(val != 0) {
+ break;
+ }
+ wnaf[pos] = 0;
+ }
+ max_pos = pos;
+ pos = 1;
+
+ while (pos <= max_pos) {
+ int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w);
+ if ((val & 1) == 0) {
+ wnaf[pos - 1] -= (1 << w);
+ wnaf[pos] = (val + 1);
+ } else {
+ wnaf[pos] = val;
+ }
+ /* Set a coefficient to zero if it is 1 or -1 and the proceeding digit
+ * is strictly negative or strictly positive respectively. Only change
+ * coefficients at previous positions because above code assumes that
+ * wnaf[pos - 1] is odd.
+ */
+ if (pos >= 2 && ((wnaf[pos - 1] == 1 && wnaf[pos - 2] < 0) || (wnaf[pos - 1] == -1 && wnaf[pos - 2] > 0))) {
+ if (wnaf[pos - 1] == 1) {
+ wnaf[pos - 2] += 1 << w;
+ } else {
+ wnaf[pos - 2] -= 1 << w;
+ }
+ wnaf[pos - 1] = 0;
+ }
+ ++pos;
+ }
+
+ return skew;
+}
+
+struct secp256k1_pippenger_point_state {
+ int skew_na;
+ size_t input_pos;
+};
+
+struct secp256k1_pippenger_state {
+ int *wnaf_na;
+ struct secp256k1_pippenger_point_state* ps;
+};
+
+/*
+ * pippenger_wnaf computes the result of a multi-point multiplication as
+ * follows: The scalars are brought into wnaf with n_wnaf elements each. Then
+ * for every i < n_wnaf, first each point is added to a "bucket" corresponding
+ * to the point's wnaf[i]. Second, the buckets are added together such that
+ * r += 1*bucket[0] + 3*bucket[1] + 5*bucket[2] + ...
+ */
+static int secp256k1_ecmult_pippenger_wnaf(secp256k1_gej *buckets, int bucket_window, struct secp256k1_pippenger_state *state, secp256k1_gej *r, const secp256k1_scalar *sc, const secp256k1_ge *pt, size_t num) {
+ size_t n_wnaf = WNAF_SIZE(bucket_window+1);
+ size_t np;
+ size_t no = 0;
+ int i;
+ int j;
+
+ for (np = 0; np < num; ++np) {
+ if (secp256k1_scalar_is_zero(&sc[np]) || secp256k1_ge_is_infinity(&pt[np])) {
+ continue;
+ }
+ state->ps[no].input_pos = np;
+ state->ps[no].skew_na = secp256k1_wnaf_fixed(&state->wnaf_na[no*n_wnaf], &sc[np], bucket_window+1);
+ no++;
+ }
+ secp256k1_gej_set_infinity(r);
+
+ if (no == 0) {
+ return 1;
+ }
+
+ for (i = n_wnaf - 1; i >= 0; i--) {
+ secp256k1_gej running_sum;
+
+ for(j = 0; j < ECMULT_TABLE_SIZE(bucket_window+2); j++) {
+ secp256k1_gej_set_infinity(&buckets[j]);
+ }
+
+ for (np = 0; np < no; ++np) {
+ int n = state->wnaf_na[np*n_wnaf + i];
+ struct secp256k1_pippenger_point_state point_state = state->ps[np];
+ secp256k1_ge tmp;
+ int idx;
+
+ if (i == 0) {
+ /* correct for wnaf skew */
+ int skew = point_state.skew_na;
+ if (skew) {
+ secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]);
+ secp256k1_gej_add_ge_var(&buckets[0], &buckets[0], &tmp, NULL);
+ }
+ }
+ if (n > 0) {
+ idx = (n - 1)/2;
+ secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &pt[point_state.input_pos], NULL);
+ } else if (n < 0) {
+ idx = -(n + 1)/2;
+ secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]);
+ secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &tmp, NULL);
+ }
+ }
+
+ for(j = 0; j < bucket_window; j++) {
+ secp256k1_gej_double_var(r, r, NULL);
+ }
+
+ secp256k1_gej_set_infinity(&running_sum);
+ /* Accumulate the sum: bucket[0] + 3*bucket[1] + 5*bucket[2] + 7*bucket[3] + ...
+ * = bucket[0] + bucket[1] + bucket[2] + bucket[3] + ...
+ * + 2 * (bucket[1] + 2*bucket[2] + 3*bucket[3] + ...)
+ * using an intermediate running sum:
+ * running_sum = bucket[0] + bucket[1] + bucket[2] + ...
+ *
+ * The doubling is done implicitly by deferring the final window doubling (of 'r').
+ */
+ for(j = ECMULT_TABLE_SIZE(bucket_window+2) - 1; j > 0; j--) {
+ secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[j], NULL);
+ secp256k1_gej_add_var(r, r, &running_sum, NULL);
+ }
+
+ secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[0], NULL);
+ secp256k1_gej_double_var(r, r, NULL);
+ secp256k1_gej_add_var(r, r, &running_sum, NULL);
+ }
+ return 1;
+}
+
+/**
+ * Returns optimal bucket_window (number of bits of a scalar represented by a
+ * set of buckets) for a given number of points.
+ */
+static int secp256k1_pippenger_bucket_window(size_t n) {
+#ifdef USE_ENDOMORPHISM
+ if (n <= 1) {
+ return 1;
+ } else if (n <= 4) {
+ return 2;
+ } else if (n <= 20) {
+ return 3;
+ } else if (n <= 57) {
+ return 4;
+ } else if (n <= 136) {
+ return 5;
+ } else if (n <= 235) {
+ return 6;
+ } else if (n <= 1260) {
+ return 7;
+ } else if (n <= 4420) {
+ return 9;
+ } else if (n <= 7880) {
+ return 10;
+ } else if (n <= 16050) {
+ return 11;
+ } else {
+ return PIPPENGER_MAX_BUCKET_WINDOW;
+ }
+#else
+ if (n <= 1) {
+ return 1;
+ } else if (n <= 11) {
+ return 2;
+ } else if (n <= 45) {
+ return 3;
+ } else if (n <= 100) {
+ return 4;
+ } else if (n <= 275) {
+ return 5;
+ } else if (n <= 625) {
+ return 6;
+ } else if (n <= 1850) {
+ return 7;
+ } else if (n <= 3400) {
+ return 8;
+ } else if (n <= 9630) {
+ return 9;
+ } else if (n <= 17900) {
+ return 10;
+ } else if (n <= 32800) {
+ return 11;
+ } else {
+ return PIPPENGER_MAX_BUCKET_WINDOW;
+ }
+#endif
+}
+
+/**
+ * Returns the maximum optimal number of points for a bucket_window.
+ */
+static size_t secp256k1_pippenger_bucket_window_inv(int bucket_window) {
+ switch(bucket_window) {
+#ifdef USE_ENDOMORPHISM
+ case 1: return 1;
+ case 2: return 4;
+ case 3: return 20;
+ case 4: return 57;
+ case 5: return 136;
+ case 6: return 235;
+ case 7: return 1260;
+ case 8: return 1260;
+ case 9: return 4420;
+ case 10: return 7880;
+ case 11: return 16050;
+ case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX;
+#else
+ case 1: return 1;
+ case 2: return 11;
+ case 3: return 45;
+ case 4: return 100;
+ case 5: return 275;
+ case 6: return 625;
+ case 7: return 1850;
+ case 8: return 3400;
+ case 9: return 9630;
+ case 10: return 17900;
+ case 11: return 32800;
+ case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX;
+#endif
+ }
+ return 0;
+}
+
+
+#ifdef USE_ENDOMORPHISM
+SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, secp256k1_scalar *s2, secp256k1_ge *p1, secp256k1_ge *p2) {
+ secp256k1_scalar tmp = *s1;
+ secp256k1_scalar_split_lambda(s1, s2, &tmp);
+ secp256k1_ge_mul_lambda(p2, p1);
+
+ if (secp256k1_scalar_is_high(s1)) {
+ secp256k1_scalar_negate(s1, s1);
+ secp256k1_ge_neg(p1, p1);
+ }
+ if (secp256k1_scalar_is_high(s2)) {
+ secp256k1_scalar_negate(s2, s2);
+ secp256k1_ge_neg(p2, p2);
+ }
+}
+#endif
+
+/**
+ * Returns the scratch size required for a given number of points (excluding
+ * base point G) without considering alignment.
+ */
+static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_window) {
+#ifdef USE_ENDOMORPHISM
+ size_t entries = 2*n_points + 2;
+#else
+ size_t entries = n_points + 1;
+#endif
+ size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int);
+ return (sizeof(secp256k1_gej) << bucket_window) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size;
+}
+
+static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
+ /* Use 2(n+1) with the endomorphism, n+1 without, when calculating batch
+ * sizes. The reason for +1 is that we add the G scalar to the list of
+ * other scalars. */
+#ifdef USE_ENDOMORPHISM
+ size_t entries = 2*n_points + 2;
+#else
+ size_t entries = n_points + 1;
+#endif
+ secp256k1_ge *points;
+ secp256k1_scalar *scalars;
+ secp256k1_gej *buckets;
+ struct secp256k1_pippenger_state *state_space;
+ size_t idx = 0;
+ size_t point_idx = 0;
+ int i, j;
+ int bucket_window;
+
+ (void)ctx;
+ secp256k1_gej_set_infinity(r);
+ if (inp_g_sc == NULL && n_points == 0) {
+ return 1;
+ }
+
+ bucket_window = secp256k1_pippenger_bucket_window(n_points);
+ if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) {
+ return 0;
+ }
+ points = (secp256k1_ge *) secp256k1_scratch_alloc(scratch, entries * sizeof(*points));
+ scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(scratch, entries * sizeof(*scalars));
+ state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(scratch, sizeof(*state_space));
+ state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(scratch, entries * sizeof(*state_space->ps));
+ state_space->wnaf_na = (int *) secp256k1_scratch_alloc(scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
+ buckets = (secp256k1_gej *) secp256k1_scratch_alloc(scratch, sizeof(*buckets) << bucket_window);
+
+ if (inp_g_sc != NULL) {
+ scalars[0] = *inp_g_sc;
+ points[0] = secp256k1_ge_const_g;
+ idx++;
+#ifdef USE_ENDOMORPHISM
+ secp256k1_ecmult_endo_split(&scalars[0], &scalars[1], &points[0], &points[1]);
+ idx++;
+#endif
+ }
+
+ while (point_idx < n_points) {
+ if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) {
+ secp256k1_scratch_deallocate_frame(scratch);
+ return 0;
+ }
+ idx++;
+#ifdef USE_ENDOMORPHISM
+ secp256k1_ecmult_endo_split(&scalars[idx - 1], &scalars[idx], &points[idx - 1], &points[idx]);
+ idx++;
+#endif
+ point_idx++;
+ }
+
+ secp256k1_ecmult_pippenger_wnaf(buckets, bucket_window, state_space, r, scalars, points, idx);
+
+ /* Clear data */
+ for(i = 0; (size_t)i < idx; i++) {
+ secp256k1_scalar_clear(&scalars[i]);
+ state_space->ps[i].skew_na = 0;
+ for(j = 0; j < WNAF_SIZE(bucket_window+1); j++) {
+ state_space->wnaf_na[i * WNAF_SIZE(bucket_window+1) + j] = 0;
+ }
+ }
+ for(i = 0; i < 1<<bucket_window; i++) {
+ secp256k1_gej_clear(&buckets[i]);
+ }
+ secp256k1_scratch_deallocate_frame(scratch);
+ return 1;
+}
+
+/* Wrapper for secp256k1_ecmult_multi_func interface */
+static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
+ return secp256k1_ecmult_pippenger_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
+}
+
+/**
+ * Returns the maximum number of points in addition to G that can be used with
+ * a given scratch space. The function ensures that fewer points may also be
+ * used.
+ */
+static size_t secp256k1_pippenger_max_points(secp256k1_scratch *scratch) {
+ size_t max_alloc = secp256k1_scratch_max_allocation(scratch, PIPPENGER_SCRATCH_OBJECTS);
+ int bucket_window;
+ size_t res = 0;
+
+ for (bucket_window = 1; bucket_window <= PIPPENGER_MAX_BUCKET_WINDOW; bucket_window++) {
+ size_t n_points;
+ size_t max_points = secp256k1_pippenger_bucket_window_inv(bucket_window);
+ size_t space_for_points;
+ size_t space_overhead;
+ size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int);
+
+#ifdef USE_ENDOMORPHISM
+ entry_size = 2*entry_size;
+#endif
+ space_overhead = (sizeof(secp256k1_gej) << bucket_window) + entry_size + sizeof(struct secp256k1_pippenger_state);
+ if (space_overhead > max_alloc) {
+ break;
+ }
+ space_for_points = max_alloc - space_overhead;
+
+ n_points = space_for_points/entry_size;
+ n_points = n_points > max_points ? max_points : n_points;
+ if (n_points > res) {
+ res = n_points;
+ }
+ if (n_points < max_points) {
+ /* A larger bucket_window may support even more points. But if we
+ * would choose that then the caller couldn't safely use any number
+ * smaller than what this function returns */
+ break;
+ }
+ }
+ return res;
+}
+
+/* Computes ecmult_multi by simply multiplying and adding each point. Does not
+ * require a scratch space */
+static int secp256k1_ecmult_multi_simple_var(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points) {
+ size_t point_idx;
+ secp256k1_scalar szero;
+ secp256k1_gej tmpj;
+
+ secp256k1_scalar_set_int(&szero, 0);
+ secp256k1_gej_set_infinity(r);
+ secp256k1_gej_set_infinity(&tmpj);
+ /* r = inp_g_sc*G */
+ secp256k1_ecmult(ctx, r, &tmpj, &szero, inp_g_sc);
+ for (point_idx = 0; point_idx < n_points; point_idx++) {
+ secp256k1_ge point;
+ secp256k1_gej pointj;
+ secp256k1_scalar scalar;
+ if (!cb(&scalar, &point, point_idx, cbdata)) {
+ return 0;
+ }
+ /* r += scalar*point */
+ secp256k1_gej_set_ge(&pointj, &point);
+ secp256k1_ecmult(ctx, &tmpj, &pointj, &scalar, NULL);
+ secp256k1_gej_add_var(r, r, &tmpj, NULL);
+ }
+ return 1;
+}
+
+/* Compute the number of batches and the batch size given the maximum batch size and the
+ * total number of points */
+static int secp256k1_ecmult_multi_batch_size_helper(size_t *n_batches, size_t *n_batch_points, size_t max_n_batch_points, size_t n) {
+ if (max_n_batch_points == 0) {
+ return 0;
+ }
+ if (max_n_batch_points > ECMULT_MAX_POINTS_PER_BATCH) {
+ max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH;
+ }
+ if (n == 0) {
+ *n_batches = 0;
+ *n_batch_points = 0;
+ return 1;
+ }
+ /* Compute ceil(n/max_n_batch_points) and ceil(n/n_batches) */
+ *n_batches = 1 + (n - 1) / max_n_batch_points;
+ *n_batch_points = 1 + (n - 1) / *n_batches;
+ return 1;
+}
+
+typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t);
+static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
+ size_t i;
+
+ int (*f)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t);
+ size_t n_batches;
+ size_t n_batch_points;
+
+ secp256k1_gej_set_infinity(r);
+ if (inp_g_sc == NULL && n == 0) {
+ return 1;
+ } else if (n == 0) {
+ secp256k1_scalar szero;
+ secp256k1_scalar_set_int(&szero, 0);
+ secp256k1_ecmult(ctx, r, r, &szero, inp_g_sc);
+ return 1;
+ }
+ if (scratch == NULL) {
+ return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
+ }
+
+ /* Compute the batch sizes for pippenger given a scratch space. If it's greater than a threshold
+ * use pippenger. Otherwise use strauss */
+ if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_pippenger_max_points(scratch), n)) {
+ return 0;
+ }
+ if (n_batch_points >= ECMULT_PIPPENGER_THRESHOLD) {
+ f = secp256k1_ecmult_pippenger_batch;
+ } else {
+ if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_strauss_max_points(scratch), n)) {
+ return 0;
+ }
+ f = secp256k1_ecmult_strauss_batch;
+ }
+ for(i = 0; i < n_batches; i++) {
+ size_t nbp = n < n_batch_points ? n : n_batch_points;
+ size_t offset = n_batch_points*i;
+ secp256k1_gej tmp;
+ if (!f(ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) {
+ return 0;
+ }
+ secp256k1_gej_add_var(r, r, &tmp, NULL);
+ n -= nbp;
+ }
+ return 1;
+}
+
#endif /* SECP256K1_ECMULT_IMPL_H */
diff --git a/src/secp256k1/src/field_10x26.h b/src/secp256k1/src/field_10x26.h
index 727c5267fb..5ff03c8abc 100644
--- a/src/secp256k1/src/field_10x26.h
+++ b/src/secp256k1/src/field_10x26.h
@@ -10,7 +10,9 @@
#include <stdint.h>
typedef struct {
- /* X = sum(i=0..9, elem[i]*2^26) mod n */
+ /* X = sum(i=0..9, n[i]*2^(i*26)) mod p
+ * where p = 2^256 - 0x1000003D1
+ */
uint32_t n[10];
#ifdef VERIFY
int magnitude;
diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h
index 94f8132fc8..4ae4fdcec8 100644
--- a/src/secp256k1/src/field_10x26_impl.h
+++ b/src/secp256k1/src/field_10x26_impl.h
@@ -8,7 +8,6 @@
#define SECP256K1_FIELD_REPR_IMPL_H
#include "util.h"
-#include "num.h"
#include "field.h"
#ifdef VERIFY
@@ -486,7 +485,8 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t
VERIFY_BITS(b[9], 26);
/** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n.
- * px is a shorthand for sum(a[i]*b[x-i], i=0..x).
+ * for 0 <= x <= 9, px is a shorthand for sum(a[i]*b[x-i], i=0..x).
+ * for 9 <= x <= 18, px is a shorthand for sum(a[i]*b[x-i], i=(x-9)..9)
* Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0].
*/
@@ -1069,6 +1069,7 @@ static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp2
secp256k1_fe_verify(a);
secp256k1_fe_verify(b);
VERIFY_CHECK(r != b);
+ VERIFY_CHECK(a != b);
#endif
secp256k1_fe_mul_inner(r->n, a->n, b->n);
#ifdef VERIFY
diff --git a/src/secp256k1/src/field_5x52.h b/src/secp256k1/src/field_5x52.h
index bccd8feb4d..fc5bfe357e 100644
--- a/src/secp256k1/src/field_5x52.h
+++ b/src/secp256k1/src/field_5x52.h
@@ -10,7 +10,9 @@
#include <stdint.h>
typedef struct {
- /* X = sum(i=0..4, elem[i]*2^52) mod n */
+ /* X = sum(i=0..4, n[i]*2^(i*52)) mod p
+ * where p = 2^256 - 0x1000003D1
+ */
uint64_t n[5];
#ifdef VERIFY
int magnitude;
diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h
index 957c61b014..f4263320d5 100644
--- a/src/secp256k1/src/field_5x52_impl.h
+++ b/src/secp256k1/src/field_5x52_impl.h
@@ -12,7 +12,6 @@
#endif
#include "util.h"
-#include "num.h"
#include "field.h"
#if defined(USE_ASM_X86_64)
@@ -422,6 +421,7 @@ static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp2
secp256k1_fe_verify(a);
secp256k1_fe_verify(b);
VERIFY_CHECK(r != b);
+ VERIFY_CHECK(a != b);
#endif
secp256k1_fe_mul_inner(r->n, a->n, b->n);
#ifdef VERIFY
diff --git a/src/secp256k1/src/field_5x52_int128_impl.h b/src/secp256k1/src/field_5x52_int128_impl.h
index 95a0d1791c..bcbfb92ac2 100644
--- a/src/secp256k1/src/field_5x52_int128_impl.h
+++ b/src/secp256k1/src/field_5x52_int128_impl.h
@@ -32,9 +32,11 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t
VERIFY_BITS(b[3], 56);
VERIFY_BITS(b[4], 52);
VERIFY_CHECK(r != b);
+ VERIFY_CHECK(a != b);
/* [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n.
- * px is a shorthand for sum(a[i]*b[x-i], i=0..x).
+ * for 0 <= x <= 4, px is a shorthand for sum(a[i]*b[x-i], i=0..x).
+ * for 4 <= x <= 8, px is a shorthand for sum(a[i]*b[x-i], i=(x-4)..4)
* Note that [x 0 0 0 0 0] = [x*R].
*/
diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h
index 20428648af..6070caccfe 100644
--- a/src/secp256k1/src/field_impl.h
+++ b/src/secp256k1/src/field_impl.h
@@ -12,6 +12,7 @@
#endif
#include "util.h"
+#include "num.h"
#if defined(USE_FIELD_10X26)
#include "field_10x26_impl.h"
@@ -48,6 +49,8 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) {
secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1;
int j;
+ VERIFY_CHECK(r != a);
+
/** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in
* { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block:
* 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]
diff --git a/src/secp256k1/src/gen_context.c b/src/secp256k1/src/gen_context.c
index 1835fd491d..87d296ebf0 100644
--- a/src/secp256k1/src/gen_context.c
+++ b/src/secp256k1/src/gen_context.c
@@ -41,7 +41,7 @@ int main(int argc, char **argv) {
fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
- fprintf(fp, "#include \"group.h\"\n");
+ fprintf(fp, "#include \"src/group.h\"\n");
fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n");
fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n");
diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h
index ea1302deb8..8e122ab429 100644
--- a/src/secp256k1/src/group.h
+++ b/src/secp256k1/src/group.h
@@ -65,12 +65,7 @@ static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a);
static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a);
/** Set a batch of group elements equal to the inputs given in jacobian coordinates */
-static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb);
-
-/** Set a batch of group elements equal to the inputs given in jacobian
- * coordinates (with known z-ratios). zr must contain the known z-ratios such
- * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */
-static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len);
+static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len);
/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to
* the same global z "denominator". zr must contain the known z-ratios such
@@ -79,6 +74,9 @@ static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej
* stored in globalz. */
static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr);
+/** Set a group element (affine) equal to the point at infinity. */
+static void secp256k1_ge_set_infinity(secp256k1_ge *r);
+
/** Set a group element (jacobian) equal to the point at infinity. */
static void secp256k1_gej_set_infinity(secp256k1_gej *r);
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index b31b6c12ef..9b93c39e92 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -38,22 +38,22 @@
*/
#if defined(EXHAUSTIVE_TEST_ORDER)
# if EXHAUSTIVE_TEST_ORDER == 199
-const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
+static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069,
0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18,
0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868,
0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED
);
-const int CURVE_B = 4;
+static const int CURVE_B = 4;
# elif EXHAUSTIVE_TEST_ORDER == 13
-const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
+static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0,
0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15,
0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e,
0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac
);
-const int CURVE_B = 2;
+static const int CURVE_B = 2;
# else
# error No known generator for the specified exhaustive test group order.
# endif
@@ -68,7 +68,7 @@ static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL
);
-const int CURVE_B = 7;
+static const int CURVE_B = 7;
#endif
static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
@@ -126,46 +126,43 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) {
r->y = a->y;
}
-static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb) {
- secp256k1_fe *az;
- secp256k1_fe *azi;
+static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len) {
+ secp256k1_fe u;
size_t i;
- size_t count = 0;
- az = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * len);
+ size_t last_i = SIZE_MAX;
+
for (i = 0; i < len; i++) {
if (!a[i].infinity) {
- az[count++] = a[i].z;
+ /* Use destination's x coordinates as scratch space */
+ if (last_i == SIZE_MAX) {
+ r[i].x = a[i].z;
+ } else {
+ secp256k1_fe_mul(&r[i].x, &r[last_i].x, &a[i].z);
+ }
+ last_i = i;
}
}
+ if (last_i == SIZE_MAX) {
+ return;
+ }
+ secp256k1_fe_inv_var(&u, &r[last_i].x);
- azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count);
- secp256k1_fe_inv_all_var(azi, az, count);
- free(az);
-
- count = 0;
- for (i = 0; i < len; i++) {
- r[i].infinity = a[i].infinity;
+ i = last_i;
+ while (i > 0) {
+ i--;
if (!a[i].infinity) {
- secp256k1_ge_set_gej_zinv(&r[i], &a[i], &azi[count++]);
+ secp256k1_fe_mul(&r[last_i].x, &r[i].x, &u);
+ secp256k1_fe_mul(&u, &u, &a[last_i].z);
+ last_i = i;
}
}
- free(azi);
-}
-
-static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len) {
- size_t i = len - 1;
- secp256k1_fe zi;
+ VERIFY_CHECK(!a[last_i].infinity);
+ r[last_i].x = u;
- if (len > 0) {
- /* Compute the inverse of the last z coordinate, and use it to compute the last affine output. */
- secp256k1_fe_inv(&zi, &a[i].z);
- secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi);
-
- /* Work out way backwards, using the z-ratios to scale the x/y values. */
- while (i > 0) {
- secp256k1_fe_mul(&zi, &zi, &zr[i]);
- i--;
- secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi);
+ for (i = 0; i < len; i++) {
+ r[i].infinity = a[i].infinity;
+ if (!a[i].infinity) {
+ secp256k1_ge_set_gej_zinv(&r[i], &a[i], &r[i].x);
}
}
}
@@ -178,6 +175,8 @@ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp
/* The z of the final point gives us the "global Z" for the table. */
r[i].x = a[i].x;
r[i].y = a[i].y;
+ /* Ensure all y values are in weak normal form for fast negation of points */
+ secp256k1_fe_normalize_weak(&r[i].y);
*globalz = a[i].z;
r[i].infinity = 0;
zs = zr[i];
@@ -200,6 +199,12 @@ static void secp256k1_gej_set_infinity(secp256k1_gej *r) {
secp256k1_fe_clear(&r->z);
}
+static void secp256k1_ge_set_infinity(secp256k1_ge *r) {
+ r->infinity = 1;
+ secp256k1_fe_clear(&r->x);
+ secp256k1_fe_clear(&r->y);
+}
+
static void secp256k1_gej_clear(secp256k1_gej *r) {
r->infinity = 0;
secp256k1_fe_clear(&r->x);
diff --git a/src/secp256k1/src/hash.h b/src/secp256k1/src/hash.h
index e08d25d225..de26e4b89f 100644
--- a/src/secp256k1/src/hash.h
+++ b/src/secp256k1/src/hash.h
@@ -14,28 +14,28 @@ typedef struct {
uint32_t s[8];
uint32_t buf[16]; /* In big endian */
size_t bytes;
-} secp256k1_sha256_t;
+} secp256k1_sha256;
-static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash);
-static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t size);
-static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32);
+static void secp256k1_sha256_initialize(secp256k1_sha256 *hash);
+static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t size);
+static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32);
typedef struct {
- secp256k1_sha256_t inner, outer;
-} secp256k1_hmac_sha256_t;
+ secp256k1_sha256 inner, outer;
+} secp256k1_hmac_sha256;
-static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t size);
-static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size);
-static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32);
+static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t size);
+static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size);
+static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32);
typedef struct {
unsigned char v[32];
unsigned char k[32];
int retry;
-} secp256k1_rfc6979_hmac_sha256_t;
+} secp256k1_rfc6979_hmac_sha256;
-static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen);
-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);
+static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen);
+static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen);
+static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng);
#endif /* SECP256K1_HASH_H */
diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h
index 4c9964ee06..009f26beba 100644
--- a/src/secp256k1/src/hash_impl.h
+++ b/src/secp256k1/src/hash_impl.h
@@ -33,7 +33,7 @@
#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24))
#endif
-static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash) {
+static void secp256k1_sha256_initialize(secp256k1_sha256 *hash) {
hash->s[0] = 0x6a09e667ul;
hash->s[1] = 0xbb67ae85ul;
hash->s[2] = 0x3c6ef372ul;
@@ -128,14 +128,15 @@ static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) {
s[7] += h;
}
-static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t len) {
+static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t len) {
size_t bufsize = hash->bytes & 0x3F;
hash->bytes += len;
while (bufsize + len >= 64) {
/* Fill the buffer, and process it. */
- memcpy(((unsigned char*)hash->buf) + bufsize, data, 64 - bufsize);
- data += 64 - bufsize;
- len -= 64 - bufsize;
+ size_t chunk_len = 64 - bufsize;
+ memcpy(((unsigned char*)hash->buf) + bufsize, data, chunk_len);
+ data += chunk_len;
+ len -= chunk_len;
secp256k1_sha256_transform(hash->s, hash->buf);
bufsize = 0;
}
@@ -145,7 +146,7 @@ static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char
}
}
-static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32) {
+static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32) {
static const unsigned char pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
uint32_t sizedesc[2];
uint32_t out[8];
@@ -161,14 +162,14 @@ static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *o
memcpy(out32, (const unsigned char*)out, 32);
}
-static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t keylen) {
- int n;
+static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) {
+ size_t n;
unsigned char rkey[64];
- if (keylen <= 64) {
+ if (keylen <= sizeof(rkey)) {
memcpy(rkey, key, keylen);
- memset(rkey + keylen, 0, 64 - keylen);
+ memset(rkey + keylen, 0, sizeof(rkey) - keylen);
} else {
- secp256k1_sha256_t sha256;
+ secp256k1_sha256 sha256;
secp256k1_sha256_initialize(&sha256);
secp256k1_sha256_write(&sha256, key, keylen);
secp256k1_sha256_finalize(&sha256, rkey);
@@ -176,24 +177,24 @@ static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, cons
}
secp256k1_sha256_initialize(&hash->outer);
- for (n = 0; n < 64; n++) {
+ for (n = 0; n < sizeof(rkey); n++) {
rkey[n] ^= 0x5c;
}
- secp256k1_sha256_write(&hash->outer, rkey, 64);
+ secp256k1_sha256_write(&hash->outer, rkey, sizeof(rkey));
secp256k1_sha256_initialize(&hash->inner);
- for (n = 0; n < 64; n++) {
+ for (n = 0; n < sizeof(rkey); n++) {
rkey[n] ^= 0x5c ^ 0x36;
}
- secp256k1_sha256_write(&hash->inner, rkey, 64);
- memset(rkey, 0, 64);
+ secp256k1_sha256_write(&hash->inner, rkey, sizeof(rkey));
+ memset(rkey, 0, sizeof(rkey));
}
-static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size) {
+static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size) {
secp256k1_sha256_write(&hash->inner, data, size);
}
-static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32) {
+static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32) {
unsigned char temp[32];
secp256k1_sha256_finalize(&hash->inner, temp);
secp256k1_sha256_write(&hash->outer, temp, 32);
@@ -202,8 +203,8 @@ static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsign
}
-static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen) {
- secp256k1_hmac_sha256_t hmac;
+static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen) {
+ secp256k1_hmac_sha256 hmac;
static const unsigned char zero[1] = {0x00};
static const unsigned char one[1] = {0x01};
@@ -232,11 +233,11 @@ static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha2
rng->retry = 0;
}
-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_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen) {
/* RFC6979 3.2.h. */
static const unsigned char zero[1] = {0x00};
if (rng->retry) {
- secp256k1_hmac_sha256_t hmac;
+ secp256k1_hmac_sha256 hmac;
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32);
secp256k1_hmac_sha256_write(&hmac, rng->v, 32);
secp256k1_hmac_sha256_write(&hmac, zero, 1);
@@ -247,7 +248,7 @@ static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256
}
while (outlen > 0) {
- secp256k1_hmac_sha256_t hmac;
+ secp256k1_hmac_sha256 hmac;
int now = outlen;
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32);
secp256k1_hmac_sha256_write(&hmac, rng->v, 32);
@@ -263,7 +264,7 @@ static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256
rng->retry = 1;
}
-static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng) {
+static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng) {
memset(rng->k, 0, 32);
memset(rng->v, 0, 32);
rng->retry = 0;
diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
index c00d08899b..d766a1029c 100644
--- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
+++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
@@ -52,7 +52,7 @@ public class NativeSecp256k1Test {
}
/**
- * This tests secret key verify() for a invalid secretkey
+ * This tests secret key verify() for an invalid secretkey
*/
public static void testSecKeyVerifyNeg() throws AssertFailException{
boolean result = false;
diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
index bcef7b32ce..b50970b4f2 100644
--- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
+++ b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
@@ -83,7 +83,7 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
secp256k1_ecdsa_signature sig[72];
- int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL );
+ int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL);
unsigned char outputSer[72];
size_t outputLen = 72;
@@ -353,7 +353,9 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
ctx,
nonce_res,
&pubkey,
- secdata
+ secdata,
+ NULL,
+ NULL
);
}
diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h
index 01ecba4d53..44cb68e750 100644
--- a/src/secp256k1/src/modules/ecdh/main_impl.h
+++ b/src/secp256k1/src/modules/ecdh/main_impl.h
@@ -10,16 +10,35 @@
#include "include/secp256k1_ecdh.h"
#include "ecmult_const_impl.h"
-int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const secp256k1_pubkey *point, const unsigned char *scalar) {
+static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) {
+ unsigned char version = (y[31] & 0x01) | 0x02;
+ secp256k1_sha256 sha;
+ (void)data;
+
+ secp256k1_sha256_initialize(&sha);
+ secp256k1_sha256_write(&sha, &version, 1);
+ secp256k1_sha256_write(&sha, x, 32);
+ secp256k1_sha256_finalize(&sha, output);
+
+ return 1;
+}
+
+const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256 = ecdh_hash_function_sha256;
+const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default = ecdh_hash_function_sha256;
+
+int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pubkey *point, const unsigned char *scalar, secp256k1_ecdh_hash_function hashfp, void *data) {
int ret = 0;
int overflow = 0;
secp256k1_gej res;
secp256k1_ge pt;
secp256k1_scalar s;
VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(result != NULL);
+ ARG_CHECK(output != NULL);
ARG_CHECK(point != NULL);
ARG_CHECK(scalar != NULL);
+ if (hashfp == NULL) {
+ hashfp = secp256k1_ecdh_hash_function_default;
+ }
secp256k1_pubkey_load(ctx, &pt, point);
secp256k1_scalar_set_b32(&s, scalar, &overflow);
@@ -27,24 +46,18 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const se
ret = 0;
} else {
unsigned char x[32];
- unsigned char y[1];
- secp256k1_sha256_t sha;
+ unsigned char y[32];
- secp256k1_ecmult_const(&res, &pt, &s);
+ secp256k1_ecmult_const(&res, &pt, &s, 256);
secp256k1_ge_set_gej(&pt, &res);
- /* Compute a hash of the point in compressed form
- * Note we cannot use secp256k1_eckey_pubkey_serialize here since it does not
- * expect its output to be secret and has a timing sidechannel. */
+
+ /* Compute a hash of the point */
secp256k1_fe_normalize(&pt.x);
secp256k1_fe_normalize(&pt.y);
secp256k1_fe_get_b32(x, &pt.x);
- y[0] = 0x02 | secp256k1_fe_is_odd(&pt.y);
+ secp256k1_fe_get_b32(y, &pt.y);
- secp256k1_sha256_initialize(&sha);
- secp256k1_sha256_write(&sha, y, sizeof(y));
- secp256k1_sha256_write(&sha, x, sizeof(x));
- secp256k1_sha256_finalize(&sha, result);
- ret = 1;
+ ret = hashfp(output, x, y, data);
}
secp256k1_scalar_clear(&s);
diff --git a/src/secp256k1/src/modules/ecdh/tests_impl.h b/src/secp256k1/src/modules/ecdh/tests_impl.h
index cec30b67c6..fe26e8fb69 100644
--- a/src/secp256k1/src/modules/ecdh/tests_impl.h
+++ b/src/secp256k1/src/modules/ecdh/tests_impl.h
@@ -7,6 +7,23 @@
#ifndef SECP256K1_MODULE_ECDH_TESTS_H
#define SECP256K1_MODULE_ECDH_TESTS_H
+int ecdh_hash_function_test_fail(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) {
+ (void)output;
+ (void)x;
+ (void)y;
+ (void)data;
+ return 0;
+}
+
+int ecdh_hash_function_custom(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) {
+ (void)data;
+ /* Save x and y as uncompressed public key */
+ output[0] = 0x04;
+ memcpy(output + 1, x, 32);
+ memcpy(output + 33, y, 32);
+ return 1;
+}
+
void test_ecdh_api(void) {
/* Setup context that just counts errors */
secp256k1_context *tctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
@@ -21,15 +38,15 @@ void test_ecdh_api(void) {
CHECK(secp256k1_ec_pubkey_create(tctx, &point, s_one) == 1);
/* Check all NULLs are detected */
- CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1);
+ CHECK(secp256k1_ecdh(tctx, res, &point, s_one, NULL, NULL) == 1);
CHECK(ecount == 0);
- CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one) == 0);
+ CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one, NULL, NULL) == 0);
CHECK(ecount == 1);
- CHECK(secp256k1_ecdh(tctx, res, NULL, s_one) == 0);
+ CHECK(secp256k1_ecdh(tctx, res, NULL, s_one, NULL, NULL) == 0);
CHECK(ecount == 2);
- CHECK(secp256k1_ecdh(tctx, res, &point, NULL) == 0);
+ CHECK(secp256k1_ecdh(tctx, res, &point, NULL, NULL, NULL) == 0);
CHECK(ecount == 3);
- CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1);
+ CHECK(secp256k1_ecdh(tctx, res, &point, s_one, NULL, NULL) == 1);
CHECK(ecount == 3);
/* Cleanup */
@@ -44,29 +61,36 @@ void test_ecdh_generator_basepoint(void) {
s_one[31] = 1;
/* Check against pubkey creation when the basepoint is the generator */
for (i = 0; i < 100; ++i) {
- secp256k1_sha256_t sha;
+ secp256k1_sha256 sha;
unsigned char s_b32[32];
- unsigned char output_ecdh[32];
+ unsigned char output_ecdh[65];
unsigned char output_ser[32];
- unsigned char point_ser[33];
+ unsigned char point_ser[65];
size_t point_ser_len = sizeof(point_ser);
secp256k1_scalar s;
random_scalar_order(&s);
secp256k1_scalar_get_b32(s_b32, &s);
- /* compute using ECDH function */
CHECK(secp256k1_ec_pubkey_create(ctx, &point[0], s_one) == 1);
- CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32) == 1);
- /* compute "explicitly" */
CHECK(secp256k1_ec_pubkey_create(ctx, &point[1], s_b32) == 1);
+
+ /* compute using ECDH function with custom hash function */
+ CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32, ecdh_hash_function_custom, NULL) == 1);
+ /* compute "explicitly" */
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_UNCOMPRESSED) == 1);
+ /* compare */
+ CHECK(memcmp(output_ecdh, point_ser, 65) == 0);
+
+ /* compute using ECDH function with default hash function */
+ CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32, NULL, NULL) == 1);
+ /* compute "explicitly" */
CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1);
- CHECK(point_ser_len == sizeof(point_ser));
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, point_ser, point_ser_len);
secp256k1_sha256_finalize(&sha, output_ser);
/* compare */
- CHECK(memcmp(output_ecdh, output_ser, sizeof(output_ser)) == 0);
+ CHECK(memcmp(output_ecdh, output_ser, 32) == 0);
}
}
@@ -89,11 +113,14 @@ void test_bad_scalar(void) {
CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_rand) == 1);
/* Try to multiply it by bad values */
- CHECK(secp256k1_ecdh(ctx, output, &point, s_zero) == 0);
- CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 0);
+ CHECK(secp256k1_ecdh(ctx, output, &point, s_zero, NULL, NULL) == 0);
+ CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, NULL, NULL) == 0);
/* ...and a good one */
s_overflow[31] -= 1;
- CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 1);
+ CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, NULL, NULL) == 1);
+
+ /* Hash function failure results in ecdh failure */
+ CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, ecdh_hash_function_test_fail, NULL) == 0);
}
void run_ecdh_tests(void) {
diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h
index db1ebf94be..d378335d99 100644
--- a/src/secp256k1/src/scalar_4x64_impl.h
+++ b/src/secp256k1/src/scalar_4x64_impl.h
@@ -376,7 +376,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
/* extract m6 */
"movq %%r8, %q6\n"
: "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6)
- : "S"(l), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1)
+ : "S"(l), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
: "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc");
/* Reduce 385 bits into 258. */
@@ -455,7 +455,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
/* extract p4 */
"movq %%r9, %q4\n"
: "=&g"(p0), "=&g"(p1), "=&g"(p2), "=g"(p3), "=g"(p4)
- : "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1)
+ : "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
: "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc");
/* Reduce 258 bits into 256. */
@@ -501,7 +501,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
/* Extract c */
"movq %%r9, %q0\n"
: "=g"(c)
- : "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1)
+ : "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
: "rax", "rdx", "r8", "r9", "r10", "cc", "memory");
#else
uint128_t c;
diff --git a/src/secp256k1/src/scratch.h b/src/secp256k1/src/scratch.h
new file mode 100644
index 0000000000..fef377af0d
--- /dev/null
+++ b/src/secp256k1/src/scratch.h
@@ -0,0 +1,39 @@
+/**********************************************************************
+ * Copyright (c) 2017 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_SCRATCH_
+#define _SECP256K1_SCRATCH_
+
+#define SECP256K1_SCRATCH_MAX_FRAMES 5
+
+/* The typedef is used internally; the struct name is used in the public API
+ * (where it is exposed as a different typedef) */
+typedef struct secp256k1_scratch_space_struct {
+ void *data[SECP256K1_SCRATCH_MAX_FRAMES];
+ size_t offset[SECP256K1_SCRATCH_MAX_FRAMES];
+ size_t frame_size[SECP256K1_SCRATCH_MAX_FRAMES];
+ size_t frame;
+ size_t max_size;
+ const secp256k1_callback* error_callback;
+} secp256k1_scratch;
+
+static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size);
+
+static void secp256k1_scratch_destroy(secp256k1_scratch* scratch);
+
+/** Attempts to allocate a new stack frame with `n` available bytes. Returns 1 on success, 0 on failure */
+static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects);
+
+/** Deallocates a stack frame */
+static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch);
+
+/** Returns the maximum allocation the scratch space will allow */
+static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t n_objects);
+
+/** Returns a pointer into the most recently allocated frame, or NULL if there is insufficient available space */
+static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t n);
+
+#endif
diff --git a/src/secp256k1/src/scratch_impl.h b/src/secp256k1/src/scratch_impl.h
new file mode 100644
index 0000000000..abed713b21
--- /dev/null
+++ b/src/secp256k1/src/scratch_impl.h
@@ -0,0 +1,86 @@
+/**********************************************************************
+ * Copyright (c) 2017 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_SCRATCH_IMPL_H_
+#define _SECP256K1_SCRATCH_IMPL_H_
+
+#include "scratch.h"
+
+/* Using 16 bytes alignment because common architectures never have alignment
+ * requirements above 8 for any of the types we care about. In addition we
+ * leave some room because currently we don't care about a few bytes.
+ * TODO: Determine this at configure time. */
+#define ALIGNMENT 16
+
+static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size) {
+ secp256k1_scratch* ret = (secp256k1_scratch*)checked_malloc(error_callback, sizeof(*ret));
+ if (ret != NULL) {
+ memset(ret, 0, sizeof(*ret));
+ ret->max_size = max_size;
+ ret->error_callback = error_callback;
+ }
+ return ret;
+}
+
+static void secp256k1_scratch_destroy(secp256k1_scratch* scratch) {
+ if (scratch != NULL) {
+ VERIFY_CHECK(scratch->frame == 0);
+ free(scratch);
+ }
+}
+
+static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t objects) {
+ size_t i = 0;
+ size_t allocated = 0;
+ for (i = 0; i < scratch->frame; i++) {
+ allocated += scratch->frame_size[i];
+ }
+ if (scratch->max_size - allocated <= objects * ALIGNMENT) {
+ return 0;
+ }
+ return scratch->max_size - allocated - objects * ALIGNMENT;
+}
+
+static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects) {
+ VERIFY_CHECK(scratch->frame < SECP256K1_SCRATCH_MAX_FRAMES);
+
+ if (n <= secp256k1_scratch_max_allocation(scratch, objects)) {
+ n += objects * ALIGNMENT;
+ scratch->data[scratch->frame] = checked_malloc(scratch->error_callback, n);
+ if (scratch->data[scratch->frame] == NULL) {
+ return 0;
+ }
+ scratch->frame_size[scratch->frame] = n;
+ scratch->offset[scratch->frame] = 0;
+ scratch->frame++;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch) {
+ VERIFY_CHECK(scratch->frame > 0);
+ scratch->frame -= 1;
+ free(scratch->data[scratch->frame]);
+}
+
+static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t size) {
+ void *ret;
+ size_t frame = scratch->frame - 1;
+ size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
+
+ if (scratch->frame == 0 || size + scratch->offset[frame] > scratch->frame_size[frame]) {
+ return NULL;
+ }
+ ret = (void *) ((unsigned char *) scratch->data[frame] + scratch->offset[frame]);
+ memset(ret, 0, size);
+ scratch->offset[frame] += size;
+
+ return ret;
+}
+
+#endif
diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c
index 4f8c01655b..15981f46e2 100644
--- a/src/secp256k1/src/secp256k1.c
+++ b/src/secp256k1/src/secp256k1.c
@@ -17,6 +17,7 @@
#include "ecdsa_impl.h"
#include "eckey_impl.h"
#include "hash_impl.h"
+#include "scratch_impl.h"
#define ARG_CHECK(cond) do { \
if (EXPECT(!(cond), 0)) { \
@@ -55,6 +56,14 @@ struct secp256k1_context_struct {
secp256k1_callback error_callback;
};
+static const secp256k1_context secp256k1_context_no_precomp_ = {
+ { 0 },
+ { 0 },
+ { default_illegal_callback_fn, 0 },
+ { default_error_callback_fn, 0 }
+};
+const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_no_precomp_;
+
secp256k1_context* secp256k1_context_create(unsigned int flags) {
secp256k1_context* ret = (secp256k1_context*)checked_malloc(&default_error_callback, sizeof(secp256k1_context));
ret->illegal_callback = default_illegal_callback;
@@ -90,6 +99,7 @@ secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) {
}
void secp256k1_context_destroy(secp256k1_context* ctx) {
+ CHECK(ctx != secp256k1_context_no_precomp);
if (ctx != NULL) {
secp256k1_ecmult_context_clear(&ctx->ecmult_ctx);
secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx);
@@ -99,6 +109,7 @@ void secp256k1_context_destroy(secp256k1_context* ctx) {
}
void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) {
+ CHECK(ctx != secp256k1_context_no_precomp);
if (fun == NULL) {
fun = default_illegal_callback_fn;
}
@@ -107,6 +118,7 @@ void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(
}
void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) {
+ CHECK(ctx != secp256k1_context_no_precomp);
if (fun == NULL) {
fun = default_error_callback_fn;
}
@@ -114,13 +126,22 @@ void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(co
ctx->error_callback.data = data;
}
+secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context* ctx, size_t max_size) {
+ VERIFY_CHECK(ctx != NULL);
+ return secp256k1_scratch_create(&ctx->error_callback, max_size);
+}
+
+void secp256k1_scratch_space_destroy(secp256k1_scratch_space* scratch) {
+ secp256k1_scratch_destroy(scratch);
+}
+
static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) {
if (sizeof(secp256k1_ge_storage) == 64) {
/* When the secp256k1_ge_storage type is exactly 64 byte, use its
* representation inside secp256k1_pubkey, as conversion is very fast.
* Note that secp256k1_pubkey_save must use the same representation. */
secp256k1_ge_storage s;
- memcpy(&s, &pubkey->data[0], 64);
+ memcpy(&s, &pubkey->data[0], sizeof(s));
secp256k1_ge_from_storage(ge, &s);
} else {
/* Otherwise, fall back to 32-byte big endian for X and Y. */
@@ -137,7 +158,7 @@ static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) {
if (sizeof(secp256k1_ge_storage) == 64) {
secp256k1_ge_storage s;
secp256k1_ge_to_storage(&s, ge);
- memcpy(&pubkey->data[0], &s, 64);
+ memcpy(&pubkey->data[0], &s, sizeof(s));
} else {
VERIFY_CHECK(!secp256k1_ge_is_infinity(ge));
secp256k1_fe_normalize_var(&ge->x);
@@ -307,10 +328,15 @@ int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_s
secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &r, &s, &q, &m));
}
+static SECP256K1_INLINE void buffer_append(unsigned char *buf, unsigned int *offset, const void *data, unsigned int len) {
+ memcpy(buf + *offset, data, len);
+ *offset += len;
+}
+
static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) {
unsigned char keydata[112];
- int keylen = 64;
- secp256k1_rfc6979_hmac_sha256_t rng;
+ unsigned int offset = 0;
+ secp256k1_rfc6979_hmac_sha256 rng;
unsigned int i;
/* We feed a byte array to the PRNG as input, consisting of:
* - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d.
@@ -320,17 +346,15 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m
* different argument mixtures to emulate each other and result in the same
* nonces.
*/
- memcpy(keydata, key32, 32);
- memcpy(keydata + 32, msg32, 32);
+ buffer_append(keydata, &offset, key32, 32);
+ buffer_append(keydata, &offset, msg32, 32);
if (data != NULL) {
- memcpy(keydata + 64, data, 32);
- keylen = 96;
+ buffer_append(keydata, &offset, data, 32);
}
if (algo16 != NULL) {
- memcpy(keydata + keylen, algo16, 16);
- keylen += 16;
+ buffer_append(keydata, &offset, algo16, 16);
}
- secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen);
+ secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, offset);
memset(keydata, 0, sizeof(keydata));
for (i = 0; i <= counter; i++) {
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
@@ -546,8 +570,9 @@ int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey
int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) {
VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
- secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32);
+ if (secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)) {
+ secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32);
+ }
return 1;
}
diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h
index 1255574209..30a91e5296 100644
--- a/src/secp256k1/src/testrand_impl.h
+++ b/src/secp256k1/src/testrand_impl.h
@@ -13,7 +13,7 @@
#include "testrand.h"
#include "hash.h"
-static secp256k1_rfc6979_hmac_sha256_t secp256k1_test_rng;
+static secp256k1_rfc6979_hmac_sha256 secp256k1_test_rng;
static uint32_t secp256k1_test_rng_precomputed[8];
static int secp256k1_test_rng_precomputed_used = 8;
static uint64_t secp256k1_test_rng_integer;
diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c
index 3d9bd5ebb4..f1c4db929a 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -23,6 +23,9 @@
#include "openssl/ec.h"
#include "openssl/ecdsa.h"
#include "openssl/obj_mac.h"
+# if OPENSSL_VERSION_NUMBER < 0x10100000L
+void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {*pr = sig->r; *ps = sig->s;}
+# endif
#endif
#include "contrib/lax_der_parsing.c"
@@ -215,8 +218,12 @@ void run_context_tests(void) {
CHECK(ecount == 3);
CHECK(secp256k1_ec_pubkey_tweak_mul(vrfy, &pubkey, ctmp) == 1);
CHECK(ecount == 3);
- CHECK(secp256k1_context_randomize(vrfy, ctmp) == 0);
- CHECK(ecount == 4);
+ CHECK(secp256k1_context_randomize(vrfy, ctmp) == 1);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_context_randomize(vrfy, NULL) == 1);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_context_randomize(sign, ctmp) == 1);
+ CHECK(ecount2 == 14);
CHECK(secp256k1_context_randomize(sign, NULL) == 1);
CHECK(ecount2 == 14);
secp256k1_context_set_illegal_callback(vrfy, NULL, NULL);
@@ -248,6 +255,44 @@ void run_context_tests(void) {
secp256k1_context_destroy(NULL);
}
+void run_scratch_tests(void) {
+ int32_t ecount = 0;
+ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
+ secp256k1_scratch_space *scratch;
+
+ /* Test public API */
+ secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
+
+ scratch = secp256k1_scratch_space_create(none, 1000);
+ CHECK(scratch != NULL);
+ CHECK(ecount == 0);
+
+ /* Test internal API */
+ CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
+ CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 1000);
+
+ /* Allocating 500 bytes with no frame fails */
+ CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
+ CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
+
+ /* ...but pushing a new stack frame does affect the max allocation */
+ CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1 == 1));
+ CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 500); /* 500 - ALIGNMENT */
+ CHECK(secp256k1_scratch_alloc(scratch, 500) != NULL);
+ CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
+
+ CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1) == 0);
+
+ /* ...and this effect is undone by popping the frame */
+ secp256k1_scratch_deallocate_frame(scratch);
+ CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
+ CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
+
+ /* cleanup */
+ secp256k1_scratch_space_destroy(scratch);
+ secp256k1_context_destroy(none);
+}
+
/***** HASH TESTS *****/
void run_sha256_tests(void) {
@@ -270,7 +315,7 @@ void run_sha256_tests(void) {
int i;
for (i = 0; i < 8; i++) {
unsigned char out[32];
- secp256k1_sha256_t hasher;
+ secp256k1_sha256 hasher;
secp256k1_sha256_initialize(&hasher);
secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i]));
secp256k1_sha256_finalize(&hasher, out);
@@ -313,7 +358,7 @@ void run_hmac_sha256_tests(void) {
};
int i;
for (i = 0; i < 6; i++) {
- secp256k1_hmac_sha256_t hasher;
+ secp256k1_hmac_sha256 hasher;
unsigned char out[32];
secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i]));
secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i]));
@@ -345,7 +390,7 @@ void run_rfc6979_hmac_sha256_tests(void) {
{0x75, 0x97, 0x88, 0x7c, 0xbd, 0x76, 0x32, 0x1f, 0x32, 0xe3, 0x04, 0x40, 0x67, 0x9a, 0x22, 0xcf, 0x7f, 0x8d, 0x9d, 0x2e, 0xac, 0x39, 0x0e, 0x58, 0x1f, 0xea, 0x09, 0x1c, 0xe2, 0x02, 0xba, 0x94}
};
- secp256k1_rfc6979_hmac_sha256_t rng;
+ secp256k1_rfc6979_hmac_sha256 rng;
unsigned char out[32];
int i;
@@ -2054,7 +2099,6 @@ void test_ge(void) {
/* Test batch gej -> ge conversion with and without known z ratios. */
{
secp256k1_fe *zr = (secp256k1_fe *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_fe));
- secp256k1_ge *ge_set_table = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge));
secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge));
for (i = 0; i < 4 * runs + 1; i++) {
/* Compute gej[i + 1].z / gez[i].z (with gej[n].z taken to be 1). */
@@ -2062,20 +2106,33 @@ void test_ge(void) {
secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z);
}
}
- secp256k1_ge_set_table_gej_var(ge_set_table, gej, zr, 4 * runs + 1);
- secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1, &ctx->error_callback);
+ secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1);
for (i = 0; i < 4 * runs + 1; i++) {
secp256k1_fe s;
random_fe_non_zero(&s);
secp256k1_gej_rescale(&gej[i], &s);
- ge_equals_gej(&ge_set_table[i], &gej[i]);
ge_equals_gej(&ge_set_all[i], &gej[i]);
}
- free(ge_set_table);
free(ge_set_all);
free(zr);
}
+ /* Test batch gej -> ge conversion with many infinities. */
+ for (i = 0; i < 4 * runs + 1; i++) {
+ random_group_element_test(&ge[i]);
+ /* randomly set half the points to infinitiy */
+ if(secp256k1_fe_is_odd(&ge[i].x)) {
+ secp256k1_ge_set_infinity(&ge[i]);
+ }
+ secp256k1_gej_set_ge(&gej[i], &ge[i]);
+ }
+ /* batch invert */
+ secp256k1_ge_set_all_gej_var(ge, gej, 4 * runs + 1);
+ /* check result */
+ for (i = 0; i < 4 * runs + 1; i++) {
+ ge_equals_gej(&ge[i], &gej[i]);
+ }
+
free(ge);
free(gej);
free(zinv);
@@ -2405,7 +2462,7 @@ void ecmult_const_random_mult(void) {
0xb84e4e1b, 0xfb77e21f, 0x96baae2a, 0x63dec956
);
secp256k1_gej b;
- secp256k1_ecmult_const(&b, &a, &xn);
+ secp256k1_ecmult_const(&b, &a, &xn, 256);
CHECK(secp256k1_ge_is_valid_var(&a));
ge_equals_gej(&expected_b, &b);
@@ -2421,12 +2478,12 @@ void ecmult_const_commutativity(void) {
random_scalar_order_test(&a);
random_scalar_order_test(&b);
- secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a);
- secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b);
+ secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a, 256);
+ secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b, 256);
secp256k1_ge_set_gej(&mid1, &res1);
secp256k1_ge_set_gej(&mid2, &res2);
- secp256k1_ecmult_const(&res1, &mid1, &b);
- secp256k1_ecmult_const(&res2, &mid2, &a);
+ secp256k1_ecmult_const(&res1, &mid1, &b, 256);
+ secp256k1_ecmult_const(&res2, &mid2, &a, 256);
secp256k1_ge_set_gej(&mid1, &res1);
secp256k1_ge_set_gej(&mid2, &res2);
ge_equals_ge(&mid1, &mid2);
@@ -2442,13 +2499,13 @@ void ecmult_const_mult_zero_one(void) {
secp256k1_scalar_negate(&negone, &one);
random_group_element_test(&point);
- secp256k1_ecmult_const(&res1, &point, &zero);
+ secp256k1_ecmult_const(&res1, &point, &zero, 3);
secp256k1_ge_set_gej(&res2, &res1);
CHECK(secp256k1_ge_is_infinity(&res2));
- secp256k1_ecmult_const(&res1, &point, &one);
+ secp256k1_ecmult_const(&res1, &point, &one, 2);
secp256k1_ge_set_gej(&res2, &res1);
ge_equals_ge(&res2, &point);
- secp256k1_ecmult_const(&res1, &point, &negone);
+ secp256k1_ecmult_const(&res1, &point, &negone, 256);
secp256k1_gej_neg(&res1, &res1);
secp256k1_ge_set_gej(&res2, &res1);
ge_equals_ge(&res2, &point);
@@ -2474,7 +2531,7 @@ void ecmult_const_chain_multiply(void) {
for (i = 0; i < 100; ++i) {
secp256k1_ge tmp;
secp256k1_ge_set_gej(&tmp, &point);
- secp256k1_ecmult_const(&point, &tmp, &scalar);
+ secp256k1_ecmult_const(&point, &tmp, &scalar, 256);
}
secp256k1_ge_set_gej(&res, &point);
ge_equals_gej(&res, &expected_point);
@@ -2487,6 +2544,446 @@ void run_ecmult_const_tests(void) {
ecmult_const_chain_multiply();
}
+typedef struct {
+ secp256k1_scalar *sc;
+ secp256k1_ge *pt;
+} ecmult_multi_data;
+
+static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) {
+ ecmult_multi_data *data = (ecmult_multi_data*) cbdata;
+ *sc = data->sc[idx];
+ *pt = data->pt[idx];
+ return 1;
+}
+
+static int ecmult_multi_false_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) {
+ (void)sc;
+ (void)pt;
+ (void)idx;
+ (void)cbdata;
+ return 0;
+}
+
+void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func ecmult_multi) {
+ int ncount;
+ secp256k1_scalar szero;
+ secp256k1_scalar sc[32];
+ secp256k1_ge pt[32];
+ secp256k1_gej r;
+ secp256k1_gej r2;
+ ecmult_multi_data data;
+ secp256k1_scratch *scratch_empty;
+
+ data.sc = sc;
+ data.pt = pt;
+ secp256k1_scalar_set_int(&szero, 0);
+
+ /* No points to multiply */
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, NULL, ecmult_multi_callback, &data, 0));
+
+ /* Check 1- and 2-point multiplies against ecmult */
+ for (ncount = 0; ncount < count; ncount++) {
+ secp256k1_ge ptg;
+ secp256k1_gej ptgj;
+ random_scalar_order(&sc[0]);
+ random_scalar_order(&sc[1]);
+
+ random_group_element_test(&ptg);
+ secp256k1_gej_set_ge(&ptgj, &ptg);
+ pt[0] = ptg;
+ pt[1] = secp256k1_ge_const_g;
+
+ /* only G scalar */
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &szero, &sc[0]);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0));
+ secp256k1_gej_neg(&r2, &r2);
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+
+ /* 1-point */
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &szero);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 1));
+ secp256k1_gej_neg(&r2, &r2);
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+
+ /* Try to multiply 1 point, but scratch space is empty */
+ scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0);
+ CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1));
+ secp256k1_scratch_destroy(scratch_empty);
+
+ /* Try to multiply 1 point, but callback returns false */
+ CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1));
+
+ /* 2-point */
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 2));
+ secp256k1_gej_neg(&r2, &r2);
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+
+ /* 2-point with G scalar */
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1));
+ secp256k1_gej_neg(&r2, &r2);
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ /* Check infinite outputs of various forms */
+ for (ncount = 0; ncount < count; ncount++) {
+ secp256k1_ge ptg;
+ size_t i, j;
+ size_t sizes[] = { 2, 10, 32 };
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 32; i++) {
+ random_scalar_order(&sc[i]);
+ secp256k1_ge_set_infinity(&pt[i]);
+ }
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 32; i++) {
+ random_group_element_test(&ptg);
+ pt[i] = ptg;
+ secp256k1_scalar_set_int(&sc[i], 0);
+ }
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ for (j = 0; j < 3; j++) {
+ random_group_element_test(&ptg);
+ for (i = 0; i < 16; i++) {
+ random_scalar_order(&sc[2*i]);
+ secp256k1_scalar_negate(&sc[2*i + 1], &sc[2*i]);
+ pt[2 * i] = ptg;
+ pt[2 * i + 1] = ptg;
+ }
+
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(secp256k1_gej_is_infinity(&r));
+
+ random_scalar_order(&sc[0]);
+ for (i = 0; i < 16; i++) {
+ random_group_element_test(&ptg);
+
+ sc[2*i] = sc[0];
+ sc[2*i+1] = sc[0];
+ pt[2 * i] = ptg;
+ secp256k1_ge_neg(&pt[2*i+1], &pt[2*i]);
+ }
+
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ random_group_element_test(&ptg);
+ secp256k1_scalar_set_int(&sc[0], 0);
+ pt[0] = ptg;
+ for (i = 1; i < 32; i++) {
+ pt[i] = ptg;
+
+ random_scalar_order(&sc[i]);
+ secp256k1_scalar_add(&sc[0], &sc[0], &sc[i]);
+ secp256k1_scalar_negate(&sc[i], &sc[i]);
+ }
+
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32));
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ /* Check random points, constant scalar */
+ for (ncount = 0; ncount < count; ncount++) {
+ size_t i;
+ secp256k1_gej_set_infinity(&r);
+
+ random_scalar_order(&sc[0]);
+ for (i = 0; i < 20; i++) {
+ secp256k1_ge ptg;
+ sc[i] = sc[0];
+ random_group_element_test(&ptg);
+ pt[i] = ptg;
+ secp256k1_gej_add_ge_var(&r, &r, &pt[i], NULL);
+ }
+
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r, &sc[0], &szero);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
+ secp256k1_gej_neg(&r2, &r2);
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ /* Check random scalars, constant point */
+ for (ncount = 0; ncount < count; ncount++) {
+ size_t i;
+ secp256k1_ge ptg;
+ secp256k1_gej p0j;
+ secp256k1_scalar rs;
+ secp256k1_scalar_set_int(&rs, 0);
+
+ random_group_element_test(&ptg);
+ for (i = 0; i < 20; i++) {
+ random_scalar_order(&sc[i]);
+ pt[i] = ptg;
+ secp256k1_scalar_add(&rs, &rs, &sc[i]);
+ }
+
+ secp256k1_gej_set_ge(&p0j, &pt[0]);
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &p0j, &rs, &szero);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
+ secp256k1_gej_neg(&r2, &r2);
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ /* Sanity check that zero scalars don't cause problems */
+ for (ncount = 0; ncount < 20; ncount++) {
+ random_scalar_order(&sc[ncount]);
+ random_group_element_test(&pt[ncount]);
+ }
+
+ secp256k1_scalar_clear(&sc[0]);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
+ secp256k1_scalar_clear(&sc[1]);
+ secp256k1_scalar_clear(&sc[2]);
+ secp256k1_scalar_clear(&sc[3]);
+ secp256k1_scalar_clear(&sc[4]);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 6));
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 5));
+ CHECK(secp256k1_gej_is_infinity(&r));
+
+ /* Run through s0*(t0*P) + s1*(t1*P) exhaustively for many small values of s0, s1, t0, t1 */
+ {
+ const size_t TOP = 8;
+ size_t s0i, s1i;
+ size_t t0i, t1i;
+ secp256k1_ge ptg;
+ secp256k1_gej ptgj;
+
+ random_group_element_test(&ptg);
+ secp256k1_gej_set_ge(&ptgj, &ptg);
+
+ for(t0i = 0; t0i < TOP; t0i++) {
+ for(t1i = 0; t1i < TOP; t1i++) {
+ secp256k1_gej t0p, t1p;
+ secp256k1_scalar t0, t1;
+
+ secp256k1_scalar_set_int(&t0, (t0i + 1) / 2);
+ secp256k1_scalar_cond_negate(&t0, t0i & 1);
+ secp256k1_scalar_set_int(&t1, (t1i + 1) / 2);
+ secp256k1_scalar_cond_negate(&t1, t1i & 1);
+
+ secp256k1_ecmult(&ctx->ecmult_ctx, &t0p, &ptgj, &t0, &szero);
+ secp256k1_ecmult(&ctx->ecmult_ctx, &t1p, &ptgj, &t1, &szero);
+
+ for(s0i = 0; s0i < TOP; s0i++) {
+ for(s1i = 0; s1i < TOP; s1i++) {
+ secp256k1_scalar tmp1, tmp2;
+ secp256k1_gej expected, actual;
+
+ secp256k1_ge_set_gej(&pt[0], &t0p);
+ secp256k1_ge_set_gej(&pt[1], &t1p);
+
+ secp256k1_scalar_set_int(&sc[0], (s0i + 1) / 2);
+ secp256k1_scalar_cond_negate(&sc[0], s0i & 1);
+ secp256k1_scalar_set_int(&sc[1], (s1i + 1) / 2);
+ secp256k1_scalar_cond_negate(&sc[1], s1i & 1);
+
+ secp256k1_scalar_mul(&tmp1, &t0, &sc[0]);
+ secp256k1_scalar_mul(&tmp2, &t1, &sc[1]);
+ secp256k1_scalar_add(&tmp1, &tmp1, &tmp2);
+
+ secp256k1_ecmult(&ctx->ecmult_ctx, &expected, &ptgj, &tmp1, &szero);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &actual, &szero, ecmult_multi_callback, &data, 2));
+ secp256k1_gej_neg(&expected, &expected);
+ secp256k1_gej_add_var(&actual, &actual, &expected, NULL);
+ CHECK(secp256k1_gej_is_infinity(&actual));
+ }
+ }
+ }
+ }
+ }
+}
+
+void test_secp256k1_pippenger_bucket_window_inv(void) {
+ int i;
+
+ CHECK(secp256k1_pippenger_bucket_window_inv(0) == 0);
+ for(i = 1; i <= PIPPENGER_MAX_BUCKET_WINDOW; i++) {
+#ifdef USE_ENDOMORPHISM
+ /* Bucket_window of 8 is not used with endo */
+ if (i == 8) {
+ continue;
+ }
+#endif
+ CHECK(secp256k1_pippenger_bucket_window(secp256k1_pippenger_bucket_window_inv(i)) == i);
+ if (i != PIPPENGER_MAX_BUCKET_WINDOW) {
+ CHECK(secp256k1_pippenger_bucket_window(secp256k1_pippenger_bucket_window_inv(i)+1) > i);
+ }
+ }
+}
+
+/**
+ * Probabilistically test the function returning the maximum number of possible points
+ * for a given scratch space.
+ */
+void test_ecmult_multi_pippenger_max_points(void) {
+ size_t scratch_size = secp256k1_rand_int(256);
+ size_t max_size = secp256k1_pippenger_scratch_size(secp256k1_pippenger_bucket_window_inv(PIPPENGER_MAX_BUCKET_WINDOW-1)+512, 12);
+ secp256k1_scratch *scratch;
+ size_t n_points_supported;
+ int bucket_window = 0;
+
+ for(; scratch_size < max_size; scratch_size+=256) {
+ scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
+ CHECK(scratch != NULL);
+ n_points_supported = secp256k1_pippenger_max_points(scratch);
+ if (n_points_supported == 0) {
+ secp256k1_scratch_destroy(scratch);
+ continue;
+ }
+ bucket_window = secp256k1_pippenger_bucket_window(n_points_supported);
+ CHECK(secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points_supported, bucket_window), PIPPENGER_SCRATCH_OBJECTS));
+ secp256k1_scratch_deallocate_frame(scratch);
+ secp256k1_scratch_destroy(scratch);
+ }
+ CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW);
+}
+
+void test_ecmult_multi_batch_size_helper(void) {
+ size_t n_batches, n_batch_points, max_n_batch_points, n;
+
+ max_n_batch_points = 0;
+ n = 1;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 0);
+
+ max_n_batch_points = 1;
+ n = 0;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
+ CHECK(n_batches == 0);
+ CHECK(n_batch_points == 0);
+
+ max_n_batch_points = 2;
+ n = 5;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
+ CHECK(n_batches == 3);
+ CHECK(n_batch_points == 2);
+
+ max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH;
+ n = ECMULT_MAX_POINTS_PER_BATCH;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
+ CHECK(n_batches == 1);
+ CHECK(n_batch_points == ECMULT_MAX_POINTS_PER_BATCH);
+
+ max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH + 1;
+ n = ECMULT_MAX_POINTS_PER_BATCH + 1;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
+ CHECK(n_batches == 2);
+ CHECK(n_batch_points == ECMULT_MAX_POINTS_PER_BATCH/2 + 1);
+
+ max_n_batch_points = 1;
+ n = SIZE_MAX;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
+ CHECK(n_batches == SIZE_MAX);
+ CHECK(n_batch_points == 1);
+
+ max_n_batch_points = 2;
+ n = SIZE_MAX;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
+ CHECK(n_batches == SIZE_MAX/2 + 1);
+ CHECK(n_batch_points == 2);
+}
+
+/**
+ * Run secp256k1_ecmult_multi_var with num points and a scratch space restricted to
+ * 1 <= i <= num points.
+ */
+void test_ecmult_multi_batching(void) {
+ static const int n_points = 2*ECMULT_PIPPENGER_THRESHOLD;
+ secp256k1_scalar scG;
+ secp256k1_scalar szero;
+ secp256k1_scalar *sc = (secp256k1_scalar *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_scalar) * n_points);
+ secp256k1_ge *pt = (secp256k1_ge *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_ge) * n_points);
+ secp256k1_gej r;
+ secp256k1_gej r2;
+ ecmult_multi_data data;
+ int i;
+ secp256k1_scratch *scratch;
+
+ secp256k1_gej_set_infinity(&r2);
+ secp256k1_scalar_set_int(&szero, 0);
+
+ /* Get random scalars and group elements and compute result */
+ random_scalar_order(&scG);
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r2, &szero, &scG);
+ for(i = 0; i < n_points; i++) {
+ secp256k1_ge ptg;
+ secp256k1_gej ptgj;
+ random_group_element_test(&ptg);
+ secp256k1_gej_set_ge(&ptgj, &ptg);
+ pt[i] = ptg;
+ random_scalar_order(&sc[i]);
+ secp256k1_ecmult(&ctx->ecmult_ctx, &ptgj, &ptgj, &sc[i], NULL);
+ secp256k1_gej_add_var(&r2, &r2, &ptgj, NULL);
+ }
+ data.sc = sc;
+ data.pt = pt;
+
+ /* Test with empty scratch space */
+ scratch = secp256k1_scratch_create(&ctx->error_callback, 0);
+ CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1));
+ secp256k1_scratch_destroy(scratch);
+
+ /* Test with space for 1 point in pippenger. That's not enough because
+ * ecmult_multi selects strauss which requires more memory. */
+ scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
+ CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1));
+ secp256k1_scratch_destroy(scratch);
+
+ secp256k1_gej_neg(&r2, &r2);
+ for(i = 1; i <= n_points; i++) {
+ if (i > ECMULT_PIPPENGER_THRESHOLD) {
+ int bucket_window = secp256k1_pippenger_bucket_window(i);
+ size_t scratch_size = secp256k1_pippenger_scratch_size(i, bucket_window);
+ scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
+ } else {
+ size_t scratch_size = secp256k1_strauss_scratch_size(i);
+ scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
+ }
+ CHECK(secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points));
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+ secp256k1_scratch_destroy(scratch);
+ }
+ free(sc);
+ free(pt);
+}
+
+void run_ecmult_multi_tests(void) {
+ secp256k1_scratch *scratch;
+
+ test_secp256k1_pippenger_bucket_window_inv();
+ test_ecmult_multi_pippenger_max_points();
+ scratch = secp256k1_scratch_create(&ctx->error_callback, 819200);
+ test_ecmult_multi(scratch, secp256k1_ecmult_multi_var);
+ test_ecmult_multi(NULL, secp256k1_ecmult_multi_var);
+ test_ecmult_multi(scratch, secp256k1_ecmult_pippenger_batch_single);
+ test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single);
+ secp256k1_scratch_destroy(scratch);
+
+ /* Run test_ecmult_multi with space for exactly one point */
+ scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
+ test_ecmult_multi(scratch, secp256k1_ecmult_multi_var);
+ secp256k1_scratch_destroy(scratch);
+
+ test_ecmult_multi_batch_size_helper();
+ test_ecmult_multi_batching();
+}
+
void test_wnaf(const secp256k1_scalar *number, int w) {
secp256k1_scalar x, two, t;
int wnaf[256];
@@ -2541,6 +3038,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
int wnaf[256] = {0};
int i;
int skew;
+ int bits = 256;
secp256k1_scalar num = *number;
secp256k1_scalar_set_int(&x, 0);
@@ -2550,10 +3048,11 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
for (i = 0; i < 16; ++i) {
secp256k1_scalar_shr_int(&num, 8);
}
+ bits = 128;
#endif
- skew = secp256k1_wnaf_const(wnaf, num, w);
+ skew = secp256k1_wnaf_const(wnaf, num, w, bits);
- for (i = WNAF_SIZE(w); i >= 0; --i) {
+ for (i = WNAF_SIZE_BITS(bits, w); i >= 0; --i) {
secp256k1_scalar t;
int v = wnaf[i];
CHECK(v != 0); /* check nonzero */
@@ -2575,6 +3074,110 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
CHECK(secp256k1_scalar_eq(&x, &num));
}
+void test_fixed_wnaf(const secp256k1_scalar *number, int w) {
+ secp256k1_scalar x, shift;
+ int wnaf[256] = {0};
+ int i;
+ int skew;
+ secp256k1_scalar num = *number;
+
+ secp256k1_scalar_set_int(&x, 0);
+ secp256k1_scalar_set_int(&shift, 1 << w);
+ /* With USE_ENDOMORPHISM on we only consider 128-bit numbers */
+#ifdef USE_ENDOMORPHISM
+ for (i = 0; i < 16; ++i) {
+ secp256k1_scalar_shr_int(&num, 8);
+ }
+#endif
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+
+ for (i = WNAF_SIZE(w)-1; i >= 0; --i) {
+ secp256k1_scalar t;
+ int v = wnaf[i];
+ CHECK(v == 0 || v & 1); /* check parity */
+ CHECK(v > -(1 << w)); /* check range above */
+ CHECK(v < (1 << w)); /* check range below */
+
+ secp256k1_scalar_mul(&x, &x, &shift);
+ if (v >= 0) {
+ secp256k1_scalar_set_int(&t, v);
+ } else {
+ secp256k1_scalar_set_int(&t, -v);
+ secp256k1_scalar_negate(&t, &t);
+ }
+ secp256k1_scalar_add(&x, &x, &t);
+ }
+ /* If skew is 1 then add 1 to num */
+ secp256k1_scalar_cadd_bit(&num, 0, skew == 1);
+ CHECK(secp256k1_scalar_eq(&x, &num));
+}
+
+/* Checks that the first 8 elements of wnaf are equal to wnaf_expected and the
+ * rest is 0.*/
+void test_fixed_wnaf_small_helper(int *wnaf, int *wnaf_expected, int w) {
+ int i;
+ for (i = WNAF_SIZE(w)-1; i >= 8; --i) {
+ CHECK(wnaf[i] == 0);
+ }
+ for (i = 7; i >= 0; --i) {
+ CHECK(wnaf[i] == wnaf_expected[i]);
+ }
+}
+
+void test_fixed_wnaf_small(void) {
+ int w = 4;
+ int wnaf[256] = {0};
+ int i;
+ int skew;
+ secp256k1_scalar num;
+
+ secp256k1_scalar_set_int(&num, 0);
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+ for (i = WNAF_SIZE(w)-1; i >= 0; --i) {
+ int v = wnaf[i];
+ CHECK(v == 0);
+ }
+ CHECK(skew == 0);
+
+ secp256k1_scalar_set_int(&num, 1);
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+ for (i = WNAF_SIZE(w)-1; i >= 1; --i) {
+ int v = wnaf[i];
+ CHECK(v == 0);
+ }
+ CHECK(wnaf[0] == 1);
+ CHECK(skew == 0);
+
+ {
+ int wnaf_expected[8] = { 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf };
+ secp256k1_scalar_set_int(&num, 0xffffffff);
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+ test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w);
+ CHECK(skew == 0);
+ }
+ {
+ int wnaf_expected[8] = { -1, -1, -1, -1, -1, -1, -1, 0xf };
+ secp256k1_scalar_set_int(&num, 0xeeeeeeee);
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+ test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w);
+ CHECK(skew == 1);
+ }
+ {
+ int wnaf_expected[8] = { 1, 0, 1, 0, 1, 0, 1, 0 };
+ secp256k1_scalar_set_int(&num, 0x01010101);
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+ test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w);
+ CHECK(skew == 0);
+ }
+ {
+ int wnaf_expected[8] = { -0xf, 0, 0xf, -0xf, 0, 0xf, 1, 0 };
+ secp256k1_scalar_set_int(&num, 0x01ef1ef1);
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+ test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w);
+ CHECK(skew == 0);
+ }
+}
+
void run_wnaf(void) {
int i;
secp256k1_scalar n = {{0}};
@@ -2585,12 +3188,15 @@ void run_wnaf(void) {
test_constant_wnaf(&n, 4);
n.d[0] = 2;
test_constant_wnaf(&n, 4);
+ /* Test 0 */
+ test_fixed_wnaf_small();
/* Random tests */
for (i = 0; i < count; i++) {
random_scalar_order(&n);
test_wnaf(&n, 4+(i%10));
test_constant_wnaf_negate(&n);
test_constant_wnaf(&n, 4 + (i % 10));
+ test_fixed_wnaf(&n, 4 + (i % 10));
}
secp256k1_scalar_set_int(&n, 0);
CHECK(secp256k1_scalar_cond_negate(&n, 1) == -1);
@@ -3055,6 +3661,7 @@ void run_ec_pubkey_parse_test(void) {
ecount = 0;
VG_UNDEF(&pubkey, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 65) == 1);
+ CHECK(secp256k1_ec_pubkey_parse(secp256k1_context_no_precomp, &pubkey, pubkeyc, 65) == 1);
VG_CHECK(&pubkey, sizeof(pubkey));
CHECK(ecount == 0);
VG_UNDEF(&ge, sizeof(ge));
@@ -3177,7 +3784,7 @@ void run_eckey_edge_case_test(void) {
VG_CHECK(&pubkey, sizeof(pubkey));
CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
pubkey_negone = pubkey;
- /* Tweak of zero leaves the value changed. */
+ /* Tweak of zero leaves the value unchanged. */
memset(ctmp2, 0, 32);
CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, ctmp2) == 1);
CHECK(memcmp(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40);
@@ -3668,6 +4275,7 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_
#ifdef ENABLE_OPENSSL_TESTS
ECDSA_SIG *sig_openssl;
+ const BIGNUM *r = NULL, *s = NULL;
const unsigned char *sigptr;
unsigned char roundtrip_openssl[2048];
int len_openssl = 2048;
@@ -3719,15 +4327,16 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_
sigptr = sig;
parsed_openssl = (d2i_ECDSA_SIG(&sig_openssl, &sigptr, siglen) != NULL);
if (parsed_openssl) {
- valid_openssl = !BN_is_negative(sig_openssl->r) && !BN_is_negative(sig_openssl->s) && BN_num_bits(sig_openssl->r) > 0 && BN_num_bits(sig_openssl->r) <= 256 && BN_num_bits(sig_openssl->s) > 0 && BN_num_bits(sig_openssl->s) <= 256;
+ ECDSA_SIG_get0(sig_openssl, &r, &s);
+ valid_openssl = !BN_is_negative(r) && !BN_is_negative(s) && BN_num_bits(r) > 0 && BN_num_bits(r) <= 256 && BN_num_bits(s) > 0 && BN_num_bits(s) <= 256;
if (valid_openssl) {
unsigned char tmp[32] = {0};
- BN_bn2bin(sig_openssl->r, tmp + 32 - BN_num_bytes(sig_openssl->r));
+ BN_bn2bin(r, tmp + 32 - BN_num_bytes(r));
valid_openssl = memcmp(tmp, max_scalar, 32) < 0;
}
if (valid_openssl) {
unsigned char tmp[32] = {0};
- BN_bn2bin(sig_openssl->s, tmp + 32 - BN_num_bytes(sig_openssl->s));
+ BN_bn2bin(s, tmp + 32 - BN_num_bytes(s));
valid_openssl = memcmp(tmp, max_scalar, 32) < 0;
}
}
@@ -4431,8 +5040,9 @@ int main(int argc, char **argv) {
}
} else {
FILE *frand = fopen("/dev/urandom", "r");
- if ((frand == NULL) || !fread(&seed16, sizeof(seed16), 1, frand)) {
+ if ((frand == NULL) || fread(&seed16, 1, sizeof(seed16), frand) != sizeof(seed16)) {
uint64_t t = time(NULL) * (uint64_t)1337;
+ fprintf(stderr, "WARNING: could not read 16 bytes from /dev/urandom; falling back to insecure PRNG\n");
seed16[0] ^= t;
seed16[1] ^= t >> 8;
seed16[2] ^= t >> 16;
@@ -4442,7 +5052,9 @@ int main(int argc, char **argv) {
seed16[6] ^= t >> 48;
seed16[7] ^= t >> 56;
}
- fclose(frand);
+ if (frand) {
+ fclose(frand);
+ }
}
secp256k1_rand_seed(seed16);
@@ -4451,6 +5063,7 @@ int main(int argc, char **argv) {
/* initialize */
run_context_tests();
+ run_scratch_tests();
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
if (secp256k1_rand_bits(1)) {
secp256k1_rand256(run32);
@@ -4492,6 +5105,7 @@ int main(int argc, char **argv) {
run_ecmult_constants();
run_ecmult_gen_blind();
run_ecmult_const_tests();
+ run_ecmult_multi_tests();
run_ec_combine();
/* endomorphism tests */
diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c
index b040bb0733..ab9779b02f 100644
--- a/src/secp256k1/src/tests_exhaustive.c
+++ b/src/secp256k1/src/tests_exhaustive.c
@@ -174,7 +174,7 @@ void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *gr
ge_equals_gej(&group[(i * r_log + j) % order], &tmp);
if (i > 0) {
- secp256k1_ecmult_const(&tmp, &group[i], &ng);
+ secp256k1_ecmult_const(&tmp, &group[i], &ng, 256);
ge_equals_gej(&group[(i * j) % order], &tmp);
}
}
@@ -182,6 +182,46 @@ void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *gr
}
}
+typedef struct {
+ secp256k1_scalar sc[2];
+ secp256k1_ge pt[2];
+} ecmult_multi_data;
+
+static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) {
+ ecmult_multi_data *data = (ecmult_multi_data*) cbdata;
+ *sc = data->sc[idx];
+ *pt = data->pt[idx];
+ return 1;
+}
+
+void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
+ int i, j, k, x, y;
+ secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, 4096);
+ for (i = 0; i < order; i++) {
+ for (j = 0; j < order; j++) {
+ for (k = 0; k < order; k++) {
+ for (x = 0; x < order; x++) {
+ for (y = 0; y < order; y++) {
+ secp256k1_gej tmp;
+ secp256k1_scalar g_sc;
+ ecmult_multi_data data;
+
+ secp256k1_scalar_set_int(&data.sc[0], i);
+ secp256k1_scalar_set_int(&data.sc[1], j);
+ secp256k1_scalar_set_int(&g_sc, k);
+ data.pt[0] = group[x];
+ data.pt[1] = group[y];
+
+ secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2);
+ ge_equals_gej(&group[(i * x + j * y + k) % order], &tmp);
+ }
+ }
+ }
+ }
+ }
+ secp256k1_scratch_destroy(scratch);
+}
+
void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) {
secp256k1_fe x;
unsigned char x_bin[32];
@@ -456,6 +496,7 @@ int main(void) {
#endif
test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER);
test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER);
+ test_exhaustive_ecmult_multi(ctx, group, EXHAUSTIVE_TEST_ORDER);
test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER);
test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER);
diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h
index b0441d8e30..e1f5b76452 100644
--- a/src/secp256k1/src/util.h
+++ b/src/secp256k1/src/util.h
@@ -36,7 +36,7 @@ static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback *
} while(0)
#endif
-#ifdef HAVE_BUILTIN_EXPECT
+#if SECP256K1_GNUC_PREREQ(3, 0)
#define EXPECT(x,c) __builtin_expect((x),(c))
#else
#define EXPECT(x,c) (x)
@@ -76,6 +76,14 @@ static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_
return ret;
}
+static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void *ptr, size_t size) {
+ void *ret = realloc(ptr, size);
+ if (ret == NULL) {
+ secp256k1_callback_call(cb, "Out of memory");
+ }
+ return ret;
+}
+
/* Macro for restrict, when available and not in a VERIFY build. */
#if defined(SECP256K1_BUILD) && defined(VERIFY)
# define SECP256K1_RESTRICT
diff --git a/src/serialize.h b/src/serialize.h
index cb48dc174a..1dc27d84eb 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -89,6 +89,11 @@ template<typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj)
obj = htole32(obj);
s.write((char*)&obj, 4);
}
+template<typename Stream> inline void ser_writedata32be(Stream &s, uint32_t obj)
+{
+ obj = htobe32(obj);
+ s.write((char*)&obj, 4);
+}
template<typename Stream> inline void ser_writedata64(Stream &s, uint64_t obj)
{
obj = htole64(obj);
@@ -118,6 +123,12 @@ template<typename Stream> inline uint32_t ser_readdata32(Stream &s)
s.read((char*)&obj, 4);
return le32toh(obj);
}
+template<typename Stream> inline uint32_t ser_readdata32be(Stream &s)
+{
+ uint32_t obj;
+ s.read((char*)&obj, 4);
+ return be32toh(obj);
+}
template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
{
uint64_t obj;
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index 627018083e..5c2050e4a2 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -10,10 +10,6 @@
#endif
#ifdef WIN32
-#ifdef _WIN32_WINNT
-#undef _WIN32_WINNT
-#endif
-#define _WIN32_WINNT 0x0501
#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
diff --git a/src/sync.cpp b/src/sync.cpp
index 23ca866e53..20258d8e9a 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -2,10 +2,16 @@
// 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 <sync.h>
+#include <tinyformat.h>
#include <logging.h>
#include <util/strencodings.h>
+#include <util/threadnames.h>
#include <stdio.h>
@@ -37,23 +43,30 @@ void PrintLockContention(const char* pszName, const char* pszFile, int nLine)
//
struct CLockLocation {
- CLockLocation(const char* pszName, const char* pszFile, int nLine, bool fTryIn)
- {
- mutexName = pszName;
- sourceFile = pszFile;
- sourceLine = nLine;
- fTry = fTryIn;
- }
+ CLockLocation(
+ const char* pszName,
+ const char* pszFile,
+ int nLine,
+ bool fTryIn,
+ const std::string& thread_name)
+ : fTry(fTryIn),
+ mutexName(pszName),
+ sourceFile(pszFile),
+ m_thread_name(thread_name),
+ sourceLine(nLine) {}
std::string ToString() const
{
- return mutexName + " " + sourceFile + ":" + itostr(sourceLine) + (fTry ? " (TRY)" : "");
+ return strprintf(
+ "%s %s:%s%s (in thread %s)",
+ mutexName, sourceFile, itostr(sourceLine), (fTry ? " (TRY)" : ""), m_thread_name);
}
private:
bool fTry;
std::string mutexName;
std::string sourceFile;
+ const std::string& m_thread_name;
int sourceLine;
};
@@ -105,7 +118,7 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
LogPrintf(" %s\n", i.second.ToString());
}
if (g_debug_lockorder_abort) {
- fprintf(stderr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__);
+ tfm::format(std::cerr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__);
abort();
}
throw std::logic_error("potential deadlock detected");
@@ -125,7 +138,7 @@ static void push_lock(void* c, const CLockLocation& locklocation)
std::pair<void*, void*> p1 = std::make_pair(i.first, c);
if (lockdata.lockorders.count(p1))
continue;
- lockdata.lockorders[p1] = g_lockstack;
+ lockdata.lockorders.emplace(p1, g_lockstack);
std::pair<void*, void*> p2 = std::make_pair(c, i.first);
lockdata.invlockorders.insert(p2);
@@ -141,7 +154,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));
+ push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName()));
}
void LeaveCritical()
@@ -162,7 +175,7 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine,
for (const std::pair<void*, CLockLocation>& i : g_lockstack)
if (i.first == cs)
return;
- fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
+ tfm::format(std::cerr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
abort();
}
@@ -170,7 +183,7 @@ void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLi
{
for (const std::pair<void*, CLockLocation>& i : g_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());
+ tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
abort();
}
}
diff --git a/src/sync.h b/src/sync.h
index 3857eda56b..bdbdde1a2a 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -198,6 +198,16 @@ using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove
LeaveCritical(); \
}
+//! Run code while locking a mutex.
+//!
+//! Examples:
+//!
+//! WITH_LOCK(cs, shared_val = shared_val + 1);
+//!
+//! int val = WITH_LOCK(cs, return shared_val);
+//!
+#define WITH_LOCK(cs, code) [&] { LOCK(cs); code; }()
+
class CSemaphore
{
private:
@@ -294,4 +304,18 @@ public:
}
};
+// Utility class for indicating to compiler thread analysis that a mutex is
+// locked (when it couldn't be determined otherwise).
+struct SCOPED_LOCKABLE LockAssertion
+{
+ template <typename Mutex>
+ explicit LockAssertion(Mutex& mutex) EXCLUSIVE_LOCK_FUNCTION(mutex)
+ {
+#ifdef DEBUG_LOCKORDER
+ AssertLockHeld(mutex);
+#endif
+ }
+ ~LockAssertion() UNLOCK_FUNCTION() {}
+};
+
#endif // BITCOIN_SYNC_H
diff --git a/src/test/README.md b/src/test/README.md
index f2a4cb1818..0017e3de26 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -42,7 +42,7 @@ unit tests as possible).
The build system is setup to compile an executable called `test_bitcoin`
that runs all of the unit tests. The main source file is called
-test_bitcoin.cpp. To add a new unit test file to our test suite you need
+setup_common.cpp. To add a new unit test file to our test suite you need
to add the file to `src/Makefile.test.include`. The pattern is to create
one test file for each class or source file for which you want to create
unit tests. The file naming convention is `<source_filename>_tests.cpp`
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 22347fbc57..eeb54b4cf0 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -1,8 +1,8 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <test/setup_common.h>
#include <string>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp
index 9eded4f5b2..f255691704 100644
--- a/src/test/allocator_tests.cpp
+++ b/src/test/allocator_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <util/system.h>
#include <support/allocators/secure.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <memory>
diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp
index 1ff040b077..378fe285d5 100644
--- a/src/test/amount_tests.cpp
+++ b/src/test/amount_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <amount.h>
#include <policy/feerate.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp
index 77b6008fd0..809c627d27 100644
--- a/src/test/arith_uint256_tests.cpp
+++ b/src/test/arith_uint256_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -12,7 +12,7 @@
#include <arith_uint256.h>
#include <string>
#include <version.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
BOOST_FIXTURE_TEST_SUITE(arith_uint256_tests, BasicTestingSetup)
diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp
index 32af843bf6..b3bed2434c 100644
--- a/src/test/base32_tests.cpp
+++ b/src/test/base32_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index f8f9b3c1a7..cb376cddb6 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 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 <test/data/base58_encode_decode.json.h>
#include <base58.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <util/strencodings.h>
#include <univalue.h>
diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp
index 0abbb682a7..9ffffb0b7d 100644
--- a/src/test/base64_tests.cpp
+++ b/src/test/base64_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 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 <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp
index 6ecc9ac705..0ba492c24e 100644
--- a/src/test/bech32_tests.cpp
+++ b/src/test/bech32_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bech32.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index c9951f4b7e..0c0423c0db 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2018 The Bitcoin Core developers
+// Copyright (c) 2013-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,7 +9,7 @@
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <string>
#include <vector>
diff --git a/src/test/blockchain_tests.cpp b/src/test/blockchain_tests.cpp
index b61152985f..13afcca375 100644
--- a/src/test/blockchain_tests.cpp
+++ b/src/test/blockchain_tests.cpp
@@ -3,7 +3,7 @@
#include <stdlib.h>
#include <rpc/blockchain.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
/* Equality between doubles is imprecise. Comparison should be done
* with a small threshold of tolerance, rather than exact equality.
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index 607af8a32a..f57e1a0ebd 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -8,7 +8,7 @@
#include <pow.h>
#include <random.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -386,6 +386,7 @@ BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationOverflowTest) {
BOOST_CHECK(0);
} catch(std::ios_base::failure &) {
// deserialize should fail
+ BOOST_CHECK(true); // Needed to suppress "Test case [...] did not check any assertions"
}
}
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
new file mode 100644
index 0000000000..7ba483173c
--- /dev/null
+++ b/src/test/blockfilter_index_tests.cpp
@@ -0,0 +1,307 @@
+// Copyright (c) 2017-2019 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 <blockfilter.h>
+#include <chainparams.h>
+#include <consensus/validation.h>
+#include <index/blockfilterindex.h>
+#include <miner.h>
+#include <pow.h>
+#include <test/setup_common.h>
+#include <script/standard.h>
+#include <validation.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(blockfilter_index_tests)
+
+static bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex* block_index,
+ BlockFilter& filter)
+{
+ CBlock block;
+ if (!ReadBlockFromDisk(block, block_index->GetBlockPos(), Params().GetConsensus())) {
+ return false;
+ }
+
+ CBlockUndo block_undo;
+ if (block_index->nHeight > 0 && !UndoReadFromDisk(block_undo, block_index)) {
+ return false;
+ }
+
+ filter = BlockFilter(filter_type, block, block_undo);
+ return true;
+}
+
+static bool CheckFilterLookups(BlockFilterIndex& filter_index, const CBlockIndex* block_index,
+ uint256& last_header)
+{
+ BlockFilter expected_filter;
+ if (!ComputeFilter(filter_index.GetFilterType(), block_index, expected_filter)) {
+ BOOST_ERROR("ComputeFilter failed on block " << block_index->nHeight);
+ return false;
+ }
+
+ BlockFilter filter;
+ uint256 filter_header;
+ std::vector<BlockFilter> filters;
+ std::vector<uint256> filter_hashes;
+
+ BOOST_CHECK(filter_index.LookupFilter(block_index, filter));
+ BOOST_CHECK(filter_index.LookupFilterHeader(block_index, filter_header));
+ BOOST_CHECK(filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
+ BOOST_CHECK(filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
+ filter_hashes));
+
+ BOOST_CHECK_EQUAL(filters.size(), 1);
+ BOOST_CHECK_EQUAL(filter_hashes.size(), 1);
+
+ BOOST_CHECK_EQUAL(filter.GetHash(), expected_filter.GetHash());
+ BOOST_CHECK_EQUAL(filter_header, expected_filter.ComputeHeader(last_header));
+ BOOST_CHECK_EQUAL(filters[0].GetHash(), expected_filter.GetHash());
+ BOOST_CHECK_EQUAL(filter_hashes[0], expected_filter.GetHash());
+
+ filters.clear();
+ filter_hashes.clear();
+ last_header = filter_header;
+ return true;
+}
+
+static CBlock CreateBlock(const CBlockIndex* prev,
+ const std::vector<CMutableTransaction>& txns,
+ const CScript& scriptPubKey)
+{
+ const CChainParams& chainparams = Params();
+ std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
+ CBlock& block = pblocktemplate->block;
+ block.hashPrevBlock = prev->GetBlockHash();
+ block.nTime = prev->nTime + 1;
+
+ // Replace mempool-selected txns with just coinbase plus passed-in txns:
+ block.vtx.resize(1);
+ for (const CMutableTransaction& tx : txns) {
+ block.vtx.push_back(MakeTransactionRef(tx));
+ }
+ // IncrementExtraNonce creates a valid coinbase and merkleRoot
+ unsigned int extraNonce = 0;
+ IncrementExtraNonce(&block, prev, extraNonce);
+
+ while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
+
+ return block;
+}
+
+static bool BuildChain(const CBlockIndex* pindex, const CScript& coinbase_script_pub_key,
+ size_t length, std::vector<std::shared_ptr<CBlock>>& chain)
+{
+ std::vector<CMutableTransaction> no_txns;
+
+ chain.resize(length);
+ for (auto& block : chain) {
+ block = std::make_shared<CBlock>(CreateBlock(pindex, no_txns, coinbase_script_pub_key));
+ CBlockHeader header = block->GetBlockHeader();
+
+ CValidationState state;
+ if (!ProcessNewBlockHeaders({header}, state, Params(), &pindex, nullptr)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
+{
+ BlockFilterIndex filter_index(BlockFilterType::BASIC, 1 << 20, true);
+
+ uint256 last_header;
+
+ // Filter should not be found in the index before it is started.
+ {
+ LOCK(cs_main);
+
+ BlockFilter filter;
+ uint256 filter_header;
+ std::vector<BlockFilter> filters;
+ std::vector<uint256> filter_hashes;
+
+ for (const CBlockIndex* block_index = ::ChainActive().Genesis();
+ block_index != nullptr;
+ block_index = ::ChainActive().Next(block_index)) {
+ BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
+ BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
+ BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
+ BOOST_CHECK(!filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
+ filter_hashes));
+ }
+ }
+
+ // BlockUntilSyncedToCurrentChain should return false before index is started.
+ BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
+
+ filter_index.Start();
+
+ // Allow filter index to catch up with the block index.
+ constexpr int64_t timeout_ms = 10 * 1000;
+ int64_t time_start = GetTimeMillis();
+ while (!filter_index.BlockUntilSyncedToCurrentChain()) {
+ BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis());
+ MilliSleep(100);
+ }
+
+ // Check that filter index has all blocks that were in the chain before it started.
+ {
+ LOCK(cs_main);
+ const CBlockIndex* block_index;
+ for (block_index = ::ChainActive().Genesis();
+ block_index != nullptr;
+ block_index = ::ChainActive().Next(block_index)) {
+ CheckFilterLookups(filter_index, block_index, last_header);
+ }
+ }
+
+ // Create two forks.
+ const CBlockIndex* tip;
+ {
+ LOCK(cs_main);
+ tip = ::ChainActive().Tip();
+ }
+ CScript coinbase_script_pub_key = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));
+ std::vector<std::shared_ptr<CBlock>> chainA, chainB;
+ BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key, 10, chainA));
+ BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key, 10, chainB));
+
+ // Check that new blocks on chain A get indexed.
+ uint256 chainA_last_header = last_header;
+ for (size_t i = 0; i < 2; i++) {
+ const auto& block = chainA[i];
+ BOOST_REQUIRE(ProcessNewBlock(Params(), block, true, nullptr));
+
+ const CBlockIndex* block_index;
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(block->GetHash());
+ }
+
+ BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ CheckFilterLookups(filter_index, block_index, chainA_last_header);
+ }
+
+ // Reorg to chain B.
+ uint256 chainB_last_header = last_header;
+ for (size_t i = 0; i < 3; i++) {
+ const auto& block = chainB[i];
+ BOOST_REQUIRE(ProcessNewBlock(Params(), block, true, nullptr));
+
+ const CBlockIndex* block_index;
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(block->GetHash());
+ }
+
+ BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ CheckFilterLookups(filter_index, block_index, chainB_last_header);
+ }
+
+ // Check that filters for stale blocks on A can be retrieved.
+ chainA_last_header = last_header;
+ for (size_t i = 0; i < 2; i++) {
+ const auto& block = chainA[i];
+ const CBlockIndex* block_index;
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(block->GetHash());
+ }
+
+ BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ CheckFilterLookups(filter_index, block_index, chainA_last_header);
+ }
+
+ // Reorg back to chain A.
+ for (size_t i = 2; i < 4; i++) {
+ const auto& block = chainA[i];
+ BOOST_REQUIRE(ProcessNewBlock(Params(), block, true, nullptr));
+ }
+
+ // Check that chain A and B blocks can be retrieved.
+ chainA_last_header = last_header;
+ chainB_last_header = last_header;
+ for (size_t i = 0; i < 3; i++) {
+ const CBlockIndex* block_index;
+
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(chainA[i]->GetHash());
+ }
+ BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ CheckFilterLookups(filter_index, block_index, chainA_last_header);
+
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(chainB[i]->GetHash());
+ }
+ BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ CheckFilterLookups(filter_index, block_index, chainB_last_header);
+ }
+
+ // Test lookups for a range of filters/hashes.
+ std::vector<BlockFilter> filters;
+ std::vector<uint256> filter_hashes;
+
+ {
+ LOCK(cs_main);
+ tip = ::ChainActive().Tip();
+ }
+ BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
+ BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
+
+ BOOST_CHECK_EQUAL(filters.size(), tip->nHeight + 1);
+ BOOST_CHECK_EQUAL(filter_hashes.size(), tip->nHeight + 1);
+
+ filters.clear();
+ filter_hashes.clear();
+
+ filter_index.Interrupt();
+ filter_index.Stop();
+}
+
+BOOST_FIXTURE_TEST_CASE(blockfilter_index_init_destroy, BasicTestingSetup)
+{
+ SetDataDir("tempdir");
+
+ BlockFilterIndex* filter_index;
+
+ filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
+ BOOST_CHECK(filter_index == nullptr);
+
+ BOOST_CHECK(InitBlockFilterIndex(BlockFilterType::BASIC, 1 << 20, true, false));
+
+ filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
+ BOOST_CHECK(filter_index != nullptr);
+ BOOST_CHECK(filter_index->GetFilterType() == BlockFilterType::BASIC);
+
+ // Initialize returns false if index already exists.
+ BOOST_CHECK(!InitBlockFilterIndex(BlockFilterType::BASIC, 1 << 20, true, false));
+
+ int iter_count = 0;
+ ForEachBlockFilterIndex([&iter_count](BlockFilterIndex& _index) { iter_count++; });
+ BOOST_CHECK_EQUAL(iter_count, 1);
+
+ BOOST_CHECK(DestroyBlockFilterIndex(BlockFilterType::BASIC));
+
+ // Destroy returns false because index was already destroyed.
+ BOOST_CHECK(!DestroyBlockFilterIndex(BlockFilterType::BASIC));
+
+ filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
+ BOOST_CHECK(filter_index == nullptr);
+
+ // Reinitialize index.
+ BOOST_CHECK(InitBlockFilterIndex(BlockFilterType::BASIC, 1 << 20, true, false));
+
+ DestroyAllBlockFilterIndexes();
+
+ filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
+ BOOST_CHECK(filter_index == nullptr);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp
index 625362f446..df0a041e0e 100644
--- a/src/test/blockfilter_tests.cpp
+++ b/src/test/blockfilter_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 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 <test/data/blockfilters.json.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <blockfilter.h>
#include <core_io.h>
@@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(gcsfilter_default_constructor)
BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
{
- CScript included_scripts[5], excluded_scripts[3];
+ CScript included_scripts[5], excluded_scripts[4];
// First two are outputs on a single transaction.
included_scripts[0] << std::vector<unsigned char>(0, 65) << OP_CHECKSIG;
@@ -73,14 +73,19 @@ BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
// This script is not related to the block at all.
excluded_scripts[1] << std::vector<unsigned char>(5, 33) << OP_CHECKSIG;
+ // OP_RETURN is non-standard since it's not followed by a data push, but is still excluded from
+ // filter.
+ excluded_scripts[2] << OP_RETURN << OP_4 << OP_ADD << OP_8 << OP_EQUAL;
+
CMutableTransaction tx_1;
tx_1.vout.emplace_back(100, included_scripts[0]);
tx_1.vout.emplace_back(200, included_scripts[1]);
+ tx_1.vout.emplace_back(0, excluded_scripts[0]);
CMutableTransaction tx_2;
tx_2.vout.emplace_back(300, included_scripts[2]);
- tx_2.vout.emplace_back(0, excluded_scripts[0]);
- tx_2.vout.emplace_back(400, excluded_scripts[2]); // Script is empty
+ tx_2.vout.emplace_back(0, excluded_scripts[2]);
+ tx_2.vout.emplace_back(400, excluded_scripts[3]); // Script is empty
CBlock block;
block.vtx.push_back(MakeTransactionRef(tx_1));
@@ -90,7 +95,7 @@ BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
block_undo.vtxundo.emplace_back();
block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(500, included_scripts[3]), 1000, true);
block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(600, included_scripts[4]), 10000, false);
- block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(700, excluded_scripts[2]), 100000, false);
+ block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(700, excluded_scripts[3]), 100000, false);
BlockFilter block_filter(BlockFilterType::BASIC, block, block_undo);
const GCSFilter& filter = block_filter.GetFilter();
@@ -112,6 +117,12 @@ BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
BOOST_CHECK_EQUAL(block_filter.GetFilterType(), block_filter2.GetFilterType());
BOOST_CHECK_EQUAL(block_filter.GetBlockHash(), block_filter2.GetBlockHash());
BOOST_CHECK(block_filter.GetEncodedFilter() == block_filter2.GetEncodedFilter());
+
+ BlockFilter default_ctor_block_filter_1;
+ BlockFilter default_ctor_block_filter_2;
+ BOOST_CHECK_EQUAL(default_ctor_block_filter_1.GetFilterType(), default_ctor_block_filter_2.GetFilterType());
+ BOOST_CHECK_EQUAL(default_ctor_block_filter_1.GetBlockHash(), default_ctor_block_filter_2.GetBlockHash());
+ BOOST_CHECK(default_ctor_block_filter_1.GetEncodedFilter() == default_ctor_block_filter_2.GetEncodedFilter());
}
BOOST_AUTO_TEST_CASE(blockfilters_json_test)
@@ -168,4 +179,16 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test)
}
}
+BOOST_AUTO_TEST_CASE(blockfilter_type_names)
+{
+ BOOST_CHECK_EQUAL(BlockFilterTypeName(BlockFilterType::BASIC), "basic");
+ BOOST_CHECK_EQUAL(BlockFilterTypeName(static_cast<BlockFilterType>(255)), "");
+
+ BlockFilterType filter_type;
+ BOOST_CHECK(BlockFilterTypeByName("basic", filter_type));
+ BOOST_CHECK_EQUAL(filter_type, BlockFilterType::BASIC);
+
+ BOOST_CHECK(!BlockFilterTypeByName("unknown", filter_type));
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index c900bee199..4421494007 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -15,7 +15,7 @@
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
@@ -461,6 +461,9 @@ static std::vector<unsigned char> RandomData()
BOOST_AUTO_TEST_CASE(rolling_bloom)
{
+ SeedInsecureRand(/* deterministic */ true);
+ g_mock_deterministic_tests = true;
+
// last-100-entry, 1% false positive:
CRollingBloomFilter rb1(100, 0.01);
@@ -485,12 +488,8 @@ BOOST_AUTO_TEST_CASE(rolling_bloom)
if (rb1.contains(RandomData()))
++nHits;
}
- // Run test_bitcoin with --log_level=message to see BOOST_TEST_MESSAGEs:
- BOOST_TEST_MESSAGE("RollingBloomFilter got " << nHits << " false positives (~100 expected)");
-
- // Insanely unlikely to get a fp count outside this range:
- BOOST_CHECK(nHits > 25);
- BOOST_CHECK(nHits < 175);
+ // Expect about 100 hits
+ BOOST_CHECK_EQUAL(nHits, 75);
BOOST_CHECK(rb1.contains(data[DATASIZE-1]));
rb1.reset();
@@ -517,10 +516,8 @@ BOOST_AUTO_TEST_CASE(rolling_bloom)
if (rb1.contains(data[i]))
++nHits;
}
- // Expect about 5 false positives, more than 100 means
- // something is definitely broken.
- BOOST_TEST_MESSAGE("RollingBloomFilter got " << nHits << " false positives (~5 expected)");
- BOOST_CHECK(nHits < 100);
+ // Expect about 5 false positives
+ BOOST_CHECK_EQUAL(nHits, 6);
// last-1000-entry, 0.01% false positive:
CRollingBloomFilter rb2(1000, 0.001);
@@ -531,6 +528,7 @@ BOOST_AUTO_TEST_CASE(rolling_bloom)
for (int i = 0; i < DATASIZE; i++) {
BOOST_CHECK(rb2.contains(data[i]));
}
+ g_mock_deterministic_tests = false;
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/bswap_tests.cpp b/src/test/bswap_tests.cpp
index 8572926193..8fd4e5d5d6 100644
--- a/src/test/bswap_tests.cpp
+++ b/src/test/bswap_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <compat/byteswap.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 3469c6dfba..408a7fbda4 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -6,7 +6,7 @@
#include <util/time.h>
#include <validation.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <checkqueue.h>
#include <boost/test/unit_test.hpp>
#include <boost/thread.hpp>
@@ -167,7 +167,6 @@ static void Correct_Queue_range(std::vector<size_t> range)
BOOST_REQUIRE(control.Wait());
if (FakeCheckCheckCompletion::n_calls != i) {
BOOST_REQUIRE_EQUAL(FakeCheckCheckCompletion::n_calls, i);
- BOOST_TEST_MESSAGE("Failure on trial " << i << " expected, got " << FakeCheckCheckCompletion::n_calls);
}
}
tg.interrupt_all();
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index aa2e88477d..2c42596edc 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -1,16 +1,14 @@
-// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Copyright (c) 2014-2019 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 <attributes.h>
#include <coins.h>
-#include <consensus/validation.h>
#include <script/standard.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <uint256.h>
#include <undo.h>
#include <util/strencodings.h>
-#include <validation.h>
#include <map>
#include <vector>
@@ -279,6 +277,9 @@ UtxoData::iterator FindRandomFrom(const std::set<COutPoint> &utxoSet) {
// has the expected effect (the other duplicate is overwritten at all cache levels)
BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
{
+ SeedInsecureRand(/* deterministic */ true);
+ g_mock_deterministic_tests = true;
+
bool spent_a_duplicate_coinbase = false;
// A simple map to track what we expect the cache stack to represent.
std::map<COutPoint, Coin> result;
@@ -472,6 +473,8 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
// Verify coverage.
BOOST_CHECK(spent_a_duplicate_coinbase);
+
+ g_mock_deterministic_tests = false;
}
BOOST_AUTO_TEST_CASE(ccoins_serialization)
@@ -483,7 +486,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
BOOST_CHECK_EQUAL(cc1.fCoinBase, false);
BOOST_CHECK_EQUAL(cc1.nHeight, 203998U);
BOOST_CHECK_EQUAL(cc1.out.nValue, CAmount{60000000000});
- BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))))));
+ BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))))));
// Good example
CDataStream ss2(ParseHex("8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"), SER_DISK, CLIENT_VERSION);
@@ -492,7 +495,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
BOOST_CHECK_EQUAL(cc2.fCoinBase, true);
BOOST_CHECK_EQUAL(cc2.nHeight, 120891U);
BOOST_CHECK_EQUAL(cc2.out.nValue, 110397);
- BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
+ BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
// Smallest possible example
CDataStream ss3(ParseHex("000006"), SER_DISK, CLIENT_VERSION);
diff --git a/src/test/compilerbug_tests.cpp b/src/test/compilerbug_tests.cpp
new file mode 100644
index 0000000000..74e1eac3ea
--- /dev/null
+++ b/src/test/compilerbug_tests.cpp
@@ -0,0 +1,43 @@
+// Copyright (c) 2019 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 <test/setup_common.h>
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(compilerbug_tests, BasicTestingSetup)
+
+#if defined(__GNUC__)
+// This block will also be built under clang, which is fine (as it supports noinline)
+void __attribute__ ((noinline)) set_one(unsigned char* ptr)
+{
+ *ptr = 1;
+}
+
+int __attribute__ ((noinline)) check_zero(unsigned char const* in, unsigned int len)
+{
+ for (unsigned int i = 0; i < len; ++i) {
+ if (in[i] != 0) return 0;
+ }
+ return 1;
+}
+
+void set_one_on_stack() {
+ unsigned char buf[1];
+ set_one(buf);
+}
+
+BOOST_AUTO_TEST_CASE(gccbug_90348) {
+ // Test for GCC bug 90348. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90348
+ for (int i = 0; i <= 4; ++i) {
+ unsigned char in[4];
+ for (int j = 0; j < i; ++j) {
+ in[j] = 0;
+ set_one_on_stack(); // Apparently modifies in[0]
+ }
+ BOOST_CHECK(check_zero(in, i));
+ }
+}
+#endif
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/compress_tests.cpp b/src/test/compress_tests.cpp
index e686c05165..6cef8cd8a8 100644
--- a/src/test/compress_tests.cpp
+++ b/src/test/compress_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <compressor.h>
#include <util/system.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <stdint.h>
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 86cb00a78f..4e2acca4c3 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -1,24 +1,24 @@
-// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Copyright (c) 2014-2019 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 <crypto/aes.h>
#include <crypto/chacha20.h>
+#include <crypto/poly1305.h>
+#include <crypto/hkdf_sha256_32.h>
+#include <crypto/hmac_sha256.h>
+#include <crypto/hmac_sha512.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 <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
#include <boost/test/unit_test.hpp>
-#include <openssl/aes.h>
-#include <openssl/evp.h>
BOOST_FIXTURE_TEST_SUITE(crypto_tests, BasicTestingSetup)
@@ -66,26 +66,6 @@ static void TestHMACSHA512(const std::string &hexkey, const std::string &hexin,
TestVector(CHMAC_SHA512(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout));
}
-static void TestAES128(const std::string &hexkey, const std::string &hexin, const std::string &hexout)
-{
- std::vector<unsigned char> key = ParseHex(hexkey);
- std::vector<unsigned char> in = ParseHex(hexin);
- std::vector<unsigned char> correctout = ParseHex(hexout);
- std::vector<unsigned char> buf, buf2;
-
- assert(key.size() == 16);
- assert(in.size() == 16);
- assert(correctout.size() == 16);
- AES128Encrypt enc(key.data());
- buf.resize(correctout.size());
- buf2.resize(correctout.size());
- enc.Encrypt(buf.data(), in.data());
- BOOST_CHECK_EQUAL(HexStr(buf), HexStr(correctout));
- AES128Decrypt dec(key.data());
- dec.Decrypt(buf2.data(), buf.data());
- BOOST_CHECK_EQUAL(HexStr(buf2), HexStr(in));
-}
-
static void TestAES256(const std::string &hexkey, const std::string &hexin, const std::string &hexout)
{
std::vector<unsigned char> key = ParseHex(hexkey);
@@ -105,47 +85,6 @@ static void TestAES256(const std::string &hexkey, const std::string &hexin, cons
BOOST_CHECK(buf == in);
}
-static void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout)
-{
- std::vector<unsigned char> key = ParseHex(hexkey);
- std::vector<unsigned char> iv = ParseHex(hexiv);
- std::vector<unsigned char> in = ParseHex(hexin);
- std::vector<unsigned char> correctout = ParseHex(hexout);
- std::vector<unsigned char> realout(in.size() + AES_BLOCKSIZE);
-
- // Encrypt the plaintext and verify that it equals the cipher
- 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.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);
-
- // Encrypt and re-decrypt substrings of the plaintext and verify that they equal each-other
- for(std::vector<unsigned char>::iterator i(in.begin()); i != in.end(); ++i)
- {
- std::vector<unsigned char> sub(i, in.end());
- std::vector<unsigned char> subout(sub.size() + AES_BLOCKSIZE);
- 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.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));
- }
- }
-}
-
static void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout)
{
std::vector<unsigned char> key = ParseHex(hexkey);
@@ -187,17 +126,63 @@ static void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, b
}
}
-static void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout)
+static void TestChaCha20(const std::string &hex_message, const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout)
{
std::vector<unsigned char> key = ParseHex(hexkey);
+ std::vector<unsigned char> m = ParseHex(hex_message);
ChaCha20 rng(key.data(), key.size());
rng.SetIV(nonce);
rng.Seek(seek);
std::vector<unsigned char> out = ParseHex(hexout);
std::vector<unsigned char> outres;
outres.resize(out.size());
- rng.Output(outres.data(), outres.size());
+ assert(hex_message.empty() || m.size() == out.size());
+
+ // perform the ChaCha20 round(s), if message is provided it will output the encrypted ciphertext otherwise the keystream
+ if (!hex_message.empty()) {
+ rng.Crypt(m.data(), outres.data(), outres.size());
+ } else {
+ rng.Keystream(outres.data(), outres.size());
+ }
BOOST_CHECK(out == outres);
+ if (!hex_message.empty()) {
+ // Manually XOR with the keystream and compare the output
+ rng.SetIV(nonce);
+ rng.Seek(seek);
+ std::vector<unsigned char> only_keystream(outres.size());
+ rng.Keystream(only_keystream.data(), only_keystream.size());
+ for (size_t i = 0; i != m.size(); i++) {
+ outres[i] = m[i] ^ only_keystream[i];
+ }
+ BOOST_CHECK(out == outres);
+ }
+}
+
+static void TestPoly1305(const std::string &hexmessage, const std::string &hexkey, const std::string& hextag)
+{
+ std::vector<unsigned char> key = ParseHex(hexkey);
+ std::vector<unsigned char> m = ParseHex(hexmessage);
+ std::vector<unsigned char> tag = ParseHex(hextag);
+ std::vector<unsigned char> tagres;
+ tagres.resize(POLY1305_TAGLEN);
+ poly1305_auth(tagres.data(), m.data(), m.size(), key.data());
+ BOOST_CHECK(tag == tagres);
+}
+
+static void TestHKDF_SHA256_32(const std::string &ikm_hex, const std::string &salt_hex, const std::string &info_hex, const std::string &okm_check_hex) {
+ std::vector<unsigned char> initial_key_material = ParseHex(ikm_hex);
+ std::vector<unsigned char> salt = ParseHex(salt_hex);
+ std::vector<unsigned char> info = ParseHex(info_hex);
+
+
+ // our implementation only supports strings for the "info" and "salt", stringify them
+ std::string salt_stringified(reinterpret_cast<char*>(salt.data()), salt.size());
+ std::string info_stringified(reinterpret_cast<char*>(info.data()), info.size());
+
+ CHKDF_HMAC_SHA256_L32 hkdf32(initial_key_material.data(), initial_key_material.size(), salt_stringified);
+ unsigned char out[32];
+ hkdf32.Expand32(info_stringified, out);
+ BOOST_CHECK(HexStr(out, out + 32) == okm_check_hex);
}
static std::string LongTestString() {
@@ -428,14 +413,9 @@ BOOST_AUTO_TEST_CASE(hmac_sha512_testvectors) {
BOOST_AUTO_TEST_CASE(aes_testvectors) {
// AES test vectors from FIPS 197.
- TestAES128("000102030405060708090a0b0c0d0e0f", "00112233445566778899aabbccddeeff", "69c4e0d86a7b0430d8cdb78070b4c55a");
TestAES256("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00112233445566778899aabbccddeeff", "8ea2b7ca516745bfeafc49904b496089");
// AES-ECB test vectors from NIST sp800-38a.
- TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "6bc1bee22e409f96e93d7e117393172a", "3ad77bb40d7a3660a89ecaf32466ef97");
- TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "ae2d8a571e03ac9c9eb76fac45af8e51", "f5d3d58503b9699de785895a96fdbaaf");
- TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "30c81c46a35ce411e5fbc1191a0a52ef", "43b1cd7f598ece23881b00e3ed030688");
- TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "f69f2445df4f9b17ad2b417be66c3710", "7b0c785e27e8ad3f8223207104725dd4");
TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "6bc1bee22e409f96e93d7e117393172a", "f3eed1bdb5d2a03c064b5a7e3db181f8");
TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "ae2d8a571e03ac9c9eb76fac45af8e51", "591ccb10d410ed26dc5ba74a31362870");
TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "30c81c46a35ce411e5fbc1191a0a52ef", "b6ed21b99ca6f4f9f153e7b1beafed1d");
@@ -443,27 +423,6 @@ BOOST_AUTO_TEST_CASE(aes_testvectors) {
}
BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) {
-
- // NIST AES CBC 128-bit encryption test-vectors
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090A0B0C0D0E0F", false, \
- "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d");
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", false, \
- "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b2");
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "5086cb9b507219ee95db113a917678b2", false, \
- "30c81c46a35ce411e5fbc1191a0a52ef", "73bed6b8e3c1743b7116e69e22229516");
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "73bed6b8e3c1743b7116e69e22229516", false, \
- "f69f2445df4f9b17ad2b417be66c3710", "3ff1caa1681fac09120eca307586e1a7");
-
- // The same vectors with padding enabled
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090A0B0C0D0E0F", true, \
- "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d8964e0b149c10b7b682e6e39aaeb731c");
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", true, \
- "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b255e21d7100b988ffec32feeafaf23538");
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "5086cb9b507219ee95db113a917678b2", true, \
- "30c81c46a35ce411e5fbc1191a0a52ef", "73bed6b8e3c1743b7116e69e22229516f6eccda327bf8e5ec43718b0039adceb");
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "73bed6b8e3c1743b7116e69e22229516", true, \
- "f69f2445df4f9b17ad2b417be66c3710", "3ff1caa1681fac09120eca307586e1a78cb82807230e1321d3fae00d18cc2012");
-
// NIST AES CBC 256-bit encryption test-vectors
TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \
"000102030405060708090A0B0C0D0E0F", false, "6bc1bee22e409f96e93d7e117393172a", \
@@ -497,25 +456,37 @@ BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) {
BOOST_AUTO_TEST_CASE(chacha20_testvector)
{
// Test vector from RFC 7539
- TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1,
+
+ // test encryption
+ TestChaCha20("4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756"
+ "c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e"
+ "20776f756c642062652069742e",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1,
+ "6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d"
+ "624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74"
+ "a35be6b40b8eedf2785e42874d"
+ );
+
+ // test keystream output
+ TestChaCha20("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1,
"224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cb"
"a40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a"
"832c89c167eacd901d7e2bf363");
// Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
- TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0, 0,
+ TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", 0, 0,
"76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b"
"8f41518a11cc387b669b2ee6586");
- TestChaCha20("0000000000000000000000000000000000000000000000000000000000000001", 0, 0,
+ TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000001", 0, 0,
"4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d79"
"2b1c43fea817e9ad275ae546963");
- TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0,
+ TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0,
"de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b52770"
"62eb7a0433e445f41e3");
- TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 1, 0,
+ TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", 1, 0,
"ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc4"
"97a0b466e7d6bbdb0041b2f586b");
- TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0,
+ TestChaCha20("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0,
"f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3b"
"e59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc1"
"18be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5"
@@ -524,6 +495,96 @@ BOOST_AUTO_TEST_CASE(chacha20_testvector)
"fab78c9");
}
+BOOST_AUTO_TEST_CASE(poly1305_testvector)
+{
+ // RFC 7539, section 2.5.2.
+ TestPoly1305("43727970746f6772617068696320466f72756d2052657365617263682047726f7570",
+ "85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b",
+ "a8061dc1305136c6c22b8baf0c0127a9");
+
+ // RFC 7539, section A.3.
+ TestPoly1305("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000");
+
+ TestPoly1305("416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e747269627"
+ "5746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465"
+ "726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686"
+ "520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e2022494554"
+ "4620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c20737461746"
+ "56d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c65"
+ "6374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207"
+ "768696368206172652061646472657373656420746f",
+ "0000000000000000000000000000000036e5f6b5c5e06070f0efca96227a863e",
+ "36e5f6b5c5e06070f0efca96227a863e");
+
+ TestPoly1305("416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e747269627"
+ "5746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465"
+ "726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686"
+ "520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e2022494554"
+ "4620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c20737461746"
+ "56d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c65"
+ "6374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207"
+ "768696368206172652061646472657373656420746f",
+ "36e5f6b5c5e06070f0efca96227a863e00000000000000000000000000000000",
+ "f3477e7cd95417af89a6b8794c310cf0");
+
+ TestPoly1305("2754776173206272696c6c69672c20616e642074686520736c6974687920746f7665730a446964206779726520616e6420676"
+ "96d626c6520696e2074686520776162653a0a416c6c206d696d737920776572652074686520626f726f676f7665732c0a416e"
+ "6420746865206d6f6d65207261746873206f757467726162652e",
+ "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0",
+ "4541669a7eaaee61e708dc7cbcc5eb62");
+
+ TestPoly1305("ffffffffffffffffffffffffffffffff",
+ "0200000000000000000000000000000000000000000000000000000000000000",
+ "03000000000000000000000000000000");
+
+ TestPoly1305("02000000000000000000000000000000",
+ "02000000000000000000000000000000ffffffffffffffffffffffffffffffff",
+ "03000000000000000000000000000000");
+
+ TestPoly1305("fffffffffffffffffffffffffffffffff0ffffffffffffffffffffffffffffff11000000000000000000000000000000",
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "05000000000000000000000000000000");
+
+ TestPoly1305("fffffffffffffffffffffffffffffffffbfefefefefefefefefefefefefefefe01010101010101010101010101010101",
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000");
+
+ TestPoly1305("fdffffffffffffffffffffffffffffff",
+ "0200000000000000000000000000000000000000000000000000000000000000",
+ "faffffffffffffffffffffffffffffff");
+
+ TestPoly1305("e33594d7505e43b900000000000000003394d7505e4379cd01000000000000000000000000000000000000000000000001000000000000000000000000000000",
+ "0100000000000000040000000000000000000000000000000000000000000000",
+ "14000000000000005500000000000000");
+
+ TestPoly1305("e33594d7505e43b900000000000000003394d7505e4379cd010000000000000000000000000000000000000000000000",
+ "0100000000000000040000000000000000000000000000000000000000000000",
+ "13000000000000000000000000000000");
+}
+
+BOOST_AUTO_TEST_CASE(hkdf_hmac_sha256_l32_tests)
+{
+ // Use rfc5869 test vectors but truncated to 32 bytes (our implementation only support length 32)
+ TestHKDF_SHA256_32(
+ /* IKM */ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ /* salt */ "000102030405060708090a0b0c",
+ /* info */ "f0f1f2f3f4f5f6f7f8f9",
+ /* expected OKM */ "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf");
+ TestHKDF_SHA256_32(
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f",
+ "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
+ "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+ "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c");
+ TestHKDF_SHA256_32(
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "",
+ "",
+ "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d");
+}
+
BOOST_AUTO_TEST_CASE(countbits_tests)
{
FastRandomContext ctx;
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index d8286520ec..d38ede691a 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/test/unit_test.hpp>
#include <cuckoocache.h>
#include <script/sigcache.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <random.h>
#include <thread>
diff --git a/src/test/data/blockfilters.json b/src/test/data/blockfilters.json
index 134b788eed..8945296a07 100644
--- a/src/test/data/blockfilters.json
+++ b/src/test/data/blockfilters.json
@@ -3,9 +3,11 @@
[0,"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943","0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000",[],"0000000000000000000000000000000000000000000000000000000000000000","019dfca8","21584579b7eb08997773e5aeff3a7f932700042d0ed2a6129012b7d7ae81b750","Genesis block"],
[2,"000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820","0100000006128e87be8b1b4dea47a7247d5528d2702c96826c7a648497e773b800000000e241352e3bec0a95a6217e10c3abb54adfa05abb12c126695595580fb92e222032e7494dffff001d00d235340101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0432e7494d010e062f503253482fffffffff0100f2052a010000002321038a7f6ef1c8ca0c588aa53fa860128077c9e6c11e6830f4d7ee4e763a56b7718fac00000000",[],"d7bdac13a59d745b1add0d2ce852f1a0442e8945fc1bf3848d3cbffd88c24fe1","0174a170","186afd11ef2b5e7e3504f2e8cbf8df28a1fd251fe53d60dff8b1467d1b386cf0",""],
[3,"000000008b896e272758da5297bcd98fdc6d97c9b765ecec401e286dc1fdbe10","0100000020782a005255b657696ea057d5b98f34defcf75196f64f6eeac8026c0000000041ba5afc532aae03151b8aa87b65e1594f97504a768e010c98c0add79216247186e7494dffff001d058dc2b60101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0486e7494d0151062f503253482fffffffff0100f2052a01000000232103f6d9ff4c12959445ca5549c811683bf9c88e637b222dd2e0311154c4c85cf423ac00000000",[],"186afd11ef2b5e7e3504f2e8cbf8df28a1fd251fe53d60dff8b1467d1b386cf0","016cf7a0","8d63aadf5ab7257cb6d2316a57b16f517bff1c6388f124ec4c04af1212729d2a",""],
+[15007,"0000000038c44c703bae0f98cdd6bf30922326340a5996cc692aaae8bacf47ad","0100000002394092aa378fe35d7e9ac79c869b975c4de4374cd75eb5484b0e1e00000000eb9b8670abd44ad6c55cee18e3020fb0c6519e7004b01a16e9164867531b67afc33bc94fffff001d123f10050101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e04c33bc94f0115062f503253482fffffffff0100f2052a01000000232103f268e9ae07e0f8cb2f6e901d87c510d650b97230c0365b021df8f467363cafb1ac00000000",[],"18b5c2b0146d2d09d24fb00ff5b52bd0742f36c9e65527abdb9de30c027a4748","013c3710","07384b01311867949e0c046607c66b7a766d338474bb67f66c8ae9dbd454b20e","Tx has non-standard OP_RETURN output followed by opcodes"],
[49291,"0000000018b07dca1b28b4b5a119f6d6e71698ce1ed96f143f54179ce177a19c","02000000abfaf47274223ca2fea22797e44498240e482cb4c2f2baea088962f800000000604b5b52c32305b15d7542071d8b04e750a547500005d4010727694b6e72a776e55d0d51ffff001d211806480201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d038bc0000102062f503253482fffffffff01a078072a01000000232102971dd6034ed0cf52450b608d196c07d6345184fcb14deb277a6b82d526a6163dac0000000001000000081cefd96060ecb1c4fbe675ad8a4f8bdc61d634c52b3a1c4116dee23749fe80ff000000009300493046022100866859c21f306538152e83f115bcfbf59ab4bb34887a88c03483a5dff9895f96022100a6dfd83caa609bf0516debc2bf65c3df91813a4842650a1858b3f61cfa8af249014730440220296d4b818bb037d0f83f9f7111665f49532dfdcbec1e6b784526e9ac4046eaa602204acf3a5cb2695e8404d80bf49ab04828bcbe6fc31d25a2844ced7a8d24afbdff01ffffffff1cefd96060ecb1c4fbe675ad8a4f8bdc61d634c52b3a1c4116dee23749fe80ff020000009400483045022100e87899175991aa008176cb553c6f2badbb5b741f328c9845fcab89f8b18cae2302200acce689896dc82933015e7230e5230d5cff8a1ffe82d334d60162ac2c5b0c9601493046022100994ad29d1e7b03e41731a4316e5f4992f0d9b6e2efc40a1ccd2c949b461175c502210099b69fdc2db00fbba214f16e286f6a49e2d8a0d5ffc6409d87796add475478d601ffffffff1e4a6d2d280ea06680d6cf8788ac90344a9c67cca9b06005bbd6d3f6945c8272010000009500493046022100a27400ba52fd842ce07398a1de102f710a10c5599545e6c95798934352c2e4df022100f6383b0b14c9f64b6718139f55b6b9494374755b86bae7d63f5d3e583b57255a01493046022100fdf543292f34e1eeb1703b264965339ec4a450ec47585009c606b3edbc5b617b022100a5fbb1c8de8aaaa582988cdb23622838e38de90bebcaab3928d949aa502a65d401ffffffff1e4a6d2d280ea06680d6cf8788ac90344a9c67cca9b06005bbd6d3f6945c8272020000009400493046022100ac626ac3051f875145b4fe4cfe089ea895aac73f65ab837b1ac30f5d875874fa022100bc03e79fa4b7eb707fb735b95ff6613ca33adeaf3a0607cdcead4cfd3b51729801483045022100b720b04a5c5e2f61b7df0fcf334ab6fea167b7aaede5695d3f7c6973496adbf1022043328c4cc1cdc3e5db7bb895ccc37133e960b2fd3ece98350f774596badb387201ffffffff23a8733e349c97d6cd90f520fdd084ba15ce0a395aad03cd51370602bb9e5db3010000004a00483045022100e8556b72c5e9c0da7371913a45861a61c5df434dfd962de7b23848e1a28c86ca02205d41ceda00136267281be0974be132ac4cda1459fe2090ce455619d8b91045e901ffffffff6856d609b881e875a5ee141c235e2a82f6b039f2b9babe82333677a5570285a6000000006a473044022040a1c631554b8b210fbdf2a73f191b2851afb51d5171fb53502a3a040a38d2c0022040d11cf6e7b41fe1b66c3d08f6ada1aee07a047cb77f242b8ecc63812c832c9a012102bcfad931b502761e452962a5976c79158a0f6d307ad31b739611dac6a297c256ffffffff6856d609b881e875a5ee141c235e2a82f6b039f2b9babe82333677a5570285a601000000930048304502205b109df098f7e932fbf71a45869c3f80323974a826ee2770789eae178a21bfc8022100c0e75615e53ee4b6e32b9bb5faa36ac539e9c05fa2ae6b6de5d09c08455c8b9601483045022009fb7d27375c47bea23b24818634df6a54ecf72d52e0c1268fb2a2c84f1885de022100e0ed4f15d62e7f537da0d0f1863498f9c7c0c0a4e00e4679588c8d1a9eb20bb801ffffffffa563c3722b7b39481836d5edfc1461f97335d5d1e9a23ade13680d0e2c1c371f030000006c493046022100ecc38ae2b1565643dc3c0dad5e961a5f0ea09cab28d024f92fa05c922924157e022100ebc166edf6fbe4004c72bfe8cf40130263f98ddff728c8e67b113dbd621906a601210211a4ed241174708c07206601b44a4c1c29e5ad8b1f731c50ca7e1d4b2a06dc1fffffffff02d0223a00000000001976a91445db0b779c0b9fa207f12a8218c94fc77aff504588ac80f0fa02000000000000000000",["5221033423007d8f263819a2e42becaaf5b06f34cb09919e06304349d950668209eaed21021d69e2b68c3960903b702af7829fadcd80bd89b158150c85c4a75b2c8cb9c39452ae","52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821021d69e2b68c3960903b702af7829fadcd80bd89b158150c85c4a75b2c8cb9c39452ae","522102a7ae1e0971fc1689bd66d2a7296da3a1662fd21a53c9e38979e0f090a375c12d21022adb62335f41eb4e27056ac37d462cda5ad783fa8e0e526ed79c752475db285d52ae","52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821022adb62335f41eb4e27056ac37d462cda5ad783fa8e0e526ed79c752475db285d52ae","512103b9d1d0e2b4355ec3cdef7c11a5c0beff9e8b8d8372ab4b4e0aaf30e80173001951ae","76a9149144761ebaccd5b4bbdc2a35453585b5637b2f8588ac","522103f1848b40621c5d48471d9784c8174ca060555891ace6d2b03c58eece946b1a9121020ee5d32b54d429c152fdc7b1db84f2074b0564d35400d89d11870f9273ec140c52ae","76a914f4fa1cc7de742d135ea82c17adf0bb9cf5f4fb8388ac"],"ed47705334f4643892ca46396eb3f4196a5e30880589e4009ef38eae895d4a13","0afbc2920af1b027f31f87b592276eb4c32094bb4d3697021b4c6380","b6d98692cec5145f67585f3434ec3c2b3030182e1cb3ec58b855c5c164dfaaa3","Tx pays to empty output script"],
-[180480,"00000000fd3ceb2404ff07a785c7fdcc76619edc8ed61bd25134eaa22084366a","020000006058aa080a655aa991a444bd7d1f2defd9a3bbe68aabb69030cf3b4e00000000d2e826bfd7ef0beaa891a7eedbc92cd6a544a6cb61c7bdaa436762eb2123ef9790f5f552ffff001d0002c90f0501000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0300c102024608062f503253482fffffffff01c0c6072a01000000232102e769e60137a4df6b0df8ebd387cca44c4c57ae74cc0114a8e8317c8f3bfd85e9ac00000000010000000381a0802911a01ffb025c4dea0bc77963e8c1bb46313b71164c53f72f37fe5248010000000151ffffffffc904b267833d215e2128bd9575242232ac2bc311550c7fc1f0ef6f264b40d14c010000000151ffffffffdf0915666649dba81886519c531649b7b02180b4af67d6885e871299e9d5f775000000000151ffffffff0180817dcb00000000232103bb52138972c48a132fc1f637858c5189607dd0f7fe40c4f20f6ad65f2d389ba4ac0000000001000000018da38b434fba82d66052af74fc5e4e94301b114d9bc03f819dc876398404c8b4010000006c493046022100fe738b7580dc5fb5168e51fc61b5aed211125eb71068031009a22d9bbad752c5022100be5086baa384d40bcab0fa586e4f728397388d86e18b66cc417dc4f7fa4f9878012103f233299455134caa2687bdf15cb0becdfb03bd0ff2ff38e65ec6b7834295c34fffffffff022ebc1400000000001976a9147779b7fba1c1e06b717069b80ca170e8b04458a488ac9879c40f000000001976a9142a0307cd925dbb66b534c4db33003dd18c57015788ac0000000001000000026139a62e3422a602de36c873a225c1d3ca5aeee598539ceecb9f0dc8d1ad0f83010000006b483045022100ad9f32b4a0a2ddc19b5a74eba78123e57616f1b3cfd72ce68c03ea35a3dda1f002200dbd22aa6da17213df5e70dfc3b2611d40f70c98ed9626aa5e2cde9d97461f0a012103ddb295d2f1e8319187738fb4b230fdd9aa29d0e01647f69f6d770b9ab24eea90ffffffff983c82c87cf020040d671956525014d5c2b28c6d948c85e1a522362c0059eeae010000006b4830450221009ca544274c786d30a5d5d25e17759201ea16d3aedddf0b9e9721246f7ef6b32e02202cfa5564b6e87dfd9fd98957820e4d4e6238baeb0f65fe305d91506bb13f5f4f012103c99113deac0d5d044e3ac0346abc02501542af8c8d3759f1382c72ff84e704f7ffffffff02c0c62d00000000001976a914ae19d27efe12f5a886dc79af37ad6805db6f922d88ac70ce2000000000001976a9143b8d051d37a07ea1042067e93efe63dbf73920b988ac000000000100000002be566e8cd9933f0c75c4a82c027f7d0c544d5c101d0607ef6ae5d07b98e7f1dc000000006b483045022036a8cdfd5ea7ebc06c2bfb6e4f942bbf9a1caeded41680d11a3a9f5d8284abad022100cacb92a5be3f39e8bc14db1710910ef7b395fa1e18f45d41c28d914fcdde33be012102bf59abf110b5131fae0a3ce1ec379329b4c896a6ae5d443edb68529cc2bc7816ffffffff96cf67645b76ceb23fe922874847456a15feee1655082ff32d25a6bf2c0dfc90000000006a47304402203471ca2001784a5ac0abab583581f2613523da47ec5f53df833c117b5abd81500220618a2847723d57324f2984678db556dbca1a72230fc7e39df04c2239942ba942012102925c9794fd7bb9f8b29e207d5fc491b1150135a21f505041858889fa4edf436fffffffff026c840f00000000001976a914797fb8777d7991d8284d88bfd421ce520f0f843188ac00ca9a3b000000001976a9146d10f3f592699265d10b106eda37c3ce793f7a8588ac00000000",["","","","76a9142903b138c24be9e070b3e73ec495d77a204615e788ac","76a91433a1941fd9a37b9821d376f5a51bd4b52fa50e2888ac","76a914e4374e8155d0865742ca12b8d4d14d41b57d682f88ac","76a914001fa7459a6cfc64bdc178ba7e7a21603bb2568f88ac","76a914f6039952bc2b307aeec5371bfb96b66078ec17f688ac"],"b109139671dbedc2b6fcd499a5480a7461ae458af8ff9411d819aa64ba6995d1","0db414c859a07e8205876354a210a75042d0463404913d61a8e068e58a3ae2aa080026","a0af77e0a7ed20ea78d2def3200cc24f08217dcd51755c7c7feb0e2ba8316c2d","Tx spends from empty output script"],
-[926485,"000000000000015d6077a411a8f5cc95caf775ccf11c54e27df75ce58d187313","0000002060bbab0edbf3ef8a49608ee326f8fd75c473b7e3982095e2d100000000000000c30134f8c9b6d2470488d7a67a888f6fa12f8692e0c3411fbfb92f0f68f67eedae03ca57ef13021acc22dc4105010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0315230e0004ae03ca57043e3d1e1d0c8796bf579aef0c0000000000122f4e696e6a61506f6f6c2f5345475749542fffffffff038427a112000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9ed5c748e121c0fe146d973a4ac26fa4a68b0549d46ee22d25f50a5e46fe1b377ee00000000000000002952534b424c4f434b3acd16772ad61a3c5f00287480b720f6035d5e54c9efc71be94bb5e3727f10909001200000000000000000000000000000000000000000000000000000000000000000000000000100000000010145310e878941a1b2bc2d33797ee4d89d95eaaf2e13488063a2aa9a74490f510a0100000023220020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1ffffffff01002d3101000000001976a9143ebc40e411ed3c76f86711507ab952300890397288ac0400473044022001dd489a5d4e2fbd8a3ade27177f6b49296ba7695c40dbbe650ea83f106415fd02200b23a0602d8ff1bdf79dee118205fc7e9b40672bf31563e5741feb53fb86388501483045022100f88f040e90cc5dc6c6189d04718376ac19ed996bf9e4a3c29c3718d90ffd27180220761711f16c9e3a44f71aab55cbc0634907a1fa8bb635d971a9a01d368727bea10169522103b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb2103dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba621033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be153ae00000000010000000120925534261de4dcebb1ed5ab1b62bfe7a3ef968fb111dc2c910adfebc6e3bdf010000006b483045022100f50198f5ae66211a4f485190abe4dc7accdabe3bc214ebc9ea7069b97097d46e0220316a70a03014887086e335fc1b48358d46cd6bdc9af3b57c109c94af76fc915101210316cff587a01a2736d5e12e53551b18d73780b83c3bfb4fcf209c869b11b6415effffffff0220a10700000000001976a91450333046115eaa0ac9e0216565f945070e44573988ac2e7cd01a000000001976a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac00000000010000000203a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322010000006a47304402204efc3d70e4ca3049c2a425025edf22d5ca355f9ec899dbfbbeeb2268533a0f2b02204780d3739653035af4814ea52e1396d021953f948c29754edd0ee537364603dc012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff03a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322000000006a47304402202d96defdc5b4af71d6ba28c9a6042c2d5ee7bc6de565d4db84ef517445626e03022022da80320e9e489c8f41b74833dfb6a54a4eb5087cdb46eb663eef0b25caa526012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a914b7e6f7ff8658b2d1fb107e3d7be7af4742e6b1b3876f88fc00000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac0000000001000000043ffd60d3818431c495b89be84afac205d5d1ed663009291c560758bbd0a66df5010000006b483045022100f344607de9df42049688dcae8ff1db34c0c7cd25ec05516e30d2bc8f12ac9b2f022060b648f6a21745ea6d9782e17bcc4277b5808326488a1f40d41e125879723d3a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffffa5379401cce30f84731ef1ba65ce27edf2cc7ce57704507ebe8714aa16a96b92010000006a473044022020c37a63bf4d7f564c2192528709b6a38ab8271bd96898c6c2e335e5208661580220435c6f1ad4d9305d2c0a818b2feb5e45d443f2f162c0f61953a14d097fd07064012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff70e731e193235ff12c3184510895731a099112ffca4b00246c60003c40f843ce000000006a473044022053760f74c29a879e30a17b5f03a5bb057a5751a39f86fa6ecdedc36a1b7db04c022041d41c9b95f00d2d10a0373322a9025dba66c942196bc9d8adeb0e12d3024728012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff66b7a71b3e50379c8e85fc18fe3f1a408fc985f257036c34702ba205cef09f6f000000006a4730440220499bf9e2db3db6e930228d0661395f65431acae466634d098612fd80b08459ee022040e069fc9e3c60009f521cef54c38aadbd1251aee37940e6018aadb10f194d6a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a9148fc37ad460fdfbd2b44fe446f6e3071a4f64faa6878f447f0b000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac00000000",["a914feb8a29635c56d9cd913122f90678756bf23887687","76a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac"],"da49977ba1ee0d620a2c4f8f646b03cd0d230f5c6c994722e3ba884889f0be1a","09027acea61b6cc3fb33f5d52f7d088a6b2f75d234e89ca800","4cd9dd007a325199102f1fc0b7d77ca25ee3c84d46018c4353ecfcb56c0d3e7a","Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"],
-[987876,"0000000000000c00901f2049055e2a437c819d79a3d54fd63e6af796cd7b8a79","000000202694f74969fdb542090e95a56bc8aa2d646e27033850e32f1c5f000000000000f7e53676b3f12d5beb524ed617f2d25f5a93b5f4f52c1ba2678260d72712f8dd0a6dfe5740257e1a4b1768960101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1603e4120ff9c30a1c216900002f424d4920546573742fffffff0001205fa012000000001e76a914c486de584a735ec2f22da7cd9681614681f92173d83d0aa68688ac00000000",[],"e9d729b72d533c29abe5276d5cf6c152f3723f10efe000b1e0c9ca5265a8beb6","010c0b40","e6137ae5a8424c40da1e5023c16975cc97b09300b4c050e6b1c713add3836c40","Coinbase tx has unparseable output script"],
-[1263442,"000000006f27ddfe1dd680044a34548f41bed47eba9e6f0b310da21423bc5f33","000000201c8d1a529c39a396db2db234d5ec152fa651a2872966daccbde028b400000000083f14492679151dbfaa1a825ef4c18518e780c1f91044180280a7d33f4a98ff5f45765aaddc001d38333b9a02010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff230352471300fe5f45765afe94690a000963676d696e6572343208000000000000000000ffffffff024423a804000000001976a914f2c25ac3d59f3d674b1d1d0a25c27339aaac0ba688ac0000000000000000266a24aa21a9edcb26cb3052426b9ebb4d19c819ef87c19677bbf3a7c46ef0855bd1b2abe83491012000000000000000000000000000000000000000000000000000000000000000000000000002000000000101d20978463906ba4ff5e7192494b88dd5eb0de85d900ab253af909106faa22cc5010000000004000000014777ff000000000016001446c29eabe8208a33aa1023c741fa79aa92e881ff0347304402207d7ca96134f2bcfdd6b536536fdd39ad17793632016936f777ebb32c22943fda02206014d2fb8a6aa58279797f861042ba604ebd2f8f61e5bddbd9d3be5a245047b201004b632103eeaeba7ce5dc2470221e9517fb498e8d6bd4e73b85b8be655196972eb9ccd5566754b2752103a40b74d43df244799d041f32ce1ad515a6cd99501701540e38750d883ae21d3a68ac00000000",["002027a5000c7917f785d8fc6e5a55adfca8717ecb973ebb7743849ff956d896a7ed"],"a4a4d6c6034da8aa06f01fe71f1fffbd79e032006b07f6c7a2c60a66aa310c01","0385acb4f0fe889ef0","3588f34fbbc11640f9ed40b2a66a4e096215d50389691309c1dac74d4268aa81","Includes witness data"]
+[180480,"00000000fd3ceb2404ff07a785c7fdcc76619edc8ed61bd25134eaa22084366a","020000006058aa080a655aa991a444bd7d1f2defd9a3bbe68aabb69030cf3b4e00000000d2e826bfd7ef0beaa891a7eedbc92cd6a544a6cb61c7bdaa436762eb2123ef9790f5f552ffff001d0002c90f0501000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0300c102024608062f503253482fffffffff01c0c6072a01000000232102e769e60137a4df6b0df8ebd387cca44c4c57ae74cc0114a8e8317c8f3bfd85e9ac00000000010000000381a0802911a01ffb025c4dea0bc77963e8c1bb46313b71164c53f72f37fe5248010000000151ffffffffc904b267833d215e2128bd9575242232ac2bc311550c7fc1f0ef6f264b40d14c010000000151ffffffffdf0915666649dba81886519c531649b7b02180b4af67d6885e871299e9d5f775000000000151ffffffff0180817dcb00000000232103bb52138972c48a132fc1f637858c5189607dd0f7fe40c4f20f6ad65f2d389ba4ac0000000001000000018da38b434fba82d66052af74fc5e4e94301b114d9bc03f819dc876398404c8b4010000006c493046022100fe738b7580dc5fb5168e51fc61b5aed211125eb71068031009a22d9bbad752c5022100be5086baa384d40bcab0fa586e4f728397388d86e18b66cc417dc4f7fa4f9878012103f233299455134caa2687bdf15cb0becdfb03bd0ff2ff38e65ec6b7834295c34fffffffff022ebc1400000000001976a9147779b7fba1c1e06b717069b80ca170e8b04458a488ac9879c40f000000001976a9142a0307cd925dbb66b534c4db33003dd18c57015788ac0000000001000000026139a62e3422a602de36c873a225c1d3ca5aeee598539ceecb9f0dc8d1ad0f83010000006b483045022100ad9f32b4a0a2ddc19b5a74eba78123e57616f1b3cfd72ce68c03ea35a3dda1f002200dbd22aa6da17213df5e70dfc3b2611d40f70c98ed9626aa5e2cde9d97461f0a012103ddb295d2f1e8319187738fb4b230fdd9aa29d0e01647f69f6d770b9ab24eea90ffffffff983c82c87cf020040d671956525014d5c2b28c6d948c85e1a522362c0059eeae010000006b4830450221009ca544274c786d30a5d5d25e17759201ea16d3aedddf0b9e9721246f7ef6b32e02202cfa5564b6e87dfd9fd98957820e4d4e6238baeb0f65fe305d91506bb13f5f4f012103c99113deac0d5d044e3ac0346abc02501542af8c8d3759f1382c72ff84e704f7ffffffff02c0c62d00000000001976a914ae19d27efe12f5a886dc79af37ad6805db6f922d88ac70ce2000000000001976a9143b8d051d37a07ea1042067e93efe63dbf73920b988ac000000000100000002be566e8cd9933f0c75c4a82c027f7d0c544d5c101d0607ef6ae5d07b98e7f1dc000000006b483045022036a8cdfd5ea7ebc06c2bfb6e4f942bbf9a1caeded41680d11a3a9f5d8284abad022100cacb92a5be3f39e8bc14db1710910ef7b395fa1e18f45d41c28d914fcdde33be012102bf59abf110b5131fae0a3ce1ec379329b4c896a6ae5d443edb68529cc2bc7816ffffffff96cf67645b76ceb23fe922874847456a15feee1655082ff32d25a6bf2c0dfc90000000006a47304402203471ca2001784a5ac0abab583581f2613523da47ec5f53df833c117b5abd81500220618a2847723d57324f2984678db556dbca1a72230fc7e39df04c2239942ba942012102925c9794fd7bb9f8b29e207d5fc491b1150135a21f505041858889fa4edf436fffffffff026c840f00000000001976a914797fb8777d7991d8284d88bfd421ce520f0f843188ac00ca9a3b000000001976a9146d10f3f592699265d10b106eda37c3ce793f7a8588ac00000000",["","","","76a9142903b138c24be9e070b3e73ec495d77a204615e788ac","76a91433a1941fd9a37b9821d376f5a51bd4b52fa50e2888ac","76a914e4374e8155d0865742ca12b8d4d14d41b57d682f88ac","76a914001fa7459a6cfc64bdc178ba7e7a21603bb2568f88ac","76a914f6039952bc2b307aeec5371bfb96b66078ec17f688ac"],"d34ef98386f413769502808d4bac5f20f8dfd5bffc9eedafaa71de0eb1f01489","0db414c859a07e8205876354a210a75042d0463404913d61a8e068e58a3ae2aa080026","c582d51c0ca365e3fcf36c51cb646d7f83a67e867cb4743fd2128e3e022b700c","Tx spends from empty output script"],
+[926485,"000000000000015d6077a411a8f5cc95caf775ccf11c54e27df75ce58d187313","0000002060bbab0edbf3ef8a49608ee326f8fd75c473b7e3982095e2d100000000000000c30134f8c9b6d2470488d7a67a888f6fa12f8692e0c3411fbfb92f0f68f67eedae03ca57ef13021acc22dc4105010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0315230e0004ae03ca57043e3d1e1d0c8796bf579aef0c0000000000122f4e696e6a61506f6f6c2f5345475749542fffffffff038427a112000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9ed5c748e121c0fe146d973a4ac26fa4a68b0549d46ee22d25f50a5e46fe1b377ee00000000000000002952534b424c4f434b3acd16772ad61a3c5f00287480b720f6035d5e54c9efc71be94bb5e3727f10909001200000000000000000000000000000000000000000000000000000000000000000000000000100000000010145310e878941a1b2bc2d33797ee4d89d95eaaf2e13488063a2aa9a74490f510a0100000023220020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1ffffffff01002d3101000000001976a9143ebc40e411ed3c76f86711507ab952300890397288ac0400473044022001dd489a5d4e2fbd8a3ade27177f6b49296ba7695c40dbbe650ea83f106415fd02200b23a0602d8ff1bdf79dee118205fc7e9b40672bf31563e5741feb53fb86388501483045022100f88f040e90cc5dc6c6189d04718376ac19ed996bf9e4a3c29c3718d90ffd27180220761711f16c9e3a44f71aab55cbc0634907a1fa8bb635d971a9a01d368727bea10169522103b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb2103dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba621033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be153ae00000000010000000120925534261de4dcebb1ed5ab1b62bfe7a3ef968fb111dc2c910adfebc6e3bdf010000006b483045022100f50198f5ae66211a4f485190abe4dc7accdabe3bc214ebc9ea7069b97097d46e0220316a70a03014887086e335fc1b48358d46cd6bdc9af3b57c109c94af76fc915101210316cff587a01a2736d5e12e53551b18d73780b83c3bfb4fcf209c869b11b6415effffffff0220a10700000000001976a91450333046115eaa0ac9e0216565f945070e44573988ac2e7cd01a000000001976a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac00000000010000000203a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322010000006a47304402204efc3d70e4ca3049c2a425025edf22d5ca355f9ec899dbfbbeeb2268533a0f2b02204780d3739653035af4814ea52e1396d021953f948c29754edd0ee537364603dc012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff03a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322000000006a47304402202d96defdc5b4af71d6ba28c9a6042c2d5ee7bc6de565d4db84ef517445626e03022022da80320e9e489c8f41b74833dfb6a54a4eb5087cdb46eb663eef0b25caa526012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a914b7e6f7ff8658b2d1fb107e3d7be7af4742e6b1b3876f88fc00000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac0000000001000000043ffd60d3818431c495b89be84afac205d5d1ed663009291c560758bbd0a66df5010000006b483045022100f344607de9df42049688dcae8ff1db34c0c7cd25ec05516e30d2bc8f12ac9b2f022060b648f6a21745ea6d9782e17bcc4277b5808326488a1f40d41e125879723d3a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffffa5379401cce30f84731ef1ba65ce27edf2cc7ce57704507ebe8714aa16a96b92010000006a473044022020c37a63bf4d7f564c2192528709b6a38ab8271bd96898c6c2e335e5208661580220435c6f1ad4d9305d2c0a818b2feb5e45d443f2f162c0f61953a14d097fd07064012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff70e731e193235ff12c3184510895731a099112ffca4b00246c60003c40f843ce000000006a473044022053760f74c29a879e30a17b5f03a5bb057a5751a39f86fa6ecdedc36a1b7db04c022041d41c9b95f00d2d10a0373322a9025dba66c942196bc9d8adeb0e12d3024728012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff66b7a71b3e50379c8e85fc18fe3f1a408fc985f257036c34702ba205cef09f6f000000006a4730440220499bf9e2db3db6e930228d0661395f65431acae466634d098612fd80b08459ee022040e069fc9e3c60009f521cef54c38aadbd1251aee37940e6018aadb10f194d6a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a9148fc37ad460fdfbd2b44fe446f6e3071a4f64faa6878f447f0b000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac00000000",["a914feb8a29635c56d9cd913122f90678756bf23887687","76a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac"],"8f13b9a9c85611635b47906c3053ac53cfcec7211455d4cb0d63dc9acc13d472","09027acea61b6cc3fb33f5d52f7d088a6b2f75d234e89ca800","546c574a0472144bcaf9b6aeabf26372ad87c7af7d1ee0dbfae5e099abeae49c","Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"],
+[987876,"0000000000000c00901f2049055e2a437c819d79a3d54fd63e6af796cd7b8a79","000000202694f74969fdb542090e95a56bc8aa2d646e27033850e32f1c5f000000000000f7e53676b3f12d5beb524ed617f2d25f5a93b5f4f52c1ba2678260d72712f8dd0a6dfe5740257e1a4b1768960101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1603e4120ff9c30a1c216900002f424d4920546573742fffffff0001205fa012000000001e76a914c486de584a735ec2f22da7cd9681614681f92173d83d0aa68688ac00000000",[],"fe4d230dbb0f4fec9bed23a5283e08baf996e3f32b93f52c7de1f641ddfd04ad","010c0b40","0965a544743bbfa36f254446e75630c09404b3d164a261892372977538928ed5","Coinbase tx has unparseable output script"],
+[1263442,"000000006f27ddfe1dd680044a34548f41bed47eba9e6f0b310da21423bc5f33","000000201c8d1a529c39a396db2db234d5ec152fa651a2872966daccbde028b400000000083f14492679151dbfaa1a825ef4c18518e780c1f91044180280a7d33f4a98ff5f45765aaddc001d38333b9a02010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff230352471300fe5f45765afe94690a000963676d696e6572343208000000000000000000ffffffff024423a804000000001976a914f2c25ac3d59f3d674b1d1d0a25c27339aaac0ba688ac0000000000000000266a24aa21a9edcb26cb3052426b9ebb4d19c819ef87c19677bbf3a7c46ef0855bd1b2abe83491012000000000000000000000000000000000000000000000000000000000000000000000000002000000000101d20978463906ba4ff5e7192494b88dd5eb0de85d900ab253af909106faa22cc5010000000004000000014777ff000000000016001446c29eabe8208a33aa1023c741fa79aa92e881ff0347304402207d7ca96134f2bcfdd6b536536fdd39ad17793632016936f777ebb32c22943fda02206014d2fb8a6aa58279797f861042ba604ebd2f8f61e5bddbd9d3be5a245047b201004b632103eeaeba7ce5dc2470221e9517fb498e8d6bd4e73b85b8be655196972eb9ccd5566754b2752103a40b74d43df244799d041f32ce1ad515a6cd99501701540e38750d883ae21d3a68ac00000000",["002027a5000c7917f785d8fc6e5a55adfca8717ecb973ebb7743849ff956d896a7ed"],"31d66d516a9eda7de865df29f6ef6cb8e4bf9309e5dac899968a9a62a5df61e3","0385acb4f0fe889ef0","4e6d564c2a2452065c205dd7eb2791124e0c4e0dbb064c410c24968572589dec","Includes witness data"],
+[1414221,"0000000000000027b2b3b3381f114f674f481544ff2be37ae3788d7e078383b1","000000204ea88307a7959d8207968f152bedca5a93aefab253f1fb2cfb032a400000000070cebb14ec6dbc27a9dfd066d9849a4d3bac5f674665f73a5fe1de01a022a0c851fda85bf05f4c19a779d1450102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff18034d94154d696e6572476174653030310d000000f238f401ffffffff01c817a804000000000000000000",[],"5e5e12d90693c8e936f01847859404c67482439681928353ca1296982042864e","00","021e8882ef5a0ed932edeebbecfeda1d7ce528ec7b3daa27641acf1189d7b5dc","Empty data"]
]
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 94e8c95345..0bde92c18d 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <dbwrapper.h>
#include <uint256.h>
#include <random.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <memory>
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index e5d62a3ab2..3a2844861b 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,13 +9,12 @@
#include <keystore.h>
#include <net.h>
#include <net_processing.h>
-#include <pow.h>
#include <script/sign.h>
#include <serialize.h>
#include <util/system.h>
#include <validation.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <stdint.h>
@@ -90,8 +89,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
// 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);
+ BOOST_CHECK(::ChainActive().Tip() != nullptr);
+ BOOST_CHECK(::ChainActive().Tip()->nChainWork > 0);
}
// Test starts here
@@ -381,7 +380,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vin[0].scriptSig << OP_1;
tx.vout.resize(1);
tx.vout[0].nValue = 1*CENT;
- tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
+ tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
AddOrphanTx(MakeTransactionRef(tx), i);
}
@@ -397,7 +396,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vin[0].prevout.hash = txPrev->GetHash();
tx.vout.resize(1);
tx.vout[0].nValue = 1*CENT;
- tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
+ tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL));
AddOrphanTx(MakeTransactionRef(tx), i);
@@ -411,7 +410,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
CMutableTransaction tx;
tx.vout.resize(1);
tx.vout[0].nValue = 1*CENT;
- tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
+ tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
tx.vin.resize(2777);
for (unsigned int j = 0; j < tx.vin.size(); j++)
{
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index 8da8cfc00c..f5bda7d5e6 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -6,7 +6,7 @@
#include <string>
#include <script/sign.h>
#include <script/standard.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
#include <script/descriptor.h>
#include <util/strencodings.h>
@@ -18,8 +18,8 @@ void CheckUnparsable(const std::string& prv, const std::string& pub)
FlatSigningProvider keys_priv, keys_pub;
auto parse_priv = Parse(prv, keys_priv);
auto parse_pub = Parse(pub, keys_pub);
- BOOST_CHECK(!parse_priv);
- BOOST_CHECK(!parse_pub);
+ BOOST_CHECK_MESSAGE(!parse_priv, prv);
+ BOOST_CHECK_MESSAGE(!parse_pub, pub);
}
constexpr int DEFAULT = 0;
@@ -28,6 +28,18 @@ constexpr int HARDENED = 2; // Derivation needs access to private keys
constexpr int UNSOLVABLE = 4; // This descriptor is not expected to be solvable
constexpr int SIGNABLE = 8; // We can sign with this descriptor (this is not true when actual BIP32 derivation is used, as that's not integrated in our signing code)
+/** Compare two descriptors. If only one of them has a checksum, the checksum is ignored. */
+bool EqualDescriptor(std::string a, std::string b)
+{
+ bool a_check = (a.size() > 9 && a[a.size() - 9] == '#');
+ bool b_check = (b.size() > 9 && b[b.size() - 9] == '#');
+ if (a_check != b_check) {
+ if (a_check) a = a.substr(0, a.size() - 9);
+ if (b_check) b = b.substr(0, b.size() - 9);
+ }
+ return a == b;
+}
+
std::string MaybeUseHInsteadOfApostrophy(std::string ret)
{
if (InsecureRandBool()) {
@@ -35,6 +47,7 @@ std::string MaybeUseHInsteadOfApostrophy(std::string ret)
auto it = ret.find("'");
if (it != std::string::npos) {
ret[it] = 'h';
+ if (ret.size() > 9 && ret[ret.size() - 9] == '#') ret = ret.substr(0, ret.size() - 9); // Changing apostrophe to h breaks the checksum
} else {
break;
}
@@ -63,16 +76,16 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
// Check that both versions serialize back to the public version.
std::string pub1 = parse_priv->ToString();
std::string pub2 = parse_pub->ToString();
- BOOST_CHECK_EQUAL(pub, pub1);
- BOOST_CHECK_EQUAL(pub, pub2);
+ BOOST_CHECK(EqualDescriptor(pub, pub1));
+ BOOST_CHECK(EqualDescriptor(pub, pub2));
// Check that both can be serialized with private key back to the private version, but not without private key.
std::string prv1;
BOOST_CHECK(parse_priv->ToPrivateString(keys_priv, prv1));
- BOOST_CHECK_EQUAL(prv, prv1);
+ BOOST_CHECK(EqualDescriptor(prv, prv1));
BOOST_CHECK(!parse_priv->ToPrivateString(keys_pub, prv1));
BOOST_CHECK(parse_pub->ToPrivateString(keys_priv, prv1));
- BOOST_CHECK_EQUAL(prv, prv1);
+ BOOST_CHECK(EqualDescriptor(prv, prv1));
BOOST_CHECK(!parse_pub->ToPrivateString(keys_pub, prv1));
// Check whether IsRange on both returns the expected result
@@ -141,8 +154,8 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
// Test whether the observed key path is present in the 'paths' variable (which contains expected, unobserved paths),
// and then remove it from that set.
for (const auto& origin : script_provider.origins) {
- BOOST_CHECK_MESSAGE(paths.count(origin.second.path), "Unexpected key path: " + prv);
- left_paths.erase(origin.second.path);
+ BOOST_CHECK_MESSAGE(paths.count(origin.second.second.path), "Unexpected key path: " + prv);
+ left_paths.erase(origin.second.second.path);
}
}
}
@@ -210,6 +223,15 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
CheckUnparsable("wsh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))"); // Cannot embed P2SH inside P2WSH
CheckUnparsable("sh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))"); // Cannot embed P2SH inside P2SH
CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))"); // Cannot embed P2WSH inside P2WSH
+
+ // Checksums
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#"); // Empty checksum
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfyq", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5tq"); // Too long checksum
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxf", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5"); // Too short checksum
+ CheckUnparsable("sh(multi(3,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(3,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t"); // Error in payload
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggssrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjq09x4t"); // Error in checksum
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/flatfile_tests.cpp b/src/test/flatfile_tests.cpp
new file mode 100644
index 0000000000..1db2f8054c
--- /dev/null
+++ b/src/test/flatfile_tests.cpp
@@ -0,0 +1,123 @@
+// Copyright (c) 2019 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 <flatfile.h>
+#include <test/setup_common.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(flatfile_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(flatfile_filename)
+{
+ auto data_dir = SetDataDir("flatfile_test");
+
+ FlatFilePos pos(456, 789);
+
+ FlatFileSeq seq1(data_dir, "a", 16 * 1024);
+ BOOST_CHECK_EQUAL(seq1.FileName(pos), data_dir / "a00456.dat");
+
+ FlatFileSeq seq2(data_dir / "a", "b", 16 * 1024);
+ BOOST_CHECK_EQUAL(seq2.FileName(pos), data_dir / "a" / "b00456.dat");
+}
+
+BOOST_AUTO_TEST_CASE(flatfile_open)
+{
+ auto data_dir = SetDataDir("flatfile_test");
+ FlatFileSeq seq(data_dir, "a", 16 * 1024);
+
+ std::string line1("A purely peer-to-peer version of electronic cash would allow online "
+ "payments to be sent directly from one party to another without going "
+ "through a financial institution.");
+ std::string line2("Digital signatures provide part of the solution, but the main benefits are "
+ "lost if a trusted third party is still required to prevent double-spending.");
+
+ size_t pos1 = 0;
+ size_t pos2 = pos1 + GetSerializeSize(line1, CLIENT_VERSION);
+
+ // Write first line to file.
+ {
+ CAutoFile file(seq.Open(FlatFilePos(0, pos1)), SER_DISK, CLIENT_VERSION);
+ file << LIMITED_STRING(line1, 256);
+ }
+
+ // Attempt to append to file opened in read-only mode.
+ {
+ CAutoFile file(seq.Open(FlatFilePos(0, pos2), true), SER_DISK, CLIENT_VERSION);
+ BOOST_CHECK_THROW(file << LIMITED_STRING(line2, 256), std::ios_base::failure);
+ }
+
+ // Append second line to file.
+ {
+ CAutoFile file(seq.Open(FlatFilePos(0, pos2)), SER_DISK, CLIENT_VERSION);
+ file << LIMITED_STRING(line2, 256);
+ }
+
+ // Read text from file in read-only mode.
+ {
+ std::string text;
+ CAutoFile file(seq.Open(FlatFilePos(0, pos1), true), SER_DISK, CLIENT_VERSION);
+
+ file >> LIMITED_STRING(text, 256);
+ BOOST_CHECK_EQUAL(text, line1);
+
+ file >> LIMITED_STRING(text, 256);
+ BOOST_CHECK_EQUAL(text, line2);
+ }
+
+ // Read text from file with position offset.
+ {
+ std::string text;
+ CAutoFile file(seq.Open(FlatFilePos(0, pos2)), SER_DISK, CLIENT_VERSION);
+
+ file >> LIMITED_STRING(text, 256);
+ BOOST_CHECK_EQUAL(text, line2);
+ }
+
+ // Ensure another file in the sequence has no data.
+ {
+ std::string text;
+ CAutoFile file(seq.Open(FlatFilePos(1, pos2)), SER_DISK, CLIENT_VERSION);
+ BOOST_CHECK_THROW(file >> LIMITED_STRING(text, 256), std::ios_base::failure);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(flatfile_allocate)
+{
+ auto data_dir = SetDataDir("flatfile_test");
+ FlatFileSeq seq(data_dir, "a", 100);
+
+ bool out_of_space;
+
+ BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 0), 1, out_of_space), 100);
+ BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 0))), 100);
+ BOOST_CHECK(!out_of_space);
+
+ BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 99), 1, out_of_space), 0);
+ BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 99))), 100);
+ BOOST_CHECK(!out_of_space);
+
+ BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 99), 2, out_of_space), 101);
+ BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 99))), 200);
+ BOOST_CHECK(!out_of_space);
+}
+
+BOOST_AUTO_TEST_CASE(flatfile_flush)
+{
+ auto data_dir = SetDataDir("flatfile_test");
+ FlatFileSeq seq(data_dir, "a", 100);
+
+ bool out_of_space;
+ seq.Allocate(FlatFilePos(0, 0), 1, out_of_space);
+
+ // Flush without finalize should not truncate file.
+ seq.Flush(FlatFilePos(0, 1));
+ BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 1))), 100);
+
+ // Flush with finalize should truncate file.
+ seq.Flush(FlatFilePos(0, 1), true);
+ BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 1))), 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
index 93aee10bb7..6bd6bb1be3 100644
--- a/src/test/fs_tests.cpp
+++ b/src/test/fs_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 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 <fs.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/fuzz/deserialize.cpp
index 859fba0bdc..97d7633715 100644
--- a/src/test/test_bitcoin_fuzzy.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h
index ad62a5faf0..8b03a7e46e 100644
--- a/src/test/fuzz/fuzz.h
+++ b/src/test/fuzz/fuzz.h
@@ -10,8 +10,6 @@
#include <vector>
-const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
-
void test_one_input(std::vector<uint8_t> buffer);
#endif // BITCOIN_TEST_FUZZ_FUZZ_H
diff --git a/src/test/fuzz/script_flags.cpp b/src/test/fuzz/script_flags.cpp
new file mode 100644
index 0000000000..2c0bfa360c
--- /dev/null
+++ b/src/test/fuzz/script_flags.cpp
@@ -0,0 +1,72 @@
+// Copyright (c) 2009-2019 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 <script/interpreter.h>
+#include <script/script.h>
+#include <streams.h>
+#include <version.h>
+
+#include <test/fuzz/fuzz.h>
+
+/** Flags that are not forbidden by an assert */
+static bool IsValidFlagCombination(unsigned flags);
+
+void test_one_input(std::vector<uint8_t> buffer)
+{
+ CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
+ try {
+ int nVersion;
+ ds >> nVersion;
+ ds.SetVersion(nVersion);
+ } catch (const std::ios_base::failure&) {
+ return;
+ }
+
+ try {
+ const CTransaction tx(deserialize, ds);
+ const PrecomputedTransactionData txdata(tx);
+
+ unsigned int verify_flags;
+ ds >> verify_flags;
+
+ if (!IsValidFlagCombination(verify_flags)) return;
+
+ unsigned int fuzzed_flags;
+ ds >> fuzzed_flags;
+
+ for (unsigned i = 0; i < tx.vin.size(); ++i) {
+ CTxOut prevout;
+ ds >> prevout;
+
+ const TransactionSignatureChecker checker{&tx, i, prevout.nValue, txdata};
+
+ ScriptError serror;
+ const bool ret = VerifyScript(tx.vin.at(i).scriptSig, prevout.scriptPubKey, &tx.vin.at(i).scriptWitness, verify_flags, checker, &serror);
+ assert(ret == (serror == SCRIPT_ERR_OK));
+
+ // Verify that removing flags from a passing test or adding flags to a failing test does not change the result
+ if (ret) {
+ verify_flags &= ~fuzzed_flags;
+ } else {
+ verify_flags |= fuzzed_flags;
+ }
+ if (!IsValidFlagCombination(verify_flags)) return;
+
+ ScriptError serror_fuzzed;
+ const bool ret_fuzzed = VerifyScript(tx.vin.at(i).scriptSig, prevout.scriptPubKey, &tx.vin.at(i).scriptWitness, verify_flags, checker, &serror_fuzzed);
+ assert(ret_fuzzed == (serror_fuzzed == SCRIPT_ERR_OK));
+
+ assert(ret_fuzzed == ret);
+ }
+ } catch (const std::ios_base::failure&) {
+ return;
+ }
+}
+
+static bool IsValidFlagCombination(unsigned flags)
+{
+ if (flags & SCRIPT_VERIFY_CLEANSTACK && ~flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) return false;
+ if (flags & SCRIPT_VERIFY_WITNESS && ~flags & SCRIPT_VERIFY_P2SH) return false;
+ return true;
+}
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 8048238028..8a42344642 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <util/strencodings.h>
#include <util/system.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <string>
#include <vector>
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index e8e5040855..325b7002f2 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2013-2018 The Bitcoin Core developers
+// Copyright (c) 2013-2019 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 <crypto/siphash.h>
#include <hash.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp
index bf295042de..e924f27d1b 100644
--- a/src/test/key_io_tests.cpp
+++ b/src/test/key_io_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,7 +9,7 @@
#include <key_io.h>
#include <script/script.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/key_properties.cpp b/src/test/key_properties.cpp
index c564b4eab8..8b508ed7f7 100644
--- a/src/test/key_properties.cpp
+++ b/src/test/key_properties.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 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>
@@ -8,7 +8,7 @@
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <string>
#include <vector>
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index a768b4bcbd..1b95105eab 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,7 +9,7 @@
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <string>
#include <vector>
@@ -68,10 +68,10 @@ BOOST_AUTO_TEST_CASE(key_test1)
BOOST_CHECK(!key2C.VerifyPubKey(pubkey2));
BOOST_CHECK(key2C.VerifyPubKey(pubkey2C));
- 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()));
+ BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(PKHash(pubkey1)));
+ BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(PKHash(pubkey2)));
+ BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(PKHash(pubkey1C)));
+ BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(PKHash(pubkey2C)));
for (int n=0; n<16; n++)
{
@@ -188,4 +188,36 @@ BOOST_AUTO_TEST_CASE(key_signature_tests)
BOOST_CHECK(found_small);
}
+BOOST_AUTO_TEST_CASE(key_key_negation)
+{
+ // create a dummy hash for signature comparison
+ unsigned char rnd[8];
+ std::string str = "Bitcoin key verification\n";
+ GetRandBytes(rnd, sizeof(rnd));
+ uint256 hash;
+ CHash256().Write((unsigned char*)str.data(), str.size()).Write(rnd, sizeof(rnd)).Finalize(hash.begin());
+
+ // import the static test key
+ CKey key = DecodeSecret(strSecret1C);
+
+ // create a signature
+ std::vector<unsigned char> vch_sig;
+ std::vector<unsigned char> vch_sig_cmp;
+ key.Sign(hash, vch_sig);
+
+ // negate the key twice
+ BOOST_CHECK(key.GetPubKey().data()[0] == 0x03);
+ key.Negate();
+ // after the first negation, the signature must be different
+ key.Sign(hash, vch_sig_cmp);
+ BOOST_CHECK(vch_sig_cmp != vch_sig);
+ BOOST_CHECK(key.GetPubKey().data()[0] == 0x02);
+ key.Negate();
+ // after the second negation, we should have the original key and thus the
+ // same signature
+ key.Sign(hash, vch_sig_cmp);
+ BOOST_CHECK(vch_sig_cmp == vch_sig);
+ BOOST_CHECK(key.GetPubKey().data()[0] == 0x03);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp
index 0788f75b04..00b36f51fb 100644
--- a/src/test/limitedmap_tests.cpp
+++ b/src/test/limitedmap_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <limitedmap.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/main.cpp b/src/test/main.cpp
new file mode 100644
index 0000000000..ff3f36b561
--- /dev/null
+++ b/src/test/main.cpp
@@ -0,0 +1,7 @@
+// Copyright (c) 2011-2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#define BOOST_TEST_MODULE Bitcoin Core Test Suite
+
+#include <boost/test/unit_test.hpp>
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 23ca9d89ae..0f74b379c0 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -6,7 +6,7 @@
#include <txmempool.h>
#include <util/system.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
#include <list>
diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp
index 4cdf0f003e..1684258c9f 100644
--- a/src/test/merkle_tests.cpp
+++ b/src/test/merkle_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-2019 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 <consensus/merkle.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/merkleblock_tests.cpp b/src/test/merkleblock_tests.cpp
index 4978593285..eac43471c7 100644
--- a/src/test/merkleblock_tests.cpp
+++ b/src/test/merkleblock_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 5ba1df2ec2..9a182d7bd3 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,18 +7,17 @@
#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/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
+#include <validation.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <memory>
@@ -82,11 +81,11 @@ struct {
{2, 0xbbbeb305}, {2, 0xfe1c810a},
};
-static CBlockIndex CreateBlockIndex(int nHeight)
+static CBlockIndex CreateBlockIndex(int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
CBlockIndex index;
index.nHeight = nHeight;
- index.pprev = chainActive.Tip();
+ index.pprev = ::ChainActive().Tip();
return index;
}
@@ -231,17 +230,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
LOCK(cs_main);
pblock->nVersion = 1;
- pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1;
+ pblock->nTime = ::ChainActive().Tip()->GetMedianTimePast()+1;
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1;
txCoinbase.vin[0].scriptSig = CScript();
txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce);
- txCoinbase.vin[0].scriptSig.push_back(chainActive.Height());
+ txCoinbase.vin[0].scriptSig.push_back(::ChainActive().Height());
txCoinbase.vout.resize(1); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
txCoinbase.vout[0].scriptPubKey = CScript();
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
if (txFirst.size() == 0)
- baseheight = chainActive.Height();
+ baseheight = ::ChainActive().Height();
if (txFirst.size() < 4)
txFirst.push_back(pblock->vtx[0]);
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
@@ -367,29 +366,29 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
mempool.clear();
// subsidy changing
- int nHeight = chainActive.Height();
+ int nHeight = ::ChainActive().Height();
// Create an actual 209999-long block chain (without valid blocks).
- while (chainActive.Tip()->nHeight < 209999) {
- CBlockIndex* prev = chainActive.Tip();
+ while (::ChainActive().Tip()->nHeight < 209999) {
+ CBlockIndex* prev = ::ChainActive().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
pcoinsTip->SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
- chainActive.SetTip(next);
+ ::ChainActive().SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
// Extend to a 210000-long block chain.
- while (chainActive.Tip()->nHeight < 210000) {
- CBlockIndex* prev = chainActive.Tip();
+ while (::ChainActive().Tip()->nHeight < 210000) {
+ CBlockIndex* prev = ::ChainActive().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
pcoinsTip->SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
- chainActive.SetTip(next);
+ ::ChainActive().SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
@@ -399,7 +398,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE;
script = CScript() << OP_0;
- tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
+ tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script));
hash = tx.GetHash();
mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
@@ -412,16 +411,16 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
mempool.clear();
// Delete the dummy blocks again.
- while (chainActive.Tip()->nHeight > nHeight) {
- CBlockIndex* del = chainActive.Tip();
- chainActive.SetTip(del->pprev);
+ while (::ChainActive().Tip()->nHeight > nHeight) {
+ CBlockIndex* del = ::ChainActive().Tip();
+ ::ChainActive().SetTip(del->pprev);
pcoinsTip->SetBestBlock(del->pprev->GetBlockHash());
delete del->phashBlock;
delete del;
}
// non-final txs in mempool
- SetMockTime(chainActive.Tip()->GetMedianTimePast()+1);
+ SetMockTime(::ChainActive().Tip()->GetMedianTimePast()+1);
int flags = LOCKTIME_VERIFY_SEQUENCE|LOCKTIME_MEDIAN_TIME_PAST;
// height map
std::vector<int> prevheights;
@@ -433,7 +432,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].prevout.hash = txFirst[0]->GetHash(); // only 1 transaction
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
- tx.vin[0].nSequence = chainActive.Tip()->nHeight + 1; // txFirst[0] is the 2nd block
+ tx.vin[0].nSequence = ::ChainActive().Tip()->nHeight + 1; // txFirst[0] is the 2nd block
prevheights[0] = baseheight + 1;
tx.vout.resize(1);
tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
@@ -443,11 +442,11 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(chainActive.Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
// relative time locked
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
- tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((chainActive.Tip()->GetMedianTimePast()+1-chainActive[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
+ tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((::ChainActive().Tip()->GetMedianTimePast()+1-::ChainActive()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
@@ -455,36 +454,36 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(chainActive.Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
+ ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
+ ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
// absolute height locked
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
tx.vin[0].nSequence = CTxIn::SEQUENCE_FINAL - 1;
prevheights[0] = baseheight + 3;
- tx.nLockTime = chainActive.Tip()->nHeight + 1;
+ tx.nLockTime = ::ChainActive().Tip()->nHeight + 1;
hash = tx.GetHash();
mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(CTransaction(tx), chainActive.Tip()->nHeight + 2, chainActive.Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
// absolute time locked
tx.vin[0].prevout.hash = txFirst[3]->GetHash();
- tx.nLockTime = chainActive.Tip()->GetMedianTimePast();
+ tx.nLockTime = ::ChainActive().Tip()->GetMedianTimePast();
prevheights.resize(1);
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(CTransaction(tx), chainActive.Tip()->nHeight + 2, chainActive.Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
// mempool-dependent transactions (not added)
tx.vin[0].prevout.hash = hash;
- prevheights[0] = chainActive.Tip()->nHeight + 1;
+ prevheights[0] = ::ChainActive().Tip()->nHeight + 1;
tx.nLockTime = 0;
tx.vin[0].nSequence = 0;
BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
@@ -505,14 +504,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3U);
// However if we advance height by 1 and time by 512, all of them should be mined
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- chainActive.Tip()->nHeight++;
- SetMockTime(chainActive.Tip()->GetMedianTimePast() + 1);
+ ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+ ::ChainActive().Tip()->nHeight++;
+ SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1);
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U);
- chainActive.Tip()->nHeight--;
+ ::ChainActive().Tip()->nHeight--;
SetMockTime(0);
mempool.clear();
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index 8afe4b8a59..10a732d64d 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,9 +9,8 @@
#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 <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index b4ae8e9765..54d18c0a1c 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -1,8 +1,8 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <test/setup_common.h>
#include <string>
#include <boost/test/unit_test.hpp>
#include <hash.h>
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 0d557cff13..86c0cecbf1 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <netbase.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <util/strencodings.h>
#include <string>
@@ -59,6 +59,7 @@ BOOST_AUTO_TEST_CASE(netbase_properties)
BOOST_CHECK(ResolveIP("FC00::").IsRFC4193());
BOOST_CHECK(ResolveIP("2001::2").IsRFC4380());
BOOST_CHECK(ResolveIP("2001:10::").IsRFC4843());
+ BOOST_CHECK(ResolveIP("2001:20::").IsRFC7343());
BOOST_CHECK(ResolveIP("FE80::").IsRFC4862());
BOOST_CHECK(ResolveIP("64:FF9B::").IsRFC6052());
BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor());
diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp
index 5020192804..c5513ae9fa 100644
--- a/src/test/pmt_tests.cpp
+++ b/src/test/pmt_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,7 +9,7 @@
#include <uint256.h>
#include <arith_uint256.h>
#include <version.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 7b274a1658..149094fc00 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -8,7 +8,7 @@
#include <uint256.h>
#include <util/system.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index 26cdc9bc5c..653433bfce 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,7 +7,7 @@
#include <pow.h>
#include <random.h>
#include <util/system.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index 84c1abc14a..fc1f946bba 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,7 +9,7 @@
#include <serialize.h>
#include <streams.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp
index bdb411d53f..2b01acf7fa 100644
--- a/src/test/raii_event_tests.cpp
+++ b/src/test/raii_event_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -12,7 +12,7 @@
#include <support/events.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp
index 1057d09471..e6fbe2355d 100644
--- a/src/test/random_tests.cpp
+++ b/src/test/random_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 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 <random.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -21,9 +21,14 @@ BOOST_AUTO_TEST_CASE(osrandom_tests)
BOOST_AUTO_TEST_CASE(fastrandom_tests)
{
// Check that deterministic FastRandomContexts are deterministic
+ g_mock_deterministic_tests = true;
FastRandomContext ctx1(true);
FastRandomContext ctx2(true);
+ for (int i = 10; i > 0; --i) {
+ BOOST_CHECK_EQUAL(GetRand(std::numeric_limits<uint64_t>::max()), uint64_t{10393729187455219830U});
+ BOOST_CHECK_EQUAL(GetRandInt(std::numeric_limits<int>::max()), int{769702006});
+ }
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
BOOST_CHECK_EQUAL(ctx1.rand64(), ctx2.rand64());
@@ -38,6 +43,11 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
BOOST_CHECK(ctx1.randbytes(50) == ctx2.randbytes(50));
// Check that a nondeterministic ones are not
+ g_mock_deterministic_tests = false;
+ for (int i = 10; i > 0; --i) {
+ BOOST_CHECK(GetRand(std::numeric_limits<uint64_t>::max()) != uint64_t{10393729187455219830U});
+ BOOST_CHECK(GetRandInt(std::numeric_limits<int>::max()) != int{769702006});
+ }
{
FastRandomContext ctx3, ctx4;
BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal
diff --git a/src/test/reverselock_tests.cpp b/src/test/reverselock_tests.cpp
index 91c76fda88..69db9dcf4e 100644
--- a/src/test/reverselock_tests.cpp
+++ b/src/test/reverselock_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-2019 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 <reverselock.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index ff48398925..63bfe1d346 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,10 +9,8 @@
#include <core_io.h>
#include <init.h>
#include <interfaces/chain.h>
-#include <key_io.h>
-#include <netbase.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>
@@ -31,10 +29,9 @@ UniValue CallRPC(std::string args)
request.strMethod = strMethod;
request.params = RPCConvertValues(strMethod, vArgs);
request.fHelp = false;
- BOOST_CHECK(tableRPC[strMethod]);
- rpcfn_type method = tableRPC[strMethod]->actor;
+ if (RPCIsInWarmup(nullptr)) SetRPCWarmupFinished();
try {
- UniValue result = (*method)(request);
+ UniValue result = tableRPC.execute(request);
return result;
}
catch (const UniValue& objError) {
diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp
index 8085a21928..891aa8e5c3 100644
--- a/src/test/sanity_tests.cpp
+++ b/src/test/sanity_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <compat/sanity.h>
#include <key.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index 100d65b779..42242b962b 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <random.h>
#include <scheduler.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/thread.hpp>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
index 3a2a11ef98..aa9c98c173 100644
--- a/src/test/script_p2sh_tests.cpp
+++ b/src/test/script_p2sh_tests.cpp
@@ -1,18 +1,18 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <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 <policy/settings.h>
#include <script/sign.h>
#include <script/ismine.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
@@ -68,14 +68,14 @@ BOOST_AUTO_TEST_CASE(sign)
// different keys, straight/P2SH, pubkey/pubkeyhash
CScript standardScripts[4];
standardScripts[0] << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
- standardScripts[1] = GetScriptForDestination(key[1].GetPubKey().GetID());
+ standardScripts[1] = GetScriptForDestination(PKHash(key[1].GetPubKey()));
standardScripts[2] << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
- standardScripts[3] = GetScriptForDestination(key[2].GetPubKey().GetID());
+ standardScripts[3] = GetScriptForDestination(PKHash(key[2].GetPubKey()));
CScript evalScripts[4];
for (int i = 0; i < 4; i++)
{
BOOST_CHECK(keystore.AddCScript(standardScripts[i]));
- evalScripts[i] = GetScriptForDestination(CScriptID(standardScripts[i]));
+ evalScripts[i] = GetScriptForDestination(ScriptHash(standardScripts[i]));
}
CMutableTransaction txFrom; // Funding transaction:
@@ -130,7 +130,7 @@ BOOST_AUTO_TEST_CASE(norecurse)
CScript invalidAsScript;
invalidAsScript << OP_INVALIDOPCODE << OP_INVALIDOPCODE;
- CScript p2sh = GetScriptForDestination(CScriptID(invalidAsScript));
+ CScript p2sh = GetScriptForDestination(ScriptHash(invalidAsScript));
CScript scriptSig;
scriptSig << Serialize(invalidAsScript);
@@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(norecurse)
// Try to recur, and verification should succeed because
// the inner HASH160 <> EQUAL should only check the hash:
- CScript p2sh2 = GetScriptForDestination(CScriptID(p2sh));
+ CScript p2sh2 = GetScriptForDestination(ScriptHash(p2sh));
CScript scriptSig2;
scriptSig2 << Serialize(invalidAsScript) << Serialize(p2sh);
@@ -164,7 +164,7 @@ BOOST_AUTO_TEST_CASE(set)
}
CScript inner[4];
- inner[0] = GetScriptForDestination(key[0].GetPubKey().GetID());
+ inner[0] = GetScriptForDestination(PKHash(key[0].GetPubKey()));
inner[1] = GetScriptForMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+2));
inner[2] = GetScriptForMultisig(1, std::vector<CPubKey>(keys.begin(), keys.begin()+2));
inner[3] = GetScriptForMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+3));
@@ -172,7 +172,7 @@ BOOST_AUTO_TEST_CASE(set)
CScript outer[4];
for (int i = 0; i < 4; i++)
{
- outer[i] = GetScriptForDestination(CScriptID(inner[i]));
+ outer[i] = GetScriptForDestination(ScriptHash(inner[i]));
BOOST_CHECK(keystore.AddCScript(inner[i]));
}
@@ -252,7 +252,7 @@ BOOST_AUTO_TEST_CASE(switchover)
CScript scriptSig;
scriptSig << Serialize(notValid);
- CScript fund = GetScriptForDestination(CScriptID(notValid));
+ CScript fund = GetScriptForDestination(ScriptHash(notValid));
// Validation should succeed under old rules (hash is correct):
@@ -283,11 +283,11 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
txFrom.vout.resize(7);
// First three are standard:
- CScript pay1 = GetScriptForDestination(key[0].GetPubKey().GetID());
+ CScript pay1 = GetScriptForDestination(PKHash(key[0].GetPubKey()));
BOOST_CHECK(keystore.AddCScript(pay1));
CScript pay1of3 = GetScriptForMultisig(1, keys);
- txFrom.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(pay1)); // P2SH (OP_CHECKSIG)
+ txFrom.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(pay1)); // P2SH (OP_CHECKSIG)
txFrom.vout[0].nValue = 1000;
txFrom.vout[1].scriptPubKey = pay1; // ordinary OP_CHECKSIG
txFrom.vout[1].nValue = 2000;
@@ -302,7 +302,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
oneAndTwo << OP_2 << ToByteVector(key[3].GetPubKey()) << ToByteVector(key[4].GetPubKey()) << ToByteVector(key[5].GetPubKey());
oneAndTwo << OP_3 << OP_CHECKMULTISIG;
BOOST_CHECK(keystore.AddCScript(oneAndTwo));
- txFrom.vout[3].scriptPubKey = GetScriptForDestination(CScriptID(oneAndTwo));
+ txFrom.vout[3].scriptPubKey = GetScriptForDestination(ScriptHash(oneAndTwo));
txFrom.vout[3].nValue = 4000;
// vout[4] is max sigops:
@@ -311,24 +311,24 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
fifteenSigops << ToByteVector(key[i%3].GetPubKey());
fifteenSigops << OP_15 << OP_CHECKMULTISIG;
BOOST_CHECK(keystore.AddCScript(fifteenSigops));
- txFrom.vout[4].scriptPubKey = GetScriptForDestination(CScriptID(fifteenSigops));
+ txFrom.vout[4].scriptPubKey = GetScriptForDestination(ScriptHash(fifteenSigops));
txFrom.vout[4].nValue = 5000;
// vout[5/6] are non-standard because they exceed MAX_P2SH_SIGOPS
CScript sixteenSigops; sixteenSigops << OP_16 << OP_CHECKMULTISIG;
BOOST_CHECK(keystore.AddCScript(sixteenSigops));
- txFrom.vout[5].scriptPubKey = GetScriptForDestination(CScriptID(sixteenSigops));
+ txFrom.vout[5].scriptPubKey = GetScriptForDestination(ScriptHash(sixteenSigops));
txFrom.vout[5].nValue = 5000;
CScript twentySigops; twentySigops << OP_CHECKMULTISIG;
BOOST_CHECK(keystore.AddCScript(twentySigops));
- txFrom.vout[6].scriptPubKey = GetScriptForDestination(CScriptID(twentySigops));
+ txFrom.vout[6].scriptPubKey = GetScriptForDestination(ScriptHash(twentySigops));
txFrom.vout[6].nValue = 6000;
AddCoins(coins, CTransaction(txFrom), 0);
CMutableTransaction txTo;
txTo.vout.resize(1);
- txTo.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID());
+ txTo.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
txTo.vin.resize(5);
for (int i = 0; i < 5; i++)
@@ -351,7 +351,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
CMutableTransaction txToNonStd1;
txToNonStd1.vout.resize(1);
- txToNonStd1.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID());
+ txToNonStd1.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
txToNonStd1.vout[0].nValue = 1000;
txToNonStd1.vin.resize(1);
txToNonStd1.vin[0].prevout.n = 5;
@@ -363,7 +363,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
CMutableTransaction txToNonStd2;
txToNonStd2.vout.resize(1);
- txToNonStd2.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID());
+ txToNonStd2.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
txToNonStd2.vout[0].nValue = 1000;
txToNonStd2.vin.resize(1);
txToNonStd2.vin[0].prevout.n = 6;
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index bde82018c7..9f50083335 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -8,7 +8,7 @@
#include <script/script.h>
#include <script/script_error.h>
#include <script/standard.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -179,23 +179,23 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
s.clear();
s << ToByteVector(pubkey) << OP_CHECKSIG;
BOOST_CHECK(ExtractDestination(s, address));
- BOOST_CHECK(boost::get<CKeyID>(&address) &&
- *boost::get<CKeyID>(&address) == pubkey.GetID());
+ BOOST_CHECK(boost::get<PKHash>(&address) &&
+ *boost::get<PKHash>(&address) == PKHash(pubkey));
// 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());
+ BOOST_CHECK(boost::get<PKHash>(&address) &&
+ *boost::get<PKHash>(&address) == PKHash(pubkey));
// 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));
+ BOOST_CHECK(boost::get<ScriptHash>(&address) &&
+ *boost::get<ScriptHash>(&address) == ScriptHash(redeemScript));
// TX_MULTISIG
s.clear();
@@ -255,8 +255,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK_EQUAL(whichType, TX_PUBKEY);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) &&
- *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID());
+ BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
+ *boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
// TX_PUBKEYHASH
s.clear();
@@ -265,8 +265,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) &&
- *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID());
+ BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
+ *boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
// TX_SCRIPTHASH
CScript redeemScript(s); // initialize with leftover P2PKH script
@@ -276,8 +276,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(boost::get<CScriptID>(&addresses[0]) &&
- *boost::get<CScriptID>(&addresses[0]) == CScriptID(redeemScript));
+ BOOST_CHECK(boost::get<ScriptHash>(&addresses[0]) &&
+ *boost::get<ScriptHash>(&addresses[0]) == ScriptHash(redeemScript));
// TX_MULTISIG
s.clear();
@@ -289,10 +289,10 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK_EQUAL(whichType, TX_MULTISIG);
BOOST_CHECK_EQUAL(addresses.size(), 2U);
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());
+ BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
+ *boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
+ BOOST_CHECK(boost::get<PKHash>(&addresses[1]) &&
+ *boost::get<PKHash>(&addresses[1]) == PKHash(pubkeys[1]));
// TX_NULL_DATA
s.clear();
@@ -311,17 +311,17 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
CScript expected, result;
- // CKeyID
+ // PKHash
expected.clear();
expected << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
- result = GetScriptForDestination(pubkeys[0].GetID());
+ result = GetScriptForDestination(PKHash(pubkeys[0]));
BOOST_CHECK(result == expected);
// CScriptID
CScript redeemScript(result);
expected.clear();
expected << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
- result = GetScriptForDestination(CScriptID(redeemScript));
+ result = GetScriptForDestination(ScriptHash(redeemScript));
BOOST_CHECK(result == expected);
// CNoDestination
@@ -421,7 +421,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
// P2PKH compressed
{
CBasicKeyStore keystore;
- scriptPubKey = GetScriptForDestination(pubkeys[0].GetID());
+ scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0]));
// Keystore does not have key
result = IsMine(keystore, scriptPubKey);
@@ -436,7 +436,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
// P2PKH uncompressed
{
CBasicKeyStore keystore;
- scriptPubKey = GetScriptForDestination(uncompressedPubkey.GetID());
+ scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey));
// Keystore does not have key
result = IsMine(keystore, scriptPubKey);
@@ -452,8 +452,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
{
CBasicKeyStore keystore;
- CScript redeemScript = GetScriptForDestination(pubkeys[0].GetID());
- scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
+ CScript redeemScript = GetScriptForDestination(PKHash(pubkeys[0]));
+ scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
// Keystore does not have redeemScript or key
result = IsMine(keystore, scriptPubKey);
@@ -474,9 +474,9 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
{
CBasicKeyStore keystore;
- CScript redeemscript_inner = GetScriptForDestination(pubkeys[0].GetID());
- CScript redeemscript = GetScriptForDestination(CScriptID(redeemscript_inner));
- scriptPubKey = GetScriptForDestination(CScriptID(redeemscript));
+ CScript redeemscript_inner = GetScriptForDestination(PKHash(pubkeys[0]));
+ CScript redeemscript = GetScriptForDestination(ScriptHash(redeemscript_inner));
+ scriptPubKey = GetScriptForDestination(ScriptHash(redeemscript));
BOOST_CHECK(keystore.AddCScript(redeemscript));
BOOST_CHECK(keystore.AddCScript(redeemscript_inner));
@@ -490,8 +490,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
{
CBasicKeyStore keystore;
- CScript redeemscript = GetScriptForDestination(pubkeys[0].GetID());
- CScript witnessscript = GetScriptForDestination(CScriptID(redeemscript));
+ CScript redeemscript = GetScriptForDestination(PKHash(pubkeys[0]));
+ CScript witnessscript = GetScriptForDestination(ScriptHash(redeemscript));
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
BOOST_CHECK(keystore.AddCScript(witnessscript));
@@ -506,7 +506,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
{
CBasicKeyStore keystore;
- CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID()));
+ CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(PKHash(pubkeys[0])));
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
BOOST_CHECK(keystore.AddCScript(witnessscript));
@@ -520,7 +520,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
{
CBasicKeyStore keystore;
- CScript witnessscript_inner = GetScriptForDestination(pubkeys[0].GetID());
+ CScript witnessscript_inner = GetScriptForDestination(PKHash(pubkeys[0]));
CScript witnessscript = GetScriptForDestination(WitnessV0ScriptHash(witnessscript_inner));
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
@@ -537,7 +537,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
CBasicKeyStore keystore;
BOOST_CHECK(keystore.AddKey(keys[0]));
- scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID()));
+ scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(PKHash(pubkeys[0])));
// Keystore implicitly has key and P2SH redeemScript
BOOST_CHECK(keystore.AddCScript(scriptPubKey));
@@ -550,7 +550,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
CBasicKeyStore keystore;
BOOST_CHECK(keystore.AddKey(uncompressedKey));
- scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(uncompressedPubkey.GetID()));
+ scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(PKHash(uncompressedPubkey)));
// Keystore has key, but no P2SH redeemScript
result = IsMine(keystore, scriptPubKey);
@@ -598,7 +598,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
BOOST_CHECK(keystore.AddKey(keys[1]));
CScript redeemScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
- scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
+ scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
// Keystore has no redeemScript
result = IsMine(keystore, scriptPubKey);
@@ -664,7 +664,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
- scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
+ scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
// Keystore has no witnessScript, P2SH redeemScript, or keys
result = IsMine(keystore, scriptPubKey);
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 87c3e74df0..4798909e2f 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -12,8 +12,8 @@
#include <script/sign.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
-#include <rpc/server.h>
+#include <test/setup_common.h>
+#include <rpc/util.h>
#if defined(HAVE_CONSENSUS_LIB)
#include <script/bitcoinconsensus.h>
@@ -1211,7 +1211,7 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
BOOST_CHECK(keystore.AddKey(key));
}
- CMutableTransaction txFrom = BuildCreditingTransaction(GetScriptForDestination(keys[0].GetPubKey().GetID()));
+ CMutableTransaction txFrom = BuildCreditingTransaction(GetScriptForDestination(PKHash(keys[0].GetPubKey())));
CMutableTransaction txTo = BuildSpendingTransaction(CScript(), CScriptWitness(), CTransaction(txFrom));
CScript& scriptPubKey = txFrom.vout[0].scriptPubKey;
SignatureData scriptSig;
@@ -1237,7 +1237,7 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
// P2SH, single-signature case:
CScript pkSingle; pkSingle << ToByteVector(keys[0].GetPubKey()) << OP_CHECKSIG;
BOOST_CHECK(keystore.AddCScript(pkSingle));
- scriptPubKey = GetScriptForDestination(CScriptID(pkSingle));
+ scriptPubKey = GetScriptForDestination(ScriptHash(pkSingle));
BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 0, SIGHASH_ALL));
scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]);
combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty);
diff --git a/src/test/scriptnum_tests.cpp b/src/test/scriptnum_tests.cpp
index f9b407ce3e..e7916f5000 100644
--- a/src/test/scriptnum_tests.cpp
+++ b/src/test/scriptnum_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <test/scriptnum10.h>
#include <script/script.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.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 002f61f6a2..2fab309aa4 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <serialize.h>
#include <streams.h>
#include <hash.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <stdint.h>
diff --git a/src/test/test_bitcoin.cpp b/src/test/setup_common.cpp
index 0c3fb7c398..b11d090f67 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/setup_common.cpp
@@ -1,8 +1,8 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 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 <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <banman.h>
#include <chainparams.h>
@@ -18,7 +18,7 @@
#include <rpc/server.h>
#include <script/sigcache.h>
#include <streams.h>
-#include <ui_interface.h>
+#include <util/validation.h>
#include <validation.h>
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
@@ -32,7 +32,7 @@ std::ostream& operator<<(std::ostream& os, const uint256& num)
}
BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
- : m_path_root(fs::temp_directory_path() / "test_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
+ : m_path_root(fs::temp_directory_path() / "test_common_" PACKAGE_NAME / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{
SHA256AutoDetect();
ECC_Start();
@@ -41,11 +41,12 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
InitSignatureCache();
InitScriptExecutionCache();
fCheckBlockIndex = true;
- // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
- // TODO: fix the code to support SegWit blocks.
- gArgs.ForceSetArg("-vbparams", strprintf("segwit:0:%d", (int64_t)Consensus::BIP9Deployment::NO_TIMEOUT));
SelectParams(chainName);
- noui_connect();
+ static bool noui_connected = false;
+ if (!noui_connected) {
+ noui_connect();
+ noui_connected = true;
+ }
}
BasicTestingSetup::~BasicTestingSetup()
@@ -66,36 +67,36 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
{
SetDataDir("tempdir");
const CChainParams& chainparams = Params();
- // Ideally we'd move all the RPC tests to the functional testing framework
- // instead of unit tests, but for now we need these here.
-
- RegisterAllCoreRPCCommands(tableRPC);
- ClearDatadirCache();
-
- // We have to run a scheduler thread to prevent ActivateBestChain
- // from blocking due to queue overrun.
- threadGroup.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
- GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
-
- mempool.setSanityCheck(1.0);
- 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.");
- }
- {
- CValidationState state;
- if (!ActivateBestChain(state, chainparams)) {
- throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state)));
- }
- }
- nScriptCheckThreads = 3;
- for (int i=0; i < nScriptCheckThreads-1; i++)
- threadGroup.create_thread(&ThreadScriptCheck);
-
- g_banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- g_connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
+ // Ideally we'd move all the RPC tests to the functional testing framework
+ // instead of unit tests, but for now we need these here.
+
+ RegisterAllCoreRPCCommands(tableRPC);
+ ClearDatadirCache();
+
+ // We have to run a scheduler thread to prevent ActivateBestChain
+ // from blocking due to queue overrun.
+ threadGroup.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
+ GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
+
+ mempool.setSanityCheck(1.0);
+ 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.");
+ }
+
+ CValidationState state;
+ if (!ActivateBestChain(state, chainparams)) {
+ throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state)));
+ }
+
+ nScriptCheckThreads = 3;
+ for (int i = 0; i < nScriptCheckThreads - 1; i++)
+ threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
+
+ g_banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
+ g_connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
}
TestingSetup::~TestingSetup()
@@ -114,6 +115,11 @@ TestingSetup::~TestingSetup()
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.
+ gArgs.ForceSetArg("-vbparams", strprintf("segwit:0:%d", (int64_t)Consensus::BIP9Deployment::NO_TIMEOUT));
+ SelectParams(CBaseChainParams::REGTEST);
+
// Generate a 100-block chain:
coinbaseKey.MakeNewKey(true);
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
@@ -144,7 +150,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
{
LOCK(cs_main);
unsigned int extraNonce = 0;
- IncrementExtraNonce(&block, chainActive.Tip(), extraNonce);
+ IncrementExtraNonce(&block, ::ChainActive().Tip(), extraNonce);
}
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
diff --git a/src/test/test_bitcoin.h b/src/test/setup_common.h
index 71520232ac..893eca216d 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/setup_common.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_TEST_TEST_BITCOIN_H
-#define BITCOIN_TEST_TEST_BITCOIN_H
+#ifndef BITCOIN_TEST_SETUP_COMMON_H
+#define BITCOIN_TEST_SETUP_COMMON_H
#include <chainparamsbase.h>
#include <fs.h>
@@ -35,6 +35,11 @@ std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::os
*/
extern FastRandomContext g_insecure_rand_ctx;
+/**
+ * Flag to make GetRand in random.h return the same number
+ */
+extern bool g_mock_deterministic_tests;
+
static inline void SeedInsecureRand(bool deterministic = false)
{
g_insecure_rand_ctx = FastRandomContext(deterministic);
@@ -66,10 +71,6 @@ private:
/** Testing setup that configures a complete environment.
* Included are data directory, coins database, script check threads setup.
*/
-class CConnman;
-class CNode;
-
-class PeerLogicValidation;
struct TestingSetup : public BasicTestingSetup {
boost::thread_group threadGroup;
CScheduler scheduler;
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index 04d5462acb..15f8db899b 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -1,8 +1,8 @@
-// Copyright (c) 2013-2018 The Bitcoin Core developers
+// Copyright (c) 2013-2019 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 <consensus/tx_verify.h>
+#include <consensus/tx_check.h>
#include <consensus/validation.h>
#include <test/data/sighash.json.h>
#include <hash.h>
@@ -10,7 +10,7 @@
#include <script/script.h>
#include <serialize.h>
#include <streams.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <version.h>
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index 6a0349cd4e..5c12ec13d2 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,7 +9,7 @@
#include <script/script.h>
#include <script/standard.h>
#include <uint256.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
@@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 3U);
BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 21U);
- CScript p2sh = GetScriptForDestination(CScriptID(s1));
+ CScript p2sh = GetScriptForDestination(ScriptHash(s1));
CScript scriptSig;
scriptSig << OP_0 << Serialize(s1);
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3U);
@@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
BOOST_CHECK_EQUAL(s2.GetSigOpCount(true), 3U);
BOOST_CHECK_EQUAL(s2.GetSigOpCount(false), 20U);
- p2sh = GetScriptForDestination(CScriptID(s2));
+ p2sh = GetScriptForDestination(ScriptHash(s2));
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(true), 0U);
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(false), 0U);
CScript scriptSig2;
@@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
// Multisig nested in P2SH
{
CScript redeemScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
- CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
+ CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
CScript scriptSig = CScript() << OP_0 << OP_0 << ToByteVector(redeemScript);
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, CScriptWitness());
@@ -185,7 +185,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
{
CScript p2pk = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
CScript scriptSig = GetScriptForWitness(p2pk);
- CScript scriptPubKey = GetScriptForDestination(CScriptID(scriptSig));
+ CScript scriptPubKey = GetScriptForDestination(ScriptHash(scriptSig));
scriptSig = CScript() << ToByteVector(scriptSig);
CScriptWitness scriptWitness;
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
@@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
{
CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
CScript redeemScript = GetScriptForWitness(witnessScript);
- CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
+ CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
CScript scriptSig = CScript() << ToByteVector(redeemScript);
CScriptWitness scriptWitness;
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index 5c46976ace..3d39dfdb75 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Copyright (c) 2014-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
#include <util/system.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
@@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_test)
// Pick a random element in vBlocksMain.
int r = InsecureRandRange(vBlocksMain.size());
int64_t test_time = vBlocksMain[r].nTime;
- CBlockIndex *ret = chain.FindEarliestAtLeast(test_time);
+ CBlockIndex* ret = chain.FindEarliestAtLeast(test_time, 0);
BOOST_CHECK(ret->nTimeMax >= test_time);
BOOST_CHECK((ret->pprev==nullptr) || ret->pprev->nTimeMax < test_time);
BOOST_CHECK(vBlocksMain[r].GetAncestor(ret->nHeight) == ret);
@@ -158,22 +158,34 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_edge_test)
CChain chain;
chain.SetTip(&blocks.back());
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(50)->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(100)->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(150)->nHeight, 3);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(200)->nHeight, 3);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(250)->nHeight, 6);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(300)->nHeight, 6);
- BOOST_CHECK(!chain.FindEarliestAtLeast(350));
-
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0)->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-1)->nHeight, 0);
-
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::min())->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-int64_t(std::numeric_limits<unsigned int>::max()) - 1)->nHeight, 0);
- BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::max()));
- BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::max()));
- BOOST_CHECK(!chain.FindEarliestAtLeast(int64_t(std::numeric_limits<unsigned int>::max()) + 1));
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(50, 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(100, 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(150, 0)->nHeight, 3);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(200, 0)->nHeight, 3);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(250, 0)->nHeight, 6);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(300, 0)->nHeight, 6);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(350, 0));
+
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-1, 0)->nHeight, 0);
+
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::min(), 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-int64_t(std::numeric_limits<unsigned int>::max()) - 1, 0)->nHeight, 0);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::max(), 0));
+ BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::max(), 0));
+ BOOST_CHECK(!chain.FindEarliestAtLeast(int64_t(std::numeric_limits<unsigned int>::max()) + 1, 0));
+
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, -1)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 3)->nHeight, 3);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 8)->nHeight, 8);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(0, 9));
+
+ CBlockIndex* ret1 = chain.FindEarliestAtLeast(100, 2);
+ BOOST_CHECK(ret1->nTimeMax >= 100 && ret1->nHeight == 2);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(300, 9));
+ CBlockIndex* ret2 = chain.FindEarliestAtLeast(200, 4);
+ BOOST_CHECK(ret2->nTimeMax >= 200 && ret2->nHeight == 4);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index a1940eb80e..4e37199c63 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 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 <streams.h>
#include <support/allocators/zeroafterfree.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp
index df0380546e..c1399d2dbe 100644
--- a/src/test/sync_tests.cpp
+++ b/src/test/sync_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2012-2017 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <sync.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/test_bitcoin_main.cpp b/src/test/test_bitcoin_main.cpp
deleted file mode 100644
index 46b63b93b4..0000000000
--- a/src/test/test_bitcoin_main.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#define BOOST_TEST_MODULE Bitcoin Test Suite
-
-#include <banman.h>
-#include <net.h>
-
-#include <memory>
-
-#include <boost/test/unit_test.hpp>
-
-std::unique_ptr<CConnman> g_connman;
-std::unique_ptr<BanMan> g_banman;
-
-[[noreturn]] void Shutdown(void* parg)
-{
- std::exit(EXIT_SUCCESS);
-}
-
-[[noreturn]] void StartShutdown()
-{
- std::exit(EXIT_SUCCESS);
-}
-
-bool ShutdownRequested()
-{
- return false;
-}
diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp
index 474a67497f..b4c0e6a0f4 100644
--- a/src/test/timedata_tests.cpp
+++ b/src/test/timedata_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 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 <timedata.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/torcontrol_tests.cpp b/src/test/torcontrol_tests.cpp
index c7ceb2f1e9..d846062d9b 100644
--- a/src/test/torcontrol_tests.cpp
+++ b/src/test/torcontrol_tests.cpp
@@ -2,8 +2,7 @@
// 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.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -20,7 +19,6 @@ BOOST_FIXTURE_TEST_SUITE(torcontrol_tests, BasicTestingSetup)
static void CheckSplitTorReplyLine(std::string input, std::string command, std::string args)
{
- BOOST_TEST_MESSAGE(std::string("CheckSplitTorReplyLine(") + input + ")");
auto ret = SplitTorReplyLine(input);
BOOST_CHECK_EQUAL(ret.first, command);
BOOST_CHECK_EQUAL(ret.second, args);
@@ -61,7 +59,6 @@ BOOST_AUTO_TEST_CASE(util_SplitTorReplyLine)
static void CheckParseTorReplyMapping(std::string input, std::map<std::string,std::string> expected)
{
- BOOST_TEST_MESSAGE(std::string("CheckParseTorReplyMapping(") + input + ")");
auto ret = ParseTorReplyMapping(input);
BOOST_CHECK_EQUAL(ret.size(), expected.size());
auto r_it = ret.begin();
@@ -173,7 +170,6 @@ BOOST_AUTO_TEST_CASE(util_ParseTorReplyMapping)
// Special handling for null case
// (needed because string comparison reads the null as end-of-string)
- BOOST_TEST_MESSAGE(std::string("CheckParseTorReplyMapping(Null=\"\\0\")"));
auto ret = ParseTorReplyMapping("Null=\"\\0\"");
BOOST_CHECK_EQUAL(ret.size(), 1U);
auto r_it = ret.begin();
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 39cff3f463..f5ff18c055 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -1,20 +1,21 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 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 <test/data/tx_invalid.json.h>
#include <test/data/tx_valid.json.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <clientversion.h>
#include <checkqueue.h>
-#include <consensus/tx_verify.h>
+#include <consensus/tx_check.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <key.h>
#include <keystore.h>
#include <validation.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/script_error.h>
@@ -310,9 +311,9 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
dummyTransactions[1].vout.resize(2);
dummyTransactions[1].vout[0].nValue = 21*CENT;
- dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
+ dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[2].GetPubKey()));
dummyTransactions[1].vout[1].nValue = 22*CENT;
- dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
+ dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(PKHash(key[3].GetPubKey()));
AddCoins(coinsRet, CTransaction(dummyTransactions[1]), 0);
return dummyTransactions;
@@ -561,8 +562,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// P2SH pay-to-compressed-pubkey.
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey1)), output1, input1);
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey2)), output2, input2);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey1)), output1, input1);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey2)), output2, input2);
ReplaceRedeemScript(input2.vin[0].scriptSig, scriptPubkey1);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
@@ -586,8 +587,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// P2SH witness pay-to-compressed-pubkey (v0).
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1))), output1, input1);
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey2))), output2, input2);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey1))), output1, input1);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey2))), output2, input2);
ReplaceRedeemScript(input2.vin[0].scriptSig, GetScriptForWitness(scriptPubkey1));
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
@@ -611,8 +612,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// P2SH pay-to-uncompressed-pubkey.
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey1L)), output1, input1);
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey2L)), output2, input2);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey1L)), output1, input1);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey2L)), output2, input2);
ReplaceRedeemScript(input2.vin[0].scriptSig, scriptPubkey1L);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
@@ -628,8 +629,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey2L), output2, input2, false);
// Signing disabled for P2SH witness pay-to-uncompressed-pubkey (v1).
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1L))), output1, input1, false);
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey2L))), output2, input2, false);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey1L))), output1, input1, false);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey2L))), output2, input2, false);
// Normal 2-of-2 multisig
CreateCreditAndSpend(keystore, scriptMulti, output1, input1, false);
@@ -641,10 +642,10 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
// P2SH 2-of-2 multisig
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptMulti)), output1, input1, false);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptMulti)), output1, input1, false);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, false);
- CreateCreditAndSpend(keystore2, GetScriptForDestination(CScriptID(scriptMulti)), output2, input2, false);
+ CreateCreditAndSpend(keystore2, GetScriptForDestination(ScriptHash(scriptMulti)), output2, input2, false);
CheckWithFlag(output2, input2, 0, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false);
BOOST_CHECK(*output1 == *output2);
@@ -665,10 +666,10 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
// P2SH witness 2-of-2 multisig
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptMulti))), output1, input1, false);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptMulti))), output1, input1, false);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
- CreateCreditAndSpend(keystore2, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptMulti))), output2, input2, false);
+ CreateCreditAndSpend(keystore2, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptMulti))), output2, input2, false);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
BOOST_CHECK(*output1 == *output2);
@@ -694,7 +695,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].nValue = 90*CENT;
CKey key;
key.MakeNewKey(true);
- t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
+ t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
std::string reason;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index 0301901bf0..d794d09d30 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -1,14 +1,13 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 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 <chainparams.h>
#include <index/txindex.h>
#include <script/standard.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <util/system.h>
#include <util/time.h>
-#include <validation.h>
#include <boost/test/unit_test.hpp>
@@ -56,7 +55,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
// Check that new transactions in new blocks make it into the index.
for (int i = 0; i < 10; i++) {
- CScript coinbase_script_pub_key = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
+ CScript coinbase_script_pub_key = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));
std::vector<CMutableTransaction> no_txns;
const CBlock& block = CreateAndProcessBlock(no_txns, coinbase_script_pub_key);
const CTransaction& txn = *block.vtx[0];
@@ -69,7 +68,13 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
}
}
- txindex.Stop(); // Stop thread before calling destructor
+ // shutdown sequence (c.f. Shutdown() in init.cpp)
+ txindex.Stop();
+
+ threadGroup.interrupt_all();
+ threadGroup.join_all();
+
+ // Rest of shutdown sequence and destructors happen in ~TestingSetup()
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index c2777cd6d1..26ae7be202 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -8,7 +8,7 @@
#include <consensus/validation.h>
#include <primitives/transaction.h>
#include <script/script.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -52,10 +52,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
// 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_CHECK(state.GetReason() == ValidationInvalidReason::CONSENSUS);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index c467f27836..352ce0295b 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -11,11 +11,9 @@
#include <random.h>
#include <script/standard.h>
#include <script/sign.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <util/time.h>
-#include <core_io.h>
#include <keystore.h>
-#include <policy/policy.h>
#include <boost/test/unit_test.hpp>
@@ -66,18 +64,27 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
// Test 1: block with both of those transactions should be rejected.
block = CreateAndProcessBlock(spends, scriptPubKey);
- BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
+ {
+ LOCK(cs_main);
+ BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ }
// Test 2: ... and should be rejected if spend1 is in the memory pool
BOOST_CHECK(ToMemPool(spends[0]));
block = CreateAndProcessBlock(spends, scriptPubKey);
- BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
+ {
+ LOCK(cs_main);
+ BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ }
mempool.clear();
// Test 3: ... and should be rejected if spend2 is in the memory pool
BOOST_CHECK(ToMemPool(spends[1]));
block = CreateAndProcessBlock(spends, scriptPubKey);
- BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
+ {
+ LOCK(cs_main);
+ BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ }
mempool.clear();
// Final sanity test: first spend in mempool, second in block, that's OK:
@@ -85,7 +92,10 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
oneSpend.push_back(spends[0]);
BOOST_CHECK(ToMemPool(spends[1]));
block = CreateAndProcessBlock(oneSpend, scriptPubKey);
- BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash());
+ {
+ LOCK(cs_main);
+ BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
+ }
// spends[1] should have been removed from the mempool when the
// block with spends[0] is accepted:
BOOST_CHECK_EQUAL(mempool.size(), 0U);
@@ -102,7 +112,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.
-static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache)
+static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
PrecomputedTransactionData txdata(tx);
// If we add many more flags, this loop can get too expensive, but we can
@@ -151,8 +161,8 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
}
CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
- CScript p2sh_scriptPubKey = GetScriptForDestination(CScriptID(p2pk_scriptPubKey));
- CScript p2pkh_scriptPubKey = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
+ CScript p2sh_scriptPubKey = GetScriptForDestination(ScriptHash(p2pk_scriptPubKey));
+ CScript p2pkh_scriptPubKey = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));
CScript p2wpkh_scriptPubKey = GetScriptForWitness(p2pkh_scriptPubKey);
CBasicKeyStore keystore;
@@ -219,10 +229,9 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CBlock block;
block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey);
- BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash());
- BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash());
-
LOCK(cs_main);
+ BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
+ BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash());
// Test P2SH: construct a transaction that is valid without P2SH, and
// then test validity with P2SH.
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index cca5e20296..c1749fb856 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 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 <test/setup_common.h>
#include <boost/test/unit_test.hpp>
#include <stdint.h>
diff --git a/src/test/util.cpp b/src/test/util.cpp
new file mode 100644
index 0000000000..bc09d00b7a
--- /dev/null
+++ b/src/test/util.cpp
@@ -0,0 +1,89 @@
+// Copyright (c) 2019 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 <test/util.h>
+
+#include <chainparams.h>
+#include <consensus/merkle.h>
+#include <key_io.h>
+#include <miner.h>
+#include <outputtype.h>
+#include <pow.h>
+#include <script/standard.h>
+#include <validation.h>
+#include <validationinterface.h>
+#ifdef ENABLE_WALLET
+#include <wallet/wallet.h>
+#endif
+
+#include <boost/thread.hpp>
+
+const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj";
+
+#ifdef ENABLE_WALLET
+std::string getnewaddress(CWallet& w)
+{
+ constexpr auto output_type = OutputType::BECH32;
+
+ CPubKey new_key;
+ if (!w.GetKeyFromPool(new_key)) assert(false);
+
+ w.LearnRelatedScripts(new_key, output_type);
+ const auto dest = GetDestinationForKey(new_key, output_type);
+
+ w.SetAddressBook(dest, /* label */ "", "receive");
+
+ return EncodeDestination(dest);
+}
+
+void importaddress(CWallet& wallet, const std::string& address)
+{
+ LOCK(wallet.cs_wallet);
+ const auto dest = DecodeDestination(address);
+ assert(IsValidDestination(dest));
+ const auto script = GetScriptForDestination(dest);
+ wallet.MarkDirty();
+ assert(!wallet.HaveWatchOnly(script));
+ if (!wallet.AddWatchOnly(script, 0 /* nCreateTime */)) assert(false);
+ wallet.SetAddressBook(dest, /* label */ "", "receive");
+}
+#endif // ENABLE_WALLET
+
+CTxIn generatetoaddress(const std::string& address)
+{
+ const auto dest = DecodeDestination(address);
+ assert(IsValidDestination(dest));
+ const auto coinbase_script = GetScriptForDestination(dest);
+
+ return MineBlock(coinbase_script);
+}
+
+CTxIn MineBlock(const CScript& coinbase_scriptPubKey)
+{
+ auto block = PrepareBlock(coinbase_scriptPubKey);
+
+ while (!CheckProofOfWork(block->GetHash(), block->nBits, Params().GetConsensus())) {
+ ++block->nNonce;
+ assert(block->nNonce);
+ }
+
+ bool processed{ProcessNewBlock(Params(), block, true, nullptr)};
+ assert(processed);
+
+ return CTxIn{block->vtx[0]->GetHash(), 0};
+}
+
+std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey)
+{
+ auto block = std::make_shared<CBlock>(
+ BlockAssembler{Params()}
+ .CreateNewBlock(coinbase_scriptPubKey)
+ ->block);
+
+ LOCK(cs_main);
+ block->nTime = ::ChainActive().Tip()->GetMedianTimePast() + 1;
+ block->hashMerkleRoot = BlockMerkleRoot(*block);
+
+ return block;
+}
diff --git a/src/test/util.h b/src/test/util.h
new file mode 100644
index 0000000000..f90cb0d623
--- /dev/null
+++ b/src/test/util.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_UTIL_H
+#define BITCOIN_TEST_UTIL_H
+
+#include <memory>
+#include <string>
+
+class CBlock;
+class CScript;
+class CTxIn;
+class CWallet;
+
+// Constants //
+
+extern const std::string ADDRESS_BCRT1_UNSPENDABLE;
+
+// Lower-level utils //
+
+/** Returns the generated coin */
+CTxIn MineBlock(const CScript& coinbase_scriptPubKey);
+/** Prepare a block to be mined */
+std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey);
+
+
+// RPC-like //
+
+/** Import the address to the wallet */
+void importaddress(CWallet& wallet, const std::string& address);
+/** Returns a new address from the wallet */
+std::string getnewaddress(CWallet& w);
+/** Returns the generated coin */
+CTxIn generatetoaddress(const std::string& address);
+
+/**
+ * Increment a string. Useful to enumerate all fixed length strings with
+ * characters in [min_char, max_char].
+ */
+template <typename CharType, size_t StringLength>
+bool NextString(CharType (&string)[StringLength], CharType min_char, CharType max_char)
+{
+ for (CharType& elem : string) {
+ bool has_next = elem != max_char;
+ elem = elem < min_char || elem >= max_char ? min_char : CharType(elem + 1);
+ if (has_next) return true;
+ }
+ return false;
+}
+
+/**
+ * Iterate over string values and call function for each string without
+ * successive duplicate characters.
+ */
+template <typename CharType, size_t StringLength, typename Fn>
+void ForEachNoDup(CharType (&string)[StringLength], CharType min_char, CharType max_char, Fn&& fn) {
+ for (bool has_next = true; has_next; has_next = NextString(string, min_char, max_char)) {
+ int prev = -1;
+ bool skip_string = false;
+ for (CharType c : string) {
+ if (c == prev) skip_string = true;
+ if (skip_string || c < min_char || c > max_char) break;
+ prev = c;
+ }
+ if (!skip_string) fn();
+ }
+}
+
+#endif // BITCOIN_TEST_UTIL_H
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 71b6ec7425..8fee66d6c3 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,9 +7,10 @@
#include <clientversion.h>
#include <primitives/transaction.h>
#include <sync.h>
+#include <test/util.h>
#include <util/strencodings.h>
#include <util/moneystr.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <stdint.h>
#include <vector>
@@ -36,8 +37,10 @@ BOOST_AUTO_TEST_CASE(util_criticalsection)
do {
TRY_LOCK(cs, lockTest);
- if (lockTest)
+ if (lockTest) {
+ BOOST_CHECK(true); // Needed to suppress "Test case [...] did not check any assertions"
break;
+ }
BOOST_ERROR("break was swallowed!");
} while(0);
@@ -78,80 +81,40 @@ BOOST_AUTO_TEST_CASE(util_HexStr)
"04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f");
BOOST_CHECK_EQUAL(
- HexStr(ParseHex_expected, ParseHex_expected + 5, true),
- "04 67 8a fd b0");
-
- BOOST_CHECK_EQUAL(
HexStr(ParseHex_expected + sizeof(ParseHex_expected),
ParseHex_expected + sizeof(ParseHex_expected)),
"");
BOOST_CHECK_EQUAL(
- HexStr(ParseHex_expected + sizeof(ParseHex_expected),
- ParseHex_expected + sizeof(ParseHex_expected), true),
- "");
-
- BOOST_CHECK_EQUAL(
HexStr(ParseHex_expected, ParseHex_expected),
"");
- BOOST_CHECK_EQUAL(
- HexStr(ParseHex_expected, ParseHex_expected, true),
- "");
-
std::vector<unsigned char> ParseHex_vec(ParseHex_expected, ParseHex_expected + 5);
BOOST_CHECK_EQUAL(
- HexStr(ParseHex_vec, true),
- "04 67 8a fd b0");
-
- BOOST_CHECK_EQUAL(
HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend()),
"b0fd8a6704"
);
BOOST_CHECK_EQUAL(
- HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend(), true),
- "b0 fd 8a 67 04"
- );
-
- BOOST_CHECK_EQUAL(
HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected),
std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
""
);
BOOST_CHECK_EQUAL(
- HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected),
- std::reverse_iterator<const uint8_t *>(ParseHex_expected), true),
- ""
- );
-
- BOOST_CHECK_EQUAL(
HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1),
std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
"04"
);
BOOST_CHECK_EQUAL(
- HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1),
- std::reverse_iterator<const uint8_t *>(ParseHex_expected), true),
- "04"
- );
-
- BOOST_CHECK_EQUAL(
HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5),
std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
"b0fd8a6704"
);
BOOST_CHECK_EQUAL(
- HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5),
- std::reverse_iterator<const uint8_t *>(ParseHex_expected), true),
- "b0 fd 8a 67 04"
- );
-
- BOOST_CHECK_EQUAL(
HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 65),
std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
"5f1df16b2b704c8a578d0bbaf74d385cde12c11ee50455f3c438ef4c3fbcf649b6de611feae06279a60939e028a8d65c10b73071a6f16719274855feb0fd8a6704"
@@ -169,11 +132,6 @@ BOOST_AUTO_TEST_CASE(util_FormatISO8601Date)
BOOST_CHECK_EQUAL(FormatISO8601Date(1317425777), "2011-09-30");
}
-BOOST_AUTO_TEST_CASE(util_FormatISO8601Time)
-{
- BOOST_CHECK_EQUAL(FormatISO8601Time(1317425777), "23:36:17Z");
-}
-
struct TestArgsManager : public ArgsManager
{
TestArgsManager() { m_network_only_args.clear(); }
@@ -185,9 +143,10 @@ struct TestArgsManager : public ArgsManager
{
LOCK(cs_args);
m_config_args.clear();
+ m_config_sections.clear();
}
std::string error;
- BOOST_REQUIRE(ReadConfigStream(streamConfig, error));
+ BOOST_REQUIRE(ReadConfigStream(streamConfig, "", error));
}
void SetNetworkOnlyArg(const std::string arg)
{
@@ -200,6 +159,9 @@ struct TestArgsManager : public ArgsManager
AddArg(args[i], "", false, OptionsCategory::OPTIONS);
}
}
+ using ArgsManager::ReadConfigStream;
+ using ArgsManager::cs_args;
+ using ArgsManager::m_network;
};
BOOST_AUTO_TEST_CASE(util_ParseParameters)
@@ -617,6 +579,306 @@ BOOST_AUTO_TEST_CASE(util_GetChainName)
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
}
+// Test different ways settings can be merged, and verify results. This test can
+// be used to confirm that updates to settings code don't change behavior
+// unintentionally.
+//
+// The test covers:
+//
+// - Combining different setting actions. Possible actions are: configuring a
+// setting, negating a setting (adding "-no" prefix), and configuring/negating
+// settings in a network section (adding "main." or "test." prefixes).
+//
+// - Combining settings from command line arguments and a config file.
+//
+// - Combining SoftSet and ForceSet calls.
+//
+// - Testing "main" and "test" network values to make sure settings from network
+// sections are applied and to check for mainnet-specific behaviors like
+// inheriting settings from the default section.
+//
+// - Testing network-specific settings like "-wallet", that may be ignored
+// outside a network section, and non-network specific settings like "-server"
+// that aren't sensitive to the network.
+//
+struct ArgsMergeTestingSetup : public BasicTestingSetup {
+ //! Max number of actions to sequence together. Can decrease this when
+ //! debugging to make test results easier to understand.
+ static constexpr int MAX_ACTIONS = 3;
+
+ enum Action { NONE, SET, NEGATE, SECTION_SET, SECTION_NEGATE };
+ using ActionList = Action[MAX_ACTIONS];
+
+ //! Enumerate all possible test configurations.
+ template <typename Fn>
+ void ForEachMergeSetup(Fn&& fn)
+ {
+ ActionList arg_actions = {};
+ ForEachNoDup(arg_actions, SET, SECTION_NEGATE, [&] {
+ ActionList conf_actions = {};
+ ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&] {
+ for (bool soft_set : {false, true}) {
+ for (bool force_set : {false, true}) {
+ for (const std::string& section : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) {
+ for (const std::string& network : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) {
+ for (bool net_specific : {false, true}) {
+ fn(arg_actions, conf_actions, soft_set, force_set, section, network, net_specific);
+ }
+ }
+ }
+ }
+ }
+ });
+ });
+ }
+
+ //! Translate actions into a list of <key>=<value> setting strings.
+ std::vector<std::string> GetValues(const ActionList& actions,
+ const std::string& section,
+ const std::string& name,
+ const std::string& value_prefix)
+ {
+ std::vector<std::string> values;
+ int suffix = 0;
+ for (Action action : actions) {
+ if (action == NONE) break;
+ std::string prefix;
+ if (action == SECTION_SET || action == SECTION_NEGATE) prefix = section + ".";
+ if (action == SET || action == SECTION_SET) {
+ for (int i = 0; i < 2; ++i) {
+ values.push_back(prefix + name + "=" + value_prefix + std::to_string(++suffix));
+ }
+ }
+ if (action == NEGATE || action == SECTION_NEGATE) {
+ values.push_back(prefix + "no" + name + "=1");
+ }
+ }
+ return values;
+ }
+};
+
+// Regression test covering different ways config settings can be merged. The
+// test parses and merges settings, representing the results as strings that get
+// compared against an expected hash. To debug, the result strings can be dumped
+// to a file (see comments below).
+BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup)
+{
+ CHash256 out_sha;
+ FILE* out_file = nullptr;
+ if (const char* out_path = getenv("ARGS_MERGE_TEST_OUT")) {
+ out_file = fsbridge::fopen(out_path, "w");
+ if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed");
+ }
+
+ ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions, bool soft_set, bool force_set,
+ const std::string& section, const std::string& network, bool net_specific) {
+ TestArgsManager parser;
+ LOCK(parser.cs_args);
+
+ std::string desc = "net=";
+ desc += network;
+ parser.m_network = network;
+
+ const std::string& name = net_specific ? "wallet" : "server";
+ const std::string key = "-" + name;
+ parser.AddArg(key, name, false, OptionsCategory::OPTIONS);
+ if (net_specific) parser.SetNetworkOnlyArg(key);
+
+ auto args = GetValues(arg_actions, section, name, "a");
+ std::vector<const char*> argv = {"ignored"};
+ for (auto& arg : args) {
+ arg.insert(0, "-");
+ desc += " ";
+ desc += arg;
+ argv.push_back(arg.c_str());
+ }
+ std::string error;
+ BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error));
+ BOOST_CHECK_EQUAL(error, "");
+
+ std::string conf;
+ for (auto& conf_val : GetValues(conf_actions, section, name, "c")) {
+ desc += " ";
+ desc += conf_val;
+ conf += conf_val;
+ conf += "\n";
+ }
+ std::istringstream conf_stream(conf);
+ BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error));
+ BOOST_CHECK_EQUAL(error, "");
+
+ if (soft_set) {
+ desc += " soft";
+ parser.SoftSetArg(key, "soft1");
+ parser.SoftSetArg(key, "soft2");
+ }
+
+ if (force_set) {
+ desc += " force";
+ parser.ForceSetArg(key, "force1");
+ parser.ForceSetArg(key, "force2");
+ }
+
+ desc += " || ";
+
+ if (!parser.IsArgSet(key)) {
+ desc += "unset";
+ BOOST_CHECK(!parser.IsArgNegated(key));
+ BOOST_CHECK_EQUAL(parser.GetArg(key, "default"), "default");
+ BOOST_CHECK(parser.GetArgs(key).empty());
+ } else if (parser.IsArgNegated(key)) {
+ desc += "negated";
+ BOOST_CHECK_EQUAL(parser.GetArg(key, "default"), "0");
+ BOOST_CHECK(parser.GetArgs(key).empty());
+ } else {
+ desc += parser.GetArg(key, "default");
+ desc += " |";
+ for (const auto& arg : parser.GetArgs(key)) {
+ desc += " ";
+ desc += arg;
+ }
+ }
+
+ std::set<std::string> ignored = parser.GetUnsuitableSectionOnlyArgs();
+ if (!ignored.empty()) {
+ desc += " | ignored";
+ for (const auto& arg : ignored) {
+ desc += " ";
+ desc += arg;
+ }
+ }
+
+ desc += "\n";
+
+ out_sha.Write((const unsigned char*)desc.data(), desc.size());
+ if (out_file) {
+ BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) == desc.size());
+ }
+ });
+
+ if (out_file) {
+ if (fclose(out_file)) throw std::system_error(errno, std::generic_category(), "fclose failed");
+ out_file = nullptr;
+ }
+
+ unsigned char out_sha_bytes[CSHA256::OUTPUT_SIZE];
+ out_sha.Finalize(out_sha_bytes);
+ std::string out_sha_hex = HexStr(std::begin(out_sha_bytes), std::end(out_sha_bytes));
+
+ // If check below fails, should manually dump the results with:
+ //
+ // ARGS_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_ArgsMerge
+ //
+ // And verify diff against previous results to make sure the changes are expected.
+ //
+ // Results file is formatted like:
+ //
+ // <input> || <IsArgSet/IsArgNegated/GetArg output> | <GetArgs output> | <GetUnsuitable output>
+ BOOST_CHECK_EQUAL(out_sha_hex, "b835eef5977d69114eb039a976201f8c7121f34fe2b7ea2b73cafb516e5c9dc8");
+}
+
+// Similar test as above, but for ArgsManager::GetChainName function.
+struct ChainMergeTestingSetup : public BasicTestingSetup {
+ static constexpr int MAX_ACTIONS = 2;
+
+ enum Action { NONE, ENABLE_TEST, DISABLE_TEST, NEGATE_TEST, ENABLE_REG, DISABLE_REG, NEGATE_REG };
+ using ActionList = Action[MAX_ACTIONS];
+
+ //! Enumerate all possible test configurations.
+ template <typename Fn>
+ void ForEachMergeSetup(Fn&& fn)
+ {
+ ActionList arg_actions = {};
+ ForEachNoDup(arg_actions, ENABLE_TEST, NEGATE_REG, [&] {
+ ActionList conf_actions = {};
+ ForEachNoDup(conf_actions, ENABLE_TEST, NEGATE_REG, [&] { fn(arg_actions, conf_actions); });
+ });
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
+{
+ CHash256 out_sha;
+ FILE* out_file = nullptr;
+ if (const char* out_path = getenv("CHAIN_MERGE_TEST_OUT")) {
+ out_file = fsbridge::fopen(out_path, "w");
+ if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed");
+ }
+
+ ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions) {
+ TestArgsManager parser;
+ LOCK(parser.cs_args);
+ parser.AddArg("-regtest", "regtest", false, OptionsCategory::OPTIONS);
+ parser.AddArg("-testnet", "testnet", false, OptionsCategory::OPTIONS);
+
+ auto arg = [](Action action) { return action == ENABLE_TEST ? "-testnet=1" :
+ action == DISABLE_TEST ? "-testnet=0" :
+ action == NEGATE_TEST ? "-notestnet=1" :
+ action == ENABLE_REG ? "-regtest=1" :
+ action == DISABLE_REG ? "-regtest=0" :
+ action == NEGATE_REG ? "-noregtest=1" : nullptr; };
+
+ std::string desc;
+ std::vector<const char*> argv = {"ignored"};
+ for (Action action : arg_actions) {
+ const char* argstr = arg(action);
+ if (!argstr) break;
+ argv.push_back(argstr);
+ desc += " ";
+ desc += argv.back();
+ }
+ std::string error;
+ BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error));
+ BOOST_CHECK_EQUAL(error, "");
+
+ std::string conf;
+ for (Action action : conf_actions) {
+ const char* argstr = arg(action);
+ if (!argstr) break;
+ desc += " ";
+ desc += argstr + 1;
+ conf += argstr + 1;
+ }
+ std::istringstream conf_stream(conf);
+ BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error));
+ BOOST_CHECK_EQUAL(error, "");
+
+ desc += " || ";
+ try {
+ desc += parser.GetChainName();
+ } catch (const std::runtime_error& e) {
+ desc += "error: ";
+ desc += e.what();
+ }
+ desc += "\n";
+
+ out_sha.Write((const unsigned char*)desc.data(), desc.size());
+ if (out_file) {
+ BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) == desc.size());
+ }
+ });
+
+ if (out_file) {
+ if (fclose(out_file)) throw std::system_error(errno, std::generic_category(), "fclose failed");
+ out_file = nullptr;
+ }
+
+ unsigned char out_sha_bytes[CSHA256::OUTPUT_SIZE];
+ out_sha.Finalize(out_sha_bytes);
+ std::string out_sha_hex = HexStr(std::begin(out_sha_bytes), std::end(out_sha_bytes));
+
+ // If check below fails, should manually dump the results with:
+ //
+ // CHAIN_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_ChainMerge
+ //
+ // And verify diff against previous results to make sure the changes are expected.
+ //
+ // Results file is formatted like:
+ //
+ // <input> || <output>
+ BOOST_CHECK_EQUAL(out_sha_hex, "b284f4b4a15dd6bf8c06213a69a004b1960388e1d9917173927db52ac220927f");
+}
+
BOOST_AUTO_TEST_CASE(util_FormatMoney)
{
BOOST_CHECK_EQUAL(FormatMoney(0), "0.00");
@@ -806,6 +1068,27 @@ BOOST_AUTO_TEST_CASE(gettime)
BOOST_CHECK((GetTime() & ~0xFFFFFFFFLL) == 0);
}
+BOOST_AUTO_TEST_CASE(util_time_GetTime)
+{
+ SetMockTime(111);
+ // Check that mock time does not change after a sleep
+ for (const auto& num_sleep : {0, 1}) {
+ MilliSleep(num_sleep);
+ BOOST_CHECK_EQUAL(111, GetTime()); // Deprecated time getter
+ BOOST_CHECK_EQUAL(111, GetTime<std::chrono::seconds>().count());
+ BOOST_CHECK_EQUAL(111000, GetTime<std::chrono::milliseconds>().count());
+ BOOST_CHECK_EQUAL(111000000, GetTime<std::chrono::microseconds>().count());
+ }
+
+ SetMockTime(0);
+ // Check that system time changes after a sleep
+ const auto ms_0 = GetTime<std::chrono::milliseconds>();
+ const auto us_0 = GetTime<std::chrono::microseconds>();
+ MilliSleep(1);
+ BOOST_CHECK(ms_0 < GetTime<std::chrono::milliseconds>());
+ BOOST_CHECK(us_0 < GetTime<std::chrono::microseconds>());
+}
+
BOOST_AUTO_TEST_CASE(test_IsDigit)
{
BOOST_CHECK_EQUAL(IsDigit('0'), true);
diff --git a/src/test/util_threadnames_tests.cpp b/src/test/util_threadnames_tests.cpp
new file mode 100644
index 0000000000..71c0168ca3
--- /dev/null
+++ b/src/test/util_threadnames_tests.cpp
@@ -0,0 +1,73 @@
+// Copyright (c) 2018 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 <util/threadnames.h>
+#include <test/setup_common.h>
+
+#include <thread>
+#include <vector>
+#include <set>
+#include <mutex>
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(util_threadnames_tests, BasicTestingSetup)
+
+const std::string TEST_THREAD_NAME_BASE = "test_thread.";
+
+/**
+ * Run a bunch of threads to all call util::ThreadRename.
+ *
+ * @return the set of name each thread has after attempted renaming.
+ */
+std::set<std::string> RenameEnMasse(int num_threads)
+{
+ std::vector<std::thread> threads;
+ std::set<std::string> names;
+ std::mutex lock;
+
+ auto RenameThisThread = [&](int i) {
+ util::ThreadRename(TEST_THREAD_NAME_BASE + std::to_string(i));
+ std::lock_guard<std::mutex> guard(lock);
+ names.insert(util::ThreadGetInternalName());
+ };
+
+ for (int i = 0; i < num_threads; ++i) {
+ threads.push_back(std::thread(RenameThisThread, i));
+ }
+
+ for (std::thread& thread : threads) thread.join();
+
+ return names;
+}
+
+/**
+ * Rename a bunch of threads with the same basename (expect_multiple=true), ensuring suffixes are
+ * applied properly.
+ */
+BOOST_AUTO_TEST_CASE(util_threadnames_test_rename_threaded)
+{
+ BOOST_CHECK_EQUAL(util::ThreadGetInternalName(), "");
+
+#if !defined(HAVE_THREAD_LOCAL)
+ // This test doesn't apply to platforms where we don't have thread_local.
+ return;
+#endif
+
+ std::set<std::string> names = RenameEnMasse(100);
+
+ BOOST_CHECK_EQUAL(names.size(), 100);
+
+ // Names "test_thread.[n]" should exist for n = [0, 99]
+ for (int i = 0; i < 100; ++i) {
+ BOOST_CHECK(names.find(TEST_THREAD_NAME_BASE + std::to_string(i)) != names.end());
+ }
+
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 44432cd0a1..5dee034b20 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -10,7 +10,7 @@
#include <miner.h>
#include <pow.h>
#include <random.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <validation.h>
#include <validationinterface.h>
@@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
const CBlockIndex* initial_tip = nullptr;
{
LOCK(cs_main);
- initial_tip = chainActive.Tip();
+ initial_tip = ::ChainActive().Tip();
}
TestSubscriber sub(initial_tip->GetBlockHash());
RegisterValidationInterface(&sub);
@@ -181,7 +181,8 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
UnregisterValidationInterface(&sub);
- BOOST_CHECK_EQUAL(sub.m_expected_tip, chainActive.Tip()->GetBlockHash());
+ LOCK(cs_main);
+ BOOST_CHECK_EQUAL(sub.m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/main_tests.cpp b/src/test/validation_tests.cpp
index 5b3f2bc578..101025d31e 100644
--- a/src/test/main_tests.cpp
+++ b/src/test/validation_tests.cpp
@@ -1,17 +1,17 @@
-// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Copyright (c) 2014-2019 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 <chainparams.h>
-#include <validation.h>
#include <net.h>
+#include <validation.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/signals2/signal.hpp>
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(main_tests, TestingSetup)
+BOOST_FIXTURE_TEST_SUITE(validation_tests, TestingSetup)
static void TestBlockSubsidyHalvings(const Consensus::Params& consensusParams)
{
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index ca3196454a..0ca3a17974 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Copyright (c) 2014-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
#include <versionbits.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <chainparams.h>
#include <validation.h>
#include <consensus/params.h>
@@ -271,12 +271,12 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Before MedianTimePast of the chain has crossed nStartTime, the bit
// should not be set.
CBlockIndex *lastBlock = nullptr;
- lastBlock = firstChain.Mine(2016, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
- // Mine 2011 more blocks at the old time, and check that CBV isn't setting the bit yet.
- for (int i=1; i<2012; i++) {
- lastBlock = firstChain.Mine(2016+i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ // Mine more blocks (4 less than the adjustment period) at the old time, and check that CBV isn't setting the bit yet.
+ for (uint32_t i = 1; i < mainnetParams.nMinerConfirmationWindow - 4; i++) {
+ lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// This works because VERSIONBITS_LAST_OLD_BLOCK_VERSION happens
// to be 4, and the bit we're testing happens to be bit 28.
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
@@ -284,13 +284,13 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Now mine 5 more blocks at the start time -- MTP should not have passed yet, so
// CBV should still not yet set the bit.
nTime = nStartTime;
- for (int i=2012; i<=2016; i++) {
- lastBlock = firstChain.Mine(2016+i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ for (uint32_t i = mainnetParams.nMinerConfirmationWindow - 4; i <= mainnetParams.nMinerConfirmationWindow; i++) {
+ lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
}
// Advance to the next period and transition to STARTED,
- lastBlock = firstChain.Mine(6048, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// so ComputeBlockVersion should now set the bit,
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// and should also be using the VERSIONBITS_TOP_BITS.
@@ -298,8 +298,8 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Check that ComputeBlockVersion will set the bit until nTimeout
nTime += 600;
- int blocksToMine = 4032; // test blocks for up to 2 time periods
- int nHeight = 6048;
+ uint32_t blocksToMine = mainnetParams.nMinerConfirmationWindow * 2; // test blocks for up to 2 time periods
+ uint32_t nHeight = mainnetParams.nMinerConfirmationWindow * 3;
// These blocks are all before nTimeout is reached.
while (nTime < nTimeout && blocksToMine > 0) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
@@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
nTime = nTimeout;
// FAILED is only triggered at the end of a period, so CBV should be setting
// the bit until the period transition.
- for (int i=0; i<2015; i++) {
+ for (uint32_t i = 0; i < mainnetParams.nMinerConfirmationWindow - 1; i++) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
nHeight += 1;
@@ -329,20 +329,20 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Mine one period worth of blocks, and check that the bit will be on for the
// next period.
- lastBlock = secondChain.Mine(2016, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// Mine another period worth of blocks, signaling the new bit.
- lastBlock = secondChain.Mine(4032, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
+ lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow * 2, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
// After one period of setting the bit on each block, it should have locked in.
// We keep setting the bit for one more period though, until activation.
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// Now check that we keep mining the block until the end of this period, and
// then stop at the beginning of the next period.
- lastBlock = secondChain.Mine(6047, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
- lastBlock = secondChain.Mine(6048, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = secondChain.Mine((mainnetParams.nMinerConfirmationWindow * 3) - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit)) != 0);
+ lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
// Finally, verify that after a soft fork has activated, CBV no longer uses
diff --git a/src/threadsafety.h b/src/threadsafety.h
index 33acddc65c..47e6b2ea38 100644
--- a/src/threadsafety.h
+++ b/src/threadsafety.h
@@ -54,15 +54,4 @@
#define ASSERT_EXCLUSIVE_LOCK(...)
#endif // __GNUC__
-// Utility class for indicating to compiler thread analysis that a mutex is
-// locked (when it couldn't be determined otherwise).
-struct SCOPED_LOCKABLE LockAnnotation
-{
- template <typename Mutex>
- explicit LockAnnotation(Mutex& mutex) EXCLUSIVE_LOCK_FUNCTION(mutex)
- {
- }
- ~LockAnnotation() UNLOCK_FUNCTION() {}
-};
-
#endif // BITCOIN_THREADSAFETY_H
diff --git a/src/timedata.cpp b/src/timedata.cpp
index 9c022c9ad1..b43639d729 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -12,7 +12,6 @@
#include <sync.h>
#include <ui_interface.h>
#include <util/system.h>
-#include <util/strencodings.h>
#include <warnings.h>
diff --git a/src/tinyformat.h b/src/tinyformat.h
index 14b7cd3026..182f518a0b 100644
--- a/src/tinyformat.h
+++ b/src/tinyformat.h
@@ -1063,6 +1063,7 @@ std::string format(const std::string &fmt, const Args&... args)
} // namespace tinyformat
+/** Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for details) */
#define strprintf tfm::format
#endif // TINYFORMAT_H_INCLUDED
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 550e23b222..84f54da515 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -412,7 +412,7 @@ public:
TorController(struct event_base* base, const std::string& target);
~TorController();
- /** Get name fo file to store private key in */
+ /** Get name of file to store private key in */
fs::path GetPrivateKeyFile();
/** Reconnect, after getting disconnected */
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 8447352c54..73fe2a8ee4 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -5,8 +5,6 @@
#include <txdb.h>
-#include <chainparams.h>
-#include <hash.h>
#include <random.h>
#include <pow.h>
#include <shutdown.h>
@@ -255,6 +253,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
// Load mapBlockIndex
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
+ if (ShutdownRequested()) return false;
std::pair<char, uint256> key;
if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
CDiskBlockIndex diskindex;
diff --git a/src/txdb.h b/src/txdb.h
index d5a4bfaeb1..c4ece11503 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -37,6 +37,8 @@ static const int64_t nMaxBlockDBCache = 2;
// Unlike for the UTXO database, for the txindex scenario the leveldb cache make
// a meaningful difference: https://github.com/bitcoin/bitcoin/pull/8273#issuecomment-229601991
static const int64_t nMaxTxIndexCache = 1024;
+//! Max memory allocated to all block filter index caches combined in MiB.
+static const int64_t max_filter_index_cache = 1024;
//! Max memory allocated to coin DB specific cache (MiB)
static const int64_t nMaxCoinsDBCache = 8;
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 68f47d5cce..cac7beb6a1 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -11,9 +11,8 @@
#include <validation.h>
#include <policy/policy.h>
#include <policy/fees.h>
+#include <policy/settings.h>
#include <reverse_iterator.h>
-#include <streams.h>
-#include <timedata.h>
#include <util/system.h>
#include <util/moneystr.h>
#include <util/time.h>
@@ -600,7 +599,7 @@ static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& m
CAmount txfee = 0;
bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee);
assert(fCheckResult);
- UpdateCoins(tx, mempoolDuplicate, 1000000);
+ UpdateCoins(tx, mempoolDuplicate, std::numeric_limits<int>::max());
}
void CTxMemPool::check(const CCoinsViewCache *pcoins) const
@@ -764,7 +763,7 @@ std::vector<CTxMemPool::indexed_transaction_set::const_iterator> CTxMemPool::Get
return iters;
}
-void CTxMemPool::queryHashes(std::vector<uint256>& vtxid)
+void CTxMemPool::queryHashes(std::vector<uint256>& vtxid) const
{
LOCK(cs);
auto iters = GetSortedDepthAndScore();
@@ -1090,4 +1089,16 @@ void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors,
}
}
+bool CTxMemPool::IsLoaded() const
+{
+ LOCK(cs);
+ return m_is_loaded;
+}
+
+void CTxMemPool::SetIsLoaded(bool loaded)
+{
+ LOCK(cs);
+ m_is_loaded = loaded;
+}
+
SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
diff --git a/src/txmempool.h b/src/txmempool.h
index b10c9f099f..ce0b762336 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -184,7 +184,7 @@ private:
const LockPoints& lp;
};
-// extracts a transaction hash from CTxMempoolEntry or CTransactionRef
+// extracts a transaction hash from CTxMemPoolEntry or CTransactionRef
struct mempoolentry_txid
{
typedef uint256 result_type;
@@ -455,6 +455,8 @@ private:
void trackPackageRemoved(const CFeeRate& rate) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ bool m_is_loaded GUARDED_BY(cs){false};
+
public:
static const int ROLLING_FEE_HALFLIFE = 60 * 60 * 12; // public only for testing
@@ -494,7 +496,7 @@ public:
* By design, it is guaranteed that:
*
* 1. Locking both `cs_main` and `mempool.cs` will give a view of mempool
- * that is consistent with current chain tip (`chainActive` and
+ * that is consistent with current chain tip (`::ChainActive()` and
* `pcoinsTip`) and is fully populated. Fully populated means that if the
* current active chain is missing transactions that were present in a
* previously active chain, all the missing transactions will have been
@@ -588,7 +590,7 @@ public:
void clear();
void _clear() EXCLUSIVE_LOCKS_REQUIRED(cs); //lock free
bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb);
- void queryHashes(std::vector<uint256>& vtxid);
+ void queryHashes(std::vector<uint256>& vtxid) const;
bool isSpent(const COutPoint& outpoint) const;
unsigned int GetTransactionsUpdated() const;
void AddTransactionsUpdated(unsigned int n);
@@ -672,6 +674,12 @@ public:
*/
void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) const;
+ /** @returns true if the mempool is fully loaded */
+ bool IsLoaded() const;
+
+ /** Sets the current loaded state */
+ void SetIsLoaded(bool loaded);
+
unsigned long size() const
{
LOCK(cs);
@@ -746,7 +754,8 @@ private:
* This allows transaction replacement to work as expected, as you want to
* have all inputs "available" to check signatures, and any cycles in the
* dependency graph are checked directly in AcceptToMemoryPool.
- * It also allows you to sign a double-spend directly in signrawtransaction,
+ * It also allows you to sign a double-spend directly in
+ * signrawtransactionwithkey and signrawtransactionwithwallet,
* as long as the conflicting transaction is not yet confirmed.
*/
class CCoinsViewMemPool : public CCoinsViewBacked
diff --git a/src/ui_interface.cpp b/src/ui_interface.cpp
index 947d7e2308..746514a01f 100644
--- a/src/ui_interface.cpp
+++ b/src/ui_interface.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <ui_interface.h>
-#include <util/system.h>
#include <boost/signals2/last_value.hpp>
#include <boost/signals2/signal.hpp>
@@ -28,10 +27,6 @@ struct UISignals {
boost::signals2::connection CClientUIInterface::signal_name##_connect(std::function<signal_name##Sig> fn) \
{ \
return g_ui_signals.signal_name.connect(fn); \
- } \
- void CClientUIInterface::signal_name##_disconnect(std::function<signal_name##Sig> fn) \
- { \
- return g_ui_signals.signal_name.disconnect(&fn); \
}
ADD_SIGNALS_IMPL_WRAPPER(ThreadSafeMessageBox);
@@ -52,7 +47,7 @@ void CClientUIInterface::InitMessage(const std::string& message) { return g_ui_s
void CClientUIInterface::NotifyNumConnectionsChanged(int newNumConnections) { return g_ui_signals.NotifyNumConnectionsChanged(newNumConnections); }
void CClientUIInterface::NotifyNetworkActiveChanged(bool networkActive) { return g_ui_signals.NotifyNetworkActiveChanged(networkActive); }
void CClientUIInterface::NotifyAlertChanged() { return g_ui_signals.NotifyAlertChanged(); }
-void CClientUIInterface::LoadWallet(std::shared_ptr<CWallet> wallet) { return g_ui_signals.LoadWallet(wallet); }
+void CClientUIInterface::LoadWallet(std::unique_ptr<interfaces::Wallet>& wallet) { return g_ui_signals.LoadWallet(wallet); }
void CClientUIInterface::ShowProgress(const std::string& title, int nProgress, bool resume_possible) { return g_ui_signals.ShowProgress(title, nProgress, resume_possible); }
void CClientUIInterface::NotifyBlockTip(bool b, const CBlockIndex* i) { return g_ui_signals.NotifyBlockTip(b, i); }
void CClientUIInterface::NotifyHeaderTip(bool b, const CBlockIndex* i) { return g_ui_signals.NotifyHeaderTip(b, i); }
@@ -69,13 +64,3 @@ void InitWarning(const std::string& str)
{
uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING);
}
-
-std::string AmountHighWarn(const std::string& optname)
-{
- return strprintf(_("%s is set very high!"), optname);
-}
-
-std::string AmountErrMsg(const char* const optname, const std::string& strValue)
-{
- return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue);
-}
diff --git a/src/ui_interface.h b/src/ui_interface.h
index fe466b3ca4..d408f6f889 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -11,7 +11,6 @@
#include <stdint.h>
#include <string>
-class CWallet;
class CBlockIndex;
namespace boost {
namespace signals2 {
@@ -19,6 +18,10 @@ class connection;
}
} // namespace boost
+namespace interfaces {
+class Wallet;
+} // namespace interfaces
+
/** General change type (added, updated, removed). */
enum ChangeType
{
@@ -78,8 +81,7 @@ public:
#define ADD_SIGNALS_DECL_WRAPPER(signal_name, rtype, ...) \
rtype signal_name(__VA_ARGS__); \
using signal_name##Sig = rtype(__VA_ARGS__); \
- boost::signals2::connection signal_name##_connect(std::function<signal_name##Sig> fn); \
- void signal_name##_disconnect(std::function<signal_name##Sig> fn);
+ boost::signals2::connection signal_name##_connect(std::function<signal_name##Sig> fn);
/** Show message box. */
ADD_SIGNALS_DECL_WRAPPER(ThreadSafeMessageBox, bool, const std::string& message, const std::string& caption, unsigned int style);
@@ -102,7 +104,7 @@ public:
ADD_SIGNALS_DECL_WRAPPER(NotifyAlertChanged, void, );
/** A wallet has been loaded. */
- ADD_SIGNALS_DECL_WRAPPER(LoadWallet, void, std::shared_ptr<CWallet> wallet);
+ ADD_SIGNALS_DECL_WRAPPER(LoadWallet, void, std::unique_ptr<interfaces::Wallet>& wallet);
/**
* Show progress e.g. for verifychain.
@@ -126,10 +128,6 @@ void InitWarning(const std::string& str);
/** Show error message **/
bool InitError(const std::string& str);
-std::string AmountHighWarn(const std::string& optname);
-
-std::string AmountErrMsg(const char* const optname, const std::string& strValue);
-
extern CClientUIInterface uiInterface;
#endif // BITCOIN_UI_INTERFACE_H
diff --git a/src/util/bip32.cpp b/src/util/bip32.cpp
new file mode 100644
index 0000000000..6f176dd5ec
--- /dev/null
+++ b/src/util/bip32.cpp
@@ -0,0 +1,66 @@
+// Copyright (c) 2019 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 <sstream>
+#include <stdio.h>
+#include <tinyformat.h>
+#include <util/bip32.h>
+#include <util/strencodings.h>
+
+
+bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath)
+{
+ std::stringstream ss(keypath_str);
+ std::string item;
+ bool first = true;
+ while (std::getline(ss, item, '/')) {
+ if (item.compare("m") == 0) {
+ if (first) {
+ first = false;
+ continue;
+ }
+ return false;
+ }
+ // Finds whether it is hardened
+ uint32_t path = 0;
+ size_t pos = item.find("'");
+ if (pos != std::string::npos) {
+ // The hardened tick can only be in the last index of the string
+ if (pos != item.size() - 1) {
+ return false;
+ }
+ path |= 0x80000000;
+ item = item.substr(0, item.size() - 1); // Drop the last character which is the hardened tick
+ }
+
+ // Ensure this is only numbers
+ if (item.find_first_not_of( "0123456789" ) != std::string::npos) {
+ return false;
+ }
+ uint32_t number;
+ if (!ParseUInt32(item, &number)) {
+ return false;
+ }
+ path |= number;
+
+ keypath.push_back(path);
+ first = false;
+ }
+ return true;
+}
+
+std::string FormatHDKeypath(const std::vector<uint32_t>& path)
+{
+ std::string ret;
+ for (auto i : path) {
+ ret += strprintf("/%i", (i << 1) >> 1);
+ if (i >> 31) ret += '\'';
+ }
+ return ret;
+}
+
+std::string WriteHDKeypath(const std::vector<uint32_t>& keypath)
+{
+ return "m" + FormatHDKeypath(keypath);
+}
diff --git a/src/util/bip32.h b/src/util/bip32.h
new file mode 100644
index 0000000000..7e58b79f38
--- /dev/null
+++ b/src/util/bip32.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2019 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_UTIL_BIP32_H
+#define BITCOIN_UTIL_BIP32_H
+
+#include <attributes.h>
+#include <string>
+#include <vector>
+
+/** Parse an HD keypaths like "m/7/0'/2000". */
+NODISCARD bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath);
+
+/** Write HD keypaths as strings */
+std::string WriteHDKeypath(const std::vector<uint32_t>& keypath);
+std::string FormatHDKeypath(const std::vector<uint32_t>& path);
+
+#endif // BITCOIN_UTIL_BIP32_H
diff --git a/src/util/error.cpp b/src/util/error.cpp
new file mode 100644
index 0000000000..68ffd8b046
--- /dev/null
+++ b/src/util/error.cpp
@@ -0,0 +1,43 @@
+// Copyright (c) 2010-2018 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 <util/error.h>
+
+#include <util/system.h>
+
+std::string TransactionErrorString(const TransactionError err)
+{
+ switch (err) {
+ case TransactionError::OK:
+ return "No error";
+ case TransactionError::MISSING_INPUTS:
+ return "Missing inputs";
+ case TransactionError::ALREADY_IN_CHAIN:
+ return "Transaction already in block chain";
+ case TransactionError::P2P_DISABLED:
+ return "Peer-to-peer functionality missing or disabled";
+ case TransactionError::MEMPOOL_REJECTED:
+ return "Transaction rejected by AcceptToMemoryPool";
+ case TransactionError::MEMPOOL_ERROR:
+ return "AcceptToMemoryPool failed";
+ case TransactionError::INVALID_PSBT:
+ return "PSBT is not sane";
+ case TransactionError::PSBT_MISMATCH:
+ return "PSBTs not compatible (different transactions)";
+ case TransactionError::SIGHASH_MISMATCH:
+ return "Specified sighash value does not match existing value";
+ // no default case, so the compiler can warn about missing cases
+ }
+ assert(false);
+}
+
+std::string AmountHighWarn(const std::string& optname)
+{
+ return strprintf(_("%s is set very high!"), optname);
+}
+
+std::string AmountErrMsg(const char* const optname, const std::string& strValue)
+{
+ return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue);
+}
diff --git a/src/util/error.h b/src/util/error.h
new file mode 100644
index 0000000000..d93309551b
--- /dev/null
+++ b/src/util/error.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2010-2018 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_UTIL_ERROR_H
+#define BITCOIN_UTIL_ERROR_H
+
+/**
+ * util/error.h is a common place for definitions of simple error types and
+ * string functions. Types and functions defined here should not require any
+ * outside dependencies.
+ *
+ * Error types defined here can be used in different parts of the bitcoin
+ * codebase, to avoid the need to write boilerplate code catching and
+ * translating errors passed across wallet/node/rpc/gui code boundaries.
+ */
+
+#include <string>
+
+enum class TransactionError {
+ OK, //!< No error
+ MISSING_INPUTS,
+ ALREADY_IN_CHAIN,
+ P2P_DISABLED,
+ MEMPOOL_REJECTED,
+ MEMPOOL_ERROR,
+ INVALID_PSBT,
+ PSBT_MISMATCH,
+ SIGHASH_MISMATCH,
+};
+
+std::string TransactionErrorString(const TransactionError error);
+
+std::string AmountHighWarn(const std::string& optname);
+
+std::string AmountErrMsg(const char* const optname, const std::string& strValue);
+
+#endif // BITCOIN_UTIL_ERROR_H
diff --git a/src/util/fees.cpp b/src/util/fees.cpp
new file mode 100644
index 0000000000..5fdaa1284c
--- /dev/null
+++ b/src/util/fees.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 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 <policy/fees.h>
+
+#include <string>
+
+std::string StringForFeeReason(FeeReason reason) {
+ static const std::map<FeeReason, std::string> fee_reason_strings = {
+ {FeeReason::NONE, "None"},
+ {FeeReason::HALF_ESTIMATE, "Half Target 60% Threshold"},
+ {FeeReason::FULL_ESTIMATE, "Target 85% Threshold"},
+ {FeeReason::DOUBLE_ESTIMATE, "Double Target 95% Threshold"},
+ {FeeReason::CONSERVATIVE, "Conservative Double Target longer horizon"},
+ {FeeReason::MEMPOOL_MIN, "Mempool Min Fee"},
+ {FeeReason::PAYTXFEE, "PayTxFee set"},
+ {FeeReason::FALLBACK, "Fallback fee"},
+ {FeeReason::REQUIRED, "Minimum Required Fee"},
+ {FeeReason::MAXTXFEE, "MaxTxFee limit"}
+ };
+ auto reason_string = fee_reason_strings.find(reason);
+
+ if (reason_string == fee_reason_strings.end()) return "Unknown";
+
+ return reason_string->second;
+}
+
+bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
+ static const std::map<std::string, FeeEstimateMode> fee_modes = {
+ {"UNSET", FeeEstimateMode::UNSET},
+ {"ECONOMICAL", FeeEstimateMode::ECONOMICAL},
+ {"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
+ };
+ auto mode = fee_modes.find(mode_string);
+
+ if (mode == fee_modes.end()) return false;
+
+ fee_estimate_mode = mode->second;
+ return true;
+}
diff --git a/src/util/fees.h b/src/util/fees.h
new file mode 100644
index 0000000000..fc355ce9c2
--- /dev/null
+++ b/src/util/fees.h
@@ -0,0 +1,16 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 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_UTIL_FEES_H
+#define BITCOIN_UTIL_FEES_H
+
+#include <string>
+
+enum class FeeEstimateMode;
+enum class FeeReason;
+
+bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode);
+std::string StringForFeeReason(FeeReason reason);
+
+#endif // BITCOIN_UTIL_FEES_H
diff --git a/src/util/rbf.cpp b/src/util/rbf.cpp
new file mode 100644
index 0000000000..d520a9606d
--- /dev/null
+++ b/src/util/rbf.cpp
@@ -0,0 +1,17 @@
+// Copyright (c) 2016-2018 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 <util/rbf.h>
+
+#include <primitives/transaction.h>
+
+bool SignalsOptInRBF(const CTransaction &tx)
+{
+ for (const CTxIn &txin : tx.vin) {
+ if (txin.nSequence <= MAX_BIP125_RBF_SEQUENCE) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/src/util/rbf.h b/src/util/rbf.h
new file mode 100644
index 0000000000..d3ef110628
--- /dev/null
+++ b/src/util/rbf.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2016-2018 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_UTIL_RBF_H
+#define BITCOIN_UTIL_RBF_H
+
+#include <cstdint>
+
+class CTransaction;
+
+static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
+
+// Check whether the sequence numbers on this transaction are signaling
+// opt-in to replace-by-fee, according to BIP 125
+bool SignalsOptInRBF(const CTransaction &tx);
+
+#endif // BITCOIN_UTIL_RBF_H
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index fedeeac39b..0acbb4f117 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -141,7 +141,7 @@ std::string EncodeBase64(const std::string& str)
return EncodeBase64((const unsigned char*)str.c_str(), str.size());
}
-std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid)
+std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid)
{
static const int decode64_table[256] =
{
@@ -183,14 +183,14 @@ std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid)
++p;
}
valid = valid && (p - e) % 4 == 0 && p - q < 4;
- if (pfInvalid) *pfInvalid = !valid;
+ if (pf_invalid) *pf_invalid = !valid;
return ret;
}
-std::string DecodeBase64(const std::string& str)
+std::string DecodeBase64(const std::string& str, bool* pf_invalid)
{
- std::vector<unsigned char> vchRet = DecodeBase64(str.c_str());
+ std::vector<unsigned char> vchRet = DecodeBase64(str.c_str(), pf_invalid);
return std::string((const char*)vchRet.data(), vchRet.size());
}
@@ -210,7 +210,7 @@ std::string EncodeBase32(const std::string& str)
return EncodeBase32((const unsigned char*)str.c_str(), str.size());
}
-std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid)
+std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid)
{
static const int decode32_table[256] =
{
@@ -252,14 +252,14 @@ std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid)
++p;
}
valid = valid && (p - e) % 8 == 0 && p - q < 8;
- if (pfInvalid) *pfInvalid = !valid;
+ if (pf_invalid) *pf_invalid = !valid;
return ret;
}
-std::string DecodeBase32(const std::string& str)
+std::string DecodeBase32(const std::string& str, bool* pf_invalid)
{
- std::vector<unsigned char> vchRet = DecodeBase32(str.c_str());
+ std::vector<unsigned char> vchRet = DecodeBase32(str.c_str(), pf_invalid);
return std::string((const char*)vchRet.data(), vchRet.size());
}
@@ -546,47 +546,6 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
return true;
}
-bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath)
-{
- std::stringstream ss(keypath_str);
- std::string item;
- bool first = true;
- while (std::getline(ss, item, '/')) {
- if (item.compare("m") == 0) {
- if (first) {
- first = false;
- continue;
- }
- return false;
- }
- // Finds whether it is hardened
- uint32_t path = 0;
- size_t pos = item.find("'");
- if (pos != std::string::npos) {
- // The hardened tick can only be in the last index of the string
- if (pos != item.size() - 1) {
- return false;
- }
- path |= 0x80000000;
- item = item.substr(0, item.size() - 1); // Drop the last character which is the hardened tick
- }
-
- // Ensure this is only numbers
- if (item.find_first_not_of( "0123456789" ) != std::string::npos) {
- return false;
- }
- uint32_t number;
- if (!ParseUInt32(item, &number)) {
- return false;
- }
- path |= number;
-
- keypath.push_back(path);
- first = false;
- }
- return true;
-}
-
void Downcase(std::string& str)
{
std::transform(str.begin(), str.end(), str.begin(), [](char c){return ToLower(c);});
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index e392055f27..7c4364a082 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -12,6 +12,7 @@
#include <attributes.h>
#include <cstdint>
+#include <iterator>
#include <string>
#include <vector>
@@ -44,12 +45,12 @@ 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::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid = nullptr);
+std::string DecodeBase64(const std::string& str, bool* pf_invalid = nullptr);
std::string EncodeBase64(const unsigned char* pch, size_t len);
std::string EncodeBase64(const std::string& str);
-std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid = nullptr);
-std::string DecodeBase32(const std::string& str);
+std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid = nullptr);
+std::string DecodeBase32(const std::string& str, bool* pf_invalid = nullptr);
std::string EncodeBase32(const unsigned char* pch, size_t len);
std::string EncodeBase32(const std::string& str);
@@ -121,28 +122,25 @@ NODISCARD bool ParseUInt64(const std::string& str, uint64_t *out);
NODISCARD bool ParseDouble(const std::string& str, double *out);
template<typename T>
-std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
+std::string HexStr(const T itbegin, const T itend)
{
std::string rv;
static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- rv.reserve((itend-itbegin)*3);
+ rv.reserve(std::distance(itbegin, itend) * 2);
for(T it = itbegin; it < itend; ++it)
{
unsigned char val = (unsigned char)(*it);
- if(fSpaces && it != itbegin)
- rv.push_back(' ');
rv.push_back(hexmap[val>>4]);
rv.push_back(hexmap[val&15]);
}
-
return rv;
}
template<typename T>
-inline std::string HexStr(const T& vch, bool fSpaces=false)
+inline std::string HexStr(const T& vch)
{
- return HexStr(vch.begin(), vch.end(), fSpaces);
+ return HexStr(vch.begin(), vch.end());
}
/**
@@ -197,9 +195,6 @@ bool ConvertBits(const O& outfn, I it, I end) {
return true;
}
-/** Parse an HD keypaths like "m/7/0'/2000". */
-NODISCARD bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath);
-
/**
* Converts the given character to its lowercase equivalent.
* This function is locale independent. It only converts uppercase
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 06317a3a90..fca29a9f31 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -6,8 +6,6 @@
#include <util/system.h>
#include <chainparamsbase.h>
-#include <random.h>
-#include <serialize.h>
#include <util/strencodings.h>
#include <stdarg.h>
@@ -44,11 +42,6 @@
#pragma warning(disable:4717)
#endif
-#ifdef _WIN32_WINNT
-#undef _WIN32_WINNT
-#endif
-#define _WIN32_WINNT 0x0501
-
#ifdef _WIN32_IE
#undef _WIN32_IE
#endif
@@ -65,10 +58,6 @@
#include <shlobj.h>
#endif
-#ifdef HAVE_SYS_PRCTL_H
-#include <sys/prctl.h>
-#endif
-
#ifdef HAVE_MALLOPT_ARENA_MAX
#include <malloc.h>
#endif
@@ -79,7 +68,6 @@
const int64_t nStartupTime = GetTime();
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
-const char * const BITCOIN_PID_FILENAME = "bitcoind.pid";
ArgsManager gArgs;
@@ -116,6 +104,12 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
return true;
}
+void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name)
+{
+ std::lock_guard<std::mutex> lock(cs_dir_locks);
+ dir_locks.erase((directory / lockfile_name).string());
+}
+
void ReleaseDirectoryLocks()
{
std::lock_guard<std::mutex> ulock(cs_dir_locks);
@@ -135,6 +129,14 @@ bool DirIsWritable(const fs::path& directory)
return true;
}
+bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
+{
+ constexpr uint64_t min_disk_space = 52428800; // 50 MiB
+
+ uint64_t free_bytes_available = fs::space(dir).available;
+ return free_bytes_available >= min_disk_space + additional_bytes;
+}
+
/**
* Interpret a string argument as a boolean.
*
@@ -353,8 +355,7 @@ const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
return unsuitables;
}
-
-const std::set<std::string> ArgsManager::GetUnrecognizedSections() const
+const std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const
{
// Section names to be recognized in the config file.
static const std::set<std::string> available_sections{
@@ -362,14 +363,11 @@ const std::set<std::string> ArgsManager::GetUnrecognizedSections() const
CBaseChainParams::TESTNET,
CBaseChainParams::MAIN
};
- std::set<std::string> diff;
LOCK(cs_args);
- std::set_difference(
- m_config_sections.begin(), m_config_sections.end(),
- available_sections.begin(), available_sections.end(),
- std::inserter(diff, diff.end()));
- return diff;
+ std::list<SectionInfo> unrecognized = m_config_sections;
+ unrecognized.remove_if([](const SectionInfo& appeared){ return available_sections.find(appeared.m_name) != available_sections.end(); });
+ return unrecognized;
}
void ArgsManager::SelectConfigNetwork(const std::string& network)
@@ -634,6 +632,12 @@ bool HelpRequested(const ArgsManager& args)
return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help") || args.IsArgSet("-help-debug");
}
+void SetupHelpOptions(ArgsManager& args)
+{
+ args.AddArg("-?", "Print this help message and exit", false, OptionsCategory::OPTIONS);
+ args.AddHiddenArgs({"-h", "-help"});
+}
+
static const int screenWidth = 79;
static const int optIndent = 2;
static const int msgIndent = 7;
@@ -669,7 +673,7 @@ void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
{
std::string message = FormatException(pex, pszThread);
LogPrintf("\n\n************************\n%s\n", message);
- fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
+ tfm::format(std::cerr, "\n\n************************\n%s\n", message.c_str());
}
fs::path GetDefaultDataDir()
@@ -787,7 +791,7 @@ static std::string TrimString(const std::string& str, const std::string& pattern
return str.substr(front, end - front + 1);
}
-static bool GetConfigOptions(std::istream& stream, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::set<std::string>& sections)
+static bool GetConfigOptions(std::istream& stream, const std::string& filepath, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::list<SectionInfo>& sections)
{
std::string str, prefix;
std::string::size_type pos;
@@ -803,7 +807,7 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
if (!str.empty()) {
if (*str.begin() == '[' && *str.rbegin() == ']') {
const std::string section = str.substr(1, str.size() - 2);
- sections.insert(section);
+ sections.emplace_back(SectionInfo{section, filepath, linenr});
prefix = section + '.';
} else if (*str.begin() == '-') {
error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str);
@@ -816,8 +820,8 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
return false;
}
options.emplace_back(name, value);
- if ((pos = name.rfind('.')) != std::string::npos) {
- sections.insert(name.substr(0, pos));
+ if ((pos = name.rfind('.')) != std::string::npos && prefix.length() <= pos) {
+ sections.emplace_back(SectionInfo{name.substr(0, pos), filepath, linenr});
}
} else {
error = strprintf("parse error on line %i: %s", linenr, str);
@@ -832,12 +836,11 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
return true;
}
-bool ArgsManager::ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys)
+bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys)
{
LOCK(cs_args);
std::vector<std::pair<std::string, std::string>> options;
- m_config_sections.clear();
- if (!GetConfigOptions(stream, error, options, m_config_sections)) {
+ if (!GetConfigOptions(stream, filepath, error, options, m_config_sections)) {
return false;
}
for (const std::pair<std::string, std::string>& option : options) {
@@ -868,6 +871,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
{
LOCK(cs_args);
m_config_args.clear();
+ m_config_sections.clear();
}
const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME);
@@ -875,7 +879,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
// ok to not have a config file
if (stream.good()) {
- if (!ReadConfigStream(stream, error, ignore_invalid_keys)) {
+ if (!ReadConfigStream(stream, confPath, error, ignore_invalid_keys)) {
return false;
}
// if there is an -includeconf in the override args, but it is empty, that means the user
@@ -906,7 +910,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
for (const std::string& to_include : includeconf) {
fsbridge::ifstream include_config(GetConfigFile(to_include));
if (include_config.good()) {
- if (!ReadConfigStream(include_config, error, ignore_invalid_keys)) {
+ if (!ReadConfigStream(include_config, to_include, error, ignore_invalid_keys)) {
return false;
}
LogPrintf("Included configuration file %s\n", to_include.c_str());
@@ -929,7 +933,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
}
}
for (const std::string& to_include : includeconf) {
- fprintf(stderr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", to_include.c_str());
+ tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", to_include.c_str());
}
}
}
@@ -958,23 +962,6 @@ std::string ArgsManager::GetChainName() const
return CBaseChainParams::MAIN;
}
-#ifndef WIN32
-fs::path GetPidFile()
-{
- return AbsPathForConfigVal(fs::path(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME)));
-}
-
-void CreatePidFile(const fs::path &path, pid_t pid)
-{
- FILE* file = fsbridge::fopen(path, "w");
- if (file)
- {
- fprintf(file, "%d\n", pid);
- fclose(file);
- }
-}
-#endif
-
bool RenameOver(fs::path src, fs::path dest)
{
#ifdef WIN32
@@ -1096,11 +1083,12 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
fcntl(fileno(file), F_PREALLOCATE, &fst);
}
ftruncate(fileno(file), fst.fst_length);
-#elif defined(__linux__)
+#else
+ #if defined(__linux__)
// Version using posix_fallocate
off_t nEndPos = (off_t)offset + length;
- posix_fallocate(fileno(file), 0, nEndPos);
-#else
+ if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
+ #endif
// Fallback version
// TODO: just write one byte per block
static const char buf[65536] = {};
@@ -1144,22 +1132,6 @@ void runCommand(const std::string& strCommand)
LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
}
-void RenameThread(const char* name)
-{
-#if defined(PR_SET_NAME)
- // Only the first 15 characters are used (16 - NUL terminator)
- ::prctl(PR_SET_NAME, name, 0, 0, 0);
-#elif (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
- pthread_set_name_np(pthread_self(), name);
-
-#elif defined(MAC_OSX)
- pthread_setname_np(name);
-#else
- // Prevent warnings for unused parameters...
- (void)name;
-#endif
-}
-
void SetupEnvironment()
{
#ifdef HAVE_MALLOPT_ARENA_MAX
diff --git a/src/util/system.h b/src/util/system.h
index 5932e55793..1a83cb67b1 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -16,9 +16,11 @@
#include <attributes.h>
#include <compat.h>
+#include <compat/assumptions.h>
#include <fs.h>
#include <logging.h>
#include <sync.h>
+#include <util/threadnames.h>
#include <tinyformat.h>
#include <util/memory.h>
#include <util/time.h>
@@ -39,7 +41,6 @@
int64_t GetStartupTime();
extern const char * const BITCOIN_CONF_FILENAME;
-extern const char * const BITCOIN_PID_FILENAME;
/** Translate a message to the native language of the user. */
const extern std::function<std::string(const char*)> G_TRANSLATION_FUN;
@@ -70,7 +71,9 @@ int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
bool RenameOver(fs::path src, fs::path dest);
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only=false);
+void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name);
bool DirIsWritable(const fs::path& directory);
+bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes = 0);
/** Release all directory locks. This is used for unit testing only, at runtime
* the global destructor will take care of the locks.
@@ -84,10 +87,6 @@ const fs::path &GetBlocksDir();
const fs::path &GetDataDir(bool fNetSpecific = true);
void ClearDatadirCache();
fs::path GetConfigFile(const std::string& confPath);
-#ifndef WIN32
-fs::path GetPidFile();
-void CreatePidFile(const fs::path &path, pid_t pid);
-#endif
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
@@ -130,6 +129,13 @@ enum class OptionsCategory {
HIDDEN // Always the last option to avoid printing these in the help
};
+struct SectionInfo
+{
+ std::string m_name;
+ std::string m_file;
+ int m_line;
+};
+
class ArgsManager
{
protected:
@@ -150,9 +156,9 @@ protected:
std::string m_network GUARDED_BY(cs_args);
std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
- std::set<std::string> m_config_sections GUARDED_BY(cs_args);
+ std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
- NODISCARD bool ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys = false);
+ NODISCARD bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
public:
ArgsManager();
@@ -176,7 +182,7 @@ public:
/**
* Log warnings for unrecognized section names in the config file.
*/
- const std::set<std::string> GetUnrecognizedSections() const;
+ const std::list<SectionInfo> GetUnrecognizedSections() const;
/**
* Return a vector of strings of the given argument
@@ -294,6 +300,9 @@ extern ArgsManager gArgs;
*/
bool HelpRequested(const ArgsManager& args);
+/** Add help options to the args manager */
+void SetupHelpOptions(ArgsManager& args);
+
/**
* Format a string to be used as group of options in help messages
*
@@ -317,15 +326,12 @@ std::string HelpMessageOpt(const std::string& option, const std::string& message
*/
int GetNumCores();
-void RenameThread(const char* name);
-
/**
* .. and a wrapper that just calls func once
*/
template <typename Callable> void TraceThread(const char* name, Callable func)
{
- std::string s = strprintf("bitcoin-%s", name);
- RenameThread(s.c_str());
+ util::ThreadRename(name);
try
{
LogPrintf("%s thread start\n", name);
diff --git a/src/util/threadnames.cpp b/src/util/threadnames.cpp
new file mode 100644
index 0000000000..b221b0c975
--- /dev/null
+++ b/src/util/threadnames.cpp
@@ -0,0 +1,62 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <atomic>
+#include <thread>
+
+#if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
+#include <pthread.h>
+#include <pthread_np.h>
+#endif
+
+#include <util/threadnames.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h> // For prctl, PR_SET_NAME, PR_GET_NAME
+#endif
+
+//! Set the thread's name at the process level. Does not affect the
+//! internal name.
+static void SetThreadName(const char* name)
+{
+#if defined(PR_SET_NAME)
+ // Only the first 15 characters are used (16 - NUL terminator)
+ ::prctl(PR_SET_NAME, name, 0, 0, 0);
+#elif (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
+ pthread_set_name_np(pthread_self(), name);
+#elif defined(MAC_OSX)
+ pthread_setname_np(name);
+#else
+ // Prevent warnings for unused parameters...
+ (void)name;
+#endif
+}
+
+// If we have thread_local, just keep thread ID and name in a thread_local
+// global.
+#if defined(HAVE_THREAD_LOCAL)
+
+static thread_local std::string g_thread_name;
+const std::string& util::ThreadGetInternalName() { return g_thread_name; }
+//! Set the in-memory internal name for this thread. Does not affect the process
+//! name.
+static void SetInternalName(std::string name) { g_thread_name = std::move(name); }
+
+// Without thread_local available, don't handle internal name at all.
+#else
+
+static const std::string empty_string;
+const std::string& util::ThreadGetInternalName() { return empty_string; }
+static void SetInternalName(std::string name) { }
+#endif
+
+void util::ThreadRename(std::string&& name)
+{
+ SetThreadName(("bitcoin-" + name).c_str());
+ SetInternalName(std::move(name));
+}
diff --git a/src/util/threadnames.h b/src/util/threadnames.h
new file mode 100644
index 0000000000..aaf07b9bf8
--- /dev/null
+++ b/src/util/threadnames.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2018 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_UTIL_THREADNAMES_H
+#define BITCOIN_UTIL_THREADNAMES_H
+
+#include <string>
+
+namespace util {
+//! Rename a thread both in terms of an internal (in-memory) name as well
+//! as its system thread name.
+void ThreadRename(std::string&&);
+
+//! Get the thread's internal (in-memory) name; used e.g. for identification in
+//! logging.
+const std::string& ThreadGetInternalName();
+
+} // namespace util
+
+#endif // BITCOIN_UTIL_THREADNAMES_H
diff --git a/src/util/time.cpp b/src/util/time.cpp
index 83a7937d8f..2b202ae95f 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -27,6 +27,20 @@ int64_t GetTime()
return now;
}
+template <typename T>
+T GetTime()
+{
+ const std::chrono::seconds mocktime{nMockTime.load(std::memory_order_relaxed)};
+
+ return std::chrono::duration_cast<T>(
+ mocktime.count() ?
+ mocktime :
+ std::chrono::microseconds{GetTimeMicros()});
+}
+template std::chrono::seconds GetTime();
+template std::chrono::milliseconds GetTime();
+template std::chrono::microseconds GetTime();
+
void SetMockTime(int64_t nMockTimeIn)
{
nMockTime.store(nMockTimeIn, std::memory_order_relaxed);
@@ -97,14 +111,3 @@ std::string FormatISO8601Date(int64_t nTime) {
#endif
return strprintf("%04i-%02i-%02i", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday);
}
-
-std::string FormatISO8601Time(int64_t nTime) {
- struct tm ts;
- time_t time_val = nTime;
-#ifdef _MSC_VER
- gmtime_s(&ts, &time_val);
-#else
- gmtime_r(&time_val, &ts);
-#endif
- return strprintf("%02i:%02i:%02iZ", ts.tm_hour, ts.tm_min, ts.tm_sec);
-}
diff --git a/src/util/time.h b/src/util/time.h
index f2e2747434..e4f9996777 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -8,31 +8,37 @@
#include <stdint.h>
#include <string>
+#include <chrono>
/**
- * GetTimeMicros() and GetTimeMillis() both return the system time, but in
- * different units. GetTime() returns the system time in seconds, but also
- * supports mocktime, where the time can be specified by the user, eg for
- * testing (eg with the setmocktime rpc, or -mocktime argument).
- *
- * TODO: Rework these functions to be type-safe (so that we don't inadvertently
- * compare numbers with different units, or compare a mocktime to system time).
+ * DEPRECATED
+ * Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable)
*/
-
int64_t GetTime();
+
+/** Returns the system time (not mockable) */
int64_t GetTimeMillis();
+/** Returns the system time (not mockable) */
int64_t GetTimeMicros();
+/** Returns the system time (not mockable) */
int64_t GetSystemTimeInSeconds(); // Like GetTime(), but not mockable
+
+/** For testing. Set e.g. with the setmocktime rpc, or -mocktime argument */
void SetMockTime(int64_t nMockTimeIn);
+/** For testing */
int64_t GetMockTime();
+
void MilliSleep(int64_t n);
+/** Return system time (or mocked time, if set) */
+template <typename T>
+T GetTime();
+
/**
- * ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date,Time}
+ * ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date}
* helper functions if possible.
*/
std::string FormatISO8601DateTime(int64_t nTime);
std::string FormatISO8601Date(int64_t nTime);
-std::string FormatISO8601Time(int64_t nTime);
#endif // BITCOIN_UTIL_TIME_H
diff --git a/src/util/url.cpp b/src/util/url.cpp
new file mode 100644
index 0000000000..49eacbf2d0
--- /dev/null
+++ b/src/util/url.cpp
@@ -0,0 +1,21 @@
+// Copyright (c) 2015-2018 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 <util/url.h>
+
+#include <event2/http.h>
+#include <stdlib.h>
+#include <string>
+
+std::string urlDecode(const std::string &urlEncoded) {
+ std::string res;
+ if (!urlEncoded.empty()) {
+ char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr);
+ if (decoded) {
+ res = std::string(decoded);
+ free(decoded);
+ }
+ }
+ return res;
+}
diff --git a/src/util/url.h b/src/util/url.h
new file mode 100644
index 0000000000..3d7315a338
--- /dev/null
+++ b/src/util/url.h
@@ -0,0 +1,12 @@
+// Copyright (c) 2015-2018 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_UTIL_URL_H
+#define BITCOIN_UTIL_URL_H
+
+#include <string>
+
+std::string urlDecode(const std::string &urlEncoded);
+
+#endif // BITCOIN_UTIL_URL_H
diff --git a/src/util/validation.cpp b/src/util/validation.cpp
new file mode 100644
index 0000000000..fe1f5a277e
--- /dev/null
+++ b/src/util/validation.cpp
@@ -0,0 +1,20 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 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 <util/validation.h>
+
+#include <consensus/validation.h>
+#include <tinyformat.h>
+
+/** Convert CValidationState to a human-readable message for logging */
+std::string FormatStateMessage(const CValidationState &state)
+{
+ return strprintf("%s%s (code %i)",
+ state.GetRejectReason(),
+ state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(),
+ state.GetRejectCode());
+}
+
+const std::string strMessageMagic = "Bitcoin Signed Message:\n";
diff --git a/src/util/validation.h b/src/util/validation.h
new file mode 100644
index 0000000000..32559853ee
--- /dev/null
+++ b/src/util/validation.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 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_UTIL_VALIDATION_H
+#define BITCOIN_UTIL_VALIDATION_H
+
+#include <string>
+
+class CValidationState;
+
+/** Convert CValidationState to a human-readable message for logging */
+std::string FormatStateMessage(const CValidationState &state);
+
+extern const std::string strMessageMagic;
+
+#endif // BITCOIN_UTIL_VALIDATION_H
diff --git a/src/validation.cpp b/src/validation.cpp
index de9c0d96db..3026df34fe 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -8,18 +8,19 @@
#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_check.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <cuckoocache.h>
+#include <flatfile.h>
#include <hash.h>
#include <index/txindex.h>
#include <policy/fees.h>
#include <policy/policy.h>
-#include <policy/rbf.h>
+#include <policy/settings.h>
#include <pow.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
@@ -34,15 +35,19 @@
#include <txdb.h>
#include <txmempool.h>
#include <ui_interface.h>
+#include <uint256.h>
#include <undo.h>
-#include <util/system.h>
#include <util/moneystr.h>
+#include <util/rbf.h>
#include <util/strencodings.h>
+#include <util/system.h>
+#include <util/validation.h>
#include <validationinterface.h>
#include <warnings.h>
#include <future>
#include <sstream>
+#include <string>
#include <boost/algorithm/string/replace.hpp>
#include <boost/thread.hpp>
@@ -54,161 +59,29 @@
#define MICRO 0.000001
#define MILLI 0.001
-/**
- * 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;
+bool CBlockIndexWorkComparator::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;
-/**
- * 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;
+ // ... then by earliest time received, ...
+ if (pa->nSequenceId < pb->nSequenceId) return false;
+ if (pa->nSequenceId > pb->nSequenceId) return true;
- /**
- * 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*> m_failed_blocks;
-
- /**
- * the ChainState CriticalSection
- * A lock that must be held when modifying this ChainState - held in ActivateBestChain()
- */
- CCriticalSection m_cs_chainstate;
+ // 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;
-public:
- CChain chainActive;
- BlockMap mapBlockIndex;
- std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
- CBlockIndex *pindexBestInvalid = nullptr;
-
- bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock);
-
- /**
- * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
- * that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
- */
- bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- // 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) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- // Block disconnection on our pcoinsTip:
- bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- // Manual block validity manipulation:
- bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
- bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- 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) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Create a new block index entry for a given block hash */
- CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /**
- * Make various assertions about the state of the block index.
- *
- * By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex.
- */
- void CheckBlockIndex(const Consensus::Params& consensusParams);
+ // Identical blocks.
+ return false;
+}
- void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+CChainState g_chainstate;
+CChainState& ChainstateActive() { return g_chainstate; }
- bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-} g_chainstate;
+CChain& ChainActive() { return g_chainstate.m_chain; }
/**
* Mutex to guard access to validation specific variables, such as reading
@@ -222,8 +95,7 @@ private:
*/
RecursiveMutex cs_main;
-BlockMap& mapBlockIndex = g_chainstate.mapBlockIndex;
-CChain& chainActive = g_chainstate.chainActive;
+BlockMap& mapBlockIndex = ::ChainstateActive().mapBlockIndex;
CBlockIndex *pindexBestHeader = nullptr;
Mutex g_best_block_mutex;
std::condition_variable g_best_block_cv;
@@ -233,38 +105,32 @@ std::atomic_bool fImporting(false);
std::atomic_bool fReindex(false);
bool fHavePruned = false;
bool fPruneMode = false;
-bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
bool fRequireStandard = true;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
size_t nCoinCacheUsage = 5000 * 300;
uint64_t nPruneTarget = 0;
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;
CBlockPolicyEstimator feeEstimator;
CTxMemPool mempool(&feeEstimator);
-std::atomic_bool g_is_mempool_loaded{false};
/** Constant stuff for coinbase transactions we create: */
CScript COINBASE_FLAGS;
-const std::string strMessageMagic = "Bitcoin Signed Message:\n";
-
// Internal stuff
namespace {
- CBlockIndex *&pindexBestInvalid = g_chainstate.pindexBestInvalid;
+ CBlockIndex *&pindexBestInvalid = ::ChainstateActive().pindexBestInvalid;
/** 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 = g_chainstate.mapBlocksUnlinked;
+ std::multimap<CBlockIndex*, CBlockIndex*>& mapBlocksUnlinked = ::ChainstateActive().mapBlocksUnlinked;
CCriticalSection cs_LastBlockFile;
std::vector<CBlockFileInfo> vinfoBlockFile;
@@ -305,19 +171,13 @@ std::unique_ptr<CCoinsViewDB> pcoinsdbview;
std::unique_ptr<CCoinsViewCache> pcoinsTip;
std::unique_ptr<CBlockTreeDB> pblocktree;
-enum class FlushStateMode {
- NONE,
- IF_NEEDED,
- PERIODIC,
- ALWAYS
-};
-
// See definition for documentation
-static bool FlushStateToDisk(const CChainParams& chainParams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0);
static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
-static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
+static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false);
+static FlatFileSeq BlockFileSeq();
+static FlatFileSeq UndoFileSeq();
bool CheckFinalTx(const CTransaction &tx, int flags)
{
@@ -331,13 +191,13 @@ bool CheckFinalTx(const CTransaction &tx, int flags)
// scheduled, so no flags are set.
flags = std::max(flags, 0);
- // CheckFinalTx() uses chainActive.Height()+1 to evaluate
+ // CheckFinalTx() uses ::ChainActive().Height()+1 to evaluate
// nLockTime because when IsFinalTx() is called within
// CBlock::AcceptBlock(), the height of the block *being*
// evaluated is what is used. Thus if we want to know if a
// transaction can be part of the *next* block, we need to call
- // IsFinalTx() with one more than chainActive.Height().
- const int nBlockHeight = chainActive.Height() + 1;
+ // IsFinalTx() with one more than ::ChainActive().Height().
+ const int nBlockHeight = ::ChainActive().Height() + 1;
// BIP113 requires that time-locked transactions have nLockTime set to
// less than the median time of the previous block they're contained in.
@@ -345,7 +205,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags)
// chain tip, so we use that to calculate the median time passed to
// IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set.
const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST)
- ? chainActive.Tip()->GetMedianTimePast()
+ ? ::ChainActive().Tip()->GetMedianTimePast()
: GetAdjustedTime();
return IsFinalTx(tx, nBlockHeight, nBlockTime);
@@ -358,9 +218,9 @@ bool TestLockPointValidity(const LockPoints* lp)
// If there are relative lock times then the maxInputBlock will be set
// If there are no relative lock times, the LockPoints don't depend on the chain
if (lp->maxInputBlock) {
- // Check whether chainActive is an extension of the block at which the LockPoints
+ // Check whether ::ChainActive() is an extension of the block at which the LockPoints
// calculation was valid. If not LockPoints are no longer valid
- if (!chainActive.Contains(lp->maxInputBlock)) {
+ if (!::ChainActive().Contains(lp->maxInputBlock)) {
return false;
}
}
@@ -374,17 +234,17 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
AssertLockHeld(cs_main);
AssertLockHeld(pool.cs);
- CBlockIndex* tip = chainActive.Tip();
+ CBlockIndex* tip = ::ChainActive().Tip();
assert(tip != nullptr);
CBlockIndex index;
index.pprev = tip;
- // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate
+ // CheckSequenceLocks() uses ::ChainActive().Height()+1 to evaluate
// height based locks because when SequenceLocks() is called within
// ConnectBlock(), the height of the block *being*
// evaluated is what is used.
// Thus if we want to know if a transaction can be part of the
- // *next* block, we need to use one more than chainActive.Height()
+ // *next* block, we need to use one more than ::ChainActive().Height()
index.nHeight = tip->nHeight + 1;
std::pair<int, int64_t> lockPair;
@@ -394,7 +254,7 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
lockPair.second = lp->time;
}
else {
- // pcoinsTip contains the UTXO set for chainActive.Tip()
+ // pcoinsTip contains the UTXO set for ::ChainActive().Tip()
CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
std::vector<int> prevheights;
prevheights.resize(tx.vin.size());
@@ -456,23 +316,14 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
pcoinsTip->Uncache(removed);
}
-/** Convert CValidationState to a human-readable message for logging */
-std::string FormatStateMessage(const CValidationState &state)
-{
- return strprintf("%s%s (code %i)",
- state.GetRejectReason(),
- state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(),
- state.GetRejectCode());
-}
-
static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
- if (IsInitialBlockDownload())
+ if (::ChainstateActive().IsInitialBlockDownload())
return false;
- if (chainActive.Tip()->GetBlockTime() < (GetTime() - MAX_FEE_ESTIMATION_TIP_AGE))
+ if (::ChainActive().Tip()->GetBlockTime() < (GetTime() - MAX_FEE_ESTIMATION_TIP_AGE))
return false;
- if (chainActive.Height() < pindexBestHeader->nHeight - 1)
+ if (::ChainActive().Height() < pindexBestHeader->nHeight - 1)
return false;
return true;
}
@@ -524,7 +375,7 @@ static void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool,
mempool.UpdateTransactionsFromBlock(vHashUpdate);
// We also need to remove any now-immature transactions
- mempool.removeForReorg(pcoinsTip.get(), 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);
}
@@ -565,6 +416,13 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata);
}
+/**
+ * @param[out] coins_to_uncache Return any outpoints which were not previously present in the
+ * coins cache, but were added as a result of validating the tx
+ * for mempool acceptance. This allows the caller to optionally
+ * remove the cache additions if the associated transaction ends
+ * up being rejected by the mempool.
+ */
static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -582,28 +440,28 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
- return state.DoS(100, false, REJECT_INVALID, "coinbase");
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "coinbase");
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
std::string reason;
if (fRequireStandard && !IsStandardTx(tx, reason))
- return state.DoS(0, false, REJECT_NONSTANDARD, reason);
+ return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, reason);
// Do not work on transactions that are too small.
// A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes.
// Transactions smaller than this are not relayed to reduce unnecessary malloc overhead.
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE)
- return state.DoS(0, false, REJECT_NONSTANDARD, "tx-size-small");
+ return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "tx-size-small");
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
- return state.DoS(0, false, REJECT_NONSTANDARD, "non-final");
+ return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-final");
// is it already in the memory pool?
if (pool.exists(hash)) {
- return state.Invalid(false, REJECT_DUPLICATE, "txn-already-in-mempool");
+ return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-in-mempool");
}
// Check for conflicts with in-memory transactions
@@ -627,19 +485,16 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// unconfirmed ancestors anyway; doing otherwise is hopelessly
// insecure.
bool fReplacementOptOut = true;
- if (fEnableReplacement)
+ for (const CTxIn &_txin : ptxConflicting->vin)
{
- for (const CTxIn &_txin : ptxConflicting->vin)
+ if (_txin.nSequence <= MAX_BIP125_RBF_SEQUENCE)
{
- if (_txin.nSequence <= MAX_BIP125_RBF_SEQUENCE)
- {
- fReplacementOptOut = false;
- break;
- }
+ fReplacementOptOut = false;
+ break;
}
}
if (fReplacementOptOut) {
- return state.Invalid(false, REJECT_DUPLICATE, "txn-mempool-conflict");
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_DUPLICATE, "txn-mempool-conflict");
}
setConflicts.insert(ptxConflicting->GetHash());
@@ -660,12 +515,16 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
coins_to_uncache.push_back(txin.prevout);
}
+
+ // Note: this call may add txin.prevout to the coins cache
+ // (pcoinsTip.cacheCoins) by way of FetchCoin(). It should be removed
+ // later (via coins_to_uncache) if this tx turns out to be invalid.
if (!view.HaveCoin(txin.prevout)) {
// Are inputs missing because we already have the tx?
for (size_t out = 0; out < tx.vout.size(); out++) {
// Optimistically just do efficient check of cache for outputs
if (pcoinsTip->HaveCoinInCache(COutPoint(hash, out))) {
- return state.Invalid(false, REJECT_DUPLICATE, "txn-already-known");
+ return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known");
}
}
// Otherwise assume this might be an orphan tx for which we just haven't seen parents yet
@@ -688,7 +547,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// Must keep pool.cs for this unless we change CheckSequenceLocks to take a
// CoinsViewCache instead of create its own
if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
- return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
+ return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final");
CAmount nFees = 0;
if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) {
@@ -697,11 +556,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// Check for non-standard pay-to-script-hash in inputs
if (fRequireStandard && !AreInputsStandard(tx, view))
- return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
+ return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
// Check for non-standard witness in P2WSH
if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, view))
- return state.DoS(0, false, REJECT_NONSTANDARD, "bad-witness-nonstandard", true);
+ return state.Invalid(ValidationInvalidReason::TX_WITNESS_MUTATED, false, REJECT_NONSTANDARD, "bad-witness-nonstandard");
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
@@ -720,31 +579,26 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}
}
- CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, chainActive.Height(),
+ CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, ::ChainActive().Height(),
fSpendsCoinbase, nSigOpsCost, lp);
unsigned int nSize = entry.GetTxSize();
- // Check that the transaction doesn't have an excessive number of
- // sigops, making it impossible to mine. Since the coinbase transaction
- // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than
- // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than
- // merely non-standard transaction.
if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
- return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
+ return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops",
strprintf("%d", nSigOpsCost));
CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
- return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nModifiedFees, mempoolRejectFee));
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", strprintf("%d < %d", nModifiedFees, mempoolRejectFee));
}
// No transactions are allowed below minRelayTxFee except from disconnected blocks
if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) {
- return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", false, strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize)));
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize)));
}
if (nAbsurdFee && nFees > nAbsurdFee)
- return state.Invalid(false,
+ return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false,
REJECT_HIGHFEE, "absurdly-high-fee",
strprintf("%d > %d", nFees, nAbsurdFee));
@@ -756,7 +610,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
std::string errString;
if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
- return state.DoS(0, false, REJECT_NONSTANDARD, "too-long-mempool-chain", false, errString);
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", errString);
}
// A transaction that spends outputs that would be replaced by it is invalid. Now
@@ -768,8 +622,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
const uint256 &hashAncestor = ancestorIt->GetTx().GetHash();
if (setConflicts.count(hashAncestor))
{
- return state.DoS(10, false,
- REJECT_INVALID, "bad-txns-spends-conflicting-tx", false,
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-spends-conflicting-tx",
strprintf("%s spends conflicting transaction %s",
hash.ToString(),
hashAncestor.ToString()));
@@ -811,8 +664,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize());
if (newFeeRate <= oldFeeRate)
{
- return state.DoS(0, false,
- REJECT_INSUFFICIENTFEE, "insufficient fee", false,
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee",
strprintf("rejecting replacement %s; new feerate %s <= old feerate %s",
hash.ToString(),
newFeeRate.ToString(),
@@ -840,8 +692,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
nConflictingSize += it->GetTxSize();
}
} else {
- return state.DoS(0, false,
- REJECT_NONSTANDARD, "too many potential replacements", false,
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too many potential replacements",
strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
hash.ToString(),
nConflictingCount,
@@ -860,8 +711,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// it's cheaper to just check if the new input refers to a
// tx that's in the mempool.
if (pool.exists(tx.vin[j].prevout.hash)) {
- return state.DoS(0, false,
- REJECT_NONSTANDARD, "replacement-adds-unconfirmed", false,
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "replacement-adds-unconfirmed",
strprintf("replacement %s adds unconfirmed input, idx %d",
hash.ToString(), j));
}
@@ -873,8 +723,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// transactions would not be paid for.
if (nModifiedFees < nConflictingFees)
{
- return state.DoS(0, false,
- REJECT_INSUFFICIENTFEE, "insufficient fee", false,
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee",
strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
hash.ToString(), FormatMoney(nModifiedFees), FormatMoney(nConflictingFees)));
}
@@ -884,8 +733,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
CAmount nDeltaFees = nModifiedFees - nConflictingFees;
if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize))
{
- return state.DoS(0, false,
- REJECT_INSUFFICIENTFEE, "insufficient fee", false,
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee",
strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
hash.ToString(),
FormatMoney(nDeltaFees),
@@ -906,8 +754,10 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) &&
!CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) {
// Only the witness is missing, so the transaction itself may be fine.
- state.SetCorruptionPossible();
+ state.Invalid(ValidationInvalidReason::TX_WITNESS_MUTATED, false,
+ state.GetRejectCode(), state.GetRejectReason(), state.GetDebugMessage());
}
+ assert(IsTransactionReason(state.GetReason()));
return false; // state filled in by CheckInputs
}
@@ -926,7 +776,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack.
- unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), chainparams.GetConsensus());
+ unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(::ChainActive().Tip(), chainparams.GetConsensus());
if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) {
return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s",
__func__, hash.ToString(), FormatStateMessage(state));
@@ -964,7 +814,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
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");
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full");
}
}
@@ -981,12 +831,17 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
std::vector<COutPoint> coins_to_uncache;
bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept);
if (!res) {
+ // Remove coins that were not present in the coins cache before calling ATMPW;
+ // this is to prevent memory DoS in case we receive a large number of
+ // invalid transactions that attempt to overrun the in-memory coins cache
+ // (`CCoinsViewCache::cacheCoins`).
+
for (const COutPoint& hashTx : coins_to_uncache)
pcoinsTip->Uncache(hashTx);
}
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
CValidationState stateDummy;
- FlushStateToDisk(chainparams, stateDummy, FlushStateMode::PERIODIC);
+ ::ChainstateActive().FlushStateToDisk(chainparams, stateDummy, FlushStateMode::PERIODIC);
return res;
}
@@ -1042,7 +897,7 @@ bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus
// CBlock and CBlockIndex
//
-static bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart)
+static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessageHeader::MessageStartChars& messageStart)
{
// Open history file to append
CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
@@ -1063,7 +918,7 @@ static bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMes
return true;
}
-bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
+bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams)
{
block.SetNull();
@@ -1089,7 +944,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus:
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
- CDiskBlockPos blockPos;
+ FlatFilePos blockPos;
{
LOCK(cs_main);
blockPos = pindex->GetBlockPos();
@@ -1103,9 +958,9 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus
return true;
}
-bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start)
+bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, const CMessageHeader::MessageStartChars& message_start)
{
- CDiskBlockPos hpos = pos;
+ FlatFilePos hpos = pos;
hpos.nPos -= 8; // Seek back 8 bytes for meta header
CAutoFile filein(OpenBlockFile(hpos, true), SER_DISK, CLIENT_VERSION);
if (filein.IsNull()) {
@@ -1140,7 +995,7 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos,
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start)
{
- CDiskBlockPos block_pos;
+ FlatFilePos block_pos;
{
LOCK(cs_main);
block_pos = pindex->GetBlockPos();
@@ -1162,27 +1017,30 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
return nSubsidy;
}
-bool IsInitialBlockDownload()
+// Note that though this is marked const, we may end up modifying `m_cached_finished_ibd`, which
+// is a performance-related implementation detail. This function must be marked
+// `const` so that `CValidationInterface` clients (which are given a `const CChainState*`)
+// can call it.
+//
+bool CChainState::IsInitialBlockDownload() const
{
- // Once this function has returned false, it must remain false.
- static std::atomic<bool> latchToFalse{false};
// Optimization: pre-test latch before taking the lock.
- if (latchToFalse.load(std::memory_order_relaxed))
+ if (m_cached_finished_ibd.load(std::memory_order_relaxed))
return false;
LOCK(cs_main);
- if (latchToFalse.load(std::memory_order_relaxed))
+ if (m_cached_finished_ibd.load(std::memory_order_relaxed))
return false;
if (fImporting || fReindex)
return true;
- if (chainActive.Tip() == nullptr)
+ if (m_chain.Tip() == nullptr)
return true;
- if (chainActive.Tip()->nChainWork < nMinimumChainWork)
+ if (m_chain.Tip()->nChainWork < nMinimumChainWork)
return true;
- if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
+ if (m_chain.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
return true;
LogPrintf("Leaving InitialBlockDownload (latching to false)\n");
- latchToFalse.store(true, std::memory_order_relaxed);
+ m_cached_finished_ibd.store(true, std::memory_order_relaxed);
return false;
}
@@ -1211,15 +1069,15 @@ static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
AssertLockHeld(cs_main);
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before finishing our initial sync)
- if (IsInitialBlockDownload())
+ if (::ChainstateActive().IsInitialBlockDownload())
return;
// If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it)
// of our head, drop it
- if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72)
+ if (pindexBestForkTip && ::ChainActive().Height() - pindexBestForkTip->nHeight >= 72)
pindexBestForkTip = nullptr;
- if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6)))
+ if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > ::ChainActive().Tip()->nChainWork + (GetBlockProof(*::ChainActive().Tip()) * 6)))
{
if (!GetfLargeWorkForkFound() && pindexBestForkBase)
{
@@ -1252,7 +1110,7 @@ static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) E
AssertLockHeld(cs_main);
// If we are on a fork that is sufficiently large, set a warning flag
CBlockIndex* pfork = pindexNewForkTip;
- CBlockIndex* plonger = chainActive.Tip();
+ CBlockIndex* plonger = ::ChainActive().Tip();
while (pfork && pfork != plonger)
{
while (plonger && plonger->nHeight > pfork->nHeight)
@@ -1271,7 +1129,7 @@ static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) E
// the 7-block condition and from this always have the most-likely-to-cause-warning fork
if (pfork && (!pindexBestForkTip || pindexNewForkTip->nHeight > pindexBestForkTip->nHeight) &&
pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) &&
- chainActive.Height() - pindexNewForkTip->nHeight < 72)
+ ::ChainActive().Height() - pindexNewForkTip->nHeight < 72)
{
pindexBestForkTip = pindexNewForkTip;
pindexBestForkBase = pfork;
@@ -1288,16 +1146,16 @@ void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(c
LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime()));
- CBlockIndex *tip = chainActive.Tip();
+ CBlockIndex *tip = ::ChainActive().Tip();
assert (tip);
LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__,
- tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0),
+ tip->GetBlockHash().ToString(), ::ChainActive().Height(), log(tip->nChainWork.getdouble())/log(2.0),
FormatISO8601DateTime(tip->GetBlockTime()));
CheckForkWarningConditions();
}
void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
- if (!state.CorruptionPossible()) {
+ if (state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
m_failed_blocks.insert(pindex);
setDirtyBlockIndex.insert(pindex);
@@ -1365,6 +1223,9 @@ void InitScriptExecutionCache() {
* which are matched. This is useful for checking blocks where we will likely never need the cache
* entry again.
*
+ * Note that we may set state.reason to NOT_STANDARD for extra soft-fork flags in flags, block-checking
+ * callers should probably reset it to CONSENSUS in such cases.
+ *
* Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp
*/
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -1420,22 +1281,26 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
// Check whether the failure was caused by a
// non-mandatory script verification check, such as
// non-standard DER encodings or non-null dummy
- // arguments; if so, don't trigger DoS protection to
- // avoid splitting the network between upgraded and
- // non-upgraded nodes.
+ // arguments; if so, ensure we return NOT_STANDARD
+ // instead of CONSENSUS to avoid downstream users
+ // splitting the network between upgraded and
+ // non-upgraded nodes by banning CONSENSUS-failing
+ // data providers.
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())));
+ return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
}
- // Failures of other flags indicate a transaction that is
- // invalid in new blocks, e.g. an invalid P2SH. We DoS ban
- // such nodes as they are not following the protocol. That
- // said during an upgrade careful thought should be taken
- // as to the correct behavior - we may want to continue
- // peering with non-upgraded nodes even after soft-fork
- // super-majority signaling has occurred.
- return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
+ // MANDATORY flag failures correspond to
+ // ValidationInvalidReason::CONSENSUS. Because CONSENSUS
+ // failures are the most serious case of validation
+ // failures, we may need to consider using
+ // RECENT_CONSENSUS_CHANGE for any script failure that
+ // could be due to non-upgraded nodes which we may want to
+ // support, to avoid splitting the network (but this
+ // depends on the details of how net_processing handles
+ // such errors).
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
}
}
@@ -1450,9 +1315,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
return true;
}
-namespace {
-
-bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart)
+static bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart)
{
// Open history file to append
CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION);
@@ -1479,9 +1342,9 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint
return true;
}
-static bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex *pindex)
+bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
{
- CDiskBlockPos pos = pindex->GetUndoPos();
+ FlatFilePos pos = pindex->GetUndoPos();
if (pos.IsNull()) {
return error("%s: no undo data available", __func__);
}
@@ -1528,8 +1391,6 @@ static bool AbortNode(CValidationState& state, const std::string& strMessage, co
return state.Error(strMessage);
}
-} // namespace
-
/**
* Restore the UTXO in a Coin at a given COutPoint
* @param undo The Coin to be restored.
@@ -1627,37 +1488,24 @@ void static FlushBlockFile(bool fFinalize = false)
{
LOCK(cs_LastBlockFile);
- CDiskBlockPos posOld(nLastBlockFile, 0);
- bool status = true;
-
- FILE *fileOld = OpenBlockFile(posOld);
- if (fileOld) {
- if (fFinalize)
- status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize);
- status &= FileCommit(fileOld);
- fclose(fileOld);
- }
-
- fileOld = OpenUndoFile(posOld);
- if (fileOld) {
- if (fFinalize)
- status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize);
- status &= FileCommit(fileOld);
- fclose(fileOld);
- }
+ FlatFilePos block_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nSize);
+ FlatFilePos undo_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nUndoSize);
+ bool status = true;
+ status &= BlockFileSeq().Flush(block_pos_old, fFinalize);
+ status &= UndoFileSeq().Flush(undo_pos_old, fFinalize);
if (!status) {
AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
}
}
-static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
+static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &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;
+ FlatFilePos _pos;
if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, CLIENT_VERSION) + 40))
return error("ConnectBlock(): FindUndoPos failed");
if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart()))
@@ -1674,8 +1522,8 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, CValidationState&
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
-void ThreadScriptCheck() {
- RenameThread("bitcoin-scriptch");
+void ThreadScriptCheck(int worker_num) {
+ util::ThreadRename(strprintf("scriptch.%i", worker_num));
scriptcheckqueue.Thread();
}
@@ -1812,7 +1660,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
// 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)) {
- if (state.CorruptionPossible()) {
+ if (state.GetReason() == ValidationInvalidReason::BLOCK_MUTATED) {
// We don't write down blocks to disk if they may have been
// corrupted, so this should be impossible unless we're having hardware
// problems.
@@ -1947,7 +1795,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
for (const auto& tx : block.vtx) {
for (size_t o = 0; o < tx->vout.size(); o++) {
if (view.HaveCoin(COutPoint(tx->GetHash(), o))) {
- return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, error("ConnectBlock(): tried to overwrite transaction"),
REJECT_INVALID, "bad-txns-BIP30");
}
}
@@ -1987,11 +1835,19 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
{
CAmount txfee = 0;
if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee)) {
+ if (!IsBlockReason(state.GetReason())) {
+ // CheckTxInputs may return MISSING_INPUTS or
+ // PREMATURE_SPEND but we can't return that, as it's not
+ // defined for a block, so we reset the reason flag to
+ // CONSENSUS here.
+ state.Invalid(ValidationInvalidReason::CONSENSUS, false,
+ state.GetRejectCode(), state.GetRejectReason(), state.GetDebugMessage());
+ }
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__),
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, error("%s: accumulated fee in the block out of range.", __func__),
REJECT_INVALID, "bad-txns-accumulated-fee-outofrange");
}
@@ -2004,7 +1860,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
}
if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) {
- return state.DoS(100, error("%s: contains a non-BIP68-final transaction", __func__),
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, error("%s: contains a non-BIP68-final transaction", __func__),
REJECT_INVALID, "bad-txns-nonfinal");
}
}
@@ -2015,7 +1871,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
// * witness (when witness enabled in flags and excludes coinbase)
nSigOpsCost += GetTransactionSigOpCost(tx, view, flags);
if (nSigOpsCost > MAX_BLOCK_SIGOPS_COST)
- return state.DoS(100, error("ConnectBlock(): too many sigops"),
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, error("ConnectBlock(): too many sigops"),
REJECT_INVALID, "bad-blk-sigops");
txdata.emplace_back(tx);
@@ -2023,9 +1879,20 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
{
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))
+ if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr)) {
+ if (state.GetReason() == ValidationInvalidReason::TX_NOT_STANDARD) {
+ // CheckInputs may return NOT_STANDARD for extra flags we passed,
+ // but we can't return that, as it's not defined for a block, so
+ // we reset the reason flag to CONSENSUS here.
+ // In the event of a future soft-fork, we may need to
+ // consider whether rewriting to CONSENSUS or
+ // RECENT_CONSENSUS_CHANGE would be more appropriate.
+ state.Invalid(ValidationInvalidReason::CONSENSUS, false,
+ state.GetRejectCode(), state.GetRejectReason(), state.GetDebugMessage());
+ }
return error("ConnectBlock(): CheckInputs on %s failed with %s",
tx.GetHash().ToString(), FormatStateMessage(state));
+ }
control.Add(vChecks);
}
@@ -2040,13 +1907,13 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus());
if (block.vtx[0]->GetValueOut() > blockReward)
- return state.DoS(100,
+ return state.Invalid(ValidationInvalidReason::CONSENSUS,
error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)",
block.vtx[0]->GetValueOut(), blockReward),
REJECT_INVALID, "bad-cb-amount");
if (!control.Wait())
- return state.DoS(100, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed");
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed");
int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2;
LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1, MILLI * (nTime4 - nTime2), nInputs <= 1 ? 0 : MILLI * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * MICRO, nTimeVerify * MILLI / nBlocksTotal);
@@ -2074,16 +1941,12 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
return true;
}
-/**
- * Update the on-disk chain state.
- * The caches and indexes are flushed depending on the mode we're called with
- * if they're too large, if it's been a while since the last write,
- * or always and in all cases if we're in prune mode and are deleting files.
- *
- * If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything
- * besides checking if we need to prune.
- */
-bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight) {
+bool CChainState::FlushStateToDisk(
+ const CChainParams& chainparams,
+ CValidationState &state,
+ FlushStateMode mode,
+ int nManualPruneHeight)
+{
int64_t nMempoolUsage = mempool.DynamicMemoryUsage();
LOCK(cs_main);
static int64_t nLastWrite = 0;
@@ -2134,8 +1997,9 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
// Write blocks and block index to disk.
if (fDoFullFlush || fPeriodicWrite) {
// Depend on nMinDiskSpace to ensure we can write block index
- if (!CheckDiskSpace(0, true))
- return state.Error("out of disk space");
+ if (!CheckDiskSpace(GetBlocksDir())) {
+ return AbortNode(state, "Disk space is low!", _("Error: Disk space is low!"));
+ }
// First make sure all block and undo data is flushed to disk.
FlushBlockFile();
// Then update all block file information (which may refer to block and undo files).
@@ -2168,8 +2032,9 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
// twice (once in the log, and once in the tables). This is already
// an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2.
- if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize()))
- return state.Error("out of disk space");
+ if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * pcoinsTip->GetCacheSize())) {
+ return AbortNode(state, "Disk space is low!", _("Error: Disk space is low!"));
+ }
// Flush the chainstate (which may refer to block index entries).
if (!pcoinsTip->Flush())
return AbortNode(state, "Failed to write to coin database");
@@ -2179,7 +2044,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
}
if (full_flush_completed) {
// Update best block in wallet (so we can detect restored wallets).
- GetMainSignals().ChainStateFlushed(chainActive.GetLocator());
+ GetMainSignals().ChainStateFlushed(m_chain.GetLocator());
}
} catch (const std::runtime_error& e) {
return AbortNode(state, std::string("System error while flushing: ") + e.what());
@@ -2187,19 +2052,20 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
return true;
}
-void FlushStateToDisk() {
+void CChainState::ForceFlushStateToDisk() {
CValidationState state;
const CChainParams& chainparams = Params();
- if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) {
+ if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
}
}
-void PruneAndFlush() {
+void CChainState::PruneAndFlush() {
CValidationState state;
fCheckForPruning = true;
const CChainParams& chainparams = Params();
- if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) {
+
+ if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
}
}
@@ -2233,7 +2099,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
}
std::string warningMessages;
- if (!IsInitialBlockDownload())
+ if (!::ChainstateActive().IsInitialBlockDownload())
{
int nUpgraded = 0;
const CBlockIndex* pindex = pindexNew;
@@ -2259,12 +2125,6 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
}
if (nUpgraded > 0)
AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
- if (nUpgraded > 100/2)
- {
- std::string strWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect");
- // notify GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
- DoWarning(strWarning);
- }
}
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__, /* Continued */
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
@@ -2277,7 +2137,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
}
-/** Disconnect chainActive's tip.
+/** Disconnect m_chain's tip.
* After calling, the mempool will be in an inconsistent state, with
* transactions from disconnected blocks being added to disconnectpool. You
* should make the mempool consistent again by calling UpdateMempoolForReorg.
@@ -2289,7 +2149,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
*/
bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool)
{
- CBlockIndex *pindexDelete = chainActive.Tip();
+ CBlockIndex *pindexDelete = m_chain.Tip();
assert(pindexDelete);
// Read block from disk.
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
@@ -2324,7 +2184,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha
}
}
- chainActive.SetTip(pindexDelete->pprev);
+ m_chain.SetTip(pindexDelete->pprev);
UpdateTip(pindexDelete->pprev, chainparams);
// Let wallets know transactions went from 1-confirmed to
@@ -2402,14 +2262,14 @@ public:
};
/**
- * Connect a new block to chainActive. pblock is either nullptr or a pointer to a CBlock
+ * Connect a new block to m_chain. pblock is either nullptr or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*
* The block is added to connectTrace if connection succeeds.
*/
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());
+ assert(pindexNew->pprev == m_chain.Tip());
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
std::shared_ptr<const CBlock> pthisBlock;
@@ -2450,8 +2310,8 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
// Remove conflicting transactions from the mempool.;
mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
disconnectpool.removeForBlock(blockConnecting.vtx);
- // Update chainActive & related variables.
- chainActive.SetTip(pindexNew);
+ // Update m_chain & related variables.
+ m_chain.SetTip(pindexNew);
UpdateTip(pindexNew, chainparams);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
@@ -2482,7 +2342,7 @@ CBlockIndex* CChainState::FindMostWorkChain() {
// Just going until the active chain is an optimization, as we know all blocks in it are valid already.
CBlockIndex *pindexTest = pindexNew;
bool fInvalidAncestor = false;
- while (pindexTest && !chainActive.Contains(pindexTest)) {
+ while (pindexTest && !m_chain.Contains(pindexTest)) {
assert(pindexTest->HaveTxsDownloaded() || pindexTest->nHeight == 0);
// Pruned nodes may have entries in setBlockIndexCandidates for
@@ -2525,7 +2385,7 @@ 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();
- while (it != setBlockIndexCandidates.end() && setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) {
+ while (it != setBlockIndexCandidates.end() && setBlockIndexCandidates.value_comp()(*it, m_chain.Tip())) {
setBlockIndexCandidates.erase(it++);
}
// Either the current tip or a successor of it we're working towards is left in setBlockIndexCandidates.
@@ -2540,13 +2400,13 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
{
AssertLockHeld(cs_main);
- const CBlockIndex *pindexOldTip = chainActive.Tip();
- const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork);
+ const CBlockIndex *pindexOldTip = m_chain.Tip();
+ const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork);
// Disconnect active blocks which are no longer in the best chain.
bool fBlocksDisconnected = false;
DisconnectedBlockTransactions disconnectpool;
- while (chainActive.Tip() && chainActive.Tip() != pindexFork) {
+ while (m_chain.Tip() && m_chain.Tip() != pindexFork) {
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
// This is likely a fatal error, but keep the mempool consistent,
// just in case. Only remove from the mempool in this case.
@@ -2578,7 +2438,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
- if (!state.CorruptionPossible()) {
+ if (state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) {
InvalidChainFound(vpindexToConnect.front());
}
state = CValidationState();
@@ -2594,7 +2454,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
}
} else {
PruneBlockIndexCandidates();
- if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) {
+ if (!pindexOldTip || m_chain.Tip()->nChainWork > pindexOldTip->nChainWork) {
// We're in a better position than we were. Return temporarily to release the lock.
fContinue = false;
break;
@@ -2630,7 +2490,7 @@ static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
if (pindexHeader != pindexHeaderOld) {
fNotify = true;
- fInitialBlockDownload = IsInitialBlockDownload();
+ fInitialBlockDownload = ::ChainstateActive().IsInitialBlockDownload();
pindexHeaderOld = pindexHeader;
}
}
@@ -2640,6 +2500,14 @@ static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
}
}
+static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
+ AssertLockNotHeld(cs_main);
+
+ if (GetMainSignals().CallbacksPending() > 10) {
+ SyncWithValidationInterfaceQueue();
+ }
+}
+
/**
* Make the best chain active, in multiple steps. The result is either failure
* or an activated best chain. pblock is either nullptr or a pointer to a block
@@ -2668,19 +2536,17 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
do {
boost::this_thread::interruption_point();
- if (GetMainSignals().CallbacksPending() > 10) {
- // Block until the validation queue drains. This should largely
- // never happen in normal operation, however may happen during
- // reindex, causing memory blowup if we run too far ahead.
- // Note that if a validationinterface callback ends up calling
- // ActivateBestChain this may lead to a deadlock! We should
- // probably have a DEBUG_LOCKORDER test for this in the future.
- SyncWithValidationInterfaceQueue();
- }
+ // Block until the validation queue drains. This should largely
+ // never happen in normal operation, however may happen during
+ // reindex, causing memory blowup if we run too far ahead.
+ // Note that if a validationinterface callback ends up calling
+ // ActivateBestChain this may lead to a deadlock! We should
+ // probably have a DEBUG_LOCKORDER test for this in the future.
+ LimitValidationInterfaceQueue();
{
LOCK(cs_main);
- CBlockIndex* starting_tip = chainActive.Tip();
+ CBlockIndex* starting_tip = m_chain.Tip();
bool blocks_connected = false;
do {
// We absolutely may not unlock cs_main until we've made forward progress
@@ -2692,7 +2558,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
}
// Whether we have anything to do at all.
- if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip()) {
+ if (pindexMostWork == nullptr || pindexMostWork == m_chain.Tip()) {
break;
}
@@ -2706,16 +2572,16 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
// Wipe cache, we may need another branch now.
pindexMostWork = nullptr;
}
- pindexNewTip = chainActive.Tip();
+ pindexNewTip = m_chain.Tip();
for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
assert(trace.pblock && trace.pindex);
GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
}
- } while (!chainActive.Tip() || (starting_tip && CBlockIndexWorkComparator()(chainActive.Tip(), starting_tip)));
+ } while (!m_chain.Tip() || (starting_tip && CBlockIndexWorkComparator()(m_chain.Tip(), starting_tip)));
if (!blocks_connected) return true;
- const CBlockIndex* pindexFork = chainActive.FindFork(starting_tip);
+ const CBlockIndex* pindexFork = m_chain.FindFork(starting_tip);
bool fInitialDownload = IsInitialBlockDownload();
// Notify external listeners about the new tip.
@@ -2750,22 +2616,22 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
}
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
- return g_chainstate.ActivateBestChain(state, chainparams, std::move(pblock));
+ return ::ChainstateActive().ActivateBestChain(state, chainparams, std::move(pblock));
}
bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex)
{
{
LOCK(cs_main);
- if (pindex->nChainWork < chainActive.Tip()->nChainWork) {
+ if (pindex->nChainWork < m_chain.Tip()->nChainWork) {
// Nothing to do, this block is not at the tip.
return true;
}
- if (chainActive.Tip()->nChainWork > nLastPreciousChainwork) {
+ if (m_chain.Tip()->nChainWork > nLastPreciousChainwork) {
// The chain has been extended since the last call, reset the counter.
nBlockReverseSequenceId = -1;
}
- nLastPreciousChainwork = chainActive.Tip()->nChainWork;
+ nLastPreciousChainwork = m_chain.Tip()->nChainWork;
setBlockIndexCandidates.erase(pindex);
pindex->nSequenceId = nBlockReverseSequenceId;
if (nBlockReverseSequenceId > std::numeric_limits<int32_t>::min()) {
@@ -2782,75 +2648,96 @@ bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& par
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);
+ return ::ChainstateActive().PreciousBlock(state, params, pindex);
}
bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
{
- AssertLockHeld(cs_main);
+ CBlockIndex* to_mark_failed = pindex;
+ bool pindex_was_in_chain = false;
+ int disconnected = 0;
- // 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).
+ // Disconnect (descendants of) pindex, and mark them invalid.
+ while (true) {
+ if (ShutdownRequested()) break;
- bool pindex_was_in_chain = false;
- CBlockIndex *invalid_walk_tip = chainActive.Tip();
+ // Make sure the queue of validation callbacks doesn't grow unboundedly.
+ LimitValidationInterfaceQueue();
- DisconnectedBlockTransactions disconnectpool;
- while (chainActive.Contains(pindex)) {
+ LOCK(cs_main);
+ if (!m_chain.Contains(pindex)) break;
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)) {
- // It's probably hopeless to try to make the mempool consistent
- // here if DisconnectTip failed, but we can try.
- UpdateMempoolForReorg(disconnectpool, false);
- return false;
- }
- }
+ CBlockIndex *invalid_walk_tip = m_chain.Tip();
- // 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;
+ // ActivateBestChain considers blocks already in m_chain
+ // unconditionally valid already, so force disconnect away from it.
+ DisconnectedBlockTransactions disconnectpool;
+ bool ret = DisconnectTip(state, chainparams, &disconnectpool);
+ // DisconnectTip will add transactions to disconnectpool.
+ // Adjust the mempool to be consistent with the new tip, adding
+ // transactions back to the mempool if disconnecting was successful,
+ // and we're not doing a very deep invalidation (in which case
+ // keeping the mempool up to date is probably futile anyway).
+ UpdateMempoolForReorg(disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
+ if (!ret) return false;
+ assert(invalid_walk_tip->pprev == m_chain.Tip());
+
+ // We immediately mark the disconnected 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).
+ invalid_walk_tip->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(invalid_walk_tip);
setBlockIndexCandidates.erase(invalid_walk_tip);
- invalid_walk_tip = invalid_walk_tip->pprev;
+ setBlockIndexCandidates.insert(invalid_walk_tip->pprev);
+ if (invalid_walk_tip->pprev == to_mark_failed && (to_mark_failed->nStatus & BLOCK_FAILED_VALID)) {
+ // We only want to mark the last disconnected block as BLOCK_FAILED_VALID; its children
+ // need to be BLOCK_FAILED_CHILD instead.
+ to_mark_failed->nStatus = (to_mark_failed->nStatus ^ BLOCK_FAILED_VALID) | BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(to_mark_failed);
+ }
+
+ // Track the last disconnected block, so we can correct its BLOCK_FAILED_CHILD status in future
+ // iterations, or, if it's the last one, call InvalidChainFound on it.
+ to_mark_failed = invalid_walk_tip;
}
- // Mark the block itself as invalid.
- pindex->nStatus |= BLOCK_FAILED_VALID;
- setDirtyBlockIndex.insert(pindex);
- setBlockIndexCandidates.erase(pindex);
- m_failed_blocks.insert(pindex);
+ {
+ LOCK(cs_main);
+ if (m_chain.Contains(to_mark_failed)) {
+ // If the to-be-marked invalid block is in the active chain, something is interfering and we can't proceed.
+ return false;
+ }
- // DisconnectTip will add transactions to disconnectpool; try to add these
- // back to the mempool.
- UpdateMempoolForReorg(disconnectpool, true);
+ // Mark pindex (or the last disconnected block) as invalid, even when it never was in the main chain
+ to_mark_failed->nStatus |= BLOCK_FAILED_VALID;
+ setDirtyBlockIndex.insert(to_mark_failed);
+ setBlockIndexCandidates.erase(to_mark_failed);
+ m_failed_blocks.insert(to_mark_failed);
- // The resulting new best tip may not be in setBlockIndexCandidates anymore, so
- // add it again.
- BlockMap::iterator it = mapBlockIndex.begin();
- while (it != mapBlockIndex.end()) {
- if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
- setBlockIndexCandidates.insert(it->second);
+ // The resulting new best tip may not be in setBlockIndexCandidates anymore, so
+ // add it again.
+ BlockMap::iterator it = mapBlockIndex.begin();
+ while (it != mapBlockIndex.end()) {
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, m_chain.Tip())) {
+ setBlockIndexCandidates.insert(it->second);
+ }
+ it++;
}
- it++;
- }
- InvalidChainFound(pindex);
+ InvalidChainFound(to_mark_failed);
+ }
// Only notify about a new block tip if the active chain was modified.
if (pindex_was_in_chain) {
- uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev);
+ uiInterface.NotifyBlockTip(IsInitialBlockDownload(), to_mark_failed->pprev);
}
return true;
}
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) {
- return g_chainstate.InvalidateBlock(state, chainparams, pindex);
+ return ::ChainstateActive().InvalidateBlock(state, chainparams, pindex);
}
void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
@@ -2864,7 +2751,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
it->second->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(it->second);
- if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), it->second)) {
setBlockIndexCandidates.insert(it->second);
}
if (it->second == pindexBestInvalid) {
@@ -2888,7 +2775,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
}
void ResetBlockFailureFlags(CBlockIndex *pindex) {
- return g_chainstate.ResetBlockFailureFlags(pindex);
+ return ::ChainstateActive().ResetBlockFailureFlags(pindex);
}
CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block)
@@ -2928,7 +2815,7 @@ CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block)
}
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
-void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
+void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams)
{
pindexNew->nTx = block.vtx.size();
pindexNew->nChainTx = 0;
@@ -2956,7 +2843,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
LOCK(cs_nBlockSequenceId);
pindex->nSequenceId = nBlockSequenceId++;
}
- if (chainActive.Tip() == nullptr || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) {
+ if (m_chain.Tip() == nullptr || !setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) {
setBlockIndexCandidates.insert(pindex);
}
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex);
@@ -2974,7 +2861,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
}
}
-static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
+static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
{
LOCK(cs_LastBlockFile);
@@ -3009,21 +2896,13 @@ static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int
vinfoBlockFile[nFile].nSize += nAddSize;
if (!fKnown) {
- unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE;
- unsigned int nNewChunks = (vinfoBlockFile[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE;
- if (nNewChunks > nOldChunks) {
- if (fPruneMode)
- fCheckForPruning = true;
- if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos, true)) {
- FILE *file = OpenBlockFile(pos);
- if (file) {
- LogPrintf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile);
- AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos);
- fclose(file);
- }
- }
- else
- return error("out of disk space");
+ bool out_of_space;
+ size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space);
+ if (out_of_space) {
+ return AbortNode("Disk space is low!", _("Error: Disk space is low!"));
+ }
+ if (bytes_allocated != 0 && fPruneMode) {
+ fCheckForPruning = true;
}
}
@@ -3031,32 +2910,23 @@ static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int
return true;
}
-static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
+static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &pos, unsigned int nAddSize)
{
pos.nFile = nFile;
LOCK(cs_LastBlockFile);
- unsigned int nNewSize;
pos.nPos = vinfoBlockFile[nFile].nUndoSize;
- nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize;
+ vinfoBlockFile[nFile].nUndoSize += nAddSize;
setDirtyFileInfo.insert(nFile);
- unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
- unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
- if (nNewChunks > nOldChunks) {
- if (fPruneMode)
- fCheckForPruning = true;
- if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos, true)) {
- FILE *file = OpenUndoFile(pos);
- if (file) {
- LogPrintf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile);
- AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos);
- fclose(file);
- }
- }
- else
- return state.Error("out of disk space");
+ bool out_of_space;
+ size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space);
+ if (out_of_space) {
+ return AbortNode(state, "Disk space is low!", _("Error: Disk space is low!"));
+ }
+ if (bytes_allocated != 0 && fPruneMode) {
+ fCheckForPruning = true;
}
return true;
@@ -3066,7 +2936,7 @@ static bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state,
{
// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams))
- return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed");
+ return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, false, REJECT_INVALID, "high-hash", "proof of work failed");
return true;
}
@@ -3088,13 +2958,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
bool mutated;
uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
if (block.hashMerkleRoot != hashMerkleRoot2)
- return state.DoS(100, false, REJECT_INVALID, "bad-txnmrklroot", true, "hashMerkleRoot mismatch");
+ return state.Invalid(ValidationInvalidReason::BLOCK_MUTATED, false, REJECT_INVALID, "bad-txnmrklroot", "hashMerkleRoot mismatch");
// Check for merkle tree malleability (CVE-2012-2459): repeating sequences
// of transactions in a block without affecting the merkle root of a block,
// while still invalidating it.
if (mutated)
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-duplicate", true, "duplicate transaction");
+ return state.Invalid(ValidationInvalidReason::BLOCK_MUTATED, false, REJECT_INVALID, "bad-txns-duplicate", "duplicate transaction");
}
// All potential-corruption validation must be done before we do any
@@ -3105,19 +2975,19 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
// Size limits
if (block.vtx.empty() || block.vtx.size() * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT || ::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
- return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-blk-length", "size limits failed");
// First transaction must be coinbase, the rest must not be
if (block.vtx.empty() || !block.vtx[0]->IsCoinBase())
- return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, "first tx is not coinbase");
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-missing", "first tx is not coinbase");
for (unsigned int i = 1; i < block.vtx.size(); i++)
if (block.vtx[i]->IsCoinBase())
- return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase");
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-multiple", "more than one coinbase");
// Check transactions
for (const auto& tx : block.vtx)
if (!CheckTransaction(*tx, state, true))
- return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(),
+ return state.Invalid(state.GetReason(), false, state.GetRejectCode(), state.GetRejectReason(),
strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), state.GetDebugMessage()));
unsigned int nSigOps = 0;
@@ -3126,7 +2996,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
nSigOps += GetLegacySigOpCount(*tx);
}
if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST)
- return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount");
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-blk-sigops", "out-of-bounds SigOpCount");
if (fCheckPOW && fCheckMerkleRoot)
block.fChecked = true;
@@ -3202,6 +3072,22 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
return commitment;
}
+//! Returns last CBlockIndex* that is a checkpoint
+static CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ const MapCheckpoints& checkpoints = data.mapCheckpoints;
+
+ for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
+ {
+ const uint256& hash = i.second;
+ CBlockIndex* pindex = LookupBlockIndex(hash);
+ if (pindex) {
+ return pindex;
+ }
+ }
+ return nullptr;
+}
+
/** 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().
@@ -3211,7 +3097,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
* 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)
+static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
assert(pindexPrev != nullptr);
const int nHeight = pindexPrev->nHeight + 1;
@@ -3219,32 +3105,32 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
// Check proof of work
const Consensus::Params& consensusParams = params.GetConsensus();
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
- return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work");
+ return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, false, REJECT_INVALID, "bad-diffbits", "incorrect proof of work");
// Check against checkpoints
if (fCheckpointsEnabled) {
// Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
// MapBlockIndex.
- CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(params.Checkpoints());
+ CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight)
- return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint");
+ return state.Invalid(ValidationInvalidReason::BLOCK_CHECKPOINT, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint");
}
// Check timestamp against prev
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
- return state.Invalid(false, REJECT_INVALID, "time-too-old", "block's timestamp is too early");
+ return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, false, REJECT_INVALID, "time-too-old", "block's timestamp is too early");
// Check timestamp
if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME)
- return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future");
+ return state.Invalid(ValidationInvalidReason::BLOCK_TIME_FUTURE, false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future");
// Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
// check for version 2, 3 and 4 upgrades
if((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) ||
(block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) ||
(block.nVersion < 4 && nHeight >= consensusParams.BIP65Height))
- return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion),
+ return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion),
strprintf("rejected nVersion=0x%08x block", block.nVersion));
return true;
@@ -3274,7 +3160,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
// Check that all transactions are finalized
for (const auto& tx : block.vtx) {
if (!IsFinalTx(*tx, nHeight, nLockTimeCutoff)) {
- return state.DoS(10, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction");
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-nonfinal", "non-final transaction");
}
}
@@ -3284,7 +3170,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
CScript expect = CScript() << nHeight;
if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() ||
!std::equal(expect.begin(), expect.end(), block.vtx[0]->vin[0].scriptSig.begin())) {
- return state.DoS(100, false, REJECT_INVALID, "bad-cb-height", false, "block height mismatch in coinbase");
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-height", "block height mismatch in coinbase");
}
}
@@ -3306,11 +3192,11 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
// already does not permit it, it is impossible to trigger in the
// witness tree.
if (block.vtx[0]->vin[0].scriptWitness.stack.size() != 1 || block.vtx[0]->vin[0].scriptWitness.stack[0].size() != 32) {
- return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness reserved value size", __func__));
+ return state.Invalid(ValidationInvalidReason::BLOCK_MUTATED, false, REJECT_INVALID, "bad-witness-nonce-size", strprintf("%s : invalid witness reserved value size", __func__));
}
CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0]->vin[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin());
if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
- return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch", __func__));
+ return state.Invalid(ValidationInvalidReason::BLOCK_MUTATED, false, REJECT_INVALID, "bad-witness-merkle-match", strprintf("%s : witness merkle commitment mismatch", __func__));
}
fHaveWitness = true;
}
@@ -3320,7 +3206,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
if (!fHaveWitness) {
for (const auto& tx : block.vtx) {
if (tx->HasWitness()) {
- return state.DoS(100, false, REJECT_INVALID, "unexpected-witness", true, strprintf("%s : unexpected witness data found", __func__));
+ return state.Invalid(ValidationInvalidReason::BLOCK_MUTATED, false, REJECT_INVALID, "unexpected-witness", strprintf("%s : unexpected witness data found", __func__));
}
}
}
@@ -3332,7 +3218,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
// the block hash, so we couldn't mark the block as permanently
// failed).
if (GetBlockWeight(block) > MAX_BLOCK_WEIGHT) {
- return state.DoS(100, false, REJECT_INVALID, "bad-blk-weight", false, strprintf("%s : weight limit failed", __func__));
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-blk-weight", strprintf("%s : weight limit failed", __func__));
}
return true;
@@ -3352,7 +3238,7 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
if (ppindex)
*ppindex = pindex;
if (pindex->nStatus & BLOCK_FAILED_MASK)
- return state.Invalid(error("%s: block %s is marked invalid", __func__, hash.ToString()), 0, "duplicate");
+ return state.Invalid(ValidationInvalidReason::CACHED_INVALID, error("%s: block %s is marked invalid", __func__, hash.ToString()), 0, "duplicate");
return true;
}
@@ -3363,10 +3249,10 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
CBlockIndex* pindexPrev = nullptr;
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
- return state.DoS(10, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
+ return state.Invalid(ValidationInvalidReason::BLOCK_MISSING_PREV, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
pindexPrev = (*mi).second;
if (pindexPrev->nStatus & BLOCK_FAILED_MASK)
- return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
+ return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, 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));
@@ -3403,7 +3289,7 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
setDirtyBlockIndex.insert(invalid_walk);
invalid_walk = invalid_walk->pprev;
}
- return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
+ return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
}
}
}
@@ -3427,7 +3313,7 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
LOCK(cs_main);
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
- if (!g_chainstate.AcceptBlockHeader(header, state, chainparams, &pindex)) {
+ if (!::ChainstateActive().AcceptBlockHeader(header, state, chainparams, &pindex)) {
if (first_invalid) *first_invalid = header;
return false;
}
@@ -3441,26 +3327,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 CDiskBlockPos SaveBlockToDisk(const CBlock& block, int nHeight, const CChainParams& chainparams, const CDiskBlockPos* dbp) {
+static FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, const CChainParams& chainparams, const FlatFilePos* dbp) {
unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION);
- CDiskBlockPos blockPos;
+ FlatFilePos blockPos;
if (dbp != nullptr)
blockPos = *dbp;
if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != nullptr)) {
error("%s: FindBlockPos failed", __func__);
- return CDiskBlockPos();
+ return FlatFilePos();
}
if (dbp == nullptr) {
if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) {
AbortNode("Failed to write block");
- return CDiskBlockPos();
+ return FlatFilePos();
}
}
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)
+bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock)
{
const CBlock& block = *pblock;
@@ -3477,13 +3363,13 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
// 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 fHasMoreOrSameWork = (chainActive.Tip() ? pindex->nChainWork >= chainActive.Tip()->nChainWork : true);
+ bool fHasMoreOrSameWork = (m_chain.Tip() ? pindex->nChainWork >= m_chain.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
// regardless of whether pruning is enabled; it should generally be safe to
// not process unrequested blocks.
- bool fTooFarAhead = (pindex->nHeight > int(chainActive.Height() + MIN_BLOCKS_TO_KEEP));
+ bool fTooFarAhead = (pindex->nHeight > int(m_chain.Height() + MIN_BLOCKS_TO_KEEP));
// TODO: Decouple this function from the block download logic by removing fRequested
// This requires some new chain data structure to efficiently look up if a
@@ -3507,7 +3393,8 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
if (!CheckBlock(block, state, chainparams.GetConsensus()) ||
!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) {
- if (state.IsInvalid() && !state.CorruptionPossible()) {
+ assert(IsBlockReason(state.GetReason()));
+ if (state.IsInvalid() && state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
}
@@ -3516,13 +3403,13 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
// Header is valid/has work, merkle tree and segwit merkle tree are good...RELAY NOW
// (but if it does not build on our best tip, let the SendMessages loop relay it)
- if (!IsInitialBlockDownload() && chainActive.Tip() == pindex->pprev)
+ if (!IsInitialBlockDownload() && m_chain.Tip() == pindex->pprev)
GetMainSignals().NewPoWValidBlock(pindex, pblock);
// Write block to history file
if (fNewBlock) *fNewBlock = true;
try {
- CDiskBlockPos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp);
+ FlatFilePos 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;
@@ -3557,7 +3444,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
if (ret) {
// Store to disk
- ret = g_chainstate.AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
+ ret = ::ChainstateActive().AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
}
if (!ret) {
GetMainSignals().BlockChecked(*pblock, state);
@@ -3568,7 +3455,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
NotifyHeaderTip();
CValidationState state; // Only used to report errors, not invalidity - ignore it
- if (!g_chainstate.ActivateBestChain(state, chainparams, pblock))
+ if (!::ChainstateActive().ActivateBestChain(state, chainparams, pblock))
return error("%s: ActivateBestChain failed (%s)", __func__, FormatStateMessage(state));
return true;
@@ -3577,7 +3464,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot)
{
AssertLockHeld(cs_main);
- assert(pindexPrev && pindexPrev == chainActive.Tip());
+ assert(pindexPrev && pindexPrev == ::ChainActive().Tip());
CCoinsViewCache viewNew(pcoinsTip.get());
uint256 block_hash(block.GetHash());
CBlockIndex indexDummy(block);
@@ -3592,7 +3479,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 (!g_chainstate.ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
+ if (!::ChainstateActive().ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
return false;
assert(state.IsValid());
@@ -3653,9 +3540,9 @@ void PruneOneBlockFile(const int fileNumber)
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
{
for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) {
- CDiskBlockPos pos(*it, 0);
- fs::remove(GetBlockPosFilename(pos, "blk"));
- fs::remove(GetBlockPosFilename(pos, "rev"));
+ FlatFilePos pos(*it, 0);
+ fs::remove(BlockFileSeq().FileName(pos));
+ fs::remove(UndoFileSeq().FileName(pos));
LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
}
}
@@ -3666,11 +3553,11 @@ static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPr
assert(fPruneMode && nManualPruneHeight > 0);
LOCK2(cs_main, cs_LastBlockFile);
- if (chainActive.Tip() == nullptr)
+ if (::ChainActive().Tip() == nullptr)
return;
// last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip)
- unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP);
+ unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, ::ChainActive().Tip()->nHeight - MIN_BLOCKS_TO_KEEP);
int count=0;
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune)
@@ -3687,7 +3574,8 @@ void PruneBlockFilesManual(int nManualPruneHeight)
{
CValidationState state;
const CChainParams& chainparams = Params();
- if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
+ if (!::ChainstateActive().FlushStateToDisk(
+ chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
}
}
@@ -3710,14 +3598,14 @@ void PruneBlockFilesManual(int nManualPruneHeight)
static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
{
LOCK2(cs_main, cs_LastBlockFile);
- if (chainActive.Tip() == nullptr || nPruneTarget == 0) {
+ if (::ChainActive().Tip() == nullptr || nPruneTarget == 0) {
return;
}
- if ((uint64_t)chainActive.Tip()->nHeight <= nPruneAfterHeight) {
+ if ((uint64_t)::ChainActive().Tip()->nHeight <= nPruneAfterHeight) {
return;
}
- unsigned int nLastBlockWeCanPrune = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP;
+ unsigned int nLastBlockWeCanPrune = ::ChainActive().Tip()->nHeight - MIN_BLOCKS_TO_KEEP;
uint64_t nCurrentUsage = CalculateCurrentUsage();
// We don't check to prune until after we've allocated new space for files
// So we should leave a buffer under our target to account for another allocation
@@ -3731,7 +3619,7 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
// To avoid excessive prune events negating the benefit of high dbcache
// values, we should not prune too rapidly.
// So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
- if (IsInitialBlockDownload()) {
+ if (::ChainstateActive().IsInitialBlockDownload()) {
// Since this is only relevant during IBD, we use a fixed 10%
nBuffer += nPruneTarget / 10;
}
@@ -3763,52 +3651,28 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
nLastBlockWeCanPrune, count);
}
-bool CheckDiskSpace(uint64_t nAdditionalBytes, bool blocks_dir)
+static FlatFileSeq BlockFileSeq()
{
- uint64_t nFreeBytesAvailable = fs::space(blocks_dir ? GetBlocksDir() : GetDataDir()).available;
-
- // Check for nMinDiskSpace bytes (currently 50MB)
- if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes)
- return AbortNode("Disk space is low!", _("Error: Disk space is low!"));
-
- return true;
+ return FlatFileSeq(GetBlocksDir(), "blk", BLOCKFILE_CHUNK_SIZE);
}
-static FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
+static FlatFileSeq UndoFileSeq()
{
- if (pos.IsNull())
- return nullptr;
- fs::path path = GetBlockPosFilename(pos, prefix);
- fs::create_directories(path.parent_path());
- FILE* file = fsbridge::fopen(path, fReadOnly ? "rb": "rb+");
- if (!file && !fReadOnly)
- file = fsbridge::fopen(path, "wb+");
- if (!file) {
- LogPrintf("Unable to open file %s\n", path.string());
- return nullptr;
- }
- if (pos.nPos) {
- if (fseek(file, pos.nPos, SEEK_SET)) {
- LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string());
- fclose(file);
- return nullptr;
- }
- }
- return file;
+ return FlatFileSeq(GetBlocksDir(), "rev", UNDOFILE_CHUNK_SIZE);
}
-FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) {
- return OpenDiskFile(pos, "blk", fReadOnly);
+FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly) {
+ return BlockFileSeq().Open(pos, fReadOnly);
}
/** Open an undo file (rev?????.dat) */
-static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
- return OpenDiskFile(pos, "rev", fReadOnly);
+static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly) {
+ return UndoFileSeq().Open(pos, fReadOnly);
}
-fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix)
+fs::path GetBlockPosFilename(const FlatFilePos &pos)
{
- return GetBlocksDir() / strprintf("%s%05u.dat", prefix, pos.nFile);
+ return BlockFileSeq().FileName(pos);
}
CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash)
@@ -3847,6 +3711,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
sort(vSortedByHeight.begin(), vSortedByHeight.end());
for (const std::pair<int, CBlockIndex*>& item : vSortedByHeight)
{
+ if (ShutdownRequested()) return false;
CBlockIndex* pindex = item.second;
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
@@ -3883,7 +3748,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- if (!g_chainstate.LoadBlockIndex(chainparams.GetConsensus(), *pblocktree))
+ if (!::ChainstateActive().LoadBlockIndex(chainparams.GetConsensus(), *pblocktree))
return false;
// Load block file info
@@ -3915,7 +3780,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
}
for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++)
{
- CDiskBlockPos pos(*it, 0);
+ FlatFilePos pos(*it, 0);
if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION).IsNull()) {
return false;
}
@@ -3937,33 +3802,23 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
bool LoadChainTip(const CChainParams& chainparams)
{
AssertLockHeld(cs_main);
+ assert(!pcoinsTip->GetBestBlock().IsNull()); // Never called when the coins view is empty
- if (chainActive.Tip() && chainActive.Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) return true;
-
- if (pcoinsTip->GetBestBlock().IsNull() && mapBlockIndex.size() == 1) {
- // In case we just added the genesis block, connect it now, so
- // that we always have a chainActive.Tip() when we return.
- LogPrintf("%s: Connecting genesis block...\n", __func__);
- CValidationState state;
- if (!ActivateBestChain(state, chainparams)) {
- LogPrintf("%s: failed to activate chain (%s)\n", __func__, FormatStateMessage(state));
- return false;
- }
- }
+ if (::ChainActive().Tip() && ::ChainActive().Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) return true;
// Load pointer to end of best chain
CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock());
if (!pindex) {
return false;
}
- chainActive.SetTip(pindex);
+ ::ChainActive().SetTip(pindex);
- g_chainstate.PruneBlockIndexCandidates();
+ ::ChainstateActive().PruneBlockIndexCandidates();
LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n",
- chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(),
- FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()),
- GuessVerificationProgress(chainparams.TxData(), chainActive.Tip()));
+ ::ChainActive().Tip()->GetBlockHash().ToString(), ::ChainActive().Height(),
+ FormatISO8601DateTime(::ChainActive().Tip()->GetBlockTime()),
+ GuessVerificationProgress(chainparams.TxData(), ::ChainActive().Tip()));
return true;
}
@@ -3980,12 +3835,12 @@ CVerifyDB::~CVerifyDB()
bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
{
LOCK(cs_main);
- if (chainActive.Tip() == nullptr || chainActive.Tip()->pprev == nullptr)
+ if (::ChainActive().Tip() == nullptr || ::ChainActive().Tip()->pprev == nullptr)
return true;
// Verify blocks in the best chain
- if (nCheckDepth <= 0 || nCheckDepth > chainActive.Height())
- nCheckDepth = chainActive.Height();
+ if (nCheckDepth <= 0 || nCheckDepth > ::ChainActive().Height())
+ nCheckDepth = ::ChainActive().Height();
nCheckLevel = std::max(0, std::min(4, nCheckLevel));
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
CCoinsViewCache coins(coinsview);
@@ -3995,16 +3850,16 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
CValidationState state;
int reportDone = 0;
LogPrintf("[0%%]..."); /* Continued */
- for (pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
+ for (pindex = ::ChainActive().Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
boost::this_thread::interruption_point();
- const int percentageDone = std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
+ const int percentageDone = std::max(1, std::min(99, (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
if (reportDone < percentageDone/10) {
// report every 10% step
LogPrintf("[%d%%]...", percentageDone); /* Continued */
reportDone = percentageDone/10;
}
uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false);
- if (pindex->nHeight <= chainActive.Height()-nCheckDepth)
+ if (pindex->nHeight <= ::ChainActive().Height()-nCheckDepth)
break;
if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
// If pruning, only go back as far as we have data.
@@ -4031,7 +3886,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
- DisconnectResult res = g_chainstate.DisconnectBlock(block, pindex, coins);
+ DisconnectResult res = ::ChainstateActive().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());
}
@@ -4046,27 +3901,27 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
return true;
}
if (pindexFailure)
- return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
+ return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", ::ChainActive().Height() - pindexFailure->nHeight + 1, nGoodTransactions);
// store block count as we move pindex at check level >= 4
- int block_count = chainActive.Height() - pindex->nHeight;
+ int block_count = ::ChainActive().Height() - pindex->nHeight;
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
- while (pindex != chainActive.Tip()) {
+ while (pindex != ::ChainActive().Tip()) {
boost::this_thread::interruption_point();
- const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
+ const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
if (reportDone < percentageDone/10) {
// report every 10% step
LogPrintf("[%d%%]...", percentageDone); /* Continued */
reportDone = percentageDone/10;
}
uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false);
- pindex = chainActive.Next(pindex);
+ 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 (!g_chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
+ if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, chainparams))
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state));
}
}
@@ -4165,41 +4020,117 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
}
bool ReplayBlocks(const CChainParams& params, CCoinsView* view) {
- return g_chainstate.ReplayBlocks(params, view);
+ return ::ChainstateActive().ReplayBlocks(params, view);
+}
+
+//! Helper for CChainState::RewindBlockIndex
+void CChainState::EraseBlockData(CBlockIndex* index)
+{
+ AssertLockHeld(cs_main);
+ assert(!m_chain.Contains(index)); // Make sure this block isn't active
+
+ // Reduce validity
+ index->nStatus = std::min<unsigned int>(index->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (index->nStatus & ~BLOCK_VALID_MASK);
+ // Remove have-data flags.
+ index->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO);
+ // Remove storage location.
+ index->nFile = 0;
+ index->nDataPos = 0;
+ index->nUndoPos = 0;
+ // Remove various other things
+ index->nTx = 0;
+ index->nChainTx = 0;
+ index->nSequenceId = 0;
+ // Make sure it gets written.
+ setDirtyBlockIndex.insert(index);
+ // Update indexes
+ setBlockIndexCandidates.erase(index);
+ std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> ret = mapBlocksUnlinked.equal_range(index->pprev);
+ while (ret.first != ret.second) {
+ if (ret.first->second == index) {
+ mapBlocksUnlinked.erase(ret.first++);
+ } else {
+ ++ret.first;
+ }
+ }
+ // Mark parent as eligible for main chain again
+ if (index->pprev && index->pprev->IsValid(BLOCK_VALID_TRANSACTIONS) && index->pprev->HaveTxsDownloaded()) {
+ setBlockIndexCandidates.insert(index->pprev);
+ }
}
bool CChainState::RewindBlockIndex(const CChainParams& params)
{
- LOCK(cs_main);
+ // Note that during -reindex-chainstate we are called with an empty m_chain!
- // Note that during -reindex-chainstate we are called with an empty chainActive!
+ // First erase all post-segwit blocks without witness not in the main chain,
+ // as this can we done without costly DisconnectTip calls. Active
+ // blocks will be dealt with below (releasing cs_main in between).
+ {
+ LOCK(cs_main);
+ for (const auto& entry : mapBlockIndex) {
+ if (IsWitnessEnabled(entry.second->pprev, params.GetConsensus()) && !(entry.second->nStatus & BLOCK_OPT_WITNESS) && !m_chain.Contains(entry.second)) {
+ EraseBlockData(entry.second);
+ }
+ }
+ }
+ // Find what height we need to reorganize to.
+ CBlockIndex *tip;
int nHeight = 1;
- while (nHeight <= chainActive.Height()) {
- // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all
- // blocks in ConnectBlock, we don't need to go back and
- // re-download/re-verify blocks from before segwit actually activated.
- if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
- break;
+ {
+ LOCK(cs_main);
+ while (nHeight <= m_chain.Height()) {
+ // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all
+ // blocks in ConnectBlock, we don't need to go back and
+ // re-download/re-verify blocks from before segwit actually activated.
+ if (IsWitnessEnabled(m_chain[nHeight - 1], params.GetConsensus()) && !(m_chain[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
+ break;
+ }
+ nHeight++;
}
- nHeight++;
- }
+ tip = m_chain.Tip();
+ }
// nHeight is now the height of the first insufficiently-validated block, or tipheight + 1
+
CValidationState state;
- CBlockIndex* pindex = chainActive.Tip();
- while (chainActive.Height() >= nHeight) {
- if (fPruneMode && !(chainActive.Tip()->nStatus & BLOCK_HAVE_DATA)) {
- // If pruning, don't try rewinding past the HAVE_DATA point;
- // since older blocks can't be served anyway, there's
- // no need to walk further, and trying to DisconnectTip()
- // will fail (and require a needless reindex/redownload
- // of the blockchain).
- break;
- }
- if (!DisconnectTip(state, params, nullptr)) {
- return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", pindex->nHeight, FormatStateMessage(state));
+ // Loop until the tip is below nHeight, or we reach a pruned block.
+ while (!ShutdownRequested()) {
+ {
+ LOCK(cs_main);
+ // Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active)
+ assert(tip == m_chain.Tip());
+ if (tip == nullptr || tip->nHeight < nHeight) break;
+ if (fPruneMode && !(tip->nStatus & BLOCK_HAVE_DATA)) {
+ // If pruning, don't try rewinding past the HAVE_DATA point;
+ // since older blocks can't be served anyway, there's
+ // no need to walk further, and trying to DisconnectTip()
+ // will fail (and require a needless reindex/redownload
+ // of the blockchain).
+ break;
+ }
+
+ // Disconnect block
+ if (!DisconnectTip(state, params, nullptr)) {
+ return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", tip->nHeight, FormatStateMessage(state));
+ }
+
+ // 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.
+ // Note: If we encounter an insufficiently validated block that
+ // is on m_chain, it must be because we are a pruning node, and
+ // this block or some successor doesn't HAVE_DATA, so we were unable to
+ // rewind all the way. Blocks remaining on m_chain at this point
+ // must not have their validity reduced.
+ EraseBlockData(tip);
+
+ tip = tip->pprev;
}
+ // Make sure the queue of validation callbacks doesn't grow unboundedly.
+ LimitValidationInterfaceQueue();
+
// Occasionally flush state to disk.
if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) {
LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state));
@@ -4207,69 +4138,32 @@ bool CChainState::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 (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
- // this block or some successor doesn't HAVE_DATA, so we were unable to
- // rewind all the way. Blocks remaining on chainActive at this point
- // must not have their validity reduced.
- if (IsWitnessEnabled(pindexIter->pprev, params.GetConsensus()) && !(pindexIter->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(pindexIter)) {
- // Reduce validity
- pindexIter->nStatus = std::min<unsigned int>(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (pindexIter->nStatus & ~BLOCK_VALID_MASK);
- // Remove have-data flags.
- pindexIter->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO);
- // Remove storage location.
- pindexIter->nFile = 0;
- pindexIter->nDataPos = 0;
- pindexIter->nUndoPos = 0;
- // Remove various other things
- pindexIter->nTx = 0;
- pindexIter->nChainTx = 0;
- pindexIter->nSequenceId = 0;
- // Make sure it gets written.
- setDirtyBlockIndex.insert(pindexIter);
- // Update indexes
- setBlockIndexCandidates.erase(pindexIter);
- std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> ret = mapBlocksUnlinked.equal_range(pindexIter->pprev);
- while (ret.first != ret.second) {
- if (ret.first->second == pindexIter) {
- mapBlocksUnlinked.erase(ret.first++);
- } else {
- ++ret.first;
- }
- }
- } else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->HaveTxsDownloaded()) {
- setBlockIndexCandidates.insert(pindexIter);
- }
- }
-
- if (chainActive.Tip() != nullptr) {
- // We can't prune block index candidates based on our tip if we have
- // no tip due to chainActive being empty!
- PruneBlockIndexCandidates();
+ {
+ LOCK(cs_main);
+ if (m_chain.Tip() != nullptr) {
+ // We can't prune block index candidates based on our tip if we have
+ // no tip due to m_chain being empty!
+ PruneBlockIndexCandidates();
- CheckBlockIndex(params.GetConsensus());
+ CheckBlockIndex(params.GetConsensus());
+ }
}
return true;
}
bool RewindBlockIndex(const CChainParams& params) {
- if (!g_chainstate.RewindBlockIndex(params)) {
+ if (!::ChainstateActive().RewindBlockIndex(params)) {
return false;
}
- if (chainActive.Tip() != nullptr) {
- // FlushStateToDisk can possibly read chainActive. Be conservative
+ LOCK(cs_main);
+ 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, FlushStateMode::ALWAYS)) {
+ if (!::ChainstateActive().FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) {
LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state));
return false;
}
@@ -4290,7 +4184,7 @@ void CChainState::UnloadBlockIndex() {
void UnloadBlockIndex()
{
LOCK(cs_main);
- chainActive.SetTip(nullptr);
+ ::ChainActive().SetTip(nullptr);
pindexBestInvalid = nullptr;
pindexBestHeader = nullptr;
mempool.clear();
@@ -4310,7 +4204,7 @@ void UnloadBlockIndex()
mapBlockIndex.clear();
fHavePruned = false;
- g_chainstate.UnloadBlockIndex();
+ ::ChainstateActive().UnloadBlockIndex();
}
bool LoadBlockIndex(const CChainParams& chainparams)
@@ -4340,15 +4234,15 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
LOCK(cs_main);
// Check whether we're already initialized by checking for genesis in
- // mapBlockIndex. Note that we can't use chainActive here, since it is
+ // mapBlockIndex. Note that we can't use m_chain here, since it is
// set based on the coins db, not the block index db, which is the only
// thing loaded at this point.
if (mapBlockIndex.count(chainparams.GenesisBlock().GetHash()))
return true;
try {
- CBlock &block = const_cast<CBlock&>(chainparams.GenesisBlock());
- CDiskBlockPos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr);
+ const CBlock& block = chainparams.GenesisBlock();
+ FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr);
if (blockPos.IsNull())
return error("%s: writing genesis block to disk failed", __func__);
CBlockIndex *pindex = AddToBlockIndex(block);
@@ -4362,13 +4256,13 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
bool LoadGenesisBlock(const CChainParams& chainparams)
{
- return g_chainstate.LoadGenesisBlock(chainparams);
+ return ::ChainstateActive().LoadGenesisBlock(chainparams);
}
-bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp)
+bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos *dbp)
{
// Map of disk positions for blocks with unknown parent (only used for reindex)
- static std::multimap<uint256, CDiskBlockPos> mapBlocksUnknownParent;
+ static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent;
int64_t nStart = GetTimeMillis();
int nLoaded = 0;
@@ -4427,7 +4321,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
CBlockIndex* pindex = LookupBlockIndex(hash);
if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) {
CValidationState state;
- if (g_chainstate.AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) {
+ if (::ChainstateActive().AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) {
nLoaded++;
}
if (state.IsError()) {
@@ -4454,9 +4348,9 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
while (!queue.empty()) {
uint256 head = queue.front();
queue.pop_front();
- std::pair<std::multimap<uint256, CDiskBlockPos>::iterator, std::multimap<uint256, CDiskBlockPos>::iterator> range = mapBlocksUnknownParent.equal_range(head);
+ std::pair<std::multimap<uint256, FlatFilePos>::iterator, std::multimap<uint256, FlatFilePos>::iterator> range = mapBlocksUnknownParent.equal_range(head);
while (range.first != range.second) {
- std::multimap<uint256, CDiskBlockPos>::iterator it = range.first;
+ std::multimap<uint256, FlatFilePos>::iterator it = range.first;
std::shared_ptr<CBlock> pblockrecursive = std::make_shared<CBlock>();
if (ReadBlockFromDisk(*pblockrecursive, it->second, chainparams.GetConsensus()))
{
@@ -4464,7 +4358,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
head.ToString());
LOCK(cs_main);
CValidationState dummy;
- if (g_chainstate.AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
+ if (::ChainstateActive().AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
{
nLoaded++;
queue.push_back(pblockrecursive->GetHash());
@@ -4497,8 +4391,8 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
// During a reindex, we read the genesis block and call CheckBlockIndex before ActivateBestChain,
// so we have the genesis block in mapBlockIndex but no active chain. (A few of the tests when
- // iterating the block tree require that chainActive has been initialized.)
- if (chainActive.Height() < 0) {
+ // iterating the block tree require that m_chain has been initialized.)
+ if (m_chain.Height() < 0) {
assert(mapBlockIndex.size() <= 1);
return;
}
@@ -4542,7 +4436,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
if (pindex->pprev == nullptr) {
// Genesis block checks.
assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match.
- assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block.
+ assert(pindex == m_chain.Genesis()); // The current active chain's genesis block must be this block.
}
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
@@ -4571,13 +4465,13 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
// Checks for not-invalid blocks.
assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents.
}
- if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstNeverProcessed == nullptr) {
+ if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && pindexFirstNeverProcessed == nullptr) {
if (pindexFirstInvalid == nullptr) {
// If this block sorts at least as good as the current tip and
// is valid and we have all data for its parents, it must be in
- // setBlockIndexCandidates. chainActive.Tip() must also be there
+ // setBlockIndexCandidates. m_chain.Tip() must also be there
// even if some data has been pruned.
- if (pindexFirstMissing == nullptr || pindex == chainActive.Tip()) {
+ if (pindexFirstMissing == nullptr || pindex == m_chain.Tip()) {
assert(setBlockIndexCandidates.count(pindex));
}
// If some parent is missing, then it could be that this block was in
@@ -4611,11 +4505,11 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
// - it has a descendant that at some point had more work than the
// tip, and
// - we tried switching to that descendant but were missing
- // data for some intermediate block between chainActive and the
+ // data for some intermediate block between m_chain and the
// tip.
- // So if this block is itself better than chainActive.Tip() and it wasn't in
+ // So if this block is itself better than m_chain.Tip() and it wasn't in
// setBlockIndexCandidates, then it must be in mapBlocksUnlinked.
- if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && setBlockIndexCandidates.count(pindex) == 0) {
+ if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && setBlockIndexCandidates.count(pindex) == 0) {
if (pindexFirstInvalid == nullptr) {
assert(foundInUnlinked);
}
@@ -4686,24 +4580,24 @@ CBlockFileInfo* GetBlockFileInfo(size_t n)
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
LOCK(cs_main);
- return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache);
+ return VersionBitsState(::ChainActive().Tip(), params, pos, versionbitscache);
}
BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
LOCK(cs_main);
- return VersionBitsStatistics(chainActive.Tip(), params, pos);
+ return VersionBitsStatistics(::ChainActive().Tip(), params, pos);
}
int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
LOCK(cs_main);
- return VersionBitsStateSinceHeight(chainActive.Tip(), params, pos, versionbitscache);
+ return VersionBitsStateSinceHeight(::ChainActive().Tip(), params, pos, versionbitscache);
}
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
-bool LoadMempool()
+bool LoadMempool(CTxMemPool& pool)
{
const CChainParams& chainparams = Params();
int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
@@ -4738,12 +4632,12 @@ bool LoadMempool()
CAmount amountdelta = nFeeDelta;
if (amountdelta) {
- mempool.PrioritiseTransaction(tx->GetHash(), amountdelta);
+ pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
}
CValidationState state;
if (nTime + nExpiryTimeout > nNow) {
LOCK(cs_main);
- AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, nullptr /* pfMissingInputs */, nTime,
+ AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nullptr /* pfMissingInputs */, nTime,
nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */,
false /* test_accept */);
if (state.IsValid()) {
@@ -4753,7 +4647,7 @@ bool LoadMempool()
// 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())) {
+ if (pool.exists(tx->GetHash())) {
++already_there;
} else {
++failed;
@@ -4769,7 +4663,7 @@ bool LoadMempool()
file >> mapDeltas;
for (const auto& i : mapDeltas) {
- mempool.PrioritiseTransaction(i.first, i.second);
+ pool.PrioritiseTransaction(i.first, i.second);
}
} catch (const std::exception& e) {
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
@@ -4780,7 +4674,7 @@ bool LoadMempool()
return true;
}
-bool DumpMempool()
+bool DumpMempool(const CTxMemPool& pool)
{
int64_t start = GetTimeMicros();
@@ -4791,11 +4685,11 @@ bool DumpMempool()
LOCK(dump_mutex);
{
- LOCK(mempool.cs);
- for (const auto &i : mempool.mapDeltas) {
+ LOCK(pool.cs);
+ for (const auto &i : pool.mapDeltas) {
mapDeltas[i.first] = i.second;
}
- vinfo = mempool.infoAll();
+ vinfo = pool.infoAll();
}
int64_t mid = GetTimeMicros();
diff --git a/src/validation.h b/src/validation.h
index b16d8438d7..638229952d 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -14,13 +14,14 @@
#include <coins.h>
#include <crypto/common.h> // for ReadLE64
#include <fs.h>
-#include <protocol.h> // For CMessageHeader::MessageStartChars
#include <policy/feerate.h>
+#include <protocol.h> // For CMessageHeader::MessageStartChars
#include <script/script_error.h>
#include <sync.h>
#include <versionbits.h>
#include <algorithm>
+#include <atomic>
#include <exception>
#include <map>
#include <memory>
@@ -30,10 +31,10 @@
#include <utility>
#include <vector>
-#include <atomic>
-
+class CChainState;
class CBlockIndex;
class CBlockTreeDB;
+class CBlockUndo;
class CChainParams;
class CCoinsViewDB;
class CInv;
@@ -44,6 +45,7 @@ class CTxMemPool;
class CValidationState;
struct ChainTxData;
+struct DisconnectedBlockTransactions;
struct PrecomputedTransactionData;
struct LockPoints;
@@ -53,12 +55,6 @@ static const bool DEFAULT_WHITELISTRELAY = true;
static const bool DEFAULT_WHITELISTFORCERELAY = false;
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
-//! -maxtxfee default
-static const CAmount DEFAULT_TRANSACTION_MAXFEE = COIN / 10;
-//! Discourage users to set fees higher than this amount (in satoshis) per kB
-static const CAmount HIGH_TX_FEE_PER_KB = COIN / 100;
-//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
-static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB;
/** Default for -limitancestorcount, max number of in-mempool ancestors */
static const unsigned int DEFAULT_ANCESTOR_LIMIT = 25;
/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */
@@ -114,15 +110,12 @@ static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
/** Maximum age of our tip in seconds for us to be considered current for fee estimation */
static const int64_t MAX_FEE_ESTIMATION_TIP_AGE = 3 * 60 * 60;
-/** Default for -permitbaremultisig */
-static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
static const bool DEFAULT_TXINDEX = false;
+static const char* const DEFAULT_BLOCKFILTERINDEX = "0";
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
/** Default for -persistmempool */
static const bool DEFAULT_PERSIST_MEMPOOL = true;
-/** Default for -mempoolreplacement */
-static const bool DEFAULT_ENABLE_REPLACEMENT = true;
/** Default for using fee filter */
static const bool DEFAULT_FEEFILTER = true;
@@ -149,30 +142,22 @@ extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main;
extern CBlockPolicyEstimator feeEstimator;
extern CTxMemPool mempool;
-extern std::atomic_bool g_is_mempool_loaded;
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
-extern BlockMap& mapBlockIndex;
-extern uint64_t nLastBlockTx;
-extern uint64_t nLastBlockWeight;
-extern const std::string strMessageMagic;
+extern BlockMap& mapBlockIndex GUARDED_BY(cs_main);
extern Mutex g_best_block_mutex;
extern std::condition_variable g_best_block_cv;
extern uint256 g_best_block;
extern std::atomic_bool fImporting;
extern std::atomic_bool fReindex;
extern int nScriptCheckThreads;
-extern bool fIsBareMultisigStd;
extern bool fRequireStandard;
extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
extern size_t nCoinCacheUsage;
/** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */
extern CFeeRate minRelayTxFee;
-/** Absolute maximum transaction fee (in satoshis) used by wallet and mempool (rejects high fee in sendrawtransaction) */
-extern CAmount maxTxFee;
/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */
extern int64_t nMaxTipAge;
-extern bool fEnableReplacement;
/** Block hash whose ancestors we will assume to have valid scripts without checking them. */
extern uint256 hashAssumeValid;
@@ -183,9 +168,6 @@ extern arith_uint256 nMinimumChainWork;
/** Best header we've seen so far (used for getheaders queries' starting points). */
extern CBlockIndex *pindexBestHeader;
-/** Minimum disk space required - used in CheckDiskSpace() */
-static const uint64_t nMinDiskSpace = 52428800;
-
/** Pruning-related variables and constants */
/** True if any block files have ever been pruned. */
extern bool fHavePruned;
@@ -193,7 +175,7 @@ extern bool fHavePruned;
extern bool fPruneMode;
/** Number of MiB of block files that we're trying to stay below. */
extern uint64_t nPruneTarget;
-/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */
+/** 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;
@@ -247,14 +229,12 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
*/
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr, CBlockHeader* first_invalid = nullptr) LOCKS_EXCLUDED(cs_main);
-/** Check whether enough disk space is available for an incoming block */
-bool CheckDiskSpace(uint64_t nAdditionalBytes = 0, bool blocks_dir = false);
/** Open a block file (blk?????.dat) */
-FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false);
+FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly = false);
/** Translation to a filesystem path */
-fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix);
+fs::path GetBlockPosFilename(const FlatFilePos &pos);
/** Import blocks from an external file */
-bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp = nullptr);
+bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos *dbp = nullptr);
/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
bool LoadGenesisBlock(const CChainParams& chainparams);
/** Load the block tree and coins database from disk,
@@ -265,9 +245,7 @@ bool LoadChainTip(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_m
/** Unload database information */
void UnloadBlockIndex();
/** Run an instance of the script checking thread */
-void ThreadScriptCheck();
-/** Check whether we are doing an initial block download (synchronizing from disk or network) */
-bool IsInitialBlockDownload();
+void ThreadScriptCheck(int worker_num);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256& hash, CTransactionRef& tx, const Consensus::Params& params, uint256& hashBlock, const CBlockIndex* const blockIndex = nullptr);
/**
@@ -288,17 +266,13 @@ uint64_t CalculateCurrentUsage();
/**
* Mark one block file as pruned.
*/
-void PruneOneBlockFile(const int fileNumber);
+void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Actually unlink the specified files
*/
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
-/** Flush all state, indexes and buffers to disk. */
-void FlushStateToDisk();
-/** Prune block files and flush state to disk. */
-void PruneAndFlush();
/** Prune block files up to a given height */
void PruneBlockFilesManual(int nManualPruneHeight);
@@ -308,9 +282,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Convert CValidationState to a human-readable message for logging */
-std::string FormatStateMessage(const CValidationState &state);
-
/** Get the BIP9 state for a given deployment at the current tip. */
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
@@ -393,11 +364,13 @@ void InitScriptExecutionCache();
/** Functions for disk access for blocks */
-bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
+bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams);
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
-bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start);
+bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, const CMessageHeader::MessageStartChars& message_start);
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start);
+bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex);
+
/** Functions for validating blocks and updating the block tree */
/** Context-independent validity checks */
@@ -413,7 +386,7 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa
bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);
/** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */
-bool RewindBlockIndex(const CChainParams& params);
+bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main);
/** Update uncommitted block structures (currently: only the witness reserved value). This is safe for submitted blocks. */
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams);
@@ -432,7 +405,7 @@ public:
/** Replay blocks that aren't fully applied to the database. */
bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
-inline CBlockIndex* LookupBlockIndex(const uint256& hash)
+inline CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
BlockMap::const_iterator it = mapBlockIndex.find(hash);
@@ -442,6 +415,186 @@ inline CBlockIndex* LookupBlockIndex(const uint256& hash)
/** Find the last common block between the parameter chain and a locator. */
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+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;
+
+/** @see CChainState::FlushStateToDisk */
+enum class FlushStateMode {
+ NONE,
+ IF_NEEDED,
+ PERIODIC,
+ ALWAYS
+};
+
+struct CBlockIndexWorkComparator
+{
+ bool operator()(const CBlockIndex *pa, const CBlockIndex *pb) const;
+};
+
+/**
+ * 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*> m_failed_blocks;
+
+ /**
+ * the ChainState CriticalSection
+ * A lock that must be held when modifying this ChainState - held in ActivateBestChain()
+ */
+ CCriticalSection m_cs_chainstate;
+
+ /**
+ * Whether this chainstate is undergoing initial block download.
+ *
+ * Mutable because we need to be able to mark IsInitialBlockDownload()
+ * const, which latches this for caching purposes.
+ */
+ mutable std::atomic<bool> m_cached_finished_ibd{false};
+
+public:
+ //! The current chain of blockheaders we consult and build on.
+ //! @see CChain, CBlockIndex.
+ CChain m_chain;
+ BlockMap mapBlockIndex GUARDED_BY(cs_main);
+ std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
+ CBlockIndex *pindexBestInvalid = nullptr;
+
+ bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ /**
+ * Update the on-disk chain state.
+ * The caches and indexes are flushed depending on the mode we're called with
+ * if they're too large, if it's been a while since the last write,
+ * or always and in all cases if we're in prune mode and are deleting files.
+ *
+ * If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything
+ * besides checking if we need to prune.
+ */
+ bool FlushStateToDisk(
+ const CChainParams& chainparams,
+ CValidationState &state,
+ FlushStateMode mode,
+ int nManualPruneHeight = 0);
+
+ //! Unconditionally flush all changes to disk.
+ void ForceFlushStateToDisk();
+
+ //! Prune blockfiles from the disk if necessary and then flush chainstate changes
+ //! if we pruned.
+ void PruneAndFlush();
+
+ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main);
+
+ /**
+ * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
+ * that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
+ */
+ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ // 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) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ // Block disconnection on our pcoinsTip:
+ bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ // Manual block validity manipulation:
+ bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
+ bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main);
+ bool LoadGenesisBlock(const CChainParams& chainparams);
+
+ void PruneBlockIndexCandidates();
+
+ void UnloadBlockIndex();
+
+ /** Check whether we are doing an initial block download (synchronizing from disk or network) */
+ bool IsInitialBlockDownload() const;
+
+private:
+ bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /** Create a new block index entry for a given block hash */
+ CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /**
+ * Make various assertions about the state of the block index.
+ *
+ * By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex.
+ */
+ void CheckBlockIndex(const Consensus::Params& consensusParams);
+
+ void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ //! Mark a block as not having block data
+ void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+};
+
/** Mark a block as precious and reorganize.
*
* May not be called in a
@@ -450,13 +603,16 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main);
/** Mark a block as invalid. */
-bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
/** Remove invalidity status from a block and its descendants. */
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** The currently-connected chain of blocks (protected by cs_main). */
-extern CChain& chainActive;
+/** @returns the most-work valid chainstate. */
+CChainState& ChainstateActive();
+
+/** @returns the most-work chain. */
+CChain& ChainActive();
/** Global variable that points to the coins database (protected by cs_main) */
extern std::unique_ptr<CCoinsViewDB> pcoinsdbview;
@@ -493,10 +649,10 @@ static const unsigned int REJECT_HIGHFEE = 0x100;
CBlockFileInfo* GetBlockFileInfo(size_t n);
/** Dump the mempool to disk. */
-bool DumpMempool();
+bool DumpMempool(const CTxMemPool& pool);
/** Load the mempool from disk. */
-bool LoadMempool();
+bool LoadMempool(CTxMemPool& pool);
//! Check whether the block associated with this index entry is pruned or not.
inline bool IsBlockPruned(const CBlockIndex* pblockindex)
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 2e13bef19e..59a620ab95 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -8,8 +8,6 @@
#include <primitives/block.h>
#include <scheduler.h>
#include <txmempool.h>
-#include <util/system.h>
-#include <validation.h>
#include <list>
#include <atomic>
@@ -25,7 +23,6 @@ struct ValidationInterfaceConnections {
boost::signals2::scoped_connection BlockDisconnected;
boost::signals2::scoped_connection TransactionRemovedFromMempool;
boost::signals2::scoped_connection ChainStateFlushed;
- boost::signals2::scoped_connection Broadcast;
boost::signals2::scoped_connection BlockChecked;
boost::signals2::scoped_connection NewPoWValidBlock;
};
@@ -37,7 +34,6 @@ struct MainSignalsInstance {
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool;
boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed;
- boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
@@ -101,13 +97,14 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
conns.BlockDisconnected = g_signals.m_internals->BlockDisconnected.connect(std::bind(&CValidationInterface::BlockDisconnected, pwalletIn, std::placeholders::_1));
conns.TransactionRemovedFromMempool = g_signals.m_internals->TransactionRemovedFromMempool.connect(std::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, std::placeholders::_1));
conns.ChainStateFlushed = g_signals.m_internals->ChainStateFlushed.connect(std::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, std::placeholders::_1));
- conns.Broadcast = g_signals.m_internals->Broadcast.connect(std::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, std::placeholders::_1, std::placeholders::_2));
conns.BlockChecked = g_signals.m_internals->BlockChecked.connect(std::bind(&CValidationInterface::BlockChecked, pwalletIn, std::placeholders::_1, std::placeholders::_2));
conns.NewPoWValidBlock = g_signals.m_internals->NewPoWValidBlock.connect(std::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, std::placeholders::_1, std::placeholders::_2));
}
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
- g_signals.m_internals->m_connMainSignals.erase(pwalletIn);
+ if (g_signals.m_internals) {
+ g_signals.m_internals->m_connMainSignals.erase(pwalletIn);
+ }
}
void UnregisterAllValidationInterfaces() {
@@ -173,10 +170,6 @@ void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) {
});
}
-void CMainSignals::Broadcast(int64_t nBestBlockTime, CConnman* connman) {
- m_internals->Broadcast(nBestBlockTime, connman);
-}
-
void CMainSignals::BlockChecked(const CBlock& block, const CValidationState& state) {
m_internals->BlockChecked(block, state);
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index f0374e8e78..ea1b2e7e76 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -18,7 +18,6 @@ class CBlockIndex;
struct CBlockLocator;
class CBlockIndex;
class CConnman;
-class CReserveScript;
class CValidationInterface;
class CValidationState;
class uint256;
@@ -134,8 +133,6 @@ protected:
* Called on a background thread.
*/
virtual void ChainStateFlushed(const CBlockLocator &locator) {}
- /** Tells listeners to broadcast their data. */
- virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
/**
* Notifies listeners of a block validation result.
* If the provided CValidationState IsValid, the provided block
@@ -184,7 +181,6 @@ public:
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 ChainStateFlushed(const CBlockLocator &);
- void Broadcast(int64_t nBestBlockTime, CConnman* connman);
void BlockChecked(const CBlock&, const CValidationState&);
void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr<const CBlock>&);
};
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 48a924abfb..12ba032dff 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -36,6 +36,8 @@ public:
bool m_avoid_partial_spends;
//! Fee estimation mode to control arguments to estimateSmartFee
FeeEstimateMode m_fee_mode;
+ //! Minimum chain depth value for coin availability
+ int m_min_depth{0};
CCoinControl()
{
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index 1dc78255f6..dd56ea10ab 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -182,7 +182,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no
if (!SetCrypted())
return false;
- bool keyPass = false;
+ bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys
bool keyFail = false;
CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
for (; mi != mapCryptedKeys.end(); ++mi)
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index ae40553268..b5f90deabd 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -1,15 +1,11 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 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/db.h>
-#include <addrman.h>
-#include <hash.h>
-#include <protocol.h>
#include <util/strencodings.h>
-#include <wallet/walletutil.h>
#include <stdint.h>
@@ -84,6 +80,14 @@ bool IsWalletLoaded(const fs::path& wallet_path)
return database && database->IsDatabaseLoaded(database_filename);
}
+fs::path WalletDataFilePath(const fs::path& wallet_path)
+{
+ fs::path env_directory;
+ std::string database_filename;
+ SplitWalletPath(wallet_path, env_directory, database_filename);
+ return env_directory / database_filename;
+}
+
/**
* @param[in] wallet_path Path to wallet directory. Or (for backwards compatibility only) a path to a berkeley btree data file inside a wallet directory.
* @param[out] database_filename Filename of berkeley btree data file inside the wallet directory.
@@ -126,11 +130,18 @@ void BerkeleyEnvironment::Close()
}
}
+ FILE* error_file = nullptr;
+ dbenv->get_errfile(&error_file);
+
int ret = dbenv->close(0);
if (ret != 0)
LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
if (!fMockDb)
DbEnv((u_int32_t)0).remove(strPath.c_str(), 0);
+
+ if (error_file) fclose(error_file);
+
+ UnlockDirectory(strPath, ".walletlock");
}
void BerkeleyEnvironment::Reset()
@@ -147,6 +158,7 @@ BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(dir
BerkeleyEnvironment::~BerkeleyEnvironment()
{
+ LOCK(cs_db);
g_dbenvs.erase(strPath);
Close();
}
@@ -389,14 +401,7 @@ bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, std::string& er
fs::path walletDir = env->Directory();
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(nullptr, nullptr, nullptr));
- LogPrintf("Using wallet %s\n", walletFile);
-
- // Wallet file must be a plain filename without a directory
- if (walletFile != fs::basename(walletFile) + fs::extension(walletFile))
- {
- errorStr = strprintf(_("Wallet %s resides outside wallet directory %s"), walletFile, walletDir.string());
- return false;
- }
+ LogPrintf("Using wallet %s\n", file_path.string());
if (!env->Open(true /* retry */)) {
errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
@@ -598,7 +603,9 @@ void BerkeleyBatch::Flush()
if (fReadOnly)
nMinutes = 1;
- env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
+ if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault
+ env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
+ }
}
void BerkeleyDatabase::IncrementUpdateCounter()
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 9df965305a..b3856fbaf9 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -101,6 +101,9 @@ public:
/** Return whether a wallet database is currently loaded. */
bool IsWalletLoaded(const fs::path& wallet_path);
+/** Given a wallet directory path or legacy file path, return path to main data file in the wallet database. */
+fs::path WalletDataFilePath(const fs::path& wallet_path);
+
/** Get BerkeleyEnvironment and database filename given a wallet path. */
std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 7a71aea715..46cf6b7616 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -10,12 +10,10 @@
#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 <util/moneystr.h>
+#include <util/rbf.h>
#include <util/system.h>
-#include <net.h>
+#include <util/validation.h>
//! Check whether transaction has descendant in wallet or mempool, or has been
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
@@ -27,9 +25,7 @@ static feebumper::Result PreconditionChecks(interfaces::Chain::Lock& locked_chai
}
{
- LOCK(mempool.cs);
- auto it_mp = mempool.mapTx.find(wtx.GetHash());
- if (it_mp != mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1) {
+ if (wallet->chain().hasDescendantsInMempool(wtx.GetHash())) {
errors.push_back("Transaction has descendants in the mempool");
return feebumper::Result::INVALID_PARAMETER;
}
@@ -75,9 +71,11 @@ bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid)
return res == feebumper::Result::OK;
}
-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)
+Result CreateTotalBumpTransaction(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)
{
+ new_fee = total_fee;
+
auto locked_chain = wallet->chain().lock();
LOCK(wallet->cs_wallet);
errors.clear();
@@ -121,49 +119,33 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
// calculate the old fee and fee-rate
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
// fee that our node may not be aware of.
+ CFeeRate nodeIncrementalRelayFee = wallet->chain().relayIncrementalFee();
CFeeRate walletIncrementalRelayFee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
- if (::incrementalRelayFee > walletIncrementalRelayFee) {
- walletIncrementalRelayFee = ::incrementalRelayFee;
+ if (nodeIncrementalRelayFee > walletIncrementalRelayFee) {
+ walletIncrementalRelayFee = nodeIncrementalRelayFee;
}
- if (total_fee > 0) {
- CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize);
- 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))));
- return Result::INVALID_PARAMETER;
- }
- CAmount requiredFee = GetRequiredFee(*wallet, maxNewTxSize);
- if (total_fee < requiredFee) {
- errors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)",
- FormatMoney(requiredFee)));
- return Result::INVALID_PARAMETER;
- }
- new_fee = total_fee;
- nNewFeeRate = CFeeRate(total_fee, maxNewTxSize);
- } else {
- new_fee = GetMinimumFee(*wallet, 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
- // in that unit (fee per kb).
- // However, nOldFeeRate is a calculated value from the tx fee/size, so
- // 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());
- new_fee = nNewFeeRate.GetFee(maxNewTxSize);
- }
+ CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + nodeIncrementalRelayFee.GetFee(maxNewTxSize);
+ 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(nodeIncrementalRelayFee.GetFee(maxNewTxSize))));
+ return Result::INVALID_PARAMETER;
+ }
+ CAmount requiredFee = GetRequiredFee(*wallet, maxNewTxSize);
+ if (total_fee < requiredFee) {
+ errors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)",
+ FormatMoney(requiredFee)));
+ return Result::INVALID_PARAMETER;
}
// Check that in all cases the new fee doesn't violate maxTxFee
- 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)));
+ const CAmount max_tx_fee = wallet->m_default_max_tx_fee;
+ if (new_fee > max_tx_fee) {
+ errors.push_back(strprintf("Specified or calculated fee %s is too high (cannot be higher than -maxtxfee %s)",
+ FormatMoney(new_fee), FormatMoney(max_tx_fee)));
return Result::WALLET_ERROR;
}
@@ -172,15 +154,15 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
// 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 total_fee to make an adjustment.
- CFeeRate minMempoolFeeRate = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ CFeeRate minMempoolFeeRate = wallet->chain().mempoolMinFee();
+ CFeeRate nNewFeeRate = CFeeRate(total_fee, maxNewTxSize);
if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {
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",
+ "the totalFee value should be at least %s to add transaction",
FormatMoney(nNewFeeRate.GetFeePerK()),
FormatMoney(minMempoolFeeRate.GetFeePerK()),
- FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)),
- FormatMoney(minMempoolFeeRate.GetFeePerK())));
+ FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize))));
return Result::WALLET_ERROR;
}
@@ -197,7 +179,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
// If the output would become dust, discard it (converting the dust to fee)
poutput->nValue -= nDelta;
- if (poutput->nValue <= GetDustThreshold(*poutput, GetDiscardRate(*wallet, ::feeEstimator))) {
+ if (poutput->nValue <= GetDustThreshold(*poutput, GetDiscardRate(*wallet))) {
wallet->WalletLogPrintf("Bumping fee and discarding dust output\n");
new_fee += poutput->nValue;
mtx.vout.erase(mtx.vout.begin() + nOutput);
@@ -210,6 +192,109 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
}
}
+ return Result::OK;
+}
+
+
+Result CreateRateBumpTransaction(CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<std::string>& errors,
+ CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
+{
+ // We are going to modify coin control later, copy to re-use
+ CCoinControl new_coin_control(coin_control);
+
+ auto locked_chain = wallet->chain().lock();
+ LOCK(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;
+
+ Result result = PreconditionChecks(*locked_chain, wallet, wtx, errors);
+ if (result != Result::OK) {
+ return result;
+ }
+
+ // Fill in recipients(and preserve a single change key if there is one)
+ std::vector<CRecipient> recipients;
+ for (const auto& output : wtx.tx->vout) {
+ if (!wallet->IsChange(output)) {
+ CRecipient recipient = {output.scriptPubKey, output.nValue, false};
+ recipients.push_back(recipient);
+ } else {
+ CTxDestination change_dest;
+ ExtractDestination(output.scriptPubKey, change_dest);
+ new_coin_control.destChange = change_dest;
+ }
+ }
+
+ // Get the fee rate of the original transaction. This is calculated from
+ // the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the
+ // result.
+ old_fee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
+ int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
+ // Feerate of thing we are bumping
+ CFeeRate feerate(old_fee, txSize);
+ feerate += CFeeRate(1);
+
+ // The node has a configurable incremental relay fee. Increment the fee by
+ // the minimum of that and the wallet's conservative
+ // WALLET_INCREMENTAL_RELAY_FEE value to future proof against changes to
+ // network wide policy for incremental relay fee that our node may not be
+ // aware of. This ensures we're over the over the required relay fee rate
+ // (BIP 125 rule 4). The replacement tx will be at least as large as the
+ // original tx, so the total fee will be greater (BIP 125 rule 3)
+ CFeeRate node_incremental_relay_fee = wallet->chain().relayIncrementalFee();
+ CFeeRate wallet_incremental_relay_fee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
+ feerate += std::max(node_incremental_relay_fee, wallet_incremental_relay_fee);
+
+ // Fee rate must also be at least the wallet's GetMinimumFeeRate
+ CFeeRate min_feerate(GetMinimumFeeRate(*wallet, new_coin_control, /* feeCalc */ nullptr));
+
+ // Set the required fee rate for the replacement transaction in coin control.
+ new_coin_control.m_feerate = std::max(feerate, min_feerate);
+
+ // Fill in required inputs we are double-spending(all of them)
+ // N.B.: bip125 doesn't require all the inputs in the replaced transaction to be
+ // used in the replacement transaction, but it's very important for wallets to make
+ // sure that happens. If not, it would be possible to bump a transaction A twice to
+ // A2 and A3 where A2 and A3 don't conflict (or alternatively bump A to A2 and A2
+ // to A3 where A and A3 don't conflict). If both later get confirmed then the sender
+ // has accidentally double paid.
+ for (const auto& inputs : wtx.tx->vin) {
+ new_coin_control.Select(COutPoint(inputs.prevout));
+ }
+ new_coin_control.fAllowOtherInputs = true;
+
+ // We cannot source new unconfirmed inputs(bip125 rule 2)
+ new_coin_control.m_min_depth = 1;
+
+ CTransactionRef tx_new = MakeTransactionRef();
+ CReserveKey reservekey(wallet);
+ CAmount fee_ret;
+ int change_pos_in_out = -1; // No requested location for change
+ std::string fail_reason;
+ if (!wallet->CreateTransaction(*locked_chain, recipients, tx_new, reservekey, fee_ret, change_pos_in_out, fail_reason, new_coin_control, false)) {
+ errors.push_back("Unable to create transaction: " + fail_reason);
+ return Result::WALLET_ERROR;
+ }
+
+ // If change key hasn't been ReturnKey'ed by this point, we take it out of keypool
+ reservekey.KeepKey();
+
+ // Write back new fee if successful
+ new_fee = fee_ret;
+
+ // Write back transaction
+ mtx = CMutableTransaction(*tx_new);
+ // Mark new tx not replaceable, if requested.
+ if (!coin_control.m_signal_bip125_rbf.get_value_or(wallet->m_signal_rbf)) {
+ for (auto& input : mtx.vin) {
+ if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe;
+ }
+ }
return Result::OK;
}
@@ -247,7 +332,7 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
CReserveKey reservekey(wallet);
CValidationState state;
- if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, reservekey, g_connman.get(), state)) {
+ if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, reservekey, state)) {
// NOTE: CommitTransaction never returns false, so this should never happen.
errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state)));
return Result::WALLET_ERROR;
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index a1eb5d0e0d..0c4e1cb7dd 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -28,8 +28,8 @@ enum class Result
//! Return whether transaction can be bumped.
bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid);
-//! Create bumpfee transaction.
-Result CreateTransaction(const CWallet* wallet,
+//! Create bumpfee transaction based on total amount.
+Result CreateTotalBumpTransaction(const CWallet* wallet,
const uint256& txid,
const CCoinControl& coin_control,
CAmount total_fee,
@@ -38,6 +38,15 @@ Result CreateTransaction(const CWallet* wallet,
CAmount& new_fee,
CMutableTransaction& mtx);
+//! Create bumpfee transaction based on feerate estimates.
+Result CreateRateBumpTransaction(CWallet* wallet,
+ const uint256& txid,
+ const CCoinControl& coin_control,
+ 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)
diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp
index 9e2984ff05..ad69e84358 100644
--- a/src/wallet/fees.cpp
+++ b/src/wallet/fees.cpp
@@ -1,14 +1,11 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 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/fees.h>
-#include <policy/policy.h>
-#include <txmempool.h>
#include <util/system.h>
-#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/wallet.h>
@@ -19,12 +16,13 @@ CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes)
}
-CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc)
+CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, FeeCalculation* feeCalc)
{
- CAmount fee_needed = GetMinimumFeeRate(wallet, coin_control, pool, estimator, feeCalc).GetFee(nTxBytes);
+ CAmount fee_needed = GetMinimumFeeRate(wallet, coin_control, feeCalc).GetFee(nTxBytes);
// Always obey the maximum
- if (fee_needed > maxTxFee) {
- fee_needed = maxTxFee;
+ const CAmount max_tx_fee = wallet.m_default_max_tx_fee;
+ if (fee_needed > max_tx_fee) {
+ fee_needed = max_tx_fee;
if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE;
}
return fee_needed;
@@ -32,10 +30,10 @@ CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinC
CFeeRate GetRequiredFeeRate(const CWallet& wallet)
{
- return std::max(wallet.m_min_fee, ::minRelayTxFee);
+ return std::max(wallet.m_min_fee, wallet.chain().relayMinFee());
}
-CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc)
+CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, FeeCalculation* feeCalc)
{
/* User control of how to calculate fee uses the following parameter precedence:
1. coin_control.m_feerate
@@ -64,7 +62,7 @@ CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_contr
if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true;
else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false;
- feerate_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate);
+ feerate_needed = wallet.chain().estimateSmartFee(target, conservative_estimate, feeCalc);
if (feerate_needed == CFeeRate(0)) {
// if we don't have enough data for estimateSmartFee, then use fallback fee
feerate_needed = wallet.m_fallback_fee;
@@ -74,7 +72,7 @@ CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_contr
if (wallet.m_fallback_fee == CFeeRate(0)) return feerate_needed;
}
// Obey mempool min fee when using smart fee estimation
- CFeeRate min_mempool_feerate = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ CFeeRate min_mempool_feerate = wallet.chain().mempoolMinFee();
if (feerate_needed < min_mempool_feerate) {
feerate_needed = min_mempool_feerate;
if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN;
@@ -90,13 +88,13 @@ CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_contr
return feerate_needed;
}
-CFeeRate GetDiscardRate(const CWallet& wallet, const CBlockPolicyEstimator& estimator)
+CFeeRate GetDiscardRate(const CWallet& wallet)
{
- unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
- CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */);
+ unsigned int highest_target = wallet.chain().estimateMaxBlocks();
+ CFeeRate discard_rate = wallet.chain().estimateSmartFee(highest_target, false /* conservative */);
// Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate
discard_rate = (discard_rate == CFeeRate(0)) ? wallet.m_discard_rate : std::min(discard_rate, wallet.m_discard_rate);
// Discard rate must be at least dustRelayFee
- discard_rate = std::max(discard_rate, ::dustRelayFee);
+ discard_rate = std::max(discard_rate, wallet.chain().relayDustFee());
return discard_rate;
}
diff --git a/src/wallet/fees.h b/src/wallet/fees.h
index 6bfee456c0..434f211dc2 100644
--- a/src/wallet/fees.h
+++ b/src/wallet/fees.h
@@ -8,10 +8,8 @@
#include <amount.h>
-class CBlockPolicyEstimator;
class CCoinControl;
class CFeeRate;
-class CTxMemPool;
class CWallet;
struct FeeCalculation;
@@ -25,7 +23,7 @@ CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes);
* Estimate the minimum fee considering user set parameters
* and the required fee
*/
-CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc);
+CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, FeeCalculation* feeCalc);
/**
* Return the minimum required feerate taking into account the
@@ -37,11 +35,11 @@ CFeeRate GetRequiredFeeRate(const CWallet& wallet);
* Estimate the minimum fee rate considering user set parameters
* and the required fee
*/
-CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc);
+CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, FeeCalculation* feeCalc);
/**
* Return the maximum feerate for discarding change.
*/
-CFeeRate GetDiscardRate(const CWallet& wallet, const CBlockPolicyEstimator& estimator);
+CFeeRate GetDiscardRate(const CWallet& wallet);
#endif // BITCOIN_WALLET_FEES_H
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 87cd264c3d..00d2a59a66 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -1,19 +1,15 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 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 <chainparams.h>
#include <init.h>
#include <interfaces/chain.h>
#include <net.h>
-#include <scheduler.h>
#include <outputtype.h>
#include <util/system.h>
#include <util/moneystr.h>
-#include <validation.h>
#include <walletinitinterface.h>
-#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
@@ -47,6 +43,8 @@ void WalletInit::AddWalletOptions() const
gArgs.AddArg("-fallbackfee=<amt>", strprintf("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), false, OptionsCategory::WALLET);
gArgs.AddArg("-keypool=<n>", strprintf("Set key pool size to <n> (default: %u)", DEFAULT_KEYPOOL_SIZE), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-maxtxfee=<amt>", strprintf("Maximum total fees (in %s) to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)",
+ CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-mintxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), false, OptionsCategory::WALLET);
gArgs.AddArg("-paytxfee=<amt>", strprintf("Fee (in %s/kB) to add to transactions you send (default: %s)",
@@ -123,73 +121,6 @@ bool WalletInit::ParameterInteraction() const
if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false))
return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again."));
- if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB)
- InitWarning(AmountHighWarn("-minrelaytxfee") + " " +
- _("The wallet will avoid paying less than the minimum relay fee."));
-
- if (gArgs.IsArgSet("-maxtxfee"))
- {
- CAmount nMaxFee = 0;
- if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee))
- return InitError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")));
- if (nMaxFee > HIGH_MAX_TX_FEE)
- InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
- maxTxFee = nMaxFee;
- if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee)
- {
- return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
- gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString()));
- }
- }
-
- return true;
-}
-
-bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
-{
- if (gArgs.IsArgSet("-walletdir")) {
- fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
- boost::system::error_code error;
- // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
- fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
- if (error || !fs::exists(wallet_dir)) {
- return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
- } else if (!fs::is_directory(wallet_dir)) {
- return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
- // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
- } else if (!wallet_dir.is_absolute()) {
- return InitError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
- }
- gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
- }
-
- LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
-
- uiInterface.InitMessage(_("Verifying wallet(s)..."));
-
- // Parameter interaction code should have thrown an error if -salvagewallet
- // was enabled with more than wallet file, so the wallet_files size check
- // here should have no effect.
- bool salvage_wallet = gArgs.GetBoolArg("-salvagewallet", false) && wallet_files.size() <= 1;
-
- // Keep track of each wallet absolute path to detect duplicates.
- std::set<fs::path> wallet_paths;
-
- for (const auto& wallet_file : wallet_files) {
- WalletLocation location(wallet_file);
-
- if (!wallet_paths.insert(location.GetPath()).second) {
- return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
- }
-
- std::string error_string;
- std::string warning_string;
- bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warning_string);
- if (!error_string.empty()) InitError(error_string);
- if (!warning_string.empty()) InitWarning(warning_string);
- if (!verify_success) return false;
- }
-
return true;
}
@@ -202,51 +133,3 @@ void WalletInit::Construct(InitInterfaces& interfaces) const
gArgs.SoftSetArg("-wallet", "");
interfaces.chain_clients.emplace_back(interfaces::MakeWalletClient(*interfaces.chain, gArgs.GetArgs("-wallet")));
}
-
-bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
-{
- for (const std::string& walletFile : wallet_files) {
- std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile));
- if (!pwallet) {
- return false;
- }
- AddWallet(pwallet);
- }
-
- return true;
-}
-
-void StartWallets(CScheduler& scheduler)
-{
- for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
- pwallet->postInitProcess();
- }
-
- // Run a thread to flush wallet periodically
- scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
-}
-
-void FlushWallets()
-{
- for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
- pwallet->Flush(false);
- }
-}
-
-void StopWallets()
-{
- for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
- pwallet->Flush(true);
- }
-}
-
-void UnloadWallets()
-{
- auto wallets = GetWallets();
- while (!wallets.empty()) {
- auto wallet = wallets.back();
- wallets.pop_back();
- RemoveWallet(wallet);
- UnloadWallet(std::move(wallet));
- }
-}
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
new file mode 100644
index 0000000000..54aa12dba8
--- /dev/null
+++ b/src/wallet/load.cpp
@@ -0,0 +1,112 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 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/load.h>
+
+#include <interfaces/chain.h>
+#include <scheduler.h>
+#include <util/system.h>
+#include <wallet/wallet.h>
+
+bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
+{
+ if (gArgs.IsArgSet("-walletdir")) {
+ fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
+ boost::system::error_code error;
+ // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
+ fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
+ if (error || !fs::exists(wallet_dir)) {
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
+ return false;
+ } else if (!fs::is_directory(wallet_dir)) {
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
+ return false;
+ // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
+ } else if (!wallet_dir.is_absolute()) {
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
+ return false;
+ }
+ gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
+ }
+
+ LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
+
+ chain.initMessage(_("Verifying wallet(s)..."));
+
+ // Parameter interaction code should have thrown an error if -salvagewallet
+ // was enabled with more than wallet file, so the wallet_files size check
+ // here should have no effect.
+ bool salvage_wallet = gArgs.GetBoolArg("-salvagewallet", false) && wallet_files.size() <= 1;
+
+ // Keep track of each wallet absolute path to detect duplicates.
+ std::set<fs::path> wallet_paths;
+
+ for (const auto& wallet_file : wallet_files) {
+ WalletLocation location(wallet_file);
+
+ if (!wallet_paths.insert(location.GetPath()).second) {
+ chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
+ return false;
+ }
+
+ std::string error_string;
+ std::string warning_string;
+ bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warning_string);
+ if (!error_string.empty()) chain.initError(error_string);
+ if (!warning_string.empty()) chain.initWarning(warning_string);
+ if (!verify_success) return false;
+ }
+
+ return true;
+}
+
+bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
+{
+ for (const std::string& walletFile : wallet_files) {
+ std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile));
+ if (!pwallet) {
+ return false;
+ }
+ AddWallet(pwallet);
+ }
+
+ return true;
+}
+
+void StartWallets(CScheduler& scheduler)
+{
+ for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
+ pwallet->postInitProcess();
+ }
+
+ // Schedule periodic wallet flushes and tx rebroadcasts
+ scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
+ scheduler.scheduleEvery(MaybeResendWalletTxs, 1000);
+}
+
+void FlushWallets()
+{
+ for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
+ pwallet->Flush(false);
+ }
+}
+
+void StopWallets()
+{
+ for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
+ pwallet->Flush(true);
+ }
+}
+
+void UnloadWallets()
+{
+ auto wallets = GetWallets();
+ while (!wallets.empty()) {
+ auto wallet = wallets.back();
+ wallets.pop_back();
+ RemoveWallet(wallet);
+ UnloadWallet(std::move(wallet));
+ }
+}
diff --git a/src/wallet/load.h b/src/wallet/load.h
new file mode 100644
index 0000000000..81f078fd10
--- /dev/null
+++ b/src/wallet/load.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 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_LOAD_H
+#define BITCOIN_WALLET_LOAD_H
+
+#include <string>
+#include <vector>
+
+class CScheduler;
+
+namespace interfaces {
+class Chain;
+} // namespace interfaces
+
+//! 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 (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
+bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
+
+//! Load wallet databases.
+bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
+
+//! 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 UnloadWallets();
+
+#endif // BITCOIN_WALLET_LOAD_H
diff --git a/src/wallet/psbtwallet.cpp b/src/wallet/psbtwallet.cpp
new file mode 100644
index 0000000000..ce4788dee1
--- /dev/null
+++ b/src/wallet/psbtwallet.cpp
@@ -0,0 +1,60 @@
+// Copyright (c) 2009-2019 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/psbtwallet.h>
+
+TransactionError FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs)
+{
+ LOCK(pwallet->cs_wallet);
+ // Get all of the previous transactions
+ complete = true;
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ const CTxIn& txin = psbtx.tx->vin[i];
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ if (PSBTInputSigned(input)) {
+ continue;
+ }
+
+ // Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
+ if (!input.IsSane()) {
+ return TransactionError::INVALID_PSBT;
+ }
+
+ // If we have no utxo, grab it from the wallet.
+ if (!input.non_witness_utxo && input.witness_utxo.IsNull()) {
+ const uint256& txhash = txin.prevout.hash;
+ const auto it = pwallet->mapWallet.find(txhash);
+ if (it != pwallet->mapWallet.end()) {
+ const CWalletTx& wtx = it->second;
+ // We only need the non_witness_utxo, which is a superset of the witness_utxo.
+ // The signing code will switch to the smaller witness_utxo if this is ok.
+ input.non_witness_utxo = wtx.tx;
+ }
+ }
+
+ // Get the Sighash type
+ if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
+ return TransactionError::SIGHASH_MISMATCH;
+ }
+
+ complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), psbtx, i, sighash_type);
+ }
+
+ // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
+ for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
+ const CTxOut& out = psbtx.tx->vout.at(i);
+ PSBTOutput& psbt_out = psbtx.outputs.at(i);
+
+ // Fill a SignatureData with output info
+ SignatureData sigdata;
+ psbt_out.FillSignatureData(sigdata);
+
+ MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue, 1);
+ ProduceSignature(HidingSigningProvider(pwallet, true, !bip32derivs), creator, out.scriptPubKey, sigdata);
+ psbt_out.FromSignatureData(sigdata);
+ }
+
+ return TransactionError::OK;
+}
diff --git a/src/wallet/psbtwallet.h b/src/wallet/psbtwallet.h
new file mode 100644
index 0000000000..a24a0967d2
--- /dev/null
+++ b/src/wallet/psbtwallet.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2009-2019 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_PSBTWALLET_H
+#define BITCOIN_WALLET_PSBTWALLET_H
+
+#include <node/transaction.h>
+#include <psbt.h>
+#include <primitives/transaction.h>
+#include <wallet/wallet.h>
+
+/**
+ * Fills out a PSBT with information from the wallet. Fills in UTXOs if we have
+ * them. Tries to sign if sign=true. Sets `complete` if the PSBT is now complete
+ * (i.e. has all required signatures or signature-parts, and is ready to
+ * finalize.) Sets `error` and returns false if something goes wrong.
+ *
+ * @param[in] pwallet pointer to a wallet
+ * @param[in] &psbtx reference to PartiallySignedTransaction to fill in
+ * @param[out] &complete indicates whether the PSBT is now complete
+ * @param[in] sighash_type the sighash type to use when signing (if PSBT does not specify)
+ * @param[in] sign whether to sign or not
+ * @param[in] bip32derivs whether to fill in bip32 derivation information if available
+ * return error
+ */
+NODISCARD TransactionError FillPSBT(const CWallet* pwallet,
+ PartiallySignedTransaction& psbtx,
+ bool& complete,
+ int sighash_type = 1 /* SIGHASH_ALL */,
+ bool sign = true,
+ bool bip32derivs = false);
+
+#endif // BITCOIN_WALLET_PSBTWALLET_H
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 32c36ceaeb..3112dca9f5 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,17 +9,19 @@
#include <merkleblock.h>
#include <rpc/server.h>
#include <rpc/util.h>
+#include <script/descriptor.h>
#include <script/script.h>
#include <script/standard.h>
#include <sync.h>
+#include <util/bip32.h>
#include <util/system.h>
#include <util/time.h>
-#include <validation.h>
#include <wallet/wallet.h>
#include <wallet/rpcwallet.h>
#include <stdint.h>
+#include <tuple>
#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
@@ -66,7 +68,7 @@ static std::string DecodeDumpString(const std::string &str) {
return ret.str();
}
-static bool GetWalletAddressesForKey(CWallet * const pwallet, const CKeyID &keyid, std::string &strAddr, std::string &strLabel)
+static bool GetWalletAddressesForKey(CWallet* const pwallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
bool fLabelFound = false;
CKey key;
@@ -113,11 +115,12 @@ UniValue importprivkey(const JSONRPCRequest& request)
"\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one private key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
- "may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n",
+ "may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
- {"privkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The private key (see dumpprivkey)"},
- {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "current label if address exists, otherwise \"\"", "An optional label"},
- {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Rescan the wallet for transactions"},
+ {"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key (see dumpprivkey)"},
+ {"label", RPCArg::Type::STR, /* default */ "current label if address exists, otherwise \"\"", "An optional label"},
+ {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Rescan the wallet for transactions"},
},
RPCResults{},
RPCExamples{
@@ -155,8 +158,12 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (!request.params[2].isNull())
fRescan = request.params[2].get_bool();
- if (fRescan && fPruneMode)
- throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
+ if (fRescan && pwallet->chain().havePruned()) {
+ // Exit early and print an error.
+ // If a block is pruned after this check, we will import the key(s),
+ // but fail the rescan with a generic error.
+ throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
+ }
if (fRescan && !reserver.reserve()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
@@ -213,7 +220,8 @@ UniValue abortrescan(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
RPCHelpMan{"abortrescan",
- "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n",
+ "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{},
RPCResults{},
RPCExamples{
@@ -249,7 +257,7 @@ static void ImportScript(CWallet* const pwallet, const CScript& script, const st
if (!pwallet->HaveCScript(id) && !pwallet->AddCScript(script)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
}
- ImportAddress(pwallet, id, strLabel);
+ ImportAddress(pwallet, ScriptHash(id), strLabel);
} else {
CTxDestination destination;
if (ExtractDestination(script, destination)) {
@@ -282,13 +290,15 @@ UniValue importaddress(const JSONRPCRequest& request)
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
"If you have the full public key, you should call importpubkey instead of this.\n"
+ "Hint: use importmulti to import more than one address.\n"
"\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
- "as change, and not show up in many RPCs.\n",
+ "as change, and not show up in many RPCs.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The Bitcoin address (or hex-encoded script)"},
- {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"\"", "An optional label"},
- {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Rescan the wallet for transactions"},
- {"p2sh", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Add the P2SH version of the script as well"},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Bitcoin address (or hex-encoded script)"},
+ {"label", RPCArg::Type::STR, /* default */ "\"\"", "An optional label"},
+ {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Rescan the wallet for transactions"},
+ {"p2sh", RPCArg::Type::BOOL, /* default */ "false", "Add the P2SH version of the script as well"},
},
RPCResults{},
RPCExamples{
@@ -311,8 +321,12 @@ UniValue importaddress(const JSONRPCRequest& request)
if (!request.params[2].isNull())
fRescan = request.params[2].get_bool();
- if (fRescan && fPruneMode)
- throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
+ if (fRescan && pwallet->chain().havePruned()) {
+ // Exit early and print an error.
+ // If a block is pruned after this check, we will import the key(s),
+ // but fail the rescan with a generic error.
+ throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
+ }
WalletRescanReserver reserver(pwallet);
if (fRescan && !reserver.reserve()) {
@@ -344,7 +358,11 @@ UniValue importaddress(const JSONRPCRequest& request)
if (fRescan)
{
RescanWallet(*pwallet, reserver);
- pwallet->ReacceptWalletTransactions();
+ {
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
+ pwallet->ReacceptWalletTransactions(*locked_chain);
+ }
}
return NullUniValue;
@@ -363,8 +381,8 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
RPCHelpMan{"importprunedfunds",
"\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
{
- {"rawtransaction", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A raw transaction in hex funding an already-existing address in wallet"},
- {"txoutproof", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex output from gettxoutproof that contains the transaction"},
+ {"rawtransaction", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A raw transaction in hex funding an already-existing address in wallet"},
+ {"txoutproof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex output from gettxoutproof that contains the transaction"},
},
RPCResults{},
RPCExamples{""},
@@ -430,7 +448,7 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
RPCHelpMan{"removeprunedfunds",
"\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded id of the transaction you are deleting"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded id of the transaction you are deleting"},
},
RPCResults{},
RPCExamples{
@@ -471,12 +489,14 @@ UniValue importpubkey(const JSONRPCRequest& request)
throw std::runtime_error(
RPCHelpMan{"importpubkey",
"\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"
+ "Hint: use importmulti to import more than one public key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
- "may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n",
+ "may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
- {"pubkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The hex-encoded public key"},
- {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"\"", "An optional label"},
- {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Rescan the wallet for transactions"},
+ {"pubkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The hex-encoded public key"},
+ {"label", RPCArg::Type::STR, /* default */ "\"\"", "An optional label"},
+ {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Rescan the wallet for transactions"},
},
RPCResults{},
RPCExamples{
@@ -499,8 +519,12 @@ UniValue importpubkey(const JSONRPCRequest& request)
if (!request.params[2].isNull())
fRescan = request.params[2].get_bool();
- if (fRescan && fPruneMode)
- throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
+ if (fRescan && pwallet->chain().havePruned()) {
+ // Exit early and print an error.
+ // If a block is pruned after this check, we will import the key(s),
+ // but fail the rescan with a generic error.
+ throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
+ }
WalletRescanReserver reserver(pwallet);
if (fRescan && !reserver.reserve()) {
@@ -527,7 +551,11 @@ UniValue importpubkey(const JSONRPCRequest& request)
if (fRescan)
{
RescanWallet(*pwallet, reserver);
- pwallet->ReacceptWalletTransactions();
+ {
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
+ pwallet->ReacceptWalletTransactions(*locked_chain);
+ }
}
return NullUniValue;
@@ -545,9 +573,10 @@ UniValue importwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
RPCHelpMan{"importwallet",
- "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n",
+ "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
- {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The wallet file"},
+ {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet file"},
},
RPCResults{},
RPCExamples{
@@ -560,8 +589,12 @@ UniValue importwallet(const JSONRPCRequest& request)
},
}.ToString());
- if (fPruneMode)
- throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");
+ if (pwallet->chain().havePruned()) {
+ // Exit early and print an error.
+ // If a block is pruned after this check, we will import the key(s),
+ // but fail the rescan with a generic error.
+ throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled when blocks are pruned");
+ }
WalletRescanReserver reserver(pwallet);
if (!reserver.reserve()) {
@@ -589,11 +622,11 @@ UniValue importwallet(const JSONRPCRequest& request)
// Use uiInterface.ShowProgress instead of pwallet.ShowProgress because pwallet.ShowProgress has a cancel button tied to AbortRescan which
// we don't want for this progress bar showing the import progress. uiInterface.ShowProgress does not have a cancel button.
- uiInterface.ShowProgress(strprintf("%s " + _("Importing..."), pwallet->GetDisplayName()), 0, false); // show progress dialog in GUI
+ pwallet->chain().showProgress(strprintf("%s " + _("Importing..."), pwallet->GetDisplayName()), 0, false); // show progress dialog in GUI
std::vector<std::tuple<CKey, int64_t, bool, std::string>> keys;
std::vector<std::pair<CScript, int64_t>> scripts;
while (file.good()) {
- uiInterface.ShowProgress("", std::max(1, std::min(50, (int)(((double)file.tellg() / (double)nFilesize) * 100))), false);
+ pwallet->chain().showProgress("", std::max(1, std::min(50, (int)(((double)file.tellg() / (double)nFilesize) * 100))), false);
std::string line;
std::getline(file, line);
if (line.empty() || line[0] == '#')
@@ -631,13 +664,13 @@ UniValue importwallet(const JSONRPCRequest& request)
file.close();
// We now know whether we are importing private keys, so we can error if private keys are disabled
if (keys.size() > 0 && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
- uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI
+ pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled when private keys are disabled");
}
double total = (double)(keys.size() + scripts.size());
double progress = 0;
for (const auto& key_tuple : keys) {
- uiInterface.ShowProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
+ pwallet->chain().showProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
const CKey& key = std::get<0>(key_tuple);
int64_t time = std::get<1>(key_tuple);
bool has_label = std::get<2>(key_tuple);
@@ -647,22 +680,22 @@ UniValue importwallet(const JSONRPCRequest& request)
assert(key.VerifyPubKey(pubkey));
CKeyID keyid = pubkey.GetID();
if (pwallet->HaveKey(keyid)) {
- pwallet->WalletLogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid));
+ pwallet->WalletLogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(PKHash(keyid)));
continue;
}
- pwallet->WalletLogPrintf("Importing %s...\n", EncodeDestination(keyid));
+ pwallet->WalletLogPrintf("Importing %s...\n", EncodeDestination(PKHash(keyid)));
if (!pwallet->AddKeyPubKey(key, pubkey)) {
fGood = false;
continue;
}
pwallet->mapKeyMetadata[keyid].nCreateTime = time;
if (has_label)
- pwallet->SetAddressBook(keyid, label, "receive");
+ pwallet->SetAddressBook(PKHash(keyid), label, "receive");
nTimeBegin = std::min(nTimeBegin, time);
progress++;
}
for (const auto& script_pair : scripts) {
- uiInterface.ShowProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
+ pwallet->chain().showProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
const CScript& script = script_pair.first;
int64_t time = script_pair.second;
CScriptID id(script);
@@ -681,10 +714,10 @@ UniValue importwallet(const JSONRPCRequest& request)
}
progress++;
}
- uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI
+ pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
pwallet->UpdateTimeFirstKey(nTimeBegin);
}
- uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI
+ pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
RescanWallet(*pwallet, reserver, nTimeBegin, false /* update */);
pwallet->MarkDirty();
@@ -708,7 +741,7 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
"\nReveals the private key corresponding to 'address'.\n"
"Then the importprivkey can be used with this output\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address for the private key"},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for the private key"},
},
RPCResult{
"\"key\" (string) The private key\n"
@@ -758,7 +791,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
"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",
{
- {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The filename with path (either absolute or relative to bitcoind)"},
+ {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The filename with path (either absolute or relative to bitcoind)"},
},
RPCResult{
"{ (json object)\n"
@@ -793,19 +826,16 @@ UniValue dumpwallet(const JSONRPCRequest& request)
if (!file.is_open())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
- std::map<CTxDestination, int64_t> mapKeyBirth;
+ std::map<CKeyID, int64_t> mapKeyBirth;
const std::map<CKeyID, int64_t>& mapKeyPool = pwallet->GetAllReserveKeys();
pwallet->GetKeyBirthTimes(*locked_chain, mapKeyBirth);
std::set<CScriptID> scripts = pwallet->GetCScripts();
- // TODO: include scripts in GetKeyBirthTimes() output instead of separate
// sort time/key pairs
std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
for (const auto& entry : mapKeyBirth) {
- if (const CKeyID* keyID = boost::get<CKeyID>(&entry.first)) { // set and test
- vKeyBirth.push_back(std::make_pair(entry.second, *keyID));
- }
+ vKeyBirth.push_back(std::make_pair(entry.second, entry.first));
}
mapKeyBirth.clear();
std::sort(vKeyBirth.begin(), vKeyBirth.end());
@@ -849,14 +879,14 @@ UniValue dumpwallet(const JSONRPCRequest& request)
} else {
file << "change=1";
}
- file << strprintf(" # addr=%s%s\n", strAddr, (pwallet->mapKeyMetadata[keyid].hdKeypath.size() > 0 ? " hdkeypath="+pwallet->mapKeyMetadata[keyid].hdKeypath : ""));
+ file << strprintf(" # addr=%s%s\n", strAddr, (pwallet->mapKeyMetadata[keyid].has_key_origin ? " hdkeypath="+WriteHDKeypath(pwallet->mapKeyMetadata[keyid].key_origin.path) : ""));
}
}
file << "\n";
for (const CScriptID &scriptid : scripts) {
CScript script;
std::string create_time = "0";
- std::string address = EncodeDestination(scriptid);
+ std::string address = EncodeDestination(ScriptHash(scriptid));
// get birth times for scripts with metadata
auto it = pwallet->m_script_metadata.find(scriptid);
if (it != pwallet->m_script_metadata.end()) {
@@ -886,6 +916,7 @@ struct ImportData
// Output data
std::set<CScript> import_scripts;
std::map<CKeyID, bool> used_keys; //!< Import these private keys if available (the value indicates whether if the key is required for solvability)
+ std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> key_origins;
};
enum class ScriptContext
@@ -964,196 +995,296 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
}
}
-static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys, bool& have_solving_data, const UniValue& data, std::vector<CKeyID>& ordered_pubkeys)
{
UniValue warnings(UniValue::VARR);
- UniValue result(UniValue::VOBJ);
- try {
- // First ensure scriptPubKey has either a script or JSON with "address" string
- const UniValue& scriptPubKey = data["scriptPubKey"];
- bool isScript = scriptPubKey.getType() == UniValue::VSTR;
- if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address"))) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "scriptPubKey must be string with script or JSON with address string");
+ // First ensure scriptPubKey has either a script or JSON with "address" string
+ const UniValue& scriptPubKey = data["scriptPubKey"];
+ bool isScript = scriptPubKey.getType() == UniValue::VSTR;
+ if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address"))) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "scriptPubKey must be string with script or JSON with address string");
+ }
+ const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
+
+ // Optional fields.
+ const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
+ const std::string& witness_script_hex = data.exists("witnessscript") ? data["witnessscript"].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;
+
+ if (data.exists("range")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for a non-descriptor import");
+ }
+
+ // Generate the script and destination for the scriptPubKey provided
+ CScript script;
+ if (!isScript) {
+ CTxDestination dest = DecodeDestination(output);
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address \"" + output + "\"");
}
- const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
+ script = GetScriptForDestination(dest);
+ } else {
+ if (!IsHex(output)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey \"" + output + "\"");
+ }
+ std::vector<unsigned char> vData(ParseHex(output));
+ script = CScript(vData.begin(), vData.end());
+ CTxDestination dest;
+ if (!ExtractDestination(script, dest) && !internal) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set to true for nonstandard scriptPubKey imports.");
+ }
+ }
+ script_pub_keys.emplace(script);
- // Optional fields.
- const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
- const std::string& witness_script_hex = data.exists("witnessscript") ? data["witnessscript"].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 std::string& label = data.exists("label") ? data["label"].get_str() : "";
+ // Parse all arguments
+ if (strRedeemScript.size()) {
+ if (!IsHex(strRedeemScript)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script \"" + strRedeemScript + "\": must be hex string");
+ }
+ auto parsed_redeemscript = ParseHex(strRedeemScript);
+ import_data.redeemscript = MakeUnique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end());
+ }
+ if (witness_script_hex.size()) {
+ if (!IsHex(witness_script_hex)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script \"" + witness_script_hex + "\": must be hex string");
+ }
+ auto parsed_witnessscript = ParseHex(witness_script_hex);
+ import_data.witnessscript = MakeUnique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end());
+ }
+ for (size_t i = 0; i < pubKeys.size(); ++i) {
+ const auto& str = pubKeys[i].get_str();
+ if (!IsHex(str)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" must be a hex string");
+ }
+ auto parsed_pubkey = ParseHex(str);
+ CPubKey pubkey(parsed_pubkey.begin(), parsed_pubkey.end());
+ if (!pubkey.IsFullyValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" is not a valid public key");
+ }
+ pubkey_map.emplace(pubkey.GetID(), pubkey);
+ ordered_pubkeys.push_back(pubkey.GetID());
+ }
+ for (size_t i = 0; i < keys.size(); ++i) {
+ const auto& str = keys[i].get_str();
+ CKey key = DecodeSecret(str);
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
+ }
+ CPubKey pubkey = key.GetPubKey();
+ CKeyID id = pubkey.GetID();
+ if (pubkey_map.count(id)) {
+ pubkey_map.erase(id);
+ }
+ privkey_map.emplace(id, key);
+ }
- // If private keys are disabled, abort if private keys are being imported
- if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !keys.isNull()) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
+
+ // Verify and process input data
+ have_solving_data = import_data.redeemscript || import_data.witnessscript || pubkey_map.size() || privkey_map.size();
+ if (have_solving_data) {
+ // Match up data in import_data with the scriptPubKey in script.
+ auto error = RecurseImportData(script, import_data, ScriptContext::TOP);
+
+ // Verify whether the watchonly option corresponds to the availability of private keys.
+ bool spendable = std::all_of(import_data.used_keys.begin(), import_data.used_keys.end(), [&](const std::pair<CKeyID, bool>& used_key){ return privkey_map.count(used_key.first) > 0; });
+ if (!watchOnly && !spendable) {
+ warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
+ }
+ if (watchOnly && spendable) {
+ warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
}
- // Generate the script and destination for the scriptPubKey provided
- CScript script;
- CTxDestination dest;
- if (!isScript) {
- dest = DecodeDestination(output);
- if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address \"" + output + "\"");
+ // Check that all required keys for solvability are provided.
+ if (error.empty()) {
+ for (const auto& require_key : import_data.used_keys) {
+ if (!require_key.second) continue; // Not a required key
+ if (pubkey_map.count(require_key.first) == 0 && privkey_map.count(require_key.first) == 0) {
+ error = "some required keys are missing";
+ }
}
- script = GetScriptForDestination(dest);
+ }
+
+ if (!error.empty()) {
+ warnings.push_back("Importing as non-solvable: " + error + ". If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.");
+ import_data = ImportData();
+ pubkey_map.clear();
+ privkey_map.clear();
+ have_solving_data = false;
} else {
- if (!IsHex(output)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey \"" + output + "\"");
+ // RecurseImportData() removes any relevant redeemscript/witnessscript from import_data, so we can use that to discover if a superfluous one was provided.
+ if (import_data.redeemscript) warnings.push_back("Ignoring redeemscript as this is not a P2SH script.");
+ if (import_data.witnessscript) warnings.push_back("Ignoring witnessscript as this is not a (P2SH-)P2WSH script.");
+ for (auto it = privkey_map.begin(); it != privkey_map.end(); ) {
+ auto oldit = it++;
+ if (import_data.used_keys.count(oldit->first) == 0) {
+ warnings.push_back("Ignoring irrelevant private key.");
+ privkey_map.erase(oldit);
+ }
}
- std::vector<unsigned char> vData(ParseHex(output));
- script = CScript(vData.begin(), vData.end());
- if (!ExtractDestination(script, dest) && !internal) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set to true for nonstandard scriptPubKey imports.");
+ for (auto it = pubkey_map.begin(); it != pubkey_map.end(); ) {
+ auto oldit = it++;
+ auto key_data_it = import_data.used_keys.find(oldit->first);
+ if (key_data_it == import_data.used_keys.end() || !key_data_it->second) {
+ warnings.push_back("Ignoring public key \"" + HexStr(oldit->first) + "\" as it doesn't appear inside P2PKH or P2WPKH.");
+ pubkey_map.erase(oldit);
+ }
}
}
+ }
- // Parse all arguments
- ImportData import_data;
- if (strRedeemScript.size()) {
- if (!IsHex(strRedeemScript)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script \"" + strRedeemScript + "\": must be hex string");
- }
- auto parsed_redeemscript = ParseHex(strRedeemScript);
- import_data.redeemscript = MakeUnique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end());
+ return warnings;
+}
+
+static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys, bool& have_solving_data, const UniValue& data, std::vector<CKeyID>& ordered_pubkeys)
+{
+ UniValue warnings(UniValue::VARR);
+
+ const std::string& descriptor = data["desc"].get_str();
+ FlatSigningProvider keys;
+ auto parsed_desc = Parse(descriptor, keys, /* require_checksum = */ true);
+ if (!parsed_desc) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor is invalid");
+ }
+
+ have_solving_data = parsed_desc->IsSolvable();
+ const bool watch_only = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
+
+ int64_t range_start = 0, range_end = 0;
+ if (!parsed_desc->IsRange() && data.exists("range")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
+ } else if (parsed_desc->IsRange()) {
+ if (!data.exists("range")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range");
}
- if (witness_script_hex.size()) {
- if (!IsHex(witness_script_hex)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script \"" + witness_script_hex + "\": must be hex string");
- }
- auto parsed_witnessscript = ParseHex(witness_script_hex);
- import_data.witnessscript = MakeUnique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end());
+ std::tie(range_start, range_end) = ParseDescriptorRange(data["range"]);
+ }
+
+ const UniValue& priv_keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
+
+ // Expand all descriptors to get public keys and scripts, and private keys if available.
+ for (int i = range_start; i <= range_end; ++i) {
+ FlatSigningProvider out_keys;
+ std::vector<CScript> scripts_temp;
+ parsed_desc->Expand(i, keys, scripts_temp, out_keys);
+ std::copy(scripts_temp.begin(), scripts_temp.end(), std::inserter(script_pub_keys, script_pub_keys.end()));
+ for (const auto& key_pair : out_keys.pubkeys) {
+ ordered_pubkeys.push_back(key_pair.first);
}
- std::map<CKeyID, CPubKey> pubkey_map;
- for (size_t i = 0; i < pubKeys.size(); ++i) {
- const auto& str = pubKeys[i].get_str();
- if (!IsHex(str)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" must be a hex string");
- }
- auto parsed_pubkey = ParseHex(str);
- CPubKey pubkey(parsed_pubkey.begin(), parsed_pubkey.end());
- if (!pubkey.IsFullyValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" is not a valid public key");
- }
- pubkey_map.emplace(pubkey.GetID(), pubkey);
+
+ for (const auto& x : out_keys.scripts) {
+ import_data.import_scripts.emplace(x.second);
}
- std::map<CKeyID, CKey> privkey_map;
- for (size_t i = 0; i < keys.size(); ++i) {
- const auto& str = keys[i].get_str();
- CKey key = DecodeSecret(str);
- if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
- }
- CPubKey pubkey = key.GetPubKey();
- CKeyID id = pubkey.GetID();
- if (pubkey_map.count(id)) {
- pubkey_map.erase(id);
- }
+
+ parsed_desc->ExpandPrivate(i, keys, out_keys);
+
+ std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end()));
+ std::copy(out_keys.keys.begin(), out_keys.keys.end(), std::inserter(privkey_map, privkey_map.end()));
+ import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end());
+ }
+
+ for (size_t i = 0; i < priv_keys.size(); ++i) {
+ const auto& str = priv_keys[i].get_str();
+ CKey key = DecodeSecret(str);
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
+ }
+ CPubKey pubkey = key.GetPubKey();
+ CKeyID id = pubkey.GetID();
+
+ // Check if this private key corresponds to a public key from the descriptor
+ if (!pubkey_map.count(id)) {
+ warnings.push_back("Ignoring irrelevant private key.");
+ } else {
privkey_map.emplace(id, key);
}
+ }
+
+ // Check if all the public keys have corresponding private keys in the import for spendability.
+ // This does not take into account threshold multisigs which could be spendable without all keys.
+ // Thus, threshold multisigs without all keys will be considered not spendable here, even if they are,
+ // perhaps triggering a false warning message. This is consistent with the current wallet IsMine check.
+ bool spendable = std::all_of(pubkey_map.begin(), pubkey_map.end(),
+ [&](const std::pair<CKeyID, CPubKey>& used_key) {
+ return privkey_map.count(used_key.first) > 0;
+ }) && std::all_of(import_data.key_origins.begin(), import_data.key_origins.end(),
+ [&](const std::pair<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& entry) {
+ return privkey_map.count(entry.first) > 0;
+ });
+ if (!watch_only && !spendable) {
+ warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
+ }
+ if (watch_only && spendable) {
+ warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
+ }
+ return warnings;
+}
+
+static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+{
+ UniValue warnings(UniValue::VARR);
+ UniValue result(UniValue::VOBJ);
+
+ try {
+ const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
// Internal addresses should not have a label
if (internal && data.exists("label")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
}
+ const std::string& label = data.exists("label") ? data["label"].get_str() : "";
+ const bool add_keypool = data.exists("keypool") ? data["keypool"].get_bool() : false;
- // Verify and process input data
- bool have_solving_data = import_data.redeemscript || import_data.witnessscript || pubkey_map.size() || privkey_map.size();
- if (have_solving_data) {
- // Match up data in import_data with the scriptPubKey in script.
- auto error = RecurseImportData(script, import_data, ScriptContext::TOP);
-
- // Verify whether the watchonly option corresponds to the availability of private keys.
- bool spendable = std::all_of(import_data.used_keys.begin(), import_data.used_keys.end(), [&](const std::pair<CKeyID, bool>& used_key){ return privkey_map.count(used_key.first) > 0; });
- if (!watchOnly && !spendable) {
- warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
- }
- if (watchOnly && spendable) {
- warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
- }
+ // Add to keypool only works with privkeys disabled
+ if (add_keypool && !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Keys can only be imported to the keypool when private keys are disabled");
+ }
- // Check that all required keys for solvability are provided.
- if (error.empty()) {
- for (const auto& require_key : import_data.used_keys) {
- if (!require_key.second) continue; // Not a required key
- if (pubkey_map.count(require_key.first) == 0 && privkey_map.count(require_key.first) == 0) {
- error = "some required keys are missing";
- }
- }
- }
+ ImportData import_data;
+ std::map<CKeyID, CPubKey> pubkey_map;
+ std::map<CKeyID, CKey> privkey_map;
+ std::set<CScript> script_pub_keys;
+ std::vector<CKeyID> ordered_pubkeys;
+ bool have_solving_data;
+
+ if (data.exists("scriptPubKey") && data.exists("desc")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Both a descriptor and a scriptPubKey should not be provided.");
+ } else if (data.exists("scriptPubKey")) {
+ warnings = ProcessImportLegacy(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data, ordered_pubkeys);
+ } else if (data.exists("desc")) {
+ warnings = ProcessImportDescriptor(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data, ordered_pubkeys);
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Either a descriptor or scriptPubKey must be provided.");
+ }
- if (!error.empty()) {
- warnings.push_back("Importing as non-solvable: " + error + ". If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.");
- import_data = ImportData();
- pubkey_map.clear();
- privkey_map.clear();
- have_solving_data = false;
- } else {
- // RecurseImportData() removes any relevant redeemscript/witnessscript from import_data, so we can use that to discover if a superfluous one was provided.
- if (import_data.redeemscript) warnings.push_back("Ignoring redeemscript as this is not a P2SH script.");
- if (import_data.witnessscript) warnings.push_back("Ignoring witnessscript as this is not a (P2SH-)P2WSH script.");
- for (auto it = privkey_map.begin(); it != privkey_map.end(); ) {
- auto oldit = it++;
- if (import_data.used_keys.count(oldit->first) == 0) {
- warnings.push_back("Ignoring irrelevant private key.");
- privkey_map.erase(oldit);
- }
- }
- for (auto it = pubkey_map.begin(); it != pubkey_map.end(); ) {
- auto oldit = it++;
- auto key_data_it = import_data.used_keys.find(oldit->first);
- if (key_data_it == import_data.used_keys.end() || !key_data_it->second) {
- warnings.push_back("Ignoring public key \"" + HexStr(oldit->first) + "\" as it doesn't appear inside P2PKH or P2WPKH.");
- pubkey_map.erase(oldit);
- }
- }
- }
+ // If private keys are disabled, abort if private keys are being imported
+ if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !privkey_map.empty()) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
// Check whether we have any work to do
- if (::IsMine(*pwallet, script) & ISMINE_SPENDABLE) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ for (const CScript& script : script_pub_keys) {
+ if (::IsMine(*pwallet, script) & ISMINE_SPENDABLE) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script (\"" + HexStr(script.begin(), script.end()) + "\")");
+ }
}
// All good, time to import
pwallet->MarkDirty();
- for (const auto& entry : import_data.import_scripts) {
- if (!pwallet->HaveCScript(CScriptID(entry)) && !pwallet->AddCScript(entry)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
- }
+ if (!pwallet->ImportScripts(import_data.import_scripts)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
}
- for (const auto& entry : privkey_map) {
- const CKey& key = entry.second;
- CPubKey pubkey = key.GetPubKey();
- const CKeyID& id = entry.first;
- assert(key.VerifyPubKey(pubkey));
- pwallet->mapKeyMetadata[id].nCreateTime = timestamp;
- // If the private key is not present in the wallet, insert it.
- if (!pwallet->HaveKey(id) && !pwallet->AddKeyPubKey(key, pubkey)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
- }
- pwallet->UpdateTimeFirstKey(timestamp);
+ if (!pwallet->ImportPrivKeys(privkey_map, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
}
- for (const auto& entry : pubkey_map) {
- const CPubKey& pubkey = entry.second;
- const CKeyID& id = entry.first;
- CPubKey temp;
- if (!pwallet->GetPubKey(id, temp) && !pwallet->AddWatchOnly(GetScriptForRawPubKey(pubkey), timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
- }
+ if (!pwallet->ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.key_origins, add_keypool, internal, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
- if (!have_solving_data || !::IsMine(*pwallet, script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated
- if (!pwallet->AddWatchOnly(script, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
- }
- }
- if (!internal) {
- assert(IsValidDestination(dest));
- pwallet->SetAddressBook(dest, label, "receive");
+ if (!pwallet->ImportScriptPubKeys(label, script_pub_keys, have_solving_data, internal, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
result.pushKV("success", UniValue(true));
@@ -1198,16 +1329,18 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
"If an address/script is imported without all of the private keys required to spend from that address, it will be watchonly. The 'watchonly' option must be set to true in this case or a warning will be returned.\n"
"Conversely, if all the private keys are provided and the address/script is spendable, the watchonly option must be set to false, or a warning will be returned.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
- "may report that the imported keys, addresses or scripts exists but related transactions are still missing.\n",
+ "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
- {"requests", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "Data to be imported",
+ {"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
{
- {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"scriptPubKey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "Type of scriptPubKey (string for script, json for address)",
+ {"desc", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Descriptor to import. If using descriptor, do not also provide address/scriptPubKey, scripts, or pubkeys"},
+ {"scriptPubKey", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of scriptPubKey (string for script, json for address). Should not be provided if using a descriptor",
/* oneline_description */ "", {"\"<script>\" | { \"address\":\"<address>\" }", "string / json"}
},
- {"timestamp", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n"
+ {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n"
" or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
" key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
" \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
@@ -1215,28 +1348,30 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
" creation time of all keys being imported by the importmulti call will be scanned.",
/* oneline_description */ "", {"timestamp | \"now\"", "integer / string"}
},
- {"redeemscript", RPCArg::Type::STR, /* opt */ true, /* default_val */ "omitted", "Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey"},
- {"witnessscript", RPCArg::Type::STR, /* opt */ true, /* default_val */ "omitted", "Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey"},
- {"pubkeys", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "Array of strings giving pubkeys to import. They must occur in P2PKH or P2WPKH scripts. They are not required when the private key is also provided (see the \"keys\" argument).",
+ {"redeemscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey"},
+ {"witnessscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey"},
+ {"pubkeys", RPCArg::Type::ARR, /* default */ "empty array", "Array of strings giving pubkeys to import. They must occur in P2PKH or P2WPKH scripts. They are not required when the private key is also provided (see the \"keys\" argument).",
{
- {"pubKey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", ""},
+ {"pubKey", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
}
},
- {"keys", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "Array of strings giving private keys to import. The corresponding public keys must occur in the output or redeemscript.",
+ {"keys", RPCArg::Type::ARR, /* default */ "empty array", "Array of strings giving private keys to import. The corresponding public keys must occur in the output or redeemscript.",
{
- {"key", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", ""},
+ {"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
}
},
- {"internal", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Stating whether matching outputs should be treated as not incoming payments (also known as change)"},
- {"watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Stating whether matching outputs should be considered watchonly."},
- {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "''", "Label to assign to the address, only allowed with internal=false"},
+ {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED, "If a ranged descriptor is used, this specifies the end or the range (in the form [begin,end]) to import"},
+ {"internal", RPCArg::Type::BOOL, /* default */ "false", "Stating whether matching outputs should be treated as not incoming payments (also known as change)"},
+ {"watchonly", RPCArg::Type::BOOL, /* default */ "false", "Stating whether matching outputs should be considered watchonly."},
+ {"label", RPCArg::Type::STR, /* default */ "''", "Label to assign to the address, only allowed with internal=false"},
+ {"keypool", RPCArg::Type::BOOL, /* default */ "false", "Stating whether imported public keys should be added to the keypool for when users request new addresses. Only allowed when wallet private keys are disabled"},
},
},
},
"\"requests\""},
- {"options", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "null", "",
+ {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Stating if should rescan the blockchain after all imports"},
+ {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Stating if should rescan the blockchain after all imports"},
},
"\"options\""},
},
@@ -1319,7 +1454,11 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
}
if (fRescan && fRunScan && requests.size()) {
int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, true /* update */);
- pwallet->ReacceptWalletTransactions();
+ {
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
+ pwallet->ReacceptWalletTransactions(*locked_chain);
+ }
if (pwallet->IsAbortingRescan()) {
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index c96a9b0aff..f9baabeda4 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1,35 +1,33 @@
// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <amount.h>
-#include <chain.h>
#include <consensus/validation.h>
#include <core_io.h>
-#include <httpserver.h>
#include <init.h>
#include <interfaces/chain.h>
-#include <validation.h>
#include <key_io.h>
-#include <net.h>
+#include <node/transaction.h>
#include <outputtype.h>
#include <policy/feerate.h>
#include <policy/fees.h>
-#include <policy/policy.h>
#include <policy/rbf.h>
-#include <rpc/mining.h>
-#include <rpc/rawtransaction.h>
+#include <rpc/rawtransaction_util.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/descriptor.h>
#include <script/sign.h>
-#include <shutdown.h>
-#include <timedata.h>
-#include <util/system.h>
+#include <util/bip32.h>
+#include <util/fees.h>
#include <util/moneystr.h>
+#include <util/system.h>
+#include <util/url.h>
+#include <util/validation.h>
#include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
+#include <wallet/psbtwallet.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
@@ -66,14 +64,14 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr;
}
-std::string HelpRequiringPassphrase(CWallet * const pwallet)
+std::string HelpRequiringPassphrase(const CWallet* pwallet)
{
return pwallet && pwallet->IsCrypted()
? "\nRequires wallet passphrase to be set with walletpassphrase call."
: "";
}
-bool EnsureWalletIsAvailable(CWallet * const pwallet, bool avoidException)
+bool EnsureWalletIsAvailable(const CWallet* pwallet, bool avoidException)
{
if (pwallet) return true;
if (avoidException) return false;
@@ -85,7 +83,7 @@ bool EnsureWalletIsAvailable(CWallet * const pwallet, bool avoidException)
"Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
}
-void EnsureWalletIsUnlocked(CWallet * const pwallet)
+void EnsureWalletIsUnlocked(const CWallet* pwallet)
{
if (pwallet->IsLocked()) {
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
@@ -121,8 +119,7 @@ static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& lo
// Add opt-in RBF status
std::string rbfStatus = "no";
if (confirms <= 0) {
- LOCK(mempool.cs);
- RBFTransactionState rbfState = IsRBFOptIn(*wtx.tx, mempool);
+ RBFTransactionState rbfState = chain.isRBFOptIn(*wtx.tx);
if (rbfState == RBFTransactionState::UNKNOWN)
rbfStatus = "unknown";
else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125)
@@ -158,8 +155,8 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
"If 'label' is specified, it is added to the address book \n"
"so payments received with the address will be associated with 'label'.\n",
{
- {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "The label name for the address to be linked to. If not provided, the default label \"\" is used. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name."},
- {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"label", RPCArg::Type::STR, /* default */ "\"\"", "The label name for the address to be linked to. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name."},
+ {"address_type", RPCArg::Type::STR, /* default */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
"\"address\" (string) The new bitcoin address\n"
@@ -170,12 +167,12 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
},
}.ToString());
- if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
- }
-
LOCK(pwallet->cs_wallet);
+ if (!pwallet->CanGetAddresses()) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
+ }
+
// Parse the label first so we don't generate a key if there's an error
std::string label;
if (!request.params[0].isNull())
@@ -220,7 +217,7 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n",
{
- {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "set by -changetype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"address_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
"\"address\" (string) The address\n"
@@ -231,12 +228,12 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
},
}.ToString());
- if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
- }
-
LOCK(pwallet->cs_wallet);
+ if (!pwallet->CanGetAddresses(true)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
+ }
+
if (!pwallet->IsLocked()) {
pwallet->TopUpKeyPool();
}
@@ -276,8 +273,8 @@ static UniValue setlabel(const JSONRPCRequest& request)
RPCHelpMan{"setlabel",
"\nSets the label associated with the given address.\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to be associated with a label."},
- {"label", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The label to assign to the address."},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to be associated with a label."},
+ {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
},
RPCResults{},
RPCExamples{
@@ -307,7 +304,7 @@ static UniValue setlabel(const JSONRPCRequest& request)
static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
{
- CAmount curBalance = pwallet->GetBalance();
+ CAmount curBalance = pwallet->GetBalance().m_mine_trusted;
// Check amount
if (nValue <= 0)
@@ -316,7 +313,7 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
if (nValue > curBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
- if (pwallet->GetBroadcastTransactions() && !g_connman) {
+ if (pwallet->GetBroadcastTransactions() && !pwallet->chain().p2pEnabled()) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
@@ -338,7 +335,7 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
CValidationState state;
- if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, reservekey, g_connman.get(), state)) {
+ if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, reservekey, state)) {
strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
@@ -360,18 +357,18 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
"\nSend an amount to a given address." +
HelpRequiringPassphrase(pwallet) + "\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to send to."},
- {"amount", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
- {"comment", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "A comment used to store what the transaction is for.\n"
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to send to."},
+ {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
+ {"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment used to store what the transaction is for.\n"
" This is not part of the transaction, just kept in your wallet."},
- {"comment_to", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "A comment to store the name of the person or organization\n"
+ {"comment_to", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment to store the name of the person or organization\n"
" to which you're sending the transaction. This is not part of the \n"
" transaction, just kept in your wallet."},
- {"subtractfeefromamount", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "The fee will be deducted from the amount being sent.\n"
+ {"subtractfeefromamount", RPCArg::Type::BOOL, /* default */ "false", "The fee will be deducted from the amount being sent.\n"
" The recipient will receive less bitcoins than you enter in the amount field."},
- {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
- {"conf_target", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to wallet's default", "Confirmation target (in blocks)"},
- {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "UNSET", "The fee estimate mode, must be one of:\n"
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
" \"CONSERVATIVE\""},
@@ -422,7 +419,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
}
if (!request.params[6].isNull()) {
- coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]);
+ coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
}
if (!request.params[7].isNull()) {
@@ -516,8 +513,8 @@ static UniValue signmessage(const JSONRPCRequest& request)
"\nSign a message with the private key of an address" +
HelpRequiringPassphrase(pwallet) + "\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to use for the private key."},
- {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message to create a signature of."},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to use for the private key."},
+ {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."},
},
RPCResult{
"\"signature\" (string) The signature of the message encoded in base 64\n"
@@ -547,13 +544,14 @@ static UniValue signmessage(const JSONRPCRequest& request)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
}
- const CKeyID *keyID = boost::get<CKeyID>(&dest);
- if (!keyID) {
+ const PKHash *pkhash = boost::get<PKHash>(&dest);
+ if (!pkhash) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
CKey key;
- if (!pwallet->GetKey(*keyID, key)) {
+ CKeyID keyID(*pkhash);
+ if (!pwallet->GetKey(keyID, key)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
}
@@ -582,8 +580,8 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
RPCHelpMan{"getreceivedbyaddress",
"\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address for transactions."},
- {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "Only include transactions confirmed at least this many times."},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for transactions."},
+ {"minconf", RPCArg::Type::NUM, /* default */ "1", "Only include transactions confirmed at least this many times."},
},
RPCResult{
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n"
@@ -604,7 +602,6 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -627,8 +624,9 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
CAmount nAmount = 0;
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second;
- if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
+ if (wtx.IsCoinBase() || !locked_chain->checkFinalTx(*wtx.tx)) {
continue;
+ }
for (const CTxOut& txout : wtx.tx->vout)
if (txout.scriptPubKey == scriptPubKey)
@@ -654,8 +652,8 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
RPCHelpMan{"getreceivedbylabel",
"\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
{
- {"label", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The selected label, may be the default label using \"\"."},
- {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "Only include transactions confirmed at least this many times."},
+ {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The selected label, may be the default label using \"\"."},
+ {"minconf", RPCArg::Type::NUM, /* default */ "1", "Only include transactions confirmed at least this many times."},
},
RPCResult{
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this label.\n"
@@ -676,7 +674,6 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -693,8 +690,9 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
CAmount nAmount = 0;
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second;
- if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
+ if (wtx.IsCoinBase() || !locked_chain->checkFinalTx(*wtx.tx)) {
continue;
+ }
for (const CTxOut& txout : wtx.tx->vout)
{
@@ -726,9 +724,9 @@ static UniValue getbalance(const JSONRPCRequest& request)
"The available balance is what the wallet considers currently spendable, and is\n"
"thus affected by options which limit spendability such as -spendzeroconfchange.\n",
{
- {"dummy", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "Remains for backward compatibility. Must be excluded or set to \"*\"."},
- {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Only include transactions confirmed at least this many times."},
- {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Also include balance in watch-only addresses (see 'importaddress')"},
+ {"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
+ {"minconf", RPCArg::Type::NUM, /* default */ "0", "Only include transactions confirmed at least this many times."},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Also include balance in watch-only addresses (see 'importaddress')"},
},
RPCResult{
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n"
@@ -760,12 +758,14 @@ static UniValue getbalance(const JSONRPCRequest& request)
min_depth = request.params[1].get_int();
}
- isminefilter filter = ISMINE_SPENDABLE;
+ bool include_watchonly = false;
if (!request.params[2].isNull() && request.params[2].get_bool()) {
- filter = filter | ISMINE_WATCH_ONLY;
+ include_watchonly = true;
}
- return ValueFromAmount(pwallet->GetBalance(filter, min_depth));
+ const auto bal = pwallet->GetBalance(min_depth);
+
+ return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
}
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
@@ -780,7 +780,7 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
RPCHelpMan{"getunconfirmedbalance",
- "Returns the server's total unconfirmed balance\n",
+ "DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
{},
RPCResults{},
RPCExamples{""},
@@ -793,7 +793,7 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- return ValueFromAmount(pwallet->GetUnconfirmedBalance());
+ return ValueFromAmount(pwallet->GetBalance().m_mine_untrusted_pending);
}
@@ -806,31 +806,29 @@ static UniValue sendmany(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
- throw std::runtime_error(
- RPCHelpMan{"sendmany",
+ const RPCHelpMan help{"sendmany",
"\nSend multiple times. Amounts are double-precision floating point numbers." +
HelpRequiringPassphrase(pwallet) + "\n",
{
- {"dummy", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "Must be set to \"\" for backwards compatibility.", "\"\""},
- {"amounts", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "A json object with addresses and amounts",
+ {"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.", "\"\""},
+ {"amounts", RPCArg::Type::OBJ, RPCArg::Optional::NO, "A json object with addresses and amounts",
{
- {"address", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
+ {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
},
},
- {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "Only use the balance confirmed at least this many times."},
- {"comment", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "A comment"},
- {"subtractfeefrom", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array with addresses.\n"
+ {"minconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
+ {"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment"},
+ {"subtractfeefrom", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array with addresses.\n"
" The fee will be equally deducted from the amount of each selected address.\n"
" Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
" If no addresses are specified here, the sender pays the fee.",
{
- {"address", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "Subtract fee from this address"},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
- {"conf_target", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to wallet's default", "Confirmation target (in blocks)"},
- {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "UNSET", "The fee estimate mode, must be one of:\n"
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
" \"CONSERVATIVE\""},
@@ -849,7 +847,11 @@ static UniValue sendmany(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
// 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
@@ -858,7 +860,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- if (pwallet->GetBroadcastTransactions() && !g_connman) {
+ if (pwallet->GetBroadcastTransactions() && !pwallet->chain().p2pEnabled()) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
@@ -866,9 +868,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\"");
}
UniValue sendTo = request.params[1].get_obj();
- int nMinDepth = 1;
- if (!request.params[2].isNull())
- nMinDepth = request.params[2].get_int();
mapValue_t mapValue;
if (!request.params[3].isNull() && !request.params[3].get_str().empty())
@@ -884,7 +883,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
}
if (!request.params[6].isNull()) {
- coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]);
+ coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
}
if (!request.params[7].isNull()) {
@@ -896,7 +895,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
std::set<CTxDestination> destinations;
std::vector<CRecipient> vecSend;
- CAmount totalAmount = 0;
std::vector<std::string> keys = sendTo.getKeys();
for (const std::string& name_ : keys) {
CTxDestination dest = DecodeDestination(name_);
@@ -913,7 +911,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
CAmount nAmount = AmountFromValue(sendTo[name_]);
if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
- totalAmount += nAmount;
bool fSubtractFeeFromAmount = false;
for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
@@ -928,11 +925,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
- // Check funds
- if (totalAmount > pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth)) {
- throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Wallet has insufficient funds");
- }
-
// Shuffle recipient list
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
@@ -946,7 +938,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
if (!fCreated)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
CValidationState state;
- if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, keyChange, g_connman.get(), state)) {
+ if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, keyChange, state)) {
strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
@@ -972,14 +964,14 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
"See `importaddress` for watchonly p2sh address support.\n"
"If 'label' is specified, assign address to that label.\n",
{
- {"nrequired", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The number of required signatures out of the n keys or addresses."},
- {"keys", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of bitcoin addresses or hex-encoded public keys",
+ {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys or addresses."},
+ {"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of bitcoin addresses or hex-encoded public keys",
{
- {"key", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "bitcoin address or hex-encoded public key"},
+ {"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "bitcoin address or hex-encoded public key"},
},
},
- {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "A label to assign the addresses to."},
- {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A label to assign the addresses to."},
+ {"address_type", RPCArg::Type::STR, /* default */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
"{\n"
@@ -1048,8 +1040,6 @@ struct tallyitem
static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
- LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
-
// Minimum confirmations
int nMinDepth = 1;
if (!params[0].isNull())
@@ -1080,8 +1070,9 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * co
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second;
- if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
+ if (wtx.IsCoinBase() || !locked_chain.checkFinalTx(*wtx.tx)) {
continue;
+ }
int nDepth = wtx.GetDepthInMainChain(locked_chain);
if (nDepth < nMinDepth)
@@ -1206,10 +1197,10 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
RPCHelpMan{"listreceivedbyaddress",
"\nList balances by receiving address.\n",
{
- {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "The minimum number of confirmations before payments are included."},
- {"include_empty", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include addresses that haven't received any payments."},
- {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include watch-only addresses (see 'importaddress')."},
- {"address_filter", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "If present, only return information on this address."},
+ {"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum number of confirmations before payments are included."},
+ {"include_empty", RPCArg::Type::BOOL, /* default */ "false", "Whether to include addresses that haven't received any payments."},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Whether to include watch-only addresses (see 'importaddress')."},
+ {"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If present, only return information on this address."},
},
RPCResult{
"[\n"
@@ -1259,9 +1250,9 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
RPCHelpMan{"listreceivedbylabel",
"\nList received transactions by label.\n",
{
- {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "The minimum number of confirmations before payments are included."},
- {"include_empty", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include labels that haven't received any payments."},
- {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include watch-only addresses (see 'importaddress')."},
+ {"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum number of confirmations before payments are included."},
+ {"include_empty", RPCArg::Type::BOOL, /* default */ "false", "Whether to include labels that haven't received any payments."},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Whether to include watch-only addresses (see 'importaddress')."},
},
RPCResult{
"[\n"
@@ -1309,7 +1300,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
* @param filter_ismine The "is mine" filter flags.
* @param filter_label Optional label string to filter incoming transactions.
*/
-static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label)
+static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
CAmount nFee;
std::list<COutputEntry> listReceived;
@@ -1400,11 +1391,11 @@ UniValue listtransactions(const JSONRPCRequest& request)
"\nIf a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n"
"\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n",
{
- {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "If set, should be a valid label name to return only incoming transactions\n"
+ {"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, should be a valid label name to return only incoming transactions\n"
" with the specified label, or \"*\" to disable filtering and return all transactions."},
- {"count", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "10", "The number of transactions to return"},
- {"skip", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "The number of transactions to skip"},
- {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Include transactions to watch-only addresses (see 'importaddress')"},
+ {"count", RPCArg::Type::NUM, /* default */ "10", "The number of transactions to return"},
+ {"skip", RPCArg::Type::NUM, /* default */ "0", "The number of transactions to skip"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Include transactions to watch-only addresses (see 'importaddress')"},
},
RPCResult{
"[\n"
@@ -1535,10 +1526,10 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
"If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
"Additionally, if include_removed is set, transactions affecting the wallet which were removed are returned in the \"removed\" array.\n",
{
- {"blockhash", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "If set, the block hash to list transactions since, otherwise list all transactions."},
- {"target_confirmations", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
- {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Include transactions to watch-only addresses (see 'importaddress')"},
- {"include_removed", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Show transactions that were removed due to a reorg in the \"removed\" array\n"
+ {"blockhash", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, the block hash to list transactions since, otherwise list all transactions."},
+ {"target_confirmations", RPCArg::Type::NUM, /* default */ "1", "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Include transactions to watch-only addresses (see 'importaddress')"},
+ {"include_removed", RPCArg::Type::BOOL, /* default */ "true", "Show transactions that were removed due to a reorg in the \"removed\" array\n"
" (not guaranteed to work on pruned nodes)"},
},
RPCResult{
@@ -1678,8 +1669,8 @@ static UniValue gettransaction(const JSONRPCRequest& request)
RPCHelpMan{"gettransaction",
"\nGet detailed information about in-wallet transaction <txid>\n",
{
- {"txid", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction id"},
- {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include watch-only addresses in balance calculation and details[]"},
+ {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Whether to include watch-only addresses in balance calculation and details[]"},
},
RPCResult{
"{\n"
@@ -1760,7 +1751,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
ListTransactions(*locked_chain, pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
entry.pushKV("details", details);
- std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags());
+ std::string strHex = EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags());
entry.pushKV("hex", strHex);
return entry;
@@ -1784,7 +1775,7 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
"It only works on transactions which are not included in a block and are not currently in the mempool.\n"
"It has no effect on transactions which are already abandoned.\n",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
},
RPCResults{},
RPCExamples{
@@ -1828,7 +1819,7 @@ static UniValue backupwallet(const JSONRPCRequest& request)
RPCHelpMan{"backupwallet",
"\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n",
{
- {"destination", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The destination directory or file"},
+ {"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The destination directory or file"},
},
RPCResults{},
RPCExamples{
@@ -1868,7 +1859,7 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
"\nFills the keypool."+
HelpRequiringPassphrase(pwallet) + "\n",
{
- {"newsize", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "100", "The new keypool size"},
+ {"newsize", RPCArg::Type::NUM, /* default */ "100", "The new keypool size"},
},
RPCResults{},
RPCExamples{
@@ -1921,8 +1912,8 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
"Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n"
"time that overrides the old one.\n",
{
- {"passphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The wallet passphrase"},
- {"timeout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."},
+ {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet passphrase"},
+ {"timeout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."},
},
RPCResults{},
RPCExamples{
@@ -1978,7 +1969,7 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
// wallet before the following callback is called. If a valid shared pointer
// is acquired in the callback then the wallet is still loaded.
std::weak_ptr<CWallet> weak_wallet = wallet;
- RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet] {
+ pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet] {
if (auto shared_wallet = weak_wallet.lock()) {
LOCK(shared_wallet->cs_wallet);
shared_wallet->Lock();
@@ -2004,8 +1995,8 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
RPCHelpMan{"walletpassphrasechange",
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
{
- {"oldpassphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The current passphrase"},
- {"newpassphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The new passphrase"},
+ {"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The current passphrase"},
+ {"newpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The new passphrase"},
},
RPCResults{},
RPCExamples{
@@ -2106,7 +2097,7 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
"Use the walletpassphrase call for this, and then walletlock call.\n"
"If the wallet is already encrypted, use the walletpassphrasechange call.\n",
{
- {"passphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long."},
+ {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long."},
},
RPCResults{},
RPCExamples{
@@ -2127,6 +2118,10 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
+ if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet does not contain private keys, nothing to encrypt.");
+ }
+
if (pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
}
@@ -2168,13 +2163,13 @@ static UniValue lockunspent(const JSONRPCRequest& request)
"is always cleared (by virtue of process exit) when a node stops or fails.\n"
"Also see the listunspent call\n",
{
- {"unlock", RPCArg::Type::BOOL, /* opt */ false, /* default_val */ "", "Whether to unlock (true) or lock (false) the specified transactions"},
- {"transactions", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "A json array of objects. Each object the txid (string) vout (numeric).",
+ {"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
+ {"transactions", RPCArg::Type::ARR, /* default */ "empty array", "A json array of objects. Each object the txid (string) vout (numeric).",
{
- {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
- {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
},
},
},
@@ -2348,7 +2343,7 @@ static UniValue settxfee(const JSONRPCRequest& request)
RPCHelpMan{"settxfee",
"\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n",
{
- {"amount", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The transaction fee in " + CURRENCY_UNIT + "/kB"},
+ {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee in " + CURRENCY_UNIT + "/kB"},
},
RPCResult{
"true|false (boolean) Returns true if successful\n"
@@ -2367,8 +2362,8 @@ static UniValue settxfee(const JSONRPCRequest& request)
CFeeRate tx_fee_rate(nAmount, 1000);
if (tx_fee_rate == 0) {
// automatic selection
- } else if (tx_fee_rate < ::minRelayTxFee) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than min relay tx fee (%s)", ::minRelayTxFee.ToString()));
+ } else if (tx_fee_rate < pwallet->chain().relayMinFee()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than min relay tx fee (%s)", pwallet->chain().relayMinFee().ToString()));
} else if (tx_fee_rate < pwallet->m_min_fee) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than wallet min fee (%s)", pwallet->m_min_fee.ToString()));
}
@@ -2377,6 +2372,68 @@ static UniValue settxfee(const JSONRPCRequest& request)
return true;
}
+static UniValue getbalances(const JSONRPCRequest& request)
+{
+ std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
+ if (!EnsureWalletIsAvailable(rpc_wallet.get(), request.fHelp)) {
+ return NullUniValue;
+ }
+ CWallet& wallet = *rpc_wallet;
+
+ const RPCHelpMan help{
+ "getbalances",
+ "Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
+ {},
+ RPCResult{
+ "{\n"
+ " \"mine\": { (object) balances from outputs that the wallet can sign\n"
+ " \"trusted\": xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n"
+ " \"untrusted_pending\": xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n"
+ " \"immature\": xxx (numeric) balance from immature coinbase outputs\n"
+ " },\n"
+ " \"watchonly\": { (object) watchonly balances (not present if wallet does not watch anything)\n"
+ " \"trusted\": xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n"
+ " \"untrusted_pending\": xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n"
+ " \"immature\": xxx (numeric) balance from immature coinbase outputs\n"
+ " },\n"
+ "}\n"},
+ RPCExamples{
+ HelpExampleCli("getbalances", "") +
+ HelpExampleRpc("getbalances", "")},
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
+
+ // 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
+ wallet.BlockUntilSyncedToCurrentChain();
+
+ auto locked_chain = wallet.chain().lock();
+ LOCK(wallet.cs_wallet);
+
+ UniValue obj(UniValue::VOBJ);
+
+ const auto bal = wallet.GetBalance();
+ UniValue balances{UniValue::VOBJ};
+ {
+ UniValue balances_mine{UniValue::VOBJ};
+ balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
+ balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending));
+ balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature));
+ balances.pushKV("mine", balances_mine);
+ }
+ if (wallet.HaveWatchOnly()) {
+ UniValue balances_watchonly{UniValue::VOBJ};
+ balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted));
+ balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending));
+ balances_watchonly.pushKV("immature", ValueFromAmount(bal.m_watchonly_immature));
+ balances.pushKV("watchonly", balances_watchonly);
+ }
+ return balances;
+}
+
static UniValue getwalletinfo(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -2386,18 +2443,16 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
- RPCHelpMan{"getwalletinfo",
+ const RPCHelpMan help{"getwalletinfo",
"Returns an object containing various wallet state info.\n",
{},
RPCResult{
"{\n"
" \"walletname\": xxxxx, (string) the wallet name\n"
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
- " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
- " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
- " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
+ " \"balance\": xxxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.trusted\n"
+ " \"unconfirmed_balance\": xxx, (numeric) DEPRECATED. Identical to getbalances().mine.untrusted_pending\n"
+ " \"immature_balance\": xxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.immature\n"
" \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\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 (only counts external keys)\n"
@@ -2405,15 +2460,23 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
" \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n"
- " \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias for hdseedid retained for backwards-compatibility. Will be removed in V0.18.\n"
" \"private_keys_enabled\": true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n"
+ " \"scanning\": (json object) current scanning details, or false if no scan is in progress\n"
+ " {\n"
+ " \"duration\" : xxxx (numeric) elapsed seconds since scan start\n"
+ " \"progress\" : x.xxxx, (numeric) scanning progress percentage [0.0, 1.0]\n"
+ " }\n"
"}\n"
},
RPCExamples{
HelpExampleCli("getwalletinfo", "")
+ HelpExampleRpc("getwalletinfo", "")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
// 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
@@ -2425,16 +2488,17 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
UniValue obj(UniValue::VOBJ);
size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
+ const auto bal = pwallet->GetBalance();
obj.pushKV("walletname", pwallet->GetName());
obj.pushKV("walletversion", pwallet->GetVersion());
- obj.pushKV("balance", ValueFromAmount(pwallet->GetBalance()));
- obj.pushKV("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance()));
- obj.pushKV("immature_balance", ValueFromAmount(pwallet->GetImmatureBalance()));
+ obj.pushKV("balance", ValueFromAmount(bal.m_mine_trusted));
+ obj.pushKV("unconfirmed_balance", ValueFromAmount(bal.m_mine_untrusted_pending));
+ obj.pushKV("immature_balance", ValueFromAmount(bal.m_mine_immature));
obj.pushKV("txcount", (int)pwallet->mapWallet.size());
obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
CKeyID seed_id = pwallet->GetHDChain().seed_id;
- if (!seed_id.IsNull() && pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
+ if (pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize));
}
if (pwallet->IsCrypted()) {
@@ -2443,9 +2507,16 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK()));
if (!seed_id.IsNull()) {
obj.pushKV("hdseedid", seed_id.GetHex());
- obj.pushKV("hdmasterkeyid", seed_id.GetHex());
}
obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
+ if (pwallet->IsScanning()) {
+ UniValue scanning(UniValue::VOBJ);
+ scanning.pushKV("duration", pwallet->ScanningDuration() / 1000);
+ scanning.pushKV("progress", pwallet->ScanningProgress());
+ obj.pushKV("scanning", scanning);
+ } else {
+ obj.pushKV("scanning", false);
+ }
return obj;
}
@@ -2529,7 +2600,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
"\nNote that all wallet command-line options used when starting bitcoind will be"
"\napplied to the new wallet (eg -zapwallettxes, upgradewallet, rescan, etc).\n",
{
- {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The wallet directory or .dat file."},
+ {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
},
RPCResult{
"{\n"
@@ -2544,7 +2615,6 @@ static UniValue loadwallet(const JSONRPCRequest& request)
}.ToString());
WalletLocation location(request.params[0].get_str());
- std::string error;
if (!location.Exists()) {
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + location.GetName() + " not found.");
@@ -2556,18 +2626,9 @@ static UniValue loadwallet(const JSONRPCRequest& request)
}
}
- std::string warning;
- if (!CWallet::Verify(*g_rpc_interfaces->chain, location, false, error, warning)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
- }
-
- std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location);
- if (!wallet) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Wallet loading failed.");
- }
- AddWallet(wallet);
-
- wallet->postInitProcess();
+ std::string error, warning;
+ std::shared_ptr<CWallet> const wallet = LoadWallet(*g_rpc_interfaces->chain, location, error, warning);
+ if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error);
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
@@ -2578,32 +2639,53 @@ static UniValue loadwallet(const JSONRPCRequest& request)
static UniValue createwallet(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
- throw std::runtime_error(
- RPCHelpMan{"createwallet",
- "\nCreates and loads a new wallet.\n",
- {
- {"wallet_name", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
- {"disable_private_keys", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
- },
- RPCResult{
+ const RPCHelpMan help{
+ "createwallet",
+ "\nCreates and loads a new wallet.\n",
+ {
+ {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
+ {"disable_private_keys", RPCArg::Type::BOOL, /* default */ "false", "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
+ {"blank", RPCArg::Type::BOOL, /* default */ "false", "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
+ {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."},
+ },
+ RPCResult{
"{\n"
" \"name\" : <wallet_name>, (string) The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path.\n"
" \"warning\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n"
"}\n"
- },
- RPCExamples{
- HelpExampleCli("createwallet", "\"testwallet\"")
+ },
+ RPCExamples{
+ HelpExampleCli("createwallet", "\"testwallet\"")
+ HelpExampleRpc("createwallet", "\"testwallet\"")
- },
- }.ToString());
+ },
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
}
std::string error;
std::string warning;
- bool disable_privatekeys = false;
- if (!request.params[1].isNull()) {
- disable_privatekeys = request.params[1].get_bool();
+ uint64_t flags = 0;
+ if (!request.params[1].isNull() && request.params[1].get_bool()) {
+ flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
+ }
+
+ bool create_blank = false; // Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
+ if (!request.params[2].isNull() && request.params[2].get_bool()) {
+ create_blank = true;
+ flags |= WALLET_FLAG_BLANK_WALLET;
+ }
+ SecureString passphrase;
+ passphrase.reserve(100);
+ if (!request.params[3].isNull()) {
+ passphrase = request.params[3].get_str().c_str();
+ if (passphrase.empty()) {
+ // Empty string is invalid
+ throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Cannot encrypt a wallet with a blank password");
+ }
+ // Born encrypted wallets need to be blank first so that wallet creation doesn't make any unencrypted keys
+ flags |= WALLET_FLAG_BLANK_WALLET;
}
WalletLocation location(request.params[0].get_str());
@@ -2616,10 +2698,33 @@ static UniValue createwallet(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
}
- std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location, (disable_privatekeys ? (uint64_t)WALLET_FLAG_DISABLE_PRIVATE_KEYS : 0));
+ std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location, flags);
if (!wallet) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
}
+
+ // Encrypt the wallet if there's a passphrase
+ if (!passphrase.empty() && !(flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (!wallet->EncryptWallet(passphrase)) {
+ throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Wallet created but failed to encrypt.");
+ }
+
+ if (!create_blank) {
+ // Unlock the wallet
+ if (!wallet->Unlock(passphrase)) {
+ throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Wallet was encrypted but could not be unlocked");
+ }
+
+ // Set a seed for the wallet
+ CPubKey master_pub_key = wallet->GenerateNewSeed();
+ wallet->SetHDSeed(master_pub_key);
+ wallet->NewKeyPool();
+
+ // Relock the wallet
+ wallet->Lock();
+ }
+ }
+
AddWallet(wallet);
wallet->postInitProcess();
@@ -2639,7 +2744,7 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
"Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
"Specifying the wallet name on a wallet endpoint is invalid.",
{
- {"wallet_name", RPCArg::Type::STR, /* opt */ true, /* default_val */ "the wallet name from the RPC request", "The name of the wallet to unload."},
+ {"wallet_name", RPCArg::Type::STR, /* default */ "the wallet name from the RPC request", "The name of the wallet to unload."},
},
RPCResults{},
RPCExamples{
@@ -2675,49 +2780,6 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
return NullUniValue;
}
-static UniValue resendwallettransactions(const JSONRPCRequest& request)
-{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
- RPCHelpMan{"resendwallettransactions",
- "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n"
- "Intended only for testing; the wallet code periodically re-broadcasts\n"
- "automatically.\n",
- {},
- RPCResult{
- "Returns an RPC error if -walletbroadcast is set to false.\n"
- "Returns array of transaction ids that were re-broadcast.\n"
- },
- RPCExamples{""},
- }.ToString()
- );
-
- if (!g_connman)
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
-
- auto locked_chain = pwallet->chain().lock();
- LOCK(pwallet->cs_wallet);
-
- if (!pwallet->GetBroadcastTransactions()) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast");
- }
-
- std::vector<uint256> txids = pwallet->ResendWalletTransactionsBefore(*locked_chain, GetTime(), g_connman.get());
- UniValue result(UniValue::VARR);
- for (const uint256& txid : txids)
- {
- result.push_back(txid.ToString());
- }
- return result;
-}
-
static UniValue listunspent(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -2734,21 +2796,21 @@ static UniValue listunspent(const JSONRPCRequest& request)
"with between minconf and maxconf (inclusive) confirmations.\n"
"Optionally filter to only include txouts paid to specified addresses.\n",
{
- {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "The minimum confirmations to filter"},
- {"maxconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "9999999", "The maximum confirmations to filter"},
- {"addresses", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "A json array of bitcoin addresses to filter",
+ {"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum confirmations to filter"},
+ {"maxconf", RPCArg::Type::NUM, /* default */ "9999999", "The maximum confirmations to filter"},
+ {"addresses", RPCArg::Type::ARR, /* default */ "empty array", "A json array of bitcoin addresses to filter",
{
- {"address", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "bitcoin address"},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "bitcoin address"},
},
},
- {"include_unsafe", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Include outputs that are not safe to spend\n"
+ {"include_unsafe", RPCArg::Type::BOOL, /* default */ "true", "Include outputs that are not safe to spend\n"
" See description of \"safe\" attribute below."},
- {"query_options", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "null", "JSON with query options",
+ {"query_options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "JSON with query options",
{
- {"minimumAmount", RPCArg::Type::AMOUNT, /* opt */ true, /* default_val */ "0", "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
- {"maximumAmount", RPCArg::Type::AMOUNT, /* opt */ true, /* default_val */ "unlimited", "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
- {"maximumCount", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "unlimited", "Maximum number of UTXOs"},
- {"minimumSumAmount", RPCArg::Type::AMOUNT, /* opt */ true, /* default_val */ "unlimited", "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
+ {"minimumAmount", RPCArg::Type::AMOUNT, /* default */ "0", "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
+ {"maximumAmount", RPCArg::Type::AMOUNT, /* default */ "unlimited", "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
+ {"maximumCount", RPCArg::Type::NUM, /* default */ "unlimited", "Maximum number of UTXOs"},
+ {"minimumSumAmount", RPCArg::Type::AMOUNT, /* default */ "unlimited", "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
},
"query_options"},
},
@@ -2762,7 +2824,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
" \"scriptPubKey\" : \"key\", (string) the script key\n"
" \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n"
" \"confirmations\" : n, (numeric) The number of confirmations\n"
- " \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n"
+ " \"redeemScript\" : \"script\" (string) The redeemScript if scriptPubKey is P2SH\n"
+ " \"witnessScript\" : \"script\" (string) witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH\n"
" \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n"
" \"solvable\" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys\n"
" \"desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n"
@@ -2872,10 +2935,32 @@ static UniValue listunspent(const JSONRPCRequest& request)
}
if (scriptPubKey.IsPayToScriptHash()) {
- const CScriptID& hash = boost::get<CScriptID>(address);
+ const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address));
CScript redeemScript;
if (pwallet->GetCScript(hash, redeemScript)) {
entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()));
+ // Now check if the redeemScript is actually a P2WSH script
+ CTxDestination witness_destination;
+ if (redeemScript.IsPayToWitnessScriptHash()) {
+ bool extracted = ExtractDestination(redeemScript, witness_destination);
+ assert(extracted);
+ // Also return the witness script
+ const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination);
+ CScriptID id;
+ CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
+ CScript witnessScript;
+ if (pwallet->GetCScript(id, witnessScript)) {
+ entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ }
+ }
+ }
+ } else if (scriptPubKey.IsPayToWitnessScriptHash()) {
+ const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address);
+ CScriptID id;
+ CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
+ CScript witnessScript;
+ if (pwallet->GetCScript(id, witnessScript)) {
+ entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
}
}
}
@@ -2975,7 +3060,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("feeRate")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate");
}
- coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"]);
+ coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"], pwallet->chain().estimateMaxBlocks());
}
if (options.exists("estimate_mode")) {
if (options.exists("feeRate")) {
@@ -3021,48 +3106,52 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
- throw std::runtime_error(
- RPCHelpMan{"fundrawtransaction",
+ const RPCHelpMan help{"fundrawtransaction",
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
"This will not modify existing inputs, and will add at most one change output to the outputs.\n"
"No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
"Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
- "The inputs added will not be signed, use signrawtransaction for that.\n"
+ "The inputs added will not be signed, use signrawtransactionwithkey\n"
+ " or signrawtransactionwithwallet for that.\n"
"Note that all existing inputs must have their previous output transaction be in the wallet.\n"
"Note that all inputs selected must be of standard form and P2SH scripts must be\n"
"in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
"You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
"Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n",
{
- {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex string of the raw transaction"},
- {"options", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "null", "for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}",
+ {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
+ {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}",
{
- {"changeAddress", RPCArg::Type::STR, /* opt */ true, /* default_val */ "pool address", "The bitcoin address to receive the change"},
- {"changePosition", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "random", "The index of the change output"},
- {"change_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
- {"includeWatching", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Also select inputs which are watch only"},
- {"lockUnspents", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Lock selected unspent outputs"},
- {"feeRate", RPCArg::Type::AMOUNT, /* opt */ true, /* default_val */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
- {"subtractFeeFromOutputs", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "A json array of integers.\n"
+ {"changeAddress", RPCArg::Type::STR, /* default */ "pool address", "The bitcoin address to receive the change"},
+ {"changePosition", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
+ {"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"includeWatching", RPCArg::Type::BOOL, /* default */ "false", "Also select inputs which are watch only"},
+ {"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
+ {"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
+ {"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "A json array of integers.\n"
" The fee will be equally deducted from the amount of each specified output.\n"
" Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
" If no outputs are specified here, the sender pays the fee.",
{
- {"vout_index", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "", "The zero-based output index, before a change output is added."},
+ {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "fallback to wallet's default", "Marks this transaction as BIP125 replaceable.\n"
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees"},
- {"conf_target", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to wallet's default", "Confirmation target (in blocks)"},
- {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "UNSET", "The fee estimate mode, must be one of:\n"
+ {"conf_target", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
" \"CONSERVATIVE\""},
},
"options"},
- {"iswitness", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction \n"
- " If iswitness is not present, heuristic tests will be used in decoding"},
+ {"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
+ "If iswitness is not present, heuristic tests will be used in decoding.\n"
+ "If true, only witness deserialization will be tried.\n"
+ "If false, only non-witness deserialization will be tried.\n"
+ "This boolean should reflect whether the transaction has inputs\n"
+ "(e.g. fully valid, or on-chain transactions), if known by the caller."
+ },
},
RPCResult{
"{\n"
@@ -3077,11 +3166,15 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
"\nAdd sufficient unsigned inputs to meet the output value\n"
+ HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
"\nSign the transaction\n"
- + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") +
+ + HelpExampleCli("signrawtransactionwithwallet", "\"fundedtransactionhex\"") +
"\nSend the transaction\n"
+ HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
@@ -3122,21 +3215,22 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
"this transaction depends on but may not yet be in the block chain." +
HelpRequiringPassphrase(pwallet) + "\n",
{
- {"hexstring", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction hex string"},
- {"prevtxs", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "null", "A json array of previous dependent transaction outputs",
+ {"hexstring", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction hex string"},
+ {"prevtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of previous dependent transaction outputs",
{
- {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
- {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
- {"scriptPubKey", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "script key"},
- {"redeemScript", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "omitted", "(required for P2SH or P2WSH)"},
- {"amount", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The amount spent"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
+ {"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
+ {"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
+ {"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
+ {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "(required for Segwit inputs) the amount spent"},
},
},
},
},
- {"sighashtype", RPCArg::Type::STR, /* opt */ true, /* default_val */ "ALL", "The signature hash type. Must be one of\n"
+ {"sighashtype", RPCArg::Type::STR, /* default */ "ALL", "The signature hash type. Must be one of\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -3178,7 +3272,14 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
- return SignTransaction(pwallet->chain(), mtx, request.params[1], pwallet, false, request.params[2]);
+ // Fetch previous transactions (inputs):
+ std::map<COutPoint, Coin> coins;
+ for (const CTxIn& txin : mtx.vin) {
+ coins[txin.prevout]; // Create empty map entry keyed by prevout.
+ }
+ pwallet->chain().findCoins(coins);
+
+ return SignTransaction(mtx, request.params[1], pwallet, coins, false, request.params[2]);
}
static UniValue bumpfee(const JSONRPCRequest& request)
@@ -3195,9 +3296,9 @@ static UniValue bumpfee(const JSONRPCRequest& request)
RPCHelpMan{"bumpfee",
"\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
"An opt-in RBF transaction with the given txid must be in the wallet.\n"
- "The command will pay the additional fee by decreasing (or perhaps removing) its change output.\n"
- "If the change output is not big enough to cover the increased fee, the command will currently fail\n"
- "instead of adding new inputs to compensate. (A future implementation could improve this.)\n"
+ "The command will pay the additional fee by reducing change outputs or adding inputs when necessary. It may add a new change output if one does not already exist.\n"
+ "If `totalFee` is given, adding inputs is not supported, so there must be a single change output that is big enough or it will fail.\n"
+ "All inputs in the original transaction will be included in the replacement transaction.\n"
"The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
"By default, the new fee will be calculated automatically using estimatesmartfee.\n"
"The user can specify a confirmation target for estimatesmartfee.\n"
@@ -3205,22 +3306,22 @@ static UniValue bumpfee(const JSONRPCRequest& request)
"At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
"returned by getnetworkinfo) to enter the node's mempool.\n",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The txid to be bumped"},
- {"options", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "null", "",
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid to be bumped"},
+ {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"confTarget", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to wallet's default", "Confirmation target (in blocks)"},
- {"totalFee", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to 'confTarget'", "Total fee (NOT feerate) to pay, in satoshis.\n"
+ {"confTarget", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"totalFee", RPCArg::Type::NUM, /* default */ "fallback to 'confTarget'", "Total fee (NOT feerate) to pay, in satoshis.\n"
" In rare cases, the actual fee paid might be slightly higher than the specified\n"
" totalFee if the tx change output has to be removed because it is too close to\n"
" the dust threshold."},
- {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Whether the new transaction should still be\n"
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "true", "Whether the new transaction should still be\n"
" marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n"
" be left unchanged from the original. If false, any input sequence numbers in the\n"
" original transaction that were less than 0xfffffffe will be increased to 0xfffffffe\n"
" so the new transaction will not be explicitly bip-125 replaceable (though it may\n"
" still be replaceable in practice, for example if it has unconfirmed ancestors which\n"
" are replaceable)."},
- {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "UNSET", "The fee estimate mode, must be one of:\n"
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
" \"CONSERVATIVE\""},
@@ -3263,7 +3364,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
if (options.exists("confTarget") && options.exists("totalFee")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and totalFee options should not both be set. Please provide either a confirmation target for fee estimation or an explicit total fee for the transaction.");
} else if (options.exists("confTarget")) { // TODO: alias this to conf_target
- coin_control.m_confirm_target = ParseConfirmTarget(options["confTarget"]);
+ coin_control.m_confirm_target = ParseConfirmTarget(options["confTarget"], pwallet->chain().estimateMaxBlocks());
} else if (options.exists("totalFee")) {
totalFee = options["totalFee"].get_int64();
if (totalFee <= 0) {
@@ -3294,7 +3395,14 @@ static UniValue bumpfee(const JSONRPCRequest& request)
CAmount old_fee;
CAmount new_fee;
CMutableTransaction mtx;
- feebumper::Result res = feebumper::CreateTransaction(pwallet, hash, coin_control, totalFee, errors, old_fee, new_fee, mtx);
+ feebumper::Result res;
+ if (totalFee > 0) {
+ // Targeting total fee bump. Requires a change output of sufficient size.
+ res = feebumper::CreateTotalBumpTransaction(pwallet, hash, coin_control, totalFee, errors, old_fee, new_fee, mtx);
+ } else {
+ // Targeting feerate bump.
+ res = feebumper::CreateRateBumpTransaction(pwallet, hash, coin_control, errors, old_fee, new_fee, mtx);
+ }
if (res != feebumper::Result::OK) {
switch(res) {
case feebumper::Result::INVALID_ADDRESS_OR_KEY:
@@ -3337,62 +3445,6 @@ static UniValue bumpfee(const JSONRPCRequest& request)
return result;
}
-UniValue generate(const JSONRPCRequest& request)
-{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
- throw std::runtime_error(
- RPCHelpMan{"generate",
- "\nMine up to nblocks blocks immediately (before the RPC call returns) to an address in the wallet.\n",
- {
- {"nblocks", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "How many blocks are generated immediately."},
- {"maxtries", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1000000", "How many iterations to try."},
- },
- RPCResult{
- "[ blockhashes ] (array) hashes of blocks generated\n"
- },
- RPCExamples{
- "\nGenerate 11 blocks\n"
- + HelpExampleCli("generate", "11")
- },
- }.ToString());
- }
-
- if (!IsDeprecatedRPCEnabled("generate")) {
- throw JSONRPCError(RPC_METHOD_DEPRECATED, "The wallet generate rpc method is deprecated and will be fully removed in v0.19. "
- "To use generate in v0.18, restart bitcoind with -deprecatedrpc=generate.\n"
- "Clients should transition to using the node rpc method generatetoaddress\n");
- }
-
- int num_generate = request.params[0].get_int();
- uint64_t max_tries = 1000000;
- if (!request.params[1].isNull()) {
- max_tries = request.params[1].get_int();
- }
-
- std::shared_ptr<CReserveScript> coinbase_script;
- pwallet->GetScriptForMining(coinbase_script);
-
- // If the keypool is exhausted, no script is returned at all. Catch this.
- if (!coinbase_script) {
- throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
- }
-
- //throw an error if no script was provided
- if (coinbase_script->reserveScript.empty()) {
- throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available");
- }
-
- return generateBlocks(coinbase_script, num_generate, max_tries, true);
-}
-
UniValue rescanblockchain(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -3405,15 +3457,16 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
RPCHelpMan{"rescanblockchain",
- "\nRescan the local blockchain for wallet related transactions.\n",
+ "\nRescan the local blockchain for wallet related transactions.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
- {"start_height", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "block height where the rescan should start"},
- {"stop_height", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "", "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."},
+ {"start_height", RPCArg::Type::NUM, /* default */ "0", "block height where the rescan should start"},
+ {"stop_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."},
},
RPCResult{
"{\n"
- " \"start_height\" (numeric) The block height where the rescan has started.\n"
- " \"stop_height\" (numeric) The height of the last rescanned block.\n"
+ " \"start_height\" (numeric) The block height where the rescan started (the requested height or 0)\n"
+ " \"stop_height\" (numeric) The height of the last rescanned block. May be null in rare cases if there was a reorg and the call didn't scan any blocks because they were already scanned in the background.\n"
"}\n"
},
RPCExamples{
@@ -3459,6 +3512,11 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
if (tip_height) {
start_block = locked_chain->getBlockHash(start_height);
+ // If called with a stop_height, set the stop_height here to
+ // trigger a rescan to that height.
+ // If called without a stop height, leave stop_height as null here
+ // so rescan continues to the tip (even if the tip advances during
+ // rescan).
if (stop_height) {
stop_block = locked_chain->getBlockHash(*stop_height);
}
@@ -3478,7 +3536,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
}
UniValue response(UniValue::VOBJ);
response.pushKV("start_height", start_height);
- response.pushKV("stop_height", result.stop_height ? *result.stop_height : UniValue());
+ response.pushKV("stop_height", result.last_scanned_height ? *result.last_scanned_height : UniValue());
return response;
}
@@ -3487,7 +3545,7 @@ class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue>
public:
CWallet * const pwallet;
- void ProcessSubScript(const CScript& subscript, UniValue& obj, bool include_addresses = false) const
+ void ProcessSubScript(const CScript& subscript, UniValue& obj) const
{
// Always present: script type and redeemscript
std::vector<std::vector<unsigned char>> solutions_data;
@@ -3496,7 +3554,6 @@ public:
obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));
CTxDestination embedded;
- UniValue a(UniValue::VARR);
if (ExtractDestination(subscript, embedded)) {
// Only when the script corresponds to an address.
UniValue subobj(UniValue::VOBJ);
@@ -3509,7 +3566,6 @@ public:
// Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
obj.pushKV("embedded", std::move(subobj));
- if (include_addresses) a.push_back(EncodeDestination(embedded));
} else if (which_type == TX_MULTISIG) {
// Also report some information on multisig scripts (which do not have a corresponding address).
// TODO: abstract out the common functionality between this logic and ExtractDestinations.
@@ -3517,25 +3573,19 @@ public:
UniValue pubkeys(UniValue::VARR);
for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
- if (include_addresses) a.push_back(EncodeDestination(key.GetID()));
pubkeys.push_back(HexStr(key.begin(), key.end()));
}
obj.pushKV("pubkeys", std::move(pubkeys));
}
-
- // The "addresses" field is confusing because it refers to public keys using their P2PKH address.
- // For that reason, only add the 'addresses' field when needed for backward compatibility. New applications
- // can use the 'embedded'->'address' field for P2SH or P2WSH wrapped addresses, and 'pubkeys' for
- // inspecting multisig participants.
- if (include_addresses) obj.pushKV("addresses", std::move(a));
}
explicit DescribeWalletAddressVisitor(CWallet* _pwallet) : pwallet(_pwallet) {}
UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); }
- UniValue operator()(const CKeyID& keyID) const
+ UniValue operator()(const PKHash& pkhash) const
{
+ CKeyID keyID(pkhash);
UniValue obj(UniValue::VOBJ);
CPubKey vchPubKey;
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
@@ -3545,12 +3595,13 @@ public:
return obj;
}
- UniValue operator()(const CScriptID& scriptID) const
+ UniValue operator()(const ScriptHash& scripthash) const
{
+ CScriptID scriptID(scripthash);
UniValue obj(UniValue::VOBJ);
CScript subscript;
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
- ProcessSubScript(subscript, obj, IsDeprecatedRPCEnabled("validateaddress"));
+ ProcessSubScript(subscript, obj);
}
return obj;
}
@@ -3616,7 +3667,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
"\nReturn information about the given bitcoin address. Some information requires the address\n"
"to be in the wallet.\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to get the information of."},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to get the information of."},
},
RPCResult{
"{\n"
@@ -3646,7 +3697,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
" \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
" \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
" \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed\n"
- " \"hdmasterkeyid\" : \"<hash160>\" (string, optional) alias for hdseedid maintained for backwards compatibility. Will be removed in V0.18.\n"
+ " \"hdmasterfingerprint\" : \"<hash160>\" (string, optional) The fingperint of the master key.\n"
" \"labels\" (object) Array of labels associated with the address.\n"
" [\n"
" { (json object of label data)\n"
@@ -3709,10 +3760,10 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
}
if (meta) {
ret.pushKV("timestamp", meta->nCreateTime);
- if (!meta->hdKeypath.empty()) {
- ret.pushKV("hdkeypath", meta->hdKeypath);
+ if (meta->has_key_origin) {
+ ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path));
ret.pushKV("hdseedid", meta->hd_seed_id.GetHex());
- ret.pushKV("hdmasterkeyid", meta->hd_seed_id.GetHex());
+ ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint, meta->key_origin.fingerprint + 4));
}
}
@@ -3743,7 +3794,7 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
RPCHelpMan{"getaddressesbylabel",
"\nReturns the list of addresses assigned the specified label.\n",
{
- {"label", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The label."},
+ {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
},
RPCResult{
"{ (json object with addresses as keys)\n"
@@ -3764,9 +3815,20 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
// Find all addresses that have the given label
UniValue ret(UniValue::VOBJ);
+ std::set<std::string> addresses;
for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) {
if (item.second.name == label) {
- ret.pushKV(EncodeDestination(item.first), AddressBookDataToJSON(item.second, false));
+ std::string address = EncodeDestination(item.first);
+ // CWallet::mapAddressBook is not expected to contain duplicate
+ // address strings, but build a separate set as a precaution just in
+ // case it does.
+ bool unique = addresses.emplace(address).second;
+ assert(unique);
+ // UniValue::pushKV checks if the key exists in O(N)
+ // and since duplicate addresses are unexpected (checked with
+ // std::set in O(log(N))), UniValue::__pushKV is used instead,
+ // which currently is O(1).
+ ret.__pushKV(address, AddressBookDataToJSON(item.second, false));
}
}
@@ -3791,7 +3853,7 @@ static UniValue listlabels(const JSONRPCRequest& request)
RPCHelpMan{"listlabels",
"\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
{
- {"purpose", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."},
+ {"purpose", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."},
},
RPCResult{
"[ (json array of string)\n"
@@ -3851,11 +3913,11 @@ UniValue sethdseed(const JSONRPCRequest& request)
"\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." +
HelpRequiringPassphrase(pwallet) + "\n",
{
- {"newkeypool", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
+ {"newkeypool", RPCArg::Type::BOOL, /* default */ "true", "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
" If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
" If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
" keypool will be used until it has been depleted."},
- {"seed", RPCArg::Type::STR, /* opt */ true, /* default_val */ "random seed", "The WIF private key to use as the new HD seed.\n"
+ {"seed", RPCArg::Type::STR, /* default */ "random seed", "The WIF private key to use as the new HD seed.\n"
" The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
},
RPCResults{},
@@ -3868,7 +3930,7 @@ UniValue sethdseed(const JSONRPCRequest& request)
}.ToString());
}
- if (IsInitialBlockDownload()) {
+ if (pwallet->chain().isInitialBlockDownload()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
}
@@ -3880,7 +3942,7 @@ UniValue sethdseed(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
// Do not do anything to non-HD wallets
- if (!pwallet->IsHDEnabled()) {
+ if (!pwallet->CanSupportFeature(FEATURE_HD)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed on a non-HD wallet. Start with -upgradewallet in order to upgrade a non-HD wallet to HD");
}
@@ -3913,73 +3975,6 @@ UniValue sethdseed(const JSONRPCRequest& request)
return NullUniValue;
}
-void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubKey, KeyOriginInfo>& hd_keypaths)
-{
- CPubKey vchPubKey;
- if (!pwallet->GetPubKey(keyID, vchPubKey)) {
- return;
- }
- KeyOriginInfo info;
- if (!pwallet->GetKeyOrigin(keyID, info)) {
- throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal keypath is broken");
- }
- hd_keypaths.emplace(vchPubKey, std::move(info));
-}
-
-bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs)
-{
- LOCK(pwallet->cs_wallet);
- // Get all of the previous transactions
- bool complete = true;
- for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- const CTxIn& txin = psbtx.tx->vin[i];
- PSBTInput& input = psbtx.inputs.at(i);
-
- if (PSBTInputSigned(input)) {
- continue;
- }
-
- // Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
- if (!input.IsSane()) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "PSBT input is not sane.");
- }
-
- // If we have no utxo, grab it from the wallet.
- if (!input.non_witness_utxo && input.witness_utxo.IsNull()) {
- const uint256& txhash = txin.prevout.hash;
- const auto it = pwallet->mapWallet.find(txhash);
- if (it != pwallet->mapWallet.end()) {
- const CWalletTx& wtx = it->second;
- // We only need the non_witness_utxo, which is a superset of the witness_utxo.
- // The signing code will switch to the smaller witness_utxo if this is ok.
- input.non_witness_utxo = wtx.tx;
- }
- }
-
- // Get the Sighash type
- if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Specified Sighash and sighash in PSBT do not match.");
- }
-
- complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), psbtx, i, sighash_type);
- }
-
- // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
- for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
- const CTxOut& out = psbtx.tx->vout.at(i);
- PSBTOutput& psbt_out = psbtx.outputs.at(i);
-
- // Fill a SignatureData with output info
- SignatureData sigdata;
- psbt_out.FillSignatureData(sigdata);
-
- MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue, 1);
- ProduceSignature(HidingSigningProvider(pwallet, true, !bip32derivs), creator, out.scriptPubKey, sigdata);
- psbt_out.FromSignatureData(sigdata);
- }
- return complete;
-}
-
UniValue walletprocesspsbt(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -3996,16 +3991,16 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
"that we can sign for." +
HelpRequiringPassphrase(pwallet) + "\n",
{
- {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction base64 string"},
- {"sign", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Also sign the transaction when updating"},
- {"sighashtype", RPCArg::Type::STR, /* opt */ true, /* default_val */ "ALL", "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
+ {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
+ {"sign", RPCArg::Type::BOOL, /* default */ "true", "Also sign the transaction when updating"},
+ {"sighashtype", RPCArg::Type::STR, /* default */ "ALL", "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
" \"ALL|ANYONECANPAY\"\n"
" \"NONE|ANYONECANPAY\"\n"
" \"SINGLE|ANYONECANPAY\""},
- {"bip32derivs", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If true, includes the BIP 32 derivation paths for public keys if we know them"},
+ {"bip32derivs", RPCArg::Type::BOOL, /* default */ "false", "If true, includes the BIP 32 derivation paths for public keys if we know them"},
},
RPCResult{
"{\n"
@@ -4024,7 +4019,7 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
// Unserialize the transaction
PartiallySignedTransaction psbtx;
std::string error;
- if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) {
+ if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
}
@@ -4034,7 +4029,11 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
// Fill transaction with our data and also sign
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
bool bip32derivs = request.params[3].isNull() ? false : request.params[3].get_bool();
- bool complete = FillPSBT(pwallet, psbtx, nHashType, sign, bip32derivs);
+ bool complete = true;
+ const TransactionError err = FillPSBT(pwallet, psbtx, complete, nHashType, sign, bip32derivs);
+ if (err != TransactionError::OK) {
+ throw JSONRPCTransactionError(err);
+ }
UniValue result(UniValue::VOBJ);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
@@ -4060,61 +4059,61 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
"\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
"Implements the Creator and Updater roles.\n",
{
- {"inputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of json objects",
+ {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of json objects",
{
- {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"},
- {"vout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The output number"},
- {"sequence", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The sequence number"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
+ {"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"},
},
},
},
},
- {"outputs", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
+ {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
"That is, each address can only appear once and there can only be one 'data' object.\n"
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.",
{
- {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"address", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
+ {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
},
},
- {"", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"data", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
+ {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
},
},
},
},
- {"locktime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
- {"options", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "null", "",
+ {"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
+ {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"changeAddress", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "pool address", "The bitcoin address to receive the change"},
- {"changePosition", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "random", "The index of the change output"},
- {"change_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
- {"includeWatching", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Also select inputs which are watch only"},
- {"lockUnspents", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Lock selected unspent outputs"},
- {"feeRate", RPCArg::Type::AMOUNT, /* opt */ true, /* default_val */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
- {"subtractFeeFromOutputs", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "A json array of integers.\n"
+ {"changeAddress", RPCArg::Type::STR_HEX, /* default */ "pool address", "The bitcoin address to receive the change"},
+ {"changePosition", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
+ {"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"includeWatching", RPCArg::Type::BOOL, /* default */ "false", "Also select inputs which are watch only"},
+ {"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
+ {"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
+ {"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "A json array of integers.\n"
" The fee will be equally deducted from the amount of each specified output.\n"
" Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
" If no outputs are specified here, the sender pays the fee.",
{
- {"vout_index", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "", "The zero-based output index, before a change output is added."},
+ {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Marks this transaction as BIP125 replaceable.\n"
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "false", "Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees"},
- {"conf_target", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "Fallback to wallet's confirmation target", "Confirmation target (in blocks)"},
- {"estimate_mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "UNSET", "The fee estimate mode, must be one of:\n"
+ {"conf_target", RPCArg::Type::NUM, /* default */ "Fallback to wallet's confirmation target", "Confirmation target (in blocks)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
" \"CONSERVATIVE\""},
},
"options"},
- {"bip32derivs", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If true, includes the BIP 32 derivation paths for public keys if we know them"},
+ {"bip32derivs", RPCArg::Type::BOOL, /* default */ "false", "If true, includes the BIP 32 derivation paths for public keys if we know them"},
},
RPCResult{
"{\n"
@@ -4148,7 +4147,11 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
// Fill transaction with out data but don't sign
bool bip32derivs = request.params[4].isNull() ? false : request.params[4].get_bool();
- FillPSBT(pwallet, psbtx, 1, false, bip32derivs);
+ bool complete = true;
+ const TransactionError err = FillPSBT(pwallet, psbtx, complete, 1, false, bip32derivs);
+ if (err != TransactionError::OK) {
+ throw JSONRPCTransactionError(err);
+ }
// Serialize the PSBT
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
@@ -4176,15 +4179,13 @@ UniValue importmulti(const JSONRPCRequest& request);
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
- { "generating", "generate", &generate, {"nblocks","maxtries"} },
- { "hidden", "resendwallettransactions", &resendwallettransactions, {} },
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
{ "wallet", "abortrescan", &abortrescan, {} },
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label","address_type"} },
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
- { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys"} },
+ { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase"} },
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
@@ -4197,6 +4198,7 @@ static const CRPCCommand commands[] =
{ "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf"} },
{ "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} },
{ "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} },
+ { "wallet", "getbalances", &getbalances, {} },
{ "wallet", "getwalletinfo", &getwalletinfo, {} },
{ "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} },
{ "wallet", "importmulti", &importmulti, {"requests","options"} },
@@ -4235,8 +4237,8 @@ static const CRPCCommand commands[] =
};
// clang-format on
-void RegisterWalletRPCCommands(CRPCTable &t)
+void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers)
{
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
- t.appendCommand(commands[vcidx].name, &commands[vcidx]);
+ handlers.emplace_back(chain.handleRpc(commands[vcidx]));
}
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index abd7750874..1c0523c90b 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -1,11 +1,13 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-2019 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_RPCWALLET_H
#define BITCOIN_WALLET_RPCWALLET_H
+#include <memory>
#include <string>
+#include <vector>
class CRPCTable;
class CWallet;
@@ -14,7 +16,12 @@ class UniValue;
struct PartiallySignedTransaction;
class CTransaction;
-void RegisterWalletRPCCommands(CRPCTable &t);
+namespace interfaces {
+class Chain;
+class Handler;
+}
+
+void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers);
/**
* Figures out what wallet, if any, to use for a JSONRPCRequest.
@@ -24,11 +31,10 @@ void RegisterWalletRPCCommands(CRPCTable &t);
*/
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
-std::string HelpRequiringPassphrase(CWallet *);
-void EnsureWalletIsUnlocked(CWallet *);
-bool EnsureWalletIsAvailable(CWallet *, bool avoidException);
+std::string HelpRequiringPassphrase(const CWallet*);
+void EnsureWalletIsUnlocked(const CWallet*);
+bool EnsureWalletIsAvailable(const CWallet*, bool avoidException);
UniValue getaddressinfo(const JSONRPCRequest& request);
UniValue signrawtransactionwithwallet(const JSONRPCRequest& request);
-bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false);
#endif //BITCOIN_WALLET_RPCWALLET_H
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 5c65acf601..9e7f0ed773 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -8,7 +8,7 @@
#include <amount.h>
#include <primitives/transaction.h>
#include <random.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <wallet/test/wallet_test_fixture.h>
#include <boost/test/unit_test.hpp>
@@ -29,7 +29,7 @@ typedef std::set<CInputCoin> CoinSet;
static std::vector<COutput> vCoins;
static auto testChain = interfaces::MakeChain();
-static CWallet testWallet(*testChain, WalletLocation(), WalletDatabase::CreateDummy());
+static CWallet testWallet(testChain.get(), WalletLocation(), WalletDatabase::CreateDummy());
static CAmount balance = 0;
CoinEligibilityFilter filter_standard(1, 6, 0);
@@ -69,8 +69,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx)));
if (fIsFromMe)
{
- wtx->fDebitCached = true;
- wtx->nDebitCached = 1;
+ wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
}
COutput output(wtx.get(), nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
vCoins.push_back(output);
@@ -115,7 +114,7 @@ inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
{
static std::vector<OutputGroup> static_groups;
static_groups.clear();
- for (auto& coin : coins) static_groups.emplace_back(coin.GetInputCoin(), coin.nDepth, coin.tx->fDebitCached && coin.tx->nDebitCached == 1 /* HACK: we can't figure out the is_me flag so we use the conditions defined above; perhaps set safe to false for !fIsFromMe in add_coin() */, 0, 0);
+ for (auto& coin : coins) static_groups.emplace_back(coin.GetInputCoin(), coin.nDepth, coin.tx->m_amounts[CWalletTx::DEBIT].m_cached[ISMINE_SPENDABLE] && coin.tx->m_amounts[CWalletTx::DEBIT].m_value[ISMINE_SPENDABLE] == 1 /* HACK: we can't figure out the is_me flag so we use the conditions defined above; perhaps set safe to false for !fIsFromMe in add_coin() */, 0, 0);
return static_groups;
}
@@ -135,7 +134,6 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
/////////////////////////
// Known Outcome tests //
/////////////////////////
- BOOST_TEST_MESSAGE("Testing known outcomes");
// Empty utxo pool
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index 2a64749379..e4950af4e5 100644
--- a/src/wallet/test/db_tests.cpp
+++ b/src/wallet/test/db_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,7 +7,7 @@
#include <boost/test/unit_test.hpp>
#include <fs.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <wallet/db.h>
diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h
index cd47b31da1..e2b7075085 100644
--- a/src/wallet/test/init_test_fixture.h
+++ b/src/wallet/test/init_test_fixture.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -6,7 +6,7 @@
#define BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
#include <interfaces/chain.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
struct InitWalletDirTestingSetup: public BasicTestingSetup {
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index 5852d3ef84..67e2847963 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -1,17 +1,12 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/test/unit_test.hpp>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <wallet/test/init_test_fixture.h>
-#include <init.h>
-#include <walletinitinterface.h>
-#include <wallet/wallet.h>
-
-
BOOST_FIXTURE_TEST_SUITE(init_tests, InitWalletDirTestingSetup)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index 9918eeb89f..cdf7113203 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -1,16 +1,17 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 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_io.h>
#include <script/sign.h>
+#include <util/bip32.h>
#include <util/strencodings.h>
-#include <wallet/rpcwallet.h>
+#include <wallet/psbtwallet.h>
#include <wallet/wallet.h>
#include <univalue.h>
#include <boost/test/unit_test.hpp>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <wallet/test/wallet_test_fixture.h>
BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup)
@@ -60,7 +61,8 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
ssData >> psbtx;
// Fill transaction with our data
- FillPSBT(&m_wallet, psbtx, SIGHASH_ALL, false, true);
+ bool complete = true;
+ BOOST_REQUIRE_EQUAL(TransactionError::OK, FillPSBT(&m_wallet, psbtx, complete, SIGHASH_ALL, false, true));
// Get the final tx
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp
index ae7092fa89..2f41813234 100644
--- a/src/wallet/test/wallet_crypto_tests.cpp
+++ b/src/wallet/test/wallet_crypto_tests.cpp
@@ -1,8 +1,8 @@
-// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Copyright (c) 2014-2019 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 <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <util/strencodings.h>
#include <wallet/crypter.h>
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index a5fb1db86c..7db0bc4249 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -1,24 +1,18 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-2019 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/test/wallet_test_fixture.h>
-#include <rpc/server.h>
#include <wallet/db.h>
-#include <wallet/rpcwallet.h>
-WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
- TestingSetup(chainName), m_wallet(*m_chain, WalletLocation(), WalletDatabase::CreateMock())
+WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
+ : TestingSetup(chainName),
+ m_wallet(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock())
{
bool fFirstRun;
m_wallet.LoadWallet(fFirstRun);
- RegisterValidationInterface(&m_wallet);
+ m_wallet.handleNotifications();
- RegisterWalletRPCCommands(tableRPC);
-}
-
-WalletTestingSetup::~WalletTestingSetup()
-{
- UnregisterValidationInterface(&m_wallet);
+ m_chain_client->registerRpcs();
}
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index e6fe8c9473..c1dbecdf8c 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -1,11 +1,11 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-2019 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_TEST_WALLET_TEST_FIXTURE_H
#define BITCOIN_WALLET_TEST_WALLET_TEST_FIXTURE_H
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <interfaces/chain.h>
#include <interfaces/wallet.h>
@@ -17,9 +17,9 @@
*/
struct WalletTestingSetup: public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
- ~WalletTestingSetup();
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain();
+ std::unique_ptr<interfaces::ChainClient> m_chain_client = interfaces::MakeWalletClient(*m_chain, {});
CWallet m_wallet;
};
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 8c380f1257..922bb0fe65 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -12,12 +12,12 @@
#include <consensus/validation.h>
#include <interfaces/chain.h>
+#include <policy/policy.h>
#include <rpc/server.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/test/wallet_test_fixture.h>
-#include <policy/policy.h>
#include <boost/test/unit_test.hpp>
#include <univalue.h>
@@ -36,43 +36,43 @@ static void AddKey(CWallet& wallet, const CKey& key)
BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
{
- auto chain = interfaces::MakeChain();
-
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* oldTip = chainActive.Tip();
+ CBlockIndex* oldTip = ::ChainActive().Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CBlockIndex* newTip = chainActive.Tip();
+ CBlockIndex* newTip = ::ChainActive().Tip();
+ auto chain = interfaces::MakeChain();
auto locked_chain = chain->lock();
+ LockAssertion lock(::cs_main);
// Verify ScanForWalletTransactions accommodates a null start block.
{
- CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
CWallet::ScanResult result = wallet.ScanForWalletTransactions({} /* start_block */, {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK(result.failed_block.IsNull());
- BOOST_CHECK(result.stop_block.IsNull());
- BOOST_CHECK(!result.stop_height);
- BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
+ BOOST_CHECK(result.last_failed_block.IsNull());
+ BOOST_CHECK(result.last_scanned_block.IsNull());
+ BOOST_CHECK(!result.last_scanned_height);
+ BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 0);
}
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
- CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK(result.failed_block.IsNull());
- BOOST_CHECK_EQUAL(result.stop_block, newTip->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.stop_height, newTip->nHeight);
- BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
+ BOOST_CHECK(result.last_failed_block.IsNull());
+ BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
+ BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 100 * COIN);
}
// Prune the older block file.
@@ -82,16 +82,16 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
- CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
- BOOST_CHECK_EQUAL(result.failed_block, oldTip->GetBlockHash());
- BOOST_CHECK_EQUAL(result.stop_block, newTip->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.stop_height, newTip->nHeight);
- BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
+ BOOST_CHECK_EQUAL(result.last_failed_block, oldTip->GetBlockHash());
+ BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
+ BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 50 * COIN);
}
// Prune the remaining block file.
@@ -100,30 +100,30 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions scans no blocks.
{
- CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
- BOOST_CHECK_EQUAL(result.failed_block, newTip->GetBlockHash());
- BOOST_CHECK(result.stop_block.IsNull());
- BOOST_CHECK(!result.stop_height);
- BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
+ BOOST_CHECK_EQUAL(result.last_failed_block, newTip->GetBlockHash());
+ BOOST_CHECK(result.last_scanned_block.IsNull());
+ BOOST_CHECK(!result.last_scanned_height);
+ BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 0);
}
}
BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
{
- auto chain = interfaces::MakeChain();
-
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* oldTip = chainActive.Tip();
+ CBlockIndex* oldTip = ::ChainActive().Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CBlockIndex* newTip = chainActive.Tip();
+ CBlockIndex* newTip = ::ChainActive().Tip();
+ auto chain = interfaces::MakeChain();
auto locked_chain = chain->lock();
+ LockAssertion lock(::cs_main);
// Prune the older block file.
PruneOneBlockFile(oldTip->GetBlockPos().nFile);
@@ -133,7 +133,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// before the missing block, and success for a key whose creation time is
// after.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
AddWallet(wallet);
UniValue keys;
keys.setArray();
@@ -175,11 +175,9 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// than or equal to key birthday.
BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{
- auto chain = interfaces::MakeChain();
-
// Create two blocks with same timestamp to verify that importwallet rescan
// will pick up both blocks, not just the first.
- const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5;
+ const int64_t BLOCK_TIME = ::ChainActive().Tip()->GetBlockTimeMax() + 5;
SetMockTime(BLOCK_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
@@ -190,13 +188,15 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
SetMockTime(KEY_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
+ auto chain = interfaces::MakeChain();
auto locked_chain = chain->lock();
+ LockAssertion lock(::cs_main);
std::string backup_file = (SetDataDir("importwallet_rescan") / "wallet.backup").string();
// Import key into wallet and call dumpwallet to create backup file.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
LOCK(wallet->cs_wallet);
wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
@@ -212,7 +212,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
JSONRPCRequest request;
request.params.setArray();
@@ -243,11 +243,15 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{
auto chain = interfaces::MakeChain();
- CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+
+ CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
CWalletTx wtx(&wallet, m_coinbase_txns.back());
+
auto locked_chain = chain->lock();
+ LockAssertion lock(::cs_main);
LOCK(wallet.cs_wallet);
- wtx.hashBlock = chainActive.Tip()->GetBlockHash();
+
+ wtx.hashBlock = ::ChainActive().Tip()->GetBlockHash();
wtx.nIndex = 0;
// Call GetImmatureCredit() once before adding the key to the wallet to
@@ -269,6 +273,7 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
CBlockIndex* block = nullptr;
if (blockTime > 0) {
auto locked_chain = wallet.chain().lock();
+ LockAssertion lock(::cs_main);
auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second);
const uint256& hash = inserted.first->first;
@@ -319,7 +324,7 @@ BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
{
- CTxDestination dest = CKeyID();
+ CTxDestination dest = PKHash();
LOCK(m_wallet.cs_wallet);
m_wallet.AddDestData(dest, "misc", "val_misc");
m_wallet.AddDestData(dest, "rr0", "val_rr0");
@@ -337,17 +342,17 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- wallet = MakeUnique<CWallet>(*m_chain, WalletLocation(), WalletDatabase::CreateMock());
+ wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);
AddKey(*wallet, coinbaseKey);
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
- CWallet::ScanResult result = wallet->ScanForWalletTransactions(chainActive.Genesis()->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
+ CWallet::ScanResult result = wallet->ScanForWalletTransactions(::ChainActive().Genesis()->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK_EQUAL(result.stop_block, chainActive.Tip()->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.stop_height, chainActive.Height());
- BOOST_CHECK(result.failed_block.IsNull());
+ BOOST_CHECK_EQUAL(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, ::ChainActive().Height());
+ BOOST_CHECK(result.last_failed_block.IsNull());
}
~ListCoinsTestingSetup()
@@ -363,24 +368,28 @@ public:
int changePos = -1;
std::string error;
CCoinControl dummy;
- BOOST_CHECK(wallet->CreateTransaction(*m_locked_chain, {recipient}, tx, reservekey, fee, changePos, error, dummy));
+ {
+ auto locked_chain = m_chain->lock();
+ BOOST_CHECK(wallet->CreateTransaction(*locked_chain, {recipient}, tx, reservekey, fee, changePos, error, dummy));
+ }
CValidationState state;
- BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekey, nullptr, state));
+ BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekey, state));
CMutableTransaction blocktx;
{
LOCK(wallet->cs_wallet);
blocktx = CMutableTransaction(*wallet->mapWallet.at(tx->GetHash()).tx);
}
CreateAndProcessBlock({CMutableTransaction(blocktx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
+
+ LOCK(cs_main);
LOCK(wallet->cs_wallet);
auto it = wallet->mapWallet.find(tx->GetHash());
BOOST_CHECK(it != wallet->mapWallet.end());
- it->second.SetMerkleBranch(chainActive.Tip()->GetBlockHash(), 1);
+ it->second.SetMerkleBranch(::ChainActive().Tip()->GetBlockHash(), 1);
return it->second;
}
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain();
- std::unique_ptr<interfaces::Chain::Lock> m_locked_chain = m_chain->assumeLocked(); // Temporary. Removed in upcoming lock cleanup
std::unique_ptr<CWallet> wallet;
};
@@ -392,11 +401,12 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
// address.
std::map<CTxDestination, std::vector<COutput>> list;
{
- LOCK2(cs_main, wallet->cs_wallet);
- list = wallet->ListCoins(*m_locked_chain);
+ auto locked_chain = m_chain->lock();
+ LOCK(wallet->cs_wallet);
+ list = wallet->ListCoins(*locked_chain);
}
BOOST_CHECK_EQUAL(list.size(), 1U);
- BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
+ BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U);
// Check initial balance from one mature coinbase transaction.
@@ -408,18 +418,20 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
// pubkey.
AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */});
{
- LOCK2(cs_main, wallet->cs_wallet);
- list = wallet->ListCoins(*m_locked_chain);
+ auto locked_chain = m_chain->lock();
+ LOCK(wallet->cs_wallet);
+ list = wallet->ListCoins(*locked_chain);
}
BOOST_CHECK_EQUAL(list.size(), 1U);
- BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
+ BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U);
// Lock both coins. Confirm number of available coins drops to 0.
{
- LOCK2(cs_main, wallet->cs_wallet);
+ auto locked_chain = m_chain->lock();
+ LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- wallet->AvailableCoins(*m_locked_chain, available);
+ wallet->AvailableCoins(*locked_chain, available);
BOOST_CHECK_EQUAL(available.size(), 2U);
}
for (const auto& group : list) {
@@ -429,26 +441,29 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
}
}
{
- LOCK2(cs_main, wallet->cs_wallet);
+ auto locked_chain = m_chain->lock();
+ LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- wallet->AvailableCoins(*m_locked_chain, available);
+ wallet->AvailableCoins(*locked_chain, available);
BOOST_CHECK_EQUAL(available.size(), 0U);
}
// Confirm ListCoins still returns same result as before, despite coins
// being locked.
{
- LOCK2(cs_main, wallet->cs_wallet);
- list = wallet->ListCoins(*m_locked_chain);
+ auto locked_chain = m_chain->lock();
+ LOCK(wallet->cs_wallet);
+ list = wallet->ListCoins(*locked_chain);
}
BOOST_CHECK_EQUAL(list.size(), 1U);
- BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
+ BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U);
}
BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{
auto chain = interfaces::MakeChain();
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ wallet->SetMinVersion(FEATURE_LATEST);
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
BOOST_CHECK(!wallet->TopUpKeyPool(1000));
CPubKey pubkey;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 9b643be69a..bde52d7791 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1,33 +1,33 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 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/wallet.h>
-#include <checkpoints.h>
#include <chain.h>
-#include <wallet/coincontrol.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <fs.h>
#include <interfaces/chain.h>
+#include <interfaces/wallet.h>
#include <key.h>
#include <key_io.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/descriptor.h>
#include <script/script.h>
-#include <shutdown.h>
-#include <timedata.h>
-#include <txmempool.h>
+#include <util/bip32.h>
+#include <util/error.h>
+#include <util/fees.h>
#include <util/moneystr.h>
+#include <util/rbf.h>
+#include <util/validation.h>
+#include <validation.h>
+#include <wallet/coincontrol.h>
#include <wallet/fees.h>
#include <algorithm>
@@ -94,7 +94,7 @@ static void ReleaseWallet(CWallet* wallet)
wallet->WalletLogPrintf("Releasing wallet\n");
wallet->BlockUntilSyncedToCurrentChain();
wallet->Flush();
- UnregisterValidationInterface(wallet);
+ wallet->m_chain_notifications_handler.reset();
delete wallet;
// Wallet is now released, notify UnloadWallet, if any.
{
@@ -130,6 +130,28 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
}
}
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::string& warning)
+{
+ if (!CWallet::Verify(chain, location, false, error, warning)) {
+ error = "Wallet file verification failed: " + error;
+ return nullptr;
+ }
+
+ std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location);
+ if (!wallet) {
+ error = "Wallet loading failed.";
+ return nullptr;
+ }
+ AddWallet(wallet);
+ wallet->postInitProcess();
+ return wallet;
+}
+
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning)
+{
+ return LoadWallet(chain, WalletLocation(name), error, warning);
+}
+
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
@@ -168,7 +190,8 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal)
{
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
- AssertLockHeld(cs_wallet); // mapKeyMetadata
+ assert(!IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET));
+ AssertLockHeld(cs_wallet);
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
CKey secret;
@@ -177,7 +200,7 @@ CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal)
int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime);
- // use HD key derivation if HD was enabled during wallet creation
+ // use HD key derivation if HD was enabled during wallet creation and a seed is present
if (IsHDEnabled()) {
DeriveNewChildKey(batch, metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false));
} else {
@@ -232,24 +255,33 @@ void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey
if (internal) {
chainChildKey.Derive(childKey, hdChain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/0'/1'/" + std::to_string(hdChain.nInternalChainCounter) + "'";
+ metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
+ metadata.key_origin.path.push_back(1 | BIP32_HARDENED_KEY_LIMIT);
+ metadata.key_origin.path.push_back(hdChain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
hdChain.nInternalChainCounter++;
}
else {
chainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'";
+ metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
+ metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
+ metadata.key_origin.path.push_back(hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
hdChain.nExternalChainCounter++;
}
} while (HaveKey(childKey.key.GetPubKey().GetID()));
secret = childKey.key;
metadata.hd_seed_id = hdChain.seed_id;
+ CKeyID master_id = masterKey.key.GetPubKey().GetID();
+ std::copy(master_id.begin(), master_id.begin() + 4, metadata.key_origin.fingerprint);
+ metadata.has_key_origin = true;
// update the chain model in the database
if (!batch.WriteHDChain(hdChain))
throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
}
-bool CWallet::AddKeyPubKeyWithDB(WalletBatch &batch, const CKey& secret, const CPubKey &pubkey)
+bool CWallet::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const CPubKey& pubkey)
{
- AssertLockHeld(cs_wallet); // mapKeyMetadata
+ AssertLockHeld(cs_wallet);
// Make sure we aren't adding private keys to private key disabled wallets
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
@@ -269,7 +301,7 @@ bool CWallet::AddKeyPubKeyWithDB(WalletBatch &batch, const CKey& secret, const C
// check if we need to remove from watch-only
CScript script;
- script = GetScriptForDestination(pubkey.GetID());
+ script = GetScriptForDestination(PKHash(pubkey));
if (HaveWatchOnly(script)) {
RemoveWatchOnly(script);
}
@@ -283,6 +315,7 @@ bool CWallet::AddKeyPubKeyWithDB(WalletBatch &batch, const CKey& secret, const C
secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]);
}
+ UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
return true;
}
@@ -310,20 +343,57 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
}
}
-void CWallet::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &meta)
+void CWallet::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata& meta)
{
- AssertLockHeld(cs_wallet); // mapKeyMetadata
+ AssertLockHeld(cs_wallet);
UpdateTimeFirstKey(meta.nCreateTime);
mapKeyMetadata[keyID] = meta;
}
-void CWallet::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &meta)
+void CWallet::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata& meta)
{
- AssertLockHeld(cs_wallet); // m_script_metadata
+ AssertLockHeld(cs_wallet);
UpdateTimeFirstKey(meta.nCreateTime);
m_script_metadata[script_id] = meta;
}
+void CWallet::UpgradeKeyMetadata()
+{
+ AssertLockHeld(cs_wallet);
+ if (IsLocked() || IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) {
+ return;
+ }
+
+ std::unique_ptr<WalletBatch> batch = MakeUnique<WalletBatch>(*database);
+ for (auto& meta_pair : mapKeyMetadata) {
+ CKeyMetadata& meta = meta_pair.second;
+ if (!meta.hd_seed_id.IsNull() && !meta.has_key_origin && meta.hdKeypath != "s") { // If the hdKeypath is "s", that's the seed and it doesn't have a key origin
+ CKey key;
+ GetKey(meta.hd_seed_id, key);
+ CExtKey masterKey;
+ masterKey.SetSeed(key.begin(), key.size());
+ // Add to map
+ CKeyID master_id = masterKey.key.GetPubKey().GetID();
+ std::copy(master_id.begin(), master_id.begin() + 4, meta.key_origin.fingerprint);
+ if (!ParseHDKeypath(meta.hdKeypath, meta.key_origin.path)) {
+ throw std::runtime_error("Invalid stored hdKeypath");
+ }
+ meta.has_key_origin = true;
+ if (meta.nVersion < CKeyMetadata::VERSION_WITH_KEY_ORIGIN) {
+ meta.nVersion = CKeyMetadata::VERSION_WITH_KEY_ORIGIN;
+ }
+
+ // Write meta to wallet
+ CPubKey pubkey;
+ if (GetPubKey(meta_pair.first, pubkey)) {
+ batch->WriteKeyMetadata(meta, pubkey, true);
+ }
+ }
+ }
+ batch.reset(); //write before setting the flag
+ SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA);
+}
+
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
@@ -347,9 +417,19 @@ void CWallet::UpdateTimeFirstKey(int64_t nCreateTime)
bool CWallet::AddCScript(const CScript& redeemScript)
{
+ WalletBatch batch(*database);
+ return AddCScriptWithDB(batch, redeemScript);
+}
+
+bool CWallet::AddCScriptWithDB(WalletBatch& batch, const CScript& redeemScript)
+{
if (!CCryptoKeyStore::AddCScript(redeemScript))
return false;
- return WalletBatch(*database).WriteCScript(Hash160(redeemScript), redeemScript);
+ if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) {
+ UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
+ return true;
+ }
+ return false;
}
bool CWallet::LoadCScript(const CScript& redeemScript)
@@ -359,7 +439,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 = EncodeDestination(CScriptID(redeemScript));
+ std::string strAddr = EncodeDestination(ScriptHash(redeemScript));
WalletLogPrintf("%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;
}
@@ -367,14 +447,30 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
return CCryptoKeyStore::AddCScript(redeemScript);
}
-bool CWallet::AddWatchOnly(const CScript& dest)
+bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest)
{
if (!CCryptoKeyStore::AddWatchOnly(dest))
return false;
const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)];
UpdateTimeFirstKey(meta.nCreateTime);
NotifyWatchonlyChanged(true);
- return WalletBatch(*database).WriteWatchOnly(dest, meta);
+ if (batch.WriteWatchOnly(dest, meta)) {
+ UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
+ return true;
+ }
+ return false;
+}
+
+bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time)
+{
+ m_script_metadata[CScriptID(dest)].nCreateTime = create_time;
+ return AddWatchOnlyWithDB(batch, dest);
+}
+
+bool CWallet::AddWatchOnly(const CScript& dest)
+{
+ WalletBatch batch(*database);
+ return AddWatchOnlyWithDB(batch, dest);
}
bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime)
@@ -414,8 +510,11 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool accept_no_key
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
continue; // try another master key
- if (CCryptoKeyStore::Unlock(_vMasterKey, accept_no_keys))
+ if (CCryptoKeyStore::Unlock(_vMasterKey, accept_no_keys)) {
+ // Now that we've unlocked, upgrade the key metadata
+ UpgradeKeyMetadata();
return true;
+ }
}
}
return false;
@@ -475,7 +574,7 @@ void CWallet::ChainStateFlushed(const CBlockLocator& loc)
void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in, bool fExplicit)
{
- LOCK(cs_wallet); // nWalletVersion
+ LOCK(cs_wallet);
if (nWalletVersion >= nVersion)
return;
@@ -499,7 +598,7 @@ void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in,
bool CWallet::SetMaxVersion(int nVersion)
{
- LOCK(cs_wallet); // nWalletVersion, nWalletMaxVersion
+ LOCK(cs_wallet);
// cannot downgrade below current version
if (nWalletVersion > nVersion)
return false;
@@ -783,9 +882,9 @@ DBErrors CWallet::ReorderTransactions()
return DBErrors::LOAD_OK;
}
-int64_t CWallet::IncOrderPosNext(WalletBatch *batch)
+int64_t CWallet::IncOrderPosNext(WalletBatch* batch)
{
- AssertLockHeld(cs_wallet); // nOrderPosNext
+ AssertLockHeld(cs_wallet);
int64_t nRet = nOrderPosNext++;
if (batch) {
batch->WriteOrderPosNext(nOrderPosNext);
@@ -847,7 +946,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
wtx.BindWallet(this);
bool fInsertedNew = ret.second;
if (fInsertedNew) {
- wtx.nTimeReceived = GetAdjustedTime();
+ wtx.nTimeReceived = chain().getAdjustedTime();
wtx.nOrderPos = IncOrderPosNext(&batch);
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
wtx.nTimeSmart = ComputeTimeSmart(wtx);
@@ -1149,7 +1248,8 @@ void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
}
}
-void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) {
+void CWallet::BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted) {
+ const uint256& block_hash = block.GetHash();
auto locked_chain = chain().lock();
LOCK(cs_wallet);
// TODO: Temporarily ensure that mempool removals are notified before
@@ -1164,46 +1264,37 @@ void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const
SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */);
TransactionRemovedFromMempool(ptx);
}
- for (size_t i = 0; i < pblock->vtx.size(); i++) {
- SyncTransaction(pblock->vtx[i], pindex->GetBlockHash(), i);
- TransactionRemovedFromMempool(pblock->vtx[i]);
+ for (size_t i = 0; i < block.vtx.size(); i++) {
+ SyncTransaction(block.vtx[i], block_hash, i);
+ TransactionRemovedFromMempool(block.vtx[i]);
}
- m_last_block_processed = pindex->GetBlockHash();
+ m_last_block_processed = block_hash;
}
-void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) {
+void CWallet::BlockDisconnected(const CBlock& block) {
auto locked_chain = chain().lock();
LOCK(cs_wallet);
- for (const CTransactionRef& ptx : pblock->vtx) {
+ for (const CTransactionRef& ptx : block.vtx) {
SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */);
}
}
+void CWallet::UpdatedBlockTip()
+{
+ m_best_block_time = GetTime();
+}
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, it's easier to just call it cs_main-protected.
- auto locked_chain = chain().lock();
-
- if (!m_last_block_processed.IsNull() && locked_chain->isPotentialTip(m_last_block_processed)) {
- return;
- }
- }
-
- // ...otherwise put a callback in the validation interface queue and wait
+ // Skip the queue-draining stuff if we know we're caught up with
+ // ::ChainActive().Tip(), 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).
- SyncWithValidationInterfaceQueue();
+ uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed);
+ chain().waitForNotificationsIfNewBlocksConnected(last_block_hash);
}
@@ -1375,6 +1466,7 @@ CPubKey CWallet::DeriveNewSeed(const CKey& key)
// set the hd keypath to "s" -> Seed, refers the seed to itself
metadata.hdKeypath = "s";
+ metadata.has_key_origin = false;
metadata.hd_seed_id = seed.GetID();
{
@@ -1402,6 +1494,7 @@ void CWallet::SetHDSeed(const CPubKey& seed)
newHdChain.seed_id = seed.GetID();
SetHDChain(newHdChain, false);
NotifyCanGetAddressesChanged();
+ UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET);
}
void CWallet::SetHDChain(const CHDChain& chain, bool memonly)
@@ -1418,6 +1511,30 @@ bool CWallet::IsHDEnabled() const
return !hdChain.seed_id.IsNull();
}
+bool CWallet::CanGenerateKeys()
+{
+ // A wallet can generate keys if it has an HD seed (IsHDEnabled) or it is a non-HD wallet (pre FEATURE_HD)
+ LOCK(cs_wallet);
+ return IsHDEnabled() || !CanSupportFeature(FEATURE_HD);
+}
+
+bool CWallet::CanGetAddresses(bool internal)
+{
+ LOCK(cs_wallet);
+ // Check if the keypool has keys
+ bool keypool_has_keys;
+ if (internal && CanSupportFeature(FEATURE_HD_SPLIT)) {
+ keypool_has_keys = setInternalKeyPool.size() > 0;
+ } else {
+ keypool_has_keys = KeypoolCountExternalKeys() > 0;
+ }
+ // If the keypool doesn't have keys, check if we can generate them
+ if (!keypool_has_keys) {
+ return CanGenerateKeys();
+ }
+ return keypool_has_keys;
+}
+
void CWallet::SetWalletFlag(uint64_t flags)
{
LOCK(cs_wallet);
@@ -1426,6 +1543,20 @@ void CWallet::SetWalletFlag(uint64_t flags)
throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
}
+void CWallet::UnsetWalletFlag(uint64_t flag)
+{
+ WalletBatch batch(*database);
+ UnsetWalletFlagWithDB(batch, flag);
+}
+
+void CWallet::UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag)
+{
+ LOCK(cs_wallet);
+ m_wallet_flags &= ~flag;
+ if (!batch.WriteWalletFlags(m_wallet_flags))
+ throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
+}
+
bool CWallet::IsWalletFlagSet(uint64_t flag)
{
return (m_wallet_flags & flag);
@@ -1483,6 +1614,80 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut>
return true;
}
+bool CWallet::ImportScripts(const std::set<CScript> scripts)
+{
+ WalletBatch batch(*database);
+ for (const auto& entry : scripts) {
+ if (!HaveCScript(CScriptID(entry)) && !AddCScriptWithDB(batch, entry)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool CWallet::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp)
+{
+ WalletBatch batch(*database);
+ for (const auto& entry : privkey_map) {
+ const CKey& key = entry.second;
+ CPubKey pubkey = key.GetPubKey();
+ const CKeyID& id = entry.first;
+ assert(key.VerifyPubKey(pubkey));
+ mapKeyMetadata[id].nCreateTime = timestamp;
+ // If the private key is not present in the wallet, insert it.
+ if (!HaveKey(id) && !AddKeyPubKeyWithDB(batch, key, pubkey)) {
+ return false;
+ }
+ UpdateTimeFirstKey(timestamp);
+ }
+ return true;
+}
+
+bool CWallet::ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp)
+{
+ WalletBatch batch(*database);
+ for (const auto& entry : key_origins) {
+ AddKeyOriginWithDB(batch, entry.second.first, entry.second.second);
+ }
+ for (const CKeyID& id : ordered_pubkeys) {
+ auto entry = pubkey_map.find(id);
+ if (entry == pubkey_map.end()) {
+ continue;
+ }
+ const CPubKey& pubkey = entry->second;
+ CPubKey temp;
+ if (!GetPubKey(id, temp) && !AddWatchOnlyWithDB(batch, GetScriptForRawPubKey(pubkey), timestamp)) {
+ return false;
+ }
+ mapKeyMetadata[id].nCreateTime = timestamp;
+
+ // Add to keypool only works with pubkeys
+ if (add_keypool) {
+ AddKeypoolPubkeyWithDB(pubkey, internal, batch);
+ NotifyCanGetAddressesChanged();
+ }
+ }
+ return true;
+}
+
+bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool internal, const int64_t timestamp)
+{
+ WalletBatch batch(*database);
+ for (const CScript& script : script_pub_keys) {
+ if (!have_solving_data || !::IsMine(*this, script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated
+ if (!AddWatchOnlyWithDB(batch, script, timestamp)) {
+ return false;
+ }
+ }
+ CTxDestination dest;
+ ExtractDestination(script, dest);
+ if (!internal && IsValidDestination(dest)) {
+ SetAddressBookWithDB(batch, dest, label, "receive");
+ }
+ }
+ return true;
+}
+
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
{
std::vector<CTxOut> txouts;
@@ -1593,7 +1798,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
uint256 start_block;
{
auto locked_chain = chain().lock();
- const Optional<int> start_height = locked_chain->findFirstBlockWithTime(startTime - TIMESTAMP_WINDOW, &start_block);
+ const Optional<int> start_height = locked_chain->findFirstBlockWithTimeAndHeight(startTime - TIMESTAMP_WINDOW, 0, &start_block);
const Optional<int> tip_height = locked_chain->getHeight();
WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, tip_height && start_height ? *tip_height - *start_height + 1 : 0);
}
@@ -1603,7 +1808,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
ScanResult result = ScanForWalletTransactions(start_block, {} /* stop_block */, reserver, update);
if (result.status == ScanResult::FAILURE) {
int64_t time_max;
- if (!chain().findBlock(result.failed_block, nullptr /* block */, nullptr /* time */, &time_max)) {
+ if (!chain().findBlock(result.last_failed_block, nullptr /* block */, nullptr /* time */, &time_max)) {
throw std::logic_error("ScanForWalletTransactions returned invalid block hash");
}
return time_max + TIMESTAMP_WINDOW + 1;
@@ -1617,15 +1822,17 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
* from or to us. If fUpdate is true, found transactions that already
* exist in the wallet will be updated.
*
- * @param[in] start_block if not null, the scan will start at this block instead
- * of the genesis block
- * @param[in] stop_block if not null, the scan will stop at this block instead
- * of the chain tip
+ * @param[in] start_block Scan starting block. If block is not on the active
+ * chain, the scan will return SUCCESS immediately.
+ * @param[in] stop_block Scan ending block. If block is not on the active
+ * chain, the scan will continue until it reaches the
+ * chain tip.
*
- * @return ScanResult indicating success or failure of the scan. SUCCESS if
- * scan was successful. FAILURE if a complete rescan was not possible (due to
- * pruning or corruption). USER_ABORT if the rescan was aborted before it
- * could complete.
+ * @return ScanResult returning scan information and indicating success or
+ * failure. Return status will be set to SUCCESS if scan was
+ * successful. FAILURE if a complete rescan was not possible (due to
+ * pruning or corruption). USER_ABORT if the rescan was aborted before
+ * it could complete.
*
* @pre Caller needs to make sure start_block (and the optional stop_block) are on
* the main chain after to the addition of any new keys you want to detect
@@ -1634,6 +1841,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, const uint256& stop_block, const WalletRescanReserver& reserver, bool fUpdate)
{
int64_t nNow = GetTime();
+ int64_t start_time = GetTimeMillis();
assert(reserver.isReserved());
@@ -1642,101 +1850,100 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
WalletLogPrintf("Rescan started from block %s...\n", start_block.ToString());
+ fAbortRescan = false;
+ ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
+ uint256 tip_hash;
+ // The way the 'block_height' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0.
+ Optional<int> block_height = MakeOptional(false, int());
+ double progress_begin;
+ double progress_end;
{
- fAbortRescan = false;
- ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
- uint256 tip_hash;
- // The way the 'block_height' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0.
- Optional<int> block_height = MakeOptional(false, int());
- double progress_begin;
- double progress_end;
- {
- auto locked_chain = chain().lock();
- if (Optional<int> tip_height = locked_chain->getHeight()) {
- tip_hash = locked_chain->getBlockHash(*tip_height);
- }
- block_height = locked_chain->getBlockHeight(block_hash);
- progress_begin = chain().guessVerificationProgress(block_hash);
- progress_end = chain().guessVerificationProgress(stop_block.IsNull() ? tip_hash : stop_block);
- }
- double progress_current = progress_begin;
- while (block_height && !fAbortRescan && !ShutdownRequested()) {
- if (*block_height % 100 == 0 && progress_end - progress_begin > 0.0) {
- ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), std::max(1, std::min(99, (int)((progress_current - progress_begin) / (progress_end - progress_begin) * 100))));
- }
- if (GetTime() >= nNow + 60) {
- nNow = GetTime();
- WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", *block_height, progress_current);
- }
+ auto locked_chain = chain().lock();
+ if (Optional<int> tip_height = locked_chain->getHeight()) {
+ tip_hash = locked_chain->getBlockHash(*tip_height);
+ }
+ block_height = locked_chain->getBlockHeight(block_hash);
+ progress_begin = chain().guessVerificationProgress(block_hash);
+ progress_end = chain().guessVerificationProgress(stop_block.IsNull() ? tip_hash : stop_block);
+ }
+ double progress_current = progress_begin;
+ while (block_height && !fAbortRescan && !chain().shutdownRequested()) {
+ m_scanning_progress = (progress_current - progress_begin) / (progress_end - progress_begin);
+ if (*block_height % 100 == 0 && progress_end - progress_begin > 0.0) {
+ ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), std::max(1, std::min(99, (int)(m_scanning_progress * 100))));
+ }
+ if (GetTime() >= nNow + 60) {
+ nNow = GetTime();
+ WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", *block_height, progress_current);
+ }
- CBlock block;
- if (chain().findBlock(block_hash, &block) && !block.IsNull()) {
- auto locked_chain = chain().lock();
- LOCK(cs_wallet);
- if (!locked_chain->getBlockHeight(block_hash)) {
- // Abort scan if current block is no longer active, to prevent
- // marking transactions as coming from the wrong block.
- // TODO: This should return success instead of failure, see
- // https://github.com/bitcoin/bitcoin/pull/14711#issuecomment-458342518
- result.failed_block = block_hash;
- result.status = ScanResult::FAILURE;
- break;
- }
- for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- SyncTransaction(block.vtx[posInBlock], block_hash, posInBlock, fUpdate);
- }
- // scan succeeded, record block as most recent successfully scanned
- result.stop_block = block_hash;
- result.stop_height = *block_height;
- } else {
- // could not scan block, keep scanning but record this block as the most recent failure
- result.failed_block = block_hash;
+ CBlock block;
+ if (chain().findBlock(block_hash, &block) && !block.IsNull()) {
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
+ if (!locked_chain->getBlockHeight(block_hash)) {
+ // Abort scan if current block is no longer active, to prevent
+ // marking transactions as coming from the wrong block.
+ // TODO: This should return success instead of failure, see
+ // https://github.com/bitcoin/bitcoin/pull/14711#issuecomment-458342518
+ result.last_failed_block = block_hash;
result.status = ScanResult::FAILURE;
+ break;
}
- if (block_hash == stop_block) {
+ for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
+ SyncTransaction(block.vtx[posInBlock], block_hash, posInBlock, fUpdate);
+ }
+ // scan succeeded, record block as most recent successfully scanned
+ result.last_scanned_block = block_hash;
+ result.last_scanned_height = *block_height;
+ } else {
+ // could not scan block, keep scanning but record this block as the most recent failure
+ result.last_failed_block = block_hash;
+ result.status = ScanResult::FAILURE;
+ }
+ if (block_hash == stop_block) {
+ break;
+ }
+ {
+ auto locked_chain = chain().lock();
+ Optional<int> tip_height = locked_chain->getHeight();
+ if (!tip_height || *tip_height <= block_height || !locked_chain->getBlockHeight(block_hash)) {
+ // break successfully when rescan has reached the tip, or
+ // previous block is no longer on the chain due to a reorg
break;
}
- {
- auto locked_chain = chain().lock();
- Optional<int> tip_height = locked_chain->getHeight();
- if (!tip_height || *tip_height <= block_height || !locked_chain->getBlockHeight(block_hash)) {
- // break successfully when rescan has reached the tip, or
- // previous block is no longer on the chain due to a reorg
- break;
- }
- // increment block and verification progress
- block_hash = locked_chain->getBlockHash(++*block_height);
- progress_current = chain().guessVerificationProgress(block_hash);
+ // increment block and verification progress
+ block_hash = locked_chain->getBlockHash(++*block_height);
+ progress_current = chain().guessVerificationProgress(block_hash);
- // handle updated tip hash
- const uint256 prev_tip_hash = tip_hash;
- tip_hash = locked_chain->getBlockHash(*tip_height);
- if (stop_block.IsNull() && prev_tip_hash != tip_hash) {
- // in case the tip has changed, update progress max
- progress_end = chain().guessVerificationProgress(tip_hash);
- }
+ // handle updated tip hash
+ const uint256 prev_tip_hash = tip_hash;
+ tip_hash = locked_chain->getBlockHash(*tip_height);
+ if (stop_block.IsNull() && prev_tip_hash != tip_hash) {
+ // in case the tip has changed, update progress max
+ progress_end = chain().guessVerificationProgress(tip_hash);
}
}
- ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI
- if (block_height && fAbortRescan) {
- WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", block_height.get_value_or(0), progress_current);
- result.status = ScanResult::USER_ABORT;
- } else if (block_height && ShutdownRequested()) {
- WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", block_height.get_value_or(0), progress_current);
- result.status = ScanResult::USER_ABORT;
- }
+ }
+ ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI
+ if (block_height && fAbortRescan) {
+ WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", *block_height, progress_current);
+ result.status = ScanResult::USER_ABORT;
+ } else if (block_height && chain().shutdownRequested()) {
+ WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", *block_height, progress_current);
+ result.status = ScanResult::USER_ABORT;
+ } else {
+ WalletLogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - start_time);
}
return result;
}
-void CWallet::ReacceptWalletTransactions()
+void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain)
{
// If transactions aren't being broadcasted, don't let them into local mempool either
if (!fBroadcastTransactions)
return;
- auto locked_chain = chain().lock();
- LOCK(cs_wallet);
std::map<int64_t, CWalletTx*> mapSorted;
// Sort pending wallet transactions based on their initial wallet insertion order
@@ -1746,7 +1953,7 @@ void CWallet::ReacceptWalletTransactions()
CWalletTx& wtx = item.second;
assert(wtx.GetHash() == wtxid);
- int nDepth = wtx.GetDepthInMainChain(*locked_chain);
+ int nDepth = wtx.GetDepthInMainChain(locked_chain);
if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) {
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
@@ -1757,30 +1964,31 @@ void CWallet::ReacceptWalletTransactions()
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second);
CValidationState state;
- wtx.AcceptToMemoryPool(*locked_chain, maxTxFee, state);
+ wtx.AcceptToMemoryPool(locked_chain, state);
}
}
-bool CWalletTx::RelayWalletTransaction(interfaces::Chain::Lock& locked_chain, CConnman* connman)
+bool CWalletTx::RelayWalletTransaction(interfaces::Chain::Lock& locked_chain)
{
- assert(pwallet->GetBroadcastTransactions());
- if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain(locked_chain) == 0)
- {
- CValidationState state;
- /* GetDepthInMainChain already catches known conflicts. */
- if (InMempool() || AcceptToMemoryPool(locked_chain, maxTxFee, state)) {
- pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString());
- if (connman) {
- CInv inv(MSG_TX, GetHash());
- connman->ForEachNode([&inv](CNode* pnode)
- {
- pnode->PushInventory(inv);
- });
- return true;
- }
- }
- }
- return false;
+ // Can't relay if wallet is not broadcasting
+ if (!pwallet->GetBroadcastTransactions()) return false;
+ // Don't relay coinbase transactions outside blocks
+ if (IsCoinBase()) return false;
+ // Don't relay abandoned transactions
+ if (isAbandoned()) return false;
+ // Don't relay conflicted or already confirmed transactions
+ if (GetDepthInMainChain(locked_chain) != 0) return false;
+ // Don't relay transactions that aren't accepted to the mempool
+ CValidationState unused_state;
+ if (!InMempool() && !AcceptToMemoryPool(locked_chain, unused_state)) return false;
+ // Don't try to relay if the node is not connected to the p2p network
+ if (!pwallet->chain().p2pEnabled()) return false;
+
+ // Try to relay the transaction
+ pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString());
+ pwallet->chain().relayTransaction(GetHash());
+
+ return true;
}
std::set<uint256> CWalletTx::GetConflicts() const
@@ -1795,33 +2003,26 @@ std::set<uint256> CWalletTx::GetConflicts() const
return result;
}
+CAmount CWalletTx::GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate) const
+{
+ auto& amount = m_amounts[type];
+ if (recalculate || !amount.m_cached[filter]) {
+ amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter));
+ }
+ return amount.m_value[filter];
+}
+
CAmount CWalletTx::GetDebit(const isminefilter& filter) const
{
if (tx->vin.empty())
return 0;
CAmount debit = 0;
- if(filter & ISMINE_SPENDABLE)
- {
- if (fDebitCached)
- debit += nDebitCached;
- else
- {
- nDebitCached = pwallet->GetDebit(*tx, ISMINE_SPENDABLE);
- fDebitCached = true;
- debit += nDebitCached;
- }
+ if (filter & ISMINE_SPENDABLE) {
+ debit += GetCachableAmount(DEBIT, ISMINE_SPENDABLE);
}
- if(filter & ISMINE_WATCH_ONLY)
- {
- if(fWatchDebitCached)
- debit += nWatchDebitCached;
- else
- {
- nWatchDebitCached = pwallet->GetDebit(*tx, ISMINE_WATCH_ONLY);
- fWatchDebitCached = true;
- debit += nWatchDebitCached;
- }
+ if (filter & ISMINE_WATCH_ONLY) {
+ debit += GetCachableAmount(DEBIT, ISMINE_WATCH_ONLY);
}
return debit;
}
@@ -1833,28 +2034,12 @@ CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const ismine
return 0;
CAmount credit = 0;
- if (filter & ISMINE_SPENDABLE)
- {
+ if (filter & ISMINE_SPENDABLE) {
// GetBalance can assume transactions in mapWallet won't change
- if (fCreditCached)
- credit += nCreditCached;
- else
- {
- nCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
- fCreditCached = true;
- credit += nCreditCached;
- }
+ credit += GetCachableAmount(CREDIT, ISMINE_SPENDABLE);
}
- if (filter & ISMINE_WATCH_ONLY)
- {
- if (fWatchCreditCached)
- credit += nWatchCreditCached;
- else
- {
- nWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
- fWatchCreditCached = true;
- credit += nWatchCreditCached;
- }
+ if (filter & ISMINE_WATCH_ONLY) {
+ credit += GetCachableAmount(CREDIT, ISMINE_WATCH_ONLY);
}
return credit;
}
@@ -1862,11 +2047,7 @@ CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const ismine
CAmount CWalletTx::GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache) const
{
if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
- if (fUseCache && fImmatureCreditCached)
- return nImmatureCreditCached;
- nImmatureCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
- fImmatureCreditCached = true;
- return nImmatureCreditCached;
+ return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
}
return 0;
@@ -1877,23 +2058,15 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
if (pwallet == nullptr)
return 0;
+ // Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future).
+ bool allow_cache = filter == ISMINE_SPENDABLE || filter == ISMINE_WATCH_ONLY;
+
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsImmatureCoinBase(locked_chain))
return 0;
- CAmount* cache = nullptr;
- bool* cache_used = nullptr;
-
- if (filter == ISMINE_SPENDABLE) {
- cache = &nAvailableCreditCached;
- cache_used = &fAvailableCreditCached;
- } else if (filter == ISMINE_WATCH_ONLY) {
- cache = &nAvailableWatchCreditCached;
- cache_used = &fAvailableWatchCreditCached;
- }
-
- if (fUseCache && cache_used && *cache_used) {
- return *cache;
+ if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) {
+ return m_amounts[AVAILABLE_CREDIT].m_value[filter];
}
CAmount nCredit = 0;
@@ -1909,22 +2082,17 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
}
}
- if (cache) {
- *cache = nCredit;
- assert(cache_used);
- *cache_used = true;
+ if (allow_cache) {
+ m_amounts[AVAILABLE_CREDIT].Set(filter, nCredit);
}
+
return nCredit;
}
CAmount CWalletTx::GetImmatureWatchOnlyCredit(interfaces::Chain::Lock& locked_chain, const bool fUseCache) const
{
if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
- if (fUseCache && fImmatureWatchCreditCached)
- return nImmatureWatchCreditCached;
- nImmatureWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
- fImmatureWatchCreditCached = true;
- return nImmatureWatchCreditCached;
+ return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache);
}
return 0;
@@ -1946,11 +2114,10 @@ bool CWalletTx::InMempool() const
bool CWalletTx::IsTrusted(interfaces::Chain::Lock& locked_chain) const
{
- LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
-
// Quick answer in most cases
- if (!CheckFinalTx(*tx))
+ if (!locked_chain.checkFinalTx(*tx)) {
return false;
+ }
int nDepth = GetDepthInMainChain(locked_chain);
if (nDepth >= 1)
return true;
@@ -1986,185 +2153,95 @@ bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const
return CTransaction(tx1) == CTransaction(tx2);
}
-std::vector<uint256> CWallet::ResendWalletTransactionsBefore(interfaces::Chain::Lock& locked_chain, int64_t nTime, CConnman* connman)
+// Rebroadcast transactions from the wallet. We do this on a random timer
+// to slightly obfuscate which transactions come from our wallet.
+//
+// Ideally, we'd only resend transactions that we think should have been
+// mined in the most recent block. Any transaction that wasn't in the top
+// blockweight of transactions in the mempool shouldn't have been mined,
+// and so is probably just sitting in the mempool waiting to be confirmed.
+// Rebroadcasting does nothing to speed up confirmation and only damages
+// privacy.
+void CWallet::ResendWalletTransactions()
{
- std::vector<uint256> result;
-
- LOCK(cs_wallet);
+ // During reindex, importing and IBD, old wallet transactions become
+ // unconfirmed. Don't resend them as that would spam other nodes.
+ if (!chain().isReadyToBroadcast()) return;
- // Sort them in chronological order
- std::multimap<unsigned int, CWalletTx*> mapSorted;
- for (std::pair<const uint256, CWalletTx>& item : mapWallet)
- {
- CWalletTx& wtx = item.second;
- // Don't rebroadcast if newer than nTime:
- if (wtx.nTimeReceived > nTime)
- continue;
- mapSorted.insert(std::make_pair(wtx.nTimeReceived, &wtx));
- }
- for (const std::pair<const unsigned int, CWalletTx*>& item : mapSorted)
- {
- CWalletTx& wtx = *item.second;
- if (wtx.RelayWalletTransaction(locked_chain, connman))
- result.push_back(wtx.GetHash());
- }
- return result;
-}
-
-void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman)
-{
// Do this infrequently and randomly to avoid giving away
// that these are our transactions.
- if (GetTime() < nNextResend || !fBroadcastTransactions)
- return;
+ if (GetTime() < nNextResend || !fBroadcastTransactions) return;
bool fFirst = (nNextResend == 0);
nNextResend = GetTime() + GetRand(30 * 60);
- if (fFirst)
- return;
+ if (fFirst) return;
// Only do it if there's been a new block since last time
- if (nBestBlockTime < nLastResend)
- return;
+ if (m_best_block_time < nLastResend) return;
nLastResend = GetTime();
- // Rebroadcast unconfirmed txes older than 5 minutes before the last
- // block was found:
- auto locked_chain = chain().assumeLocked(); // Temporary. Removed in upcoming lock cleanup
- std::vector<uint256> relayed = ResendWalletTransactionsBefore(*locked_chain, nBestBlockTime-5*60, connman);
- if (!relayed.empty())
- WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size());
-}
-
-/** @} */ // end of mapWallet
-
-
+ int relayed_tx_count = 0;
-
-/** @defgroup Actions
- *
- * @{
- */
-
-
-CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) const
-{
- CAmount nTotal = 0;
- {
+ { // locked_chain and cs_wallet scope
auto locked_chain = chain().lock();
LOCK(cs_wallet);
- for (const auto& entry : mapWallet)
- {
- const CWalletTx* pcoin = &entry.second;
- if (pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) >= min_depth) {
- nTotal += pcoin->GetAvailableCredit(*locked_chain, true, filter);
- }
- }
- }
-
- return nTotal;
-}
-CAmount CWallet::GetUnconfirmedBalance() const
-{
- CAmount nTotal = 0;
- {
- auto locked_chain = chain().lock();
- LOCK(cs_wallet);
- for (const auto& entry : mapWallet)
- {
- const CWalletTx* pcoin = &entry.second;
- if (!pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) == 0 && pcoin->InMempool())
- nTotal += pcoin->GetAvailableCredit(*locked_chain);
+ // Relay transactions
+ for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
+ CWalletTx& wtx = item.second;
+ // only rebroadcast unconfirmed txes older than 5 minutes before the
+ // last block was found
+ if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
+ if (wtx.RelayWalletTransaction(*locked_chain)) ++relayed_tx_count;
}
- }
- return nTotal;
-}
+ } // locked_chain and cs_wallet
-CAmount CWallet::GetImmatureBalance() const
-{
- CAmount nTotal = 0;
- {
- auto locked_chain = chain().lock();
- LOCK(cs_wallet);
- for (const auto& entry : mapWallet)
- {
- const CWalletTx* pcoin = &entry.second;
- nTotal += pcoin->GetImmatureCredit(*locked_chain);
- }
+ if (relayed_tx_count > 0) {
+ WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed_tx_count);
}
- return nTotal;
}
-CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
+/** @} */ // end of mapWallet
+
+void MaybeResendWalletTxs()
{
- CAmount nTotal = 0;
- {
- auto locked_chain = chain().lock();
- LOCK(cs_wallet);
- for (const auto& entry : mapWallet)
- {
- const CWalletTx* pcoin = &entry.second;
- if (!pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) == 0 && pcoin->InMempool())
- nTotal += pcoin->GetAvailableCredit(*locked_chain, true, ISMINE_WATCH_ONLY);
- }
+ for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
+ pwallet->ResendWalletTransactions();
}
- return nTotal;
}
-CAmount CWallet::GetImmatureWatchOnlyBalance() const
+
+/** @defgroup Actions
+ *
+ * @{
+ */
+
+
+CWallet::Balance CWallet::GetBalance(const int min_depth) const
{
- CAmount nTotal = 0;
+ Balance ret;
{
auto locked_chain = chain().lock();
LOCK(cs_wallet);
for (const auto& entry : mapWallet)
{
- const CWalletTx* pcoin = &entry.second;
- nTotal += pcoin->GetImmatureWatchOnlyCredit(*locked_chain);
- }
- }
- return nTotal;
-}
-
-// Calculate total balance in a different way from GetBalance. The biggest
-// difference is that GetBalance sums up all unspent TxOuts paying to the
-// wallet, while this sums up both spent and unspent TxOuts paying to the
-// wallet, and then subtracts the values of TxIns spending from the wallet. This
-// also has fewer restrictions on which unconfirmed transactions are considered
-// trusted.
-CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) const
-{
- LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
- auto locked_chain = chain().lock();
- LOCK(cs_wallet);
-
- CAmount balance = 0;
- for (const auto& entry : mapWallet) {
- const CWalletTx& wtx = entry.second;
- const int depth = wtx.GetDepthInMainChain(*locked_chain);
- if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase(*locked_chain)) {
- continue;
- }
-
- // Loop through tx outputs and add incoming payments. For outgoing txs,
- // treat change outputs specially, as part of the amount debited.
- CAmount debit = wtx.GetDebit(filter);
- const bool outgoing = debit > 0;
- for (const CTxOut& out : wtx.tx->vout) {
- if (outgoing && IsChange(out)) {
- debit -= out.nValue;
- } else if (IsMine(out) & filter && depth >= minDepth) {
- balance += out.nValue;
+ const CWalletTx& wtx = entry.second;
+ const bool is_trusted{wtx.IsTrusted(*locked_chain)};
+ const int tx_depth{wtx.GetDepthInMainChain(*locked_chain)};
+ const CAmount tx_credit_mine{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_SPENDABLE)};
+ const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_WATCH_ONLY)};
+ if (is_trusted && tx_depth >= min_depth) {
+ ret.m_mine_trusted += tx_credit_mine;
+ ret.m_watchonly_trusted += tx_credit_watchonly;
}
- }
-
- // For outgoing txs, subtract amount debited.
- if (outgoing) {
- balance -= debit;
+ if (!is_trusted && tx_depth == 0 && wtx.InMempool()) {
+ ret.m_mine_untrusted_pending += tx_credit_mine;
+ ret.m_watchonly_untrusted_pending += tx_credit_watchonly;
+ }
+ ret.m_mine_immature += wtx.GetImmatureCredit(*locked_chain);
+ ret.m_watchonly_immature += wtx.GetImmatureWatchOnlyCredit(*locked_chain);
}
}
-
- return balance;
+ return ret;
}
CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
@@ -2185,7 +2262,6 @@ CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, 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
{
- AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
vCoins.clear();
@@ -2194,24 +2270,25 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
for (const auto& entry : mapWallet)
{
const uint256& wtxid = entry.first;
- const CWalletTx* pcoin = &entry.second;
+ const CWalletTx& wtx = entry.second;
- if (!CheckFinalTx(*pcoin->tx))
+ if (!locked_chain.checkFinalTx(*wtx.tx)) {
continue;
+ }
- if (pcoin->IsImmatureCoinBase(locked_chain))
+ if (wtx.IsImmatureCoinBase(locked_chain))
continue;
- int nDepth = pcoin->GetDepthInMainChain(locked_chain);
+ int nDepth = wtx.GetDepthInMainChain(locked_chain);
if (nDepth < 0)
continue;
// We should not consider coins which aren't at least in our mempool
// It's possible for these to be conflicted via ancestors which we may never be able to detect
- if (nDepth == 0 && !pcoin->InMempool())
+ if (nDepth == 0 && !wtx.InMempool())
continue;
- bool safeTx = pcoin->IsTrusted(locked_chain);
+ bool safeTx = wtx.IsTrusted(locked_chain);
// We should not consider coins from transactions that are replacing
// other transactions.
@@ -2228,7 +2305,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
// be a 1-block reorg away from the chain where transactions A and C
// were accepted to another chain where B, B', and C were all
// accepted.
- if (nDepth == 0 && pcoin->mapValue.count("replaces_txid")) {
+ if (nDepth == 0 && wtx.mapValue.count("replaces_txid")) {
safeTx = false;
}
@@ -2240,7 +2317,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
// intending to replace A', but potentially resulting in a scenario
// where A, A', and D could all be accepted (instead of just B and
// D, or just A and A' like the user would want).
- if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) {
+ if (nDepth == 0 && wtx.mapValue.count("replaced_by_txid")) {
safeTx = false;
}
@@ -2251,8 +2328,8 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
if (nDepth < nMinDepth || nDepth > nMaxDepth)
continue;
- for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
- if (pcoin->tx->vout[i].nValue < nMinimumAmount || pcoin->tx->vout[i].nValue > nMaximumAmount)
+ for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
+ if (wtx.tx->vout[i].nValue < nMinimumAmount || wtx.tx->vout[i].nValue > nMaximumAmount)
continue;
if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i)))
@@ -2264,20 +2341,20 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
if (IsSpent(locked_chain, wtxid, i))
continue;
- isminetype mine = IsMine(pcoin->tx->vout[i]);
+ isminetype mine = IsMine(wtx.tx->vout[i]);
if (mine == ISMINE_NO) {
continue;
}
- bool solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey);
+ bool solvable = IsSolvable(*this, wtx.tx->vout[i].scriptPubKey);
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
- vCoins.push_back(COutput(pcoin, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
+ vCoins.push_back(COutput(&wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
// Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) {
- nTotal += pcoin->tx->vout[i].nValue;
+ nTotal += wtx.tx->vout[i].nValue;
if (nTotal >= nMinimumSumAmount) {
return;
@@ -2294,7 +2371,6 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Chain::Lock& locked_chain) const
{
- AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
std::map<CTxDestination, std::vector<COutput>> result;
@@ -2359,10 +2435,10 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
FeeCalculation feeCalc;
CCoinControl temp;
temp.m_confirm_target = 1008;
- CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, ::mempool, ::feeEstimator, &feeCalc);
+ CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, &feeCalc);
// Calculate cost of change
- CAmount cost_of_change = GetDiscardRate(*this, ::feeEstimator).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
+ CAmount cost_of_change = GetDiscardRate(*this).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
// Filter by the min conf specs and add to utxo_pool and calculate effective value
for (OutputGroup& group : groups) {
@@ -2436,13 +2512,13 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash);
if (it != mapWallet.end())
{
- const CWalletTx* pcoin = &it->second;
+ const CWalletTx& wtx = it->second;
// Clearly invalid input, fail
- if (pcoin->tx->vout.size() <= outpoint.n)
+ if (wtx.tx->vout.size() <= outpoint.n)
return false;
// Just to calculate the marginal byte size
- nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue;
- setPresetCoins.insert(CInputCoin(pcoin->tx, outpoint.n));
+ nValueFromPresetInputs += wtx.tx->vout[outpoint.n].nValue;
+ setPresetCoins.insert(CInputCoin(wtx.tx, outpoint.n));
} else
return false; // TODO: Allow non-wallet inputs
}
@@ -2488,9 +2564,9 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
return res;
}
-bool CWallet::SignTransaction(CMutableTransaction &tx)
+bool CWallet::SignTransaction(CMutableTransaction& tx)
{
- AssertLockHeld(cs_wallet); // mapWallet
+ AssertLockHeld(cs_wallet);
// sign the new tx
int nIn = 0;
@@ -2566,13 +2642,13 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
return true;
}
-static bool IsCurrentForAntiFeeSniping(interfaces::Chain::Lock& locked_chain)
+static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain)
{
- if (IsInitialBlockDownload()) {
+ if (chain.isInitialBlockDownload()) {
return false;
}
constexpr int64_t MAX_ANTI_FEE_SNIPING_TIP_AGE = 8 * 60 * 60; // in seconds
- if (chainActive.Tip()->GetBlockTime() < (GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) {
+ if (locked_chain.getBlockTime(*locked_chain.getHeight()) < (GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) {
return false;
}
return true;
@@ -2582,7 +2658,7 @@ static bool IsCurrentForAntiFeeSniping(interfaces::Chain::Lock& locked_chain)
* Return a height-based locktime for new transactions (uses the height of the
* current chain tip unless we are not synced with the current chain
*/
-static uint32_t GetLocktimeForNewTransaction(interfaces::Chain::Lock& locked_chain)
+static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain)
{
uint32_t const height = locked_chain.getHeight().get_value_or(-1);
uint32_t locktime;
@@ -2606,7 +2682,7 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain::Lock& locked_cha
// enough, that fee sniping isn't a problem yet, but by implementing a fix
// now we ensure code won't be written that makes assumptions about
// nLockTime that preclude a fix later.
- if (IsCurrentForAntiFeeSniping(locked_chain)) {
+ if (IsCurrentForAntiFeeSniping(chain, locked_chain)) {
locktime = height;
// Secondly occasionally randomly pick a nLockTime even further back, so
@@ -2680,7 +2756,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
CMutableTransaction txNew;
- txNew.nLockTime = GetLocktimeForNewTransaction(locked_chain);
+ txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chain);
FeeCalculation feeCalc;
CAmount nFeeNeeded;
@@ -2691,7 +2767,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
LOCK(cs_wallet);
{
std::vector<COutput> vAvailableCoins;
- AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control);
+ AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0, coin_control.m_min_depth);
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
// Create change script that will be used if we need change
@@ -2711,8 +2787,8 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// post-backup change.
// Reserve a new key pair from key pool
- if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
- strFailReason = _("Can't generate a change-address key. Private keys are disabled for this wallet.");
+ if (!CanGetAddresses(true)) {
+ strFailReason = _("Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.");
return false;
}
CPubKey vchPubKey;
@@ -2732,10 +2808,10 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
CTxOut change_prototype_txout(0, scriptChange);
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
- CFeeRate discard_rate = GetDiscardRate(*this, ::feeEstimator);
+ CFeeRate discard_rate = GetDiscardRate(*this);
// Get the fee rate to use effective values in coin selection
- CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, ::mempool, ::feeEstimator, &feeCalc);
+ CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc);
nFeeRet = 0;
bool pick_new_inputs = true;
@@ -2776,7 +2852,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// Include the fee cost for outputs. Note this is only used for BnB right now
coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
- if (IsDust(txout, ::dustRelayFee))
+ if (IsDust(txout, chain().relayDustFee()))
{
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
{
@@ -2868,7 +2944,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
return false;
}
- nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
+ nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, &feeCalc);
if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
// eventually allow a fallback fee
strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
@@ -2877,7 +2953,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// If we made it here and we aren't even able to meet the relay fee on the next pass, give up
// because we must be at the maximum allowed fee.
- if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes))
+ if (nFeeNeeded < chain().relayMinFee().GetFee(nBytes))
{
strFailReason = _("Transaction too large for fee policy");
return false;
@@ -2896,7 +2972,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// change output. Only try this once.
if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
unsigned int tx_size_with_change = nBytes + coin_selection_params.change_output_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size
- CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, ::mempool, ::feeEstimator, nullptr);
+ CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, nullptr);
CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) {
pick_new_inputs = false;
@@ -3001,16 +3077,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits
- LockPoints lp;
- CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp);
- CTxMemPool::setEntries setAncestors;
- size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
- size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
- size_t nLimitDescendants = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
- size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
- std::string errString;
- LOCK(::mempool.cs);
- if (!::mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
+ if (!chain().checkChainLimits(tx)) {
strFailReason = _("Transaction has too long of a mempool chain");
return false;
}
@@ -3030,7 +3097,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
/**
* Call after CreateTransaction unless you want to abort
*/
-bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state)
+bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CValidationState& state)
{
{
auto locked_chain = chain().lock();
@@ -3067,11 +3134,11 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
if (fBroadcastTransactions)
{
// Broadcast
- if (!wtx.AcceptToMemoryPool(*locked_chain, maxTxFee, state)) {
+ if (!wtx.AcceptToMemoryPool(*locked_chain, state)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", FormatStateMessage(state));
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
} else {
- wtx.RelayWalletTransaction(*locked_chain, connman);
+ wtx.RelayWalletTransaction(*locked_chain);
}
}
}
@@ -3080,7 +3147,6 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
fFirstRunRet = false;
@@ -3101,7 +3167,8 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
LOCK(cs_KeyStore);
// This wallet is in its first run if all of these are empty
- fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty() && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty()
+ && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET);
}
if (nLoadWalletRet != DBErrors::LOAD_OK)
@@ -3112,8 +3179,8 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
{
- AssertLockHeld(cs_wallet); // mapWallet
- DBErrors nZapSelectTxRet = WalletBatch(*database,"cr+").ZapSelectTx(vHashIn, vHashOut);
+ AssertLockHeld(cs_wallet);
+ DBErrors nZapSelectTxRet = WalletBatch(*database, "cr+").ZapSelectTx(vHashIn, vHashOut);
for (uint256 hash : vHashOut) {
const auto& it = mapWallet.find(hash);
wtxOrdered.erase(it->second.m_it_wtxOrdered);
@@ -3139,7 +3206,6 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
MarkDirty();
return DBErrors::LOAD_OK;
-
}
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
@@ -3165,12 +3231,11 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
return DBErrors::LOAD_OK;
}
-
-bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
+bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
{
bool fUpdated = false;
{
- LOCK(cs_wallet); // mapAddressBook
+ LOCK(cs_wallet);
std::map<CTxDestination, CAddressBookData>::iterator mi = mapAddressBook.find(address);
fUpdated = mi != mapAddressBook.end();
mapAddressBook[address].name = strName;
@@ -3179,15 +3244,21 @@ 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() && !WalletBatch(*database).WritePurpose(EncodeDestination(address), strPurpose))
+ if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
return false;
- return WalletBatch(*database).WriteName(EncodeDestination(address), strName);
+ return batch.WriteName(EncodeDestination(address), strName);
+}
+
+bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
+{
+ WalletBatch batch(*database);
+ return SetAddressBookWithDB(batch, address, strName, strPurpose);
}
bool CWallet::DelAddressBook(const CTxDestination& address)
{
{
- LOCK(cs_wallet); // mapAddressBook
+ LOCK(cs_wallet);
// Delete destdata tuples associated with address
std::string strAddress = EncodeDestination(address);
@@ -3259,7 +3330,7 @@ bool CWallet::NewKeyPool()
size_t CWallet::KeypoolCountExternalKeys()
{
- AssertLockHeld(cs_wallet); // setExternalKeyPool
+ AssertLockHeld(cs_wallet);
return setExternalKeyPool.size() + set_pre_split_keypool.size();
}
@@ -3286,7 +3357,7 @@ void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
bool CWallet::TopUpKeyPool(unsigned int kpSize)
{
- if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (!CanGenerateKeys()) {
return false;
}
{
@@ -3320,20 +3391,8 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
internal = true;
}
- assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); // How in the hell did you use so many keys?
- int64_t index = ++m_max_keypool_index;
-
CPubKey pubkey(GenerateNewKey(batch, internal));
- if (!batch.WritePool(index, CKeyPool(pubkey, internal))) {
- throw std::runtime_error(std::string(__func__) + ": writing generated key failed");
- }
-
- if (internal) {
- setInternalKeyPool.insert(index);
- } else {
- setExternalKeyPool.insert(index);
- }
- m_pool_key_to_index[pubkey.GetID()] = index;
+ AddKeypoolPubkeyWithDB(pubkey, internal, batch);
}
if (missingInternal + missingExternal > 0) {
WalletLogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(), setInternalKeyPool.size());
@@ -3343,6 +3402,22 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
return true;
}
+void CWallet::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch)
+{
+ LOCK(cs_wallet);
+ assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); // How in the hell did you use so many keys?
+ int64_t index = ++m_max_keypool_index;
+ if (!batch.WritePool(index, CKeyPool(pubkey, internal))) {
+ throw std::runtime_error(std::string(__func__) + ": writing imported pubkey failed");
+ }
+ if (internal) {
+ setInternalKeyPool.insert(index);
+ } else {
+ setExternalKeyPool.insert(index);
+ }
+ m_pool_key_to_index[pubkey.GetID()] = index;
+}
+
bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal)
{
nIndex = -1;
@@ -3353,7 +3428,8 @@ bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
if (!IsLocked())
TopUpKeyPool();
- bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal;
+ bool fReturningInternal = fRequestedInternal;
+ fReturningInternal &= (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) || IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
bool use_split_keypool = set_pre_split_keypool.empty();
std::set<int64_t>& setKeyPool = use_split_keypool ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool;
@@ -3370,7 +3446,8 @@ bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
if (!batch.ReadPool(nIndex, keypool)) {
throw std::runtime_error(std::string(__func__) + ": read failed");
}
- if (!HaveKey(keypool.vchPubKey.GetID())) {
+ CPubKey pk;
+ if (!GetPubKey(keypool.vchPubKey.GetID(), pk)) {
throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
}
// If the key was pre-split keypool, we don't care about what type it is
@@ -3416,7 +3493,7 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)
bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
{
- if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (!CanGetAddresses(internal)) {
return false;
}
@@ -3424,7 +3501,7 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
{
LOCK(cs_wallet);
int64_t nIndex;
- if (!ReserveKeyFromKeyPool(nIndex, keypool, internal)) {
+ if (!ReserveKeyFromKeyPool(nIndex, keypool, internal) && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (IsLocked()) return false;
WalletBatch batch(*database);
result = GenerateNewKey(batch, internal);
@@ -3476,27 +3553,27 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain:
LOCK(cs_wallet);
for (const auto& walletEntry : mapWallet)
{
- const CWalletTx *pcoin = &walletEntry.second;
+ const CWalletTx& wtx = walletEntry.second;
- if (!pcoin->IsTrusted(locked_chain))
+ if (!wtx.IsTrusted(locked_chain))
continue;
- if (pcoin->IsImmatureCoinBase(locked_chain))
+ if (wtx.IsImmatureCoinBase(locked_chain))
continue;
- int nDepth = pcoin->GetDepthInMainChain(locked_chain);
- if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1))
+ int nDepth = wtx.GetDepthInMainChain(locked_chain);
+ if (nDepth < (wtx.IsFromMe(ISMINE_ALL) ? 0 : 1))
continue;
- for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++)
+ for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
{
CTxDestination addr;
- if (!IsMine(pcoin->tx->vout[i]))
+ if (!IsMine(wtx.tx->vout[i]))
continue;
- if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr))
+ if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr))
continue;
- CAmount n = IsSpent(locked_chain, walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue;
+ CAmount n = IsSpent(locked_chain, walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
if (!balances.count(addr))
balances[addr] = 0;
@@ -3510,19 +3587,19 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain:
std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
{
- AssertLockHeld(cs_wallet); // mapWallet
+ AssertLockHeld(cs_wallet);
std::set< std::set<CTxDestination> > groupings;
std::set<CTxDestination> grouping;
for (const auto& walletEntry : mapWallet)
{
- const CWalletTx *pcoin = &walletEntry.second;
+ const CWalletTx& wtx = walletEntry.second;
- if (pcoin->tx->vin.size() > 0)
+ if (wtx.tx->vin.size() > 0)
{
bool any_mine = false;
// group all input addresses with each other
- for (const CTxIn& txin : pcoin->tx->vin)
+ for (const CTxIn& txin : wtx.tx->vin)
{
CTxDestination address;
if(!IsMine(txin)) /* If this input isn't mine, ignore it */
@@ -3536,7 +3613,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
// group change with input addresses
if (any_mine)
{
- for (const CTxOut& txout : pcoin->tx->vout)
+ for (const CTxOut& txout : wtx.tx->vout)
if (IsChange(txout))
{
CTxDestination txoutAddr;
@@ -3553,7 +3630,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
}
// group lone addrs by themselves
- for (const auto& txout : pcoin->tx->vout)
+ for (const auto& txout : wtx.tx->vout)
if (IsMine(txout))
{
CTxDestination address;
@@ -3617,6 +3694,10 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co
bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal)
{
+ if (!pwallet->CanGetAddresses(internal)) {
+ return false;
+ }
+
if (nIndex == -1)
{
CKeyPool keypool;
@@ -3672,38 +3753,27 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
}
}
-void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script)
-{
- std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this);
- CPubKey pubkey;
- if (!rKey->GetReservedKey(pubkey))
- return;
-
- script = rKey;
- script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
-}
-
void CWallet::LockCoin(const COutPoint& output)
{
- AssertLockHeld(cs_wallet); // setLockedCoins
+ AssertLockHeld(cs_wallet);
setLockedCoins.insert(output);
}
void CWallet::UnlockCoin(const COutPoint& output)
{
- AssertLockHeld(cs_wallet); // setLockedCoins
+ AssertLockHeld(cs_wallet);
setLockedCoins.erase(output);
}
void CWallet::UnlockAllCoins()
{
- AssertLockHeld(cs_wallet); // setLockedCoins
+ AssertLockHeld(cs_wallet);
setLockedCoins.clear();
}
bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const
{
- AssertLockHeld(cs_wallet); // setLockedCoins
+ AssertLockHeld(cs_wallet);
COutPoint outpt(hash, n);
return (setLockedCoins.count(outpt) > 0);
@@ -3711,7 +3781,7 @@ bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const
void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
{
- AssertLockHeld(cs_wallet); // setLockedCoins
+ AssertLockHeld(cs_wallet);
for (std::set<COutPoint>::iterator it = setLockedCoins.begin();
it != setLockedCoins.end(); it++) {
COutPoint outpt = (*it);
@@ -3721,8 +3791,8 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
/** @} */ // end of Actions
-void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CTxDestination, int64_t> &mapKeyBirth) const {
- AssertLockHeld(cs_wallet); // mapKeyMetadata
+void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CKeyID, int64_t>& mapKeyBirth) const {
+ AssertLockHeld(cs_wallet);
mapKeyBirth.clear();
// get birth times for keys with metadata
@@ -3867,7 +3937,6 @@ bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, st
std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
{
- LOCK(cs_wallet);
std::vector<std::string> values;
for (const auto& address : mapAddressBook) {
for (const auto& data : address.second.destdata) {
@@ -3939,7 +4008,7 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
if (salvage_wallet) {
// Recover readable keypairs:
- CWallet dummyWallet(chain, WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet dummyWallet(&chain, WalletLocation(), WalletDatabase::CreateDummy());
std::string backup_filename;
if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
return false;
@@ -3951,58 +4020,58 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, uint64_t wallet_creation_flags)
{
- const std::string& walletFile = location.GetName();
+ const std::string& walletFile = WalletDataFilePath(location.GetPath()).string();
// needed to restore wallet transaction meta data after -zapwallettxes
std::vector<CWalletTx> vWtx;
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
- uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
+ chain.initMessage(_("Zapping all transactions from wallet..."));
- std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(chain, location, WalletDatabase::Create(location.GetPath()));
+ std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(&chain, location, WalletDatabase::Create(location.GetPath()));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DBErrors::LOAD_OK) {
- InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ chain.initError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
return nullptr;
}
}
- uiInterface.InitMessage(_("Loading wallet..."));
+ chain.initMessage(_("Loading wallet..."));
int64_t nStart = GetTimeMillis();
bool fFirstRun = true;
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
- std::shared_ptr<CWallet> walletInstance(new CWallet(chain, location, WalletDatabase::Create(location.GetPath())), ReleaseWallet);
+ std::shared_ptr<CWallet> walletInstance(new CWallet(&chain, location, WalletDatabase::Create(location.GetPath())), ReleaseWallet);
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DBErrors::LOAD_OK)
{
if (nLoadWalletRet == DBErrors::CORRUPT) {
- InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ chain.initError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
return nullptr;
}
else if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR)
{
- InitWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data"
- " or address book entries might be missing or incorrect."),
+ chain.initWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data"
+ " or address book entries might be missing or incorrect."),
walletFile));
}
else if (nLoadWalletRet == DBErrors::TOO_NEW) {
- InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME)));
+ chain.initError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME)));
return nullptr;
}
else if (nLoadWalletRet == DBErrors::NEED_REWRITE)
{
- InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));
+ chain.initError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));
return nullptr;
}
else {
- InitError(strprintf(_("Error loading %s"), walletFile));
+ chain.initError(strprintf(_("Error loading %s"), walletFile));
return nullptr;
}
}
- int prev_version = walletInstance->nWalletVersion;
+ int prev_version = walletInstance->GetVersion();
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
{
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
@@ -4016,7 +4085,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
walletInstance->WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
if (nMaxVersion < walletInstance->GetVersion())
{
- InitError(_("Cannot downgrade wallet"));
+ chain.initError(_("Cannot downgrade wallet"));
return nullptr;
}
walletInstance->SetMaxVersion(nMaxVersion);
@@ -4027,9 +4096,9 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
LOCK(walletInstance->cs_wallet);
// Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
- int max_version = walletInstance->nWalletVersion;
- if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >=FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
- InitError(_("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified."));
+ int max_version = walletInstance->GetVersion();
+ if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >= FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
+ chain.initError(_("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified."));
return nullptr;
}
@@ -4057,7 +4126,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// Regenerate the keypool if upgraded to HD
if (hd_upgrade) {
if (!walletInstance->TopUpKeyPool()) {
- InitError(_("Unable to generate keys"));
+ chain.initError(_("Unable to generate keys"));
return nullptr;
}
}
@@ -4071,50 +4140,52 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if ((wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
//selective allow to set flags
walletInstance->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ } else if (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET) {
+ walletInstance->SetWalletFlag(WALLET_FLAG_BLANK_WALLET);
} else {
// generate a new seed
CPubKey seed = walletInstance->GenerateNewSeed();
walletInstance->SetHDSeed(seed);
- }
+ } // Otherwise, do not generate a new seed
// Top up the keypool
- if (!walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !walletInstance->TopUpKeyPool()) {
- InitError(_("Unable to generate initial keys"));
+ if (walletInstance->CanGenerateKeys() && !walletInstance->TopUpKeyPool()) {
+ chain.initError(_("Unable to generate initial keys"));
return nullptr;
}
- auto locked_chain = chain.assumeLocked(); // Temporary. Removed in upcoming lock cleanup
- walletInstance->ChainStateFlushed(locked_chain->getLocator());
+ auto locked_chain = chain.lock();
+ walletInstance->ChainStateFlushed(locked_chain->getTipLocator());
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
// Make it impossible to disable private keys after creation
- InitError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile));
+ chain.initError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile));
return NULL;
} else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
LOCK(walletInstance->cs_KeyStore);
if (!walletInstance->mapKeys.empty() || !walletInstance->mapCryptedKeys.empty()) {
- InitWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
+ chain.initWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
}
}
if (!gArgs.GetArg("-addresstype", "").empty() && !ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) {
- InitError(strprintf("Unknown address type '%s'", gArgs.GetArg("-addresstype", "")));
+ chain.initError(strprintf("Unknown address type '%s'", gArgs.GetArg("-addresstype", "")));
return nullptr;
}
if (!gArgs.GetArg("-changetype", "").empty() && !ParseOutputType(gArgs.GetArg("-changetype", ""), walletInstance->m_default_change_type)) {
- InitError(strprintf("Unknown change type '%s'", gArgs.GetArg("-changetype", "")));
+ chain.initError(strprintf("Unknown change type '%s'", gArgs.GetArg("-changetype", "")));
return nullptr;
}
if (gArgs.IsArgSet("-mintxfee")) {
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) {
- InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")));
+ chain.initError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")));
return nullptr;
}
if (n > HIGH_TX_FEE_PER_KB) {
- InitWarning(AmountHighWarn("-mintxfee") + " " +
- _("This is the minimum transaction fee you pay on every transaction."));
+ chain.initWarning(AmountHighWarn("-mintxfee") + " " +
+ _("This is the minimum transaction fee you pay on every transaction."));
}
walletInstance->m_min_fee = CFeeRate(n);
}
@@ -4123,12 +4194,12 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.IsArgSet("-fallbackfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) {
- InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", "")));
+ chain.initError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
- InitWarning(AmountHighWarn("-fallbackfee") + " " +
- _("This is the transaction fee you may pay when fee estimates are not available."));
+ chain.initWarning(AmountHighWarn("-fallbackfee") + " " +
+ _("This is the transaction fee you may pay when fee estimates are not available."));
}
walletInstance->m_fallback_fee = CFeeRate(nFeePerK);
walletInstance->m_allow_fallback_fee = nFeePerK != 0; //disable fallback fee in case value was set to 0, enable if non-null value
@@ -4136,32 +4207,55 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.IsArgSet("-discardfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) {
- InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", "")));
+ chain.initError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
- InitWarning(AmountHighWarn("-discardfee") + " " +
- _("This is the transaction fee you may discard if change is smaller than dust at this level"));
+ chain.initWarning(AmountHighWarn("-discardfee") + " " +
+ _("This is the transaction fee you may discard if change is smaller than dust at this level"));
}
walletInstance->m_discard_rate = CFeeRate(nFeePerK);
}
if (gArgs.IsArgSet("-paytxfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) {
- InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")));
+ chain.initError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
- InitWarning(AmountHighWarn("-paytxfee") + " " +
- _("This is the transaction fee you will pay if you send a transaction."));
+ chain.initWarning(AmountHighWarn("-paytxfee") + " " +
+ _("This is the transaction fee you will pay if you send a transaction."));
}
walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000);
- if (walletInstance->m_pay_tx_fee < ::minRelayTxFee) {
- InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
- gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString()));
+ if (walletInstance->m_pay_tx_fee < chain.relayMinFee()) {
+ chain.initError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
+ gArgs.GetArg("-paytxfee", ""), chain.relayMinFee().ToString()));
+ return nullptr;
+ }
+ }
+
+ if (gArgs.IsArgSet("-maxtxfee"))
+ {
+ CAmount nMaxFee = 0;
+ if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) {
+ chain.initError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")));
+ return nullptr;
+ }
+ if (nMaxFee > HIGH_MAX_TX_FEE) {
+ chain.initWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
+ }
+ if (CFeeRate(nMaxFee, 1000) < chain.relayMinFee()) {
+ chain.initError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
+ gArgs.GetArg("-maxtxfee", ""), chain.relayMinFee().ToString()));
return nullptr;
}
+ walletInstance->m_default_max_tx_fee = nMaxFee;
}
+
+ if (chain.relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB)
+ chain.initWarning(AmountHighWarn("-minrelaytxfee") + " " +
+ _("The wallet will avoid paying less than the minimum relay fee."));
+
walletInstance->m_confirm_target = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
walletInstance->m_spend_zero_conf_change = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
walletInstance->m_signal_rbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
@@ -4195,43 +4289,43 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (tip_height && *tip_height != rescan_height)
{
- //We can't rescan beyond non-pruned blocks, stop and throw an error
- //this might happen if a user uses an old wallet within a pruned node
- // or if he ran -disablewallet for a longer time, then decided to re-enable
- if (fPruneMode)
- {
+ // We can't rescan beyond non-pruned blocks, stop and throw an error.
+ // This might happen if a user uses an old wallet within a pruned node
+ // or if they ran -disablewallet for a longer time, then decided to re-enable
+ if (chain.havePruned()) {
+ // Exit early and print an error.
+ // If a block is pruned after this check, we will load the wallet,
+ // but fail the rescan with a generic error.
int block_height = *tip_height;
while (block_height > 0 && locked_chain->haveBlockOnDisk(block_height - 1) && rescan_height != block_height) {
--block_height;
}
if (rescan_height != block_height) {
- InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));
+ chain.initError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));
return nullptr;
}
}
- uiInterface.InitMessage(_("Rescanning..."));
+ chain.initMessage(_("Rescanning..."));
walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height);
// No need to read and scan block if block was created before
// our wallet birthday (as adjusted for block time variability)
if (walletInstance->nTimeFirstKey) {
- if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height)) {
+ if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height, nullptr)) {
rescan_height = *first_block;
}
}
- nStart = GetTimeMillis();
{
WalletRescanReserver reserver(walletInstance.get());
if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(locked_chain->getBlockHash(rescan_height), {} /* stop block */, reserver, true /* update */).status)) {
- InitError(_("Failed to rescan the wallet during initialization"));
+ chain.initError(_("Failed to rescan the wallet during initialization"));
return nullptr;
}
}
- walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nStart);
- walletInstance->ChainStateFlushed(locked_chain->getLocator());
+ walletInstance->ChainStateFlushed(locked_chain->getTipLocator());
walletInstance->database->IncrementUpdateCounter();
// Restore wallet transaction metadata after -zapwallettxes=1
@@ -4259,10 +4353,10 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
}
- uiInterface.LoadWallet(walletInstance);
+ chain.loadWallet(interfaces::MakeWallet(walletInstance));
- // Register with the validation interface. It's ok to do this after rescan since we're still holding cs_main.
- RegisterValidationInterface(walletInstance.get());
+ // Register with the validation interface. It's ok to do this after rescan since we're still holding locked_chain.
+ walletInstance->handleNotifications();
walletInstance->SetBroadcastTransactions(gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
@@ -4275,11 +4369,22 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
return walletInstance;
}
+void CWallet::handleNotifications()
+{
+ m_chain_notifications_handler = m_chain->handleNotifications(*this);
+}
+
void CWallet::postInitProcess()
{
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
+
// Add wallet transactions that aren't already in a block to mempool
// Do this here as mempool requires genesis block to be loaded
- ReacceptWalletTransactions();
+ ReacceptWalletTransactions(*locked_chain);
+
+ // Update wallet transactions with current mempool transactions.
+ chain().requestMempoolTransactions(*this);
}
bool CWallet::BackupWallet(const std::string& strDest)
@@ -4322,8 +4427,6 @@ int CMerkleTx::GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const
if (hashUnset())
return 0;
- AssertLockHeld(cs_main);
-
return locked_chain.getBlockDepth(hashBlock) * (nIndex == -1 ? -1 : 1);
}
@@ -4342,17 +4445,14 @@ bool CMerkleTx::IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const
return GetBlocksToMaturity(locked_chain) > 0;
}
-bool CWalletTx::AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, const CAmount& nAbsurdFee, CValidationState& state)
+bool CWalletTx::AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, CValidationState& state)
{
- LockAnnotation lock(::cs_main); // Temporary, for AcceptToMemoryPool below. Removed in upcoming commit.
-
// 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 this newly generated transaction's change is
// unavailable as we're not yet aware that it is in the mempool.
- bool ret = ::AcceptToMemoryPool(mempool, state, tx, nullptr /* pfMissingInputs */,
- nullptr /* plTxnReplaced */, false /* bypass_limits */, nAbsurdFee);
+ bool ret = locked_chain.submitToMemoryPool(tx, pwallet->m_default_max_tx_fee, state);
fInMempool |= ret;
return ret;
}
@@ -4383,7 +4483,7 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
CInputCoin input_coin = output.GetInputCoin();
size_t ancestors, descendants;
- mempool.GetTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
+ chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
if (!single_coin && ExtractDestination(output.tx->tx->vout[output.i].scriptPubKey, dst)) {
// Limit output groups to no more than 10 entries, to protect
// against inadvertently creating a too-large transaction
@@ -4414,18 +4514,21 @@ bool CWallet::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const
meta = it->second;
}
}
- if (!meta.hdKeypath.empty()) {
- if (!ParseHDKeypath(meta.hdKeypath, info.path)) return false;
- // Get the proper master key id
- CKey key;
- GetKey(meta.hd_seed_id, key);
- CExtKey masterKey;
- masterKey.SetSeed(key.begin(), key.size());
- // Compute identifier
- CKeyID masterid = masterKey.key.GetPubKey().GetID();
- std::copy(masterid.begin(), masterid.begin() + 4, info.fingerprint);
+ if (meta.has_key_origin) {
+ std::copy(meta.key_origin.fingerprint, meta.key_origin.fingerprint + 4, info.fingerprint);
+ info.path = meta.key_origin.path;
} else { // Single pubkeys get the master fingerprint of themselves
std::copy(keyID.begin(), keyID.begin() + 4, info.fingerprint);
}
return true;
}
+
+bool CWallet::AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info)
+{
+ LOCK(cs_wallet);
+ std::copy(info.fingerprint, info.fingerprint + 4, mapKeyMetadata[pubkey.GetID()].key_origin.fingerprint);
+ mapKeyMetadata[pubkey.GetID()].key_origin.path = info.path;
+ mapKeyMetadata[pubkey.GetID()].has_key_origin = true;
+ mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path);
+ return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true);
+}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 4776b0eacc..87aff09039 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -8,18 +8,19 @@
#include <amount.h>
#include <interfaces/chain.h>
+#include <interfaces/handler.h>
#include <outputtype.h>
#include <policy/feerate.h>
+#include <script/ismine.h>
+#include <script/sign.h>
#include <streams.h>
#include <tinyformat.h>
#include <ui_interface.h>
#include <util/strencodings.h>
-#include <validationinterface.h>
-#include <script/ismine.h>
-#include <script/sign.h>
#include <util/system.h>
-#include <wallet/crypter.h>
+#include <validationinterface.h>
#include <wallet/coinselection.h>
+#include <wallet/crypter.h>
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
@@ -34,26 +35,6 @@
#include <utility>
#include <vector>
-//! 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 (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
-bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
-
-//! Load wallet databases.
-bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
-
-//! 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 UnloadWallets();
-
//! Explicitly unload and delete the wallet.
//! Blocks the current thread after signaling the unload intent so that all
//! wallet clients release the wallet.
@@ -66,6 +47,7 @@ bool RemoveWallet(const std::shared_ptr<CWallet>& wallet);
bool HasWallets();
std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> GetWallet(const std::string& name);
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::string& warning);
//! Default for -keypool
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
@@ -91,6 +73,12 @@ 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;
+//! -maxtxfee default
+constexpr CAmount DEFAULT_TRANSACTION_MAXFEE{COIN / 10};
+//! Discourage users to set fees higher than this amount (in satoshis) per kB
+constexpr CAmount HIGH_TX_FEE_PER_KB{COIN / 100};
+//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
+constexpr CAmount HIGH_MAX_TX_FEE{100 * HIGH_TX_FEE_PER_KB};
//! Pre-calculated constants for input size estimation in *virtual size*
static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91;
@@ -99,8 +87,6 @@ class CCoinControl;
class COutput;
class CReserveKey;
class CScript;
-class CTxMemPool;
-class CBlockPolicyEstimator;
class CWalletTx;
struct FeeCalculation;
enum class FeeEstimateMode;
@@ -134,20 +120,82 @@ enum WalletFlags : uint64_t {
// wallet flags in the upper section (> 1 << 31) will lead to not opening the wallet if flag is unknown
// unknown wallet flags in the lower section <= (1 << 31) will be tolerated
+ // Indicates that the metadata has already been upgraded to contain key origins
+ WALLET_FLAG_KEY_ORIGIN_METADATA = (1ULL << 1),
+
// will enforce the rule that the wallet can't contain any private keys (only watch-only/pubkeys)
WALLET_FLAG_DISABLE_PRIVATE_KEYS = (1ULL << 32),
-};
-static constexpr uint64_t g_known_wallet_flags = WALLET_FLAG_DISABLE_PRIVATE_KEYS;
+ //! Flag set when a wallet contains no HD seed and no private keys, scripts,
+ //! addresses, and other watch only things, and is therefore "blank."
+ //!
+ //! The only function this flag serves is to distinguish a blank wallet from
+ //! a newly created wallet when the wallet database is loaded, to avoid
+ //! initialization that should only happen on first run.
+ //!
+ //! This flag is also a mandatory flag to prevent previous versions of
+ //! bitcoin from opening the wallet, thinking it was newly created, and
+ //! then improperly reinitializing it.
+ WALLET_FLAG_BLANK_WALLET = (1ULL << 33),
+};
-/** A key pool entry */
+static constexpr uint64_t g_known_wallet_flags = WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET | WALLET_FLAG_KEY_ORIGIN_METADATA;
+
+/** A key from a CWallet's keypool
+ *
+ * The wallet holds one (for pre HD-split wallets) or several keypools. These
+ * are sets of keys that have not yet been used to provide addresses or receive
+ * change.
+ *
+ * The Bitcoin Core wallet was originally a collection of unrelated private
+ * keys with their associated addresses. If a non-HD wallet generated a
+ * key/address, gave that address out and then restored a backup from before
+ * that key's generation, then any funds sent to that address would be
+ * lost definitively.
+ *
+ * The keypool was implemented to avoid this scenario (commit: 10384941). The
+ * wallet would generate a set of keys (100 by default). When a new public key
+ * was required, either to give out as an address or to use in a change output,
+ * it would be drawn from the keypool. The keypool would then be topped up to
+ * maintain 100 keys. This ensured that as long as the wallet hadn't used more
+ * than 100 keys since the previous backup, all funds would be safe, since a
+ * restored wallet would be able to scan for all owned addresses.
+ *
+ * A keypool also allowed encrypted wallets to give out addresses without
+ * having to be decrypted to generate a new private key.
+ *
+ * With the introduction of HD wallets (commit: f1902510), the keypool
+ * essentially became an address look-ahead pool. Restoring old backups can no
+ * longer definitively lose funds as long as the addresses used were from the
+ * wallet's HD seed (since all private keys can be rederived from the seed).
+ * However, if many addresses were used since the backup, then the wallet may
+ * not know how far ahead in the HD chain to look for its addresses. The
+ * keypool is used to implement a 'gap limit'. The keypool maintains a set of
+ * keys (by default 1000) ahead of the last used key and scans for the
+ * addresses of those keys. This avoids the risk of not seeing transactions
+ * involving the wallet's addresses, or of re-using the same address.
+ *
+ * The HD-split wallet feature added a second keypool (commit: 02592f4c). There
+ * is an external keypool (for addresses to hand out) and an internal keypool
+ * (for change addresses).
+ *
+ * Keypool keys are stored in the wallet/keystore's keymap. The keypool data is
+ * stored as sets of indexes in the wallet (setInternalKeyPool,
+ * setExternalKeyPool and set_pre_split_keypool), and a map from the key to the
+ * index (m_pool_key_to_index). The CKeyPool object is used to
+ * serialize/deserialize the pool data to/from the database.
+ */
class CKeyPool
{
public:
+ //! The time at which the key was generated. Set in AddKeypoolPubKeyWithDB
int64_t nTime;
+ //! The public key
CPubKey vchPubKey;
- bool fInternal; // for change outputs
- bool m_pre_split; // For keys generated before keypool split upgrade
+ //! Whether this keypool entry is in the internal keypool (for change outputs)
+ bool fInternal;
+ //! Whether this key was generated for a keypool before the wallet was upgraded to HD-split
+ bool m_pre_split;
CKeyPool();
CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
@@ -186,6 +234,57 @@ public:
}
};
+/** A wrapper to reserve a key from a wallet keypool
+ *
+ * CReserveKey is used to reserve a key from the keypool. It is passed around
+ * during the CreateTransaction/CommitTransaction procedure.
+ *
+ * Instantiating a CReserveKey does not reserve a keypool key. To do so,
+ * GetReservedKey() needs to be called on the object. Once a key has been
+ * reserved, call KeepKey() on the CReserveKey object to make sure it is not
+ * returned to the keypool. Call ReturnKey() to return the key to the keypool
+ * so it can be re-used (for example, if the key was used in a new transaction
+ * and that transaction was not completed and needed to be aborted).
+ *
+ * If a key is reserved and KeepKey() is not called, then the key will be
+ * returned to the keypool when the CReserveObject goes out of scope.
+ */
+class CReserveKey
+{
+protected:
+ //! The wallet to reserve the keypool key from
+ CWallet* pwallet;
+ //! The index of the key in the keypool
+ int64_t nIndex{-1};
+ //! The public key
+ CPubKey vchPubKey;
+ //! Whether this is from the internal (change output) keypool
+ bool fInternal{false};
+
+public:
+ //! Construct a CReserveKey object. This does NOT reserve a key from the keypool yet
+ explicit CReserveKey(CWallet* pwalletIn)
+ {
+ pwallet = pwalletIn;
+ }
+
+ CReserveKey(const CReserveKey&) = delete;
+ CReserveKey& operator=(const CReserveKey&) = delete;
+
+ //! Destructor. If a key has been reserved and not KeepKey'ed, it will be returned to the keypool
+ ~CReserveKey()
+ {
+ ReturnKey();
+ }
+
+ //! Reserve a key from the keypool
+ bool GetReservedKey(CPubKey &pubkey, bool internal = false);
+ //! Return a key to the keypool
+ void ReturnKey();
+ //! Keep the key. Do not return it to the keypool when this object goes out of scope
+ void KeepKey();
+};
+
/** Address book data */
class CAddressBookData
{
@@ -374,24 +473,11 @@ public:
std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
// memory only
- mutable bool fDebitCached;
- mutable bool fCreditCached;
- mutable bool fImmatureCreditCached;
- mutable bool fAvailableCreditCached;
- mutable bool fWatchDebitCached;
- mutable bool fWatchCreditCached;
- mutable bool fImmatureWatchCreditCached;
- mutable bool fAvailableWatchCreditCached;
+ enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
+ CAmount GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate = false) const;
+ mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
mutable bool fChangeCached;
mutable bool fInMempool;
- mutable CAmount nDebitCached;
- mutable CAmount nCreditCached;
- mutable CAmount nImmatureCreditCached;
- mutable CAmount nAvailableCreditCached;
- mutable CAmount nWatchDebitCached;
- mutable CAmount nWatchCreditCached;
- mutable CAmount nImmatureWatchCreditCached;
- mutable CAmount nAvailableWatchCreditCached;
mutable CAmount nChangeCached;
CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg))
@@ -408,24 +494,8 @@ public:
nTimeReceived = 0;
nTimeSmart = 0;
fFromMe = false;
- fDebitCached = false;
- fCreditCached = false;
- fImmatureCreditCached = false;
- fAvailableCreditCached = false;
- fWatchDebitCached = false;
- fWatchCreditCached = false;
- fImmatureWatchCreditCached = false;
- fAvailableWatchCreditCached = false;
fChangeCached = false;
fInMempool = false;
- nDebitCached = 0;
- nCreditCached = 0;
- nImmatureCreditCached = 0;
- nAvailableCreditCached = 0;
- nWatchDebitCached = 0;
- nWatchCreditCached = 0;
- nAvailableWatchCreditCached = 0;
- nImmatureWatchCreditCached = 0;
nChangeCached = 0;
nOrderPos = -1;
}
@@ -469,14 +539,10 @@ public:
//! make sure balances are recalculated
void MarkDirty()
{
- fCreditCached = false;
- fAvailableCreditCached = false;
- fImmatureCreditCached = false;
- fWatchDebitCached = false;
- fWatchCreditCached = false;
- fAvailableWatchCreditCached = false;
- fImmatureWatchCreditCached = false;
- fDebitCached = false;
+ m_amounts[DEBIT].Reset();
+ m_amounts[CREDIT].Reset();
+ m_amounts[IMMATURE_CREDIT].Reset();
+ m_amounts[AVAILABLE_CREDIT].Reset();
fChangeCached = false;
}
@@ -491,7 +557,7 @@ public:
CAmount GetCredit(interfaces::Chain::Lock& locked_chain, const isminefilter& filter) const;
CAmount GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache=true) const;
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(cs_main, pwallet->cs_wallet)". The
+ // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The
// annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
// having to resolve the issue of member access into incomplete type CWallet.
CAmount GetAvailableCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS;
@@ -520,11 +586,11 @@ public:
int64_t GetTxTime() const;
- // RelayWalletTransaction may only be called if fBroadcastTransactions!
- bool RelayWalletTransaction(interfaces::Chain::Lock& locked_chain, CConnman* connman);
+ // Pass this transaction to the node to relay to its peers
+ bool RelayWalletTransaction(interfaces::Chain::Lock& locked_chain);
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
- bool AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, const CAmount& nAbsurdFee, CValidationState& state);
+ bool AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, CValidationState& state);
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
@@ -623,18 +689,20 @@ class WalletRescanReserver; //forward declarations for ScanForWalletTransactions
* A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
* and provides the ability to create new transactions.
*/
-class CWallet final : public CCryptoKeyStore, public CValidationInterface
+class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notifications
{
private:
std::atomic<bool> fAbortRescan{false};
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
+ std::atomic<int64_t> m_scanning_start{0};
+ std::atomic<double> m_scanning_progress{0};
std::mutex mutexScanning;
friend class WalletRescanReserver;
WalletBatch *encrypted_batch GUARDED_BY(cs_wallet) = nullptr;
//! the current wallet version: clients below this version are not able to load the wallet
- int nWalletVersion = FEATURE_BASE;
+ int nWalletVersion GUARDED_BY(cs_wallet){FEATURE_BASE};
//! the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded
int nWalletMaxVersion GUARDED_BY(cs_wallet) = FEATURE_BASE;
@@ -642,6 +710,8 @@ private:
int64_t nNextResend = 0;
int64_t nLastResend = 0;
bool fBroadcastTransactions = false;
+ // Local time that the tip block was received. Used to schedule wallet rebroadcasts.
+ std::atomic<int64_t> m_best_block_time {0};
/**
* Used to keep track of spent outpoints, and
@@ -684,11 +754,11 @@ private:
CHDChain hdChain;
/* HD derive new child key (on internal or external chain) */
- void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secret, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- std::set<int64_t> setInternalKeyPool;
+ std::set<int64_t> setInternalKeyPool GUARDED_BY(cs_wallet);
std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_wallet);
- std::set<int64_t> set_pre_split_keypool;
+ std::set<int64_t> set_pre_split_keypool GUARDED_BY(cs_wallet);
int64_t m_max_keypool_index GUARDED_BY(cs_wallet) = 0;
std::map<CKeyID, int64_t> m_pool_key_to_index;
std::atomic<uint64_t> m_wallet_flags{0};
@@ -705,9 +775,29 @@ private:
* nTimeFirstKey more intelligently for more efficient rescans.
*/
bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
+ /** Add a KeyOriginInfo to the wallet */
+ bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info);
+
+ //! Adds a key to the store, and saves it to disk.
+ bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
+ //! Adds a watch-only address to the store, and saves it to disk.
+ bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
+ void AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch);
+
+ bool SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose);
+
+ //! Adds a script to the store and saves it to disk
+ bool AddCScriptWithDB(WalletBatch& batch, const CScript& script);
+
+ //! Unsets a wallet flag and saves it to disk
+ void UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag);
/** Interface for accessing chain state. */
- interfaces::Chain& m_chain;
+ interfaces::Chain* m_chain;
/** Wallet location which includes wallet name (see WalletLocation). */
WalletLocation m_location;
@@ -723,7 +813,7 @@ private:
* to have seen all transactions in the chain, but is only used to track
* live BlockConnected callbacks.
*/
- uint256 m_last_block_processed;
+ uint256 m_last_block_processed GUARDED_BY(cs_wallet);
public:
/*
@@ -768,7 +858,10 @@ public:
unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */
- CWallet(interfaces::Chain& chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database) : m_chain(chain), m_location(location), database(std::move(database))
+ CWallet(interfaces::Chain* chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database)
+ : m_chain(chain),
+ m_location(location),
+ database(std::move(database))
{
}
@@ -788,12 +881,18 @@ public:
int64_t nOrderPosNext GUARDED_BY(cs_wallet) = 0;
uint64_t nAccountingEntryNumber = 0;
- std::map<CTxDestination, CAddressBookData> mapAddressBook;
+ std::map<CTxDestination, CAddressBookData> mapAddressBook GUARDED_BY(cs_wallet);
std::set<COutPoint> setLockedCoins GUARDED_BY(cs_wallet);
+ /** Registered interfaces::Chain::Notifications handler. */
+ std::unique_ptr<interfaces::Handler> m_chain_notifications_handler;
+
+ /** Register the wallet for chain notifications */
+ void handleNotifications();
+
/** Interface for accessing chain state. */
- interfaces::Chain& chain() const { return m_chain; }
+ interfaces::Chain& chain() const { assert(m_chain); return *m_chain; }
const CWalletTx* GetWalletTx(const uint256& hash) const;
@@ -839,6 +938,8 @@ public:
void AbortRescan() { fAbortRescan = true; }
bool IsAbortingRescan() { return fAbortRescan; }
bool IsScanning() { return fScanningWallet; }
+ int64_t ScanningDuration() const { return fScanningWallet ? GetTimeMillis() - m_scanning_start : 0; }
+ double ScanningProgress() const { return fScanningWallet ? (double) m_scanning_progress : 0; }
/**
* keystore implementation
@@ -847,12 +948,13 @@ public:
CPubKey GenerateNewKey(WalletBatch& batch, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! 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)
void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ //! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo
+ void UpgradeKeyMetadata() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; }
void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -865,15 +967,15 @@ public:
bool LoadCScript(const CScript& redeemScript);
//! Adds a destination data tuple to the store, and saves it to disk
- bool AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value);
+ bool AddDestData(const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Erases a destination data tuple in the store and on disk
- bool EraseDestData(const CTxDestination &dest, const std::string &key);
+ bool EraseDestData(const CTxDestination& dest, const std::string& key) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a destination data tuple to the store, without saving it to disk
- void LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value);
+ void LoadDestData(const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Look up a destination data tuple in the store, return true if found false otherwise
- bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
+ bool GetDestData(const CTxDestination& dest, const std::string& key, std::string* value) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Get all destination values matching a prefix.
- std::vector<std::string> GetDestValues(const std::string& prefix) const;
+ std::vector<std::string> GetDestValues(const std::string& prefix) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -888,7 +990,7 @@ public:
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
- void GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CTxDestination, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CKeyID, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
unsigned int ComputeTimeSmart(const CWalletTx& wtx) const;
/**
@@ -902,8 +1004,9 @@ public:
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
void LoadToWallet(const CWalletTx& wtxIn) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void TransactionAddedToMempool(const CTransactionRef& tx) override;
- void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
- void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
+ void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted) override;
+ void BlockDisconnected(const CBlock& block) override;
+ void UpdatedBlockTip() override;
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
struct ScanResult {
@@ -912,27 +1015,28 @@ public:
//! Hash and height of most recent block that was successfully scanned.
//! Unset if no blocks were scanned due to read errors or the chain
//! being empty.
- uint256 stop_block;
- Optional<int> stop_height;
+ uint256 last_scanned_block;
+ Optional<int> last_scanned_height;
//! Height of the most recent block that could not be scanned due to
//! read errors or pruning. Will be set if status is FAILURE, unset if
//! status is SUCCESS, and may or may not be set if status is
//! USER_ABORT.
- uint256 failed_block;
+ uint256 last_failed_block;
};
ScanResult ScanForWalletTransactions(const uint256& first_block, const uint256& last_block, const WalletRescanReserver& reserver, bool fUpdate);
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
- void ReacceptWalletTransactions();
- void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- // ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
- std::vector<uint256> ResendWalletTransactionsBefore(interfaces::Chain::Lock& locked_chain, int64_t nTime, CConnman* connman);
- CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
- CAmount GetUnconfirmedBalance() const;
- CAmount GetImmatureBalance() const;
- CAmount GetUnconfirmedWatchOnlyBalance() const;
- CAmount GetImmatureWatchOnlyBalance() const;
- CAmount GetLegacyBalance(const isminefilter& filter, int minDepth) const;
+ void ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void ResendWalletTransactions();
+ struct Balance {
+ CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
+ CAmount m_mine_untrusted_pending{0}; //!< Untrusted, but in mempool (pending)
+ CAmount m_mine_immature{0}; //!< Immature coinbases in the main chain
+ CAmount m_watchonly_trusted{0};
+ CAmount m_watchonly_untrusted_pending{0};
+ CAmount m_watchonly_immature{0};
+ };
+ Balance GetBalance(int min_depth = 0) const;
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend);
@@ -951,7 +1055,7 @@ public:
*/
bool CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
- bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
+ bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CValidationState& state);
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
{
@@ -962,6 +1066,11 @@ public:
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig = false) const;
bool DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig = false) const;
+ bool ImportScripts(const std::set<CScript> scripts) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET};
bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE};
@@ -977,6 +1086,8 @@ public:
CFeeRate m_discard_rate{DEFAULT_DISCARD_FEE};
OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE};
OutputType m_default_change_type{DEFAULT_CHANGE_TYPE};
+ /** Absolute maximum transaction fee (in satoshis) used by default for the wallet */
+ CAmount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE};
bool NewKeyPool();
size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -1041,13 +1152,11 @@ public:
bool DelAddressBook(const CTxDestination& address);
- const std::string& GetLabelName(const CScript& scriptPubKey) const;
-
- void GetScriptForMining(std::shared_ptr<CReserveScript> &script);
+ const std::string& GetLabelName(const CScript& scriptPubKey) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
unsigned int GetKeyPoolSize() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
{
- AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool
+ AssertLockHeld(cs_wallet);
return setInternalKeyPool.size() + setExternalKeyPool.size();
}
@@ -1132,6 +1241,12 @@ public:
/* Returns true if HD is enabled */
bool IsHDEnabled() const;
+ /* Returns true if the wallet can generate new keys */
+ bool CanGenerateKeys();
+
+ /* Returns true if the wallet can give out new addresses. This means it has keys in the keypool or can generate new keys */
+ bool CanGetAddresses(bool internal = false);
+
/* Generates a new HD seed (will not be activated) */
CPubKey GenerateNewSeed();
@@ -1169,6 +1284,9 @@ public:
/** set a single wallet flag */
void SetWalletFlag(uint64_t flags);
+ /** Unsets a single wallet flag */
+ void UnsetWalletFlag(uint64_t flag);
+
/** check if a certain wallet flag is set */
bool IsWalletFlagSet(uint64_t flag);
@@ -1192,34 +1310,11 @@ public:
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
};
-/** A key allocated from the key pool. */
-class CReserveKey final : public CReserveScript
-{
-protected:
- CWallet* pwallet;
- int64_t nIndex{-1};
- CPubKey vchPubKey;
- bool fInternal{false};
-
-public:
- explicit CReserveKey(CWallet* pwalletIn)
- {
- pwallet = pwalletIn;
- }
-
- CReserveKey(const CReserveKey&) = delete;
- CReserveKey& operator=(const CReserveKey&) = delete;
-
- ~CReserveKey()
- {
- ReturnKey();
- }
-
- void ReturnKey();
- bool GetReservedKey(CPubKey &pubkey, bool internal = false);
- void KeepKey();
- void KeepScript() override { KeepKey(); }
-};
+/**
+ * Called periodically by the schedule thread. Prompts individual wallets to resend
+ * their transactions. Actual rebroadcast schedule is managed by the wallets themselves.
+ */
+void MaybeResendWalletTxs();
/** RAII object to check and reserve a wallet rescan */
class WalletRescanReserver
@@ -1237,6 +1332,8 @@ public:
if (m_wallet->fScanningWallet) {
return false;
}
+ m_wallet->m_scanning_start = GetTimeMillis();
+ m_wallet->m_scanning_progress = 0;
m_wallet->fScanningWallet = true;
m_could_reserve = true;
return true;
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 6e037808e3..ece97e2a75 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -1,11 +1,11 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 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/walletdb.h>
-#include <consensus/tx_verify.h>
+#include <consensus/tx_check.h>
#include <consensus/validation.h>
#include <fs.h>
#include <key_io.h>
@@ -57,9 +57,14 @@ bool WalletBatch::EraseTx(uint256 hash)
return EraseIC(std::make_pair(std::string("tx"), hash));
}
+bool WalletBatch::WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, const bool overwrite)
+{
+ return WriteIC(std::make_pair(std::string("keymeta"), pubkey), meta, overwrite);
+}
+
bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
{
- if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta, false)) {
+ if (!WriteKeyMetadata(keyMeta, vchPubKey, false)) {
return false;
}
@@ -76,7 +81,7 @@ bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
const std::vector<unsigned char>& vchCryptedSecret,
const CKeyMetadata &keyMeta)
{
- if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) {
+ if (!WriteKeyMetadata(keyMeta, vchPubKey, true)) {
return false;
}
@@ -417,8 +422,15 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
strType != "minversion" && strType != "acentry") {
wss.m_unknown_records++;
}
- } catch (...)
- {
+ } catch (const std::exception& e) {
+ if (strErr.empty()) {
+ strErr = e.what();
+ }
+ return false;
+ } catch (...) {
+ if (strErr.empty()) {
+ strErr = "Caught unknown exception in ReadKeyValue";
+ }
return false;
}
return true;
@@ -529,6 +541,14 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
if (wss.fAnyUnordered)
result = pwallet->ReorderTransactions();
+ // Upgrade all of the wallet keymetadata to have the hd master key id
+ // This operation is not atomic, but if it fails, updated entries are still backwards compatible with older software
+ try {
+ pwallet->UpgradeKeyMetadata();
+ } catch (...) {
+ result = DBErrors::CORRUPT;
+ }
+
return result;
}
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 5584407a56..d4a3bba97a 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -8,6 +8,7 @@
#include <amount.h>
#include <primitives/transaction.h>
+#include <script/sign.h>
#include <wallet/db.h>
#include <key.h>
@@ -93,11 +94,14 @@ class CKeyMetadata
public:
static const int VERSION_BASIC=1;
static const int VERSION_WITH_HDDATA=10;
- static const int CURRENT_VERSION=VERSION_WITH_HDDATA;
+ static const int VERSION_WITH_KEY_ORIGIN = 12;
+ static const int CURRENT_VERSION=VERSION_WITH_KEY_ORIGIN;
int nVersion;
int64_t nCreateTime; // 0 means unknown
- std::string hdKeypath; //optional HD/bip32 keypath
+ std::string hdKeypath; //optional HD/bip32 keypath. Still used to determine whether a key is a seed. Also kept for backwards compatibility
CKeyID hd_seed_id; //id of the HD seed used to derive this key
+ KeyOriginInfo key_origin; // Key origin info with path and fingerprint
+ bool has_key_origin = false; //< Whether the key_origin is useful
CKeyMetadata()
{
@@ -120,6 +124,11 @@ public:
READWRITE(hdKeypath);
READWRITE(hd_seed_id);
}
+ if (this->nVersion >= VERSION_WITH_KEY_ORIGIN)
+ {
+ READWRITE(key_origin);
+ READWRITE(has_key_origin);
+ }
}
void SetNull()
@@ -128,13 +137,17 @@ public:
nCreateTime = 0;
hdKeypath.clear();
hd_seed_id.SetNull();
+ key_origin.clear();
+ has_key_origin = false;
}
};
/** Access to the wallet database.
- * This represents a single transaction at the
- * database. It will be committed when the object goes out of scope.
- * Optionally (on by default) it will flush to disk as well.
+ * Opens the database and provides read and write access to it. Each read and write is its own transaction.
+ * Multiple operation transactions can be started using TxnBegin() and committed using TxnCommit()
+ * Otherwise the transaction will be committed when the object goes out of scope.
+ * Optionally (on by default) it will flush to disk on close.
+ * Every 1000 writes will automatically trigger a flush to disk.
*/
class WalletBatch
{
@@ -146,6 +159,9 @@ private:
return false;
}
m_database.IncrementUpdateCounter();
+ if (m_database.nUpdateCounter % 1000 == 0) {
+ m_batch.Flush();
+ }
return true;
}
@@ -156,6 +172,9 @@ private:
return false;
}
m_database.IncrementUpdateCounter();
+ if (m_database.nUpdateCounter % 1000 == 0) {
+ m_batch.Flush();
+ }
return true;
}
@@ -177,6 +196,7 @@ public:
bool WriteTx(const CWalletTx& wtx);
bool EraseTx(uint256 hash);
+ bool WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, const bool overwrite);
bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata &keyMeta);
bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector<unsigned char>& vchCryptedSecret, const CKeyMetadata &keyMeta);
bool WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey);
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 30b0c48eef..0843194511 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -1,10 +1,8 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-2019 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 <base58.h>
#include <fs.h>
-#include <interfaces/chain.h>
#include <util/system.h>
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
@@ -17,23 +15,22 @@ namespace WalletTool {
static void WalletToolReleaseWallet(CWallet* wallet)
{
wallet->WalletLogPrintf("Releasing wallet\n");
- wallet->Flush();
+ wallet->Flush(true);
delete wallet;
}
static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path)
{
if (fs::exists(path)) {
- fprintf(stderr, "Error: File exists already\n");
+ tfm::format(std::cerr, "Error: File exists already\n");
return nullptr;
}
// dummy chain interface
- auto chain = interfaces::MakeChain();
- std::shared_ptr<CWallet> wallet_instance(new CWallet(*chain, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet);
+ std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet);
bool first_run = true;
DBErrors load_wallet_ret = wallet_instance->LoadWallet(first_run);
if (load_wallet_ret != DBErrors::LOAD_OK) {
- fprintf(stderr, "Error creating %s", name.c_str());
+ tfm::format(std::cerr, "Error creating %s", name.c_str());
return nullptr;
}
@@ -43,7 +40,7 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
CPubKey seed = wallet_instance->GenerateNewSeed();
wallet_instance->SetHDSeed(seed);
- fprintf(stdout, "Topping up keypool...\n");
+ tfm::format(std::cout, "Topping up keypool...\n");
wallet_instance->TopUpKeyPool();
return wallet_instance;
}
@@ -51,40 +48,39 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path)
{
if (!fs::exists(path)) {
- fprintf(stderr, "Error: Wallet files does not exist\n");
+ tfm::format(std::cerr, "Error: Wallet files does not exist\n");
return nullptr;
}
// dummy chain interface
- auto chain = interfaces::MakeChain();
- std::shared_ptr<CWallet> wallet_instance(new CWallet(*chain, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet);
+ std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet);
DBErrors load_wallet_ret;
try {
bool first_run;
load_wallet_ret = wallet_instance->LoadWallet(first_run);
- } catch (const std::runtime_error) {
- fprintf(stderr, "Error loading %s. Is wallet being used by another process?\n", name.c_str());
+ } catch (const std::runtime_error&) {
+ tfm::format(std::cerr, "Error loading %s. Is wallet being used by another process?\n", name.c_str());
return nullptr;
}
if (load_wallet_ret != DBErrors::LOAD_OK) {
wallet_instance = nullptr;
if (load_wallet_ret == DBErrors::CORRUPT) {
- fprintf(stderr, "Error loading %s: Wallet corrupted", name.c_str());
+ tfm::format(std::cerr, "Error loading %s: Wallet corrupted", name.c_str());
return nullptr;
} else if (load_wallet_ret == DBErrors::NONCRITICAL_ERROR) {
- fprintf(stderr, "Error reading %s! All keys read correctly, but transaction data"
+ tfm::format(std::cerr, "Error reading %s! All keys read correctly, but transaction data"
" or address book entries might be missing or incorrect.",
name.c_str());
} else if (load_wallet_ret == DBErrors::TOO_NEW) {
- fprintf(stderr, "Error loading %s: Wallet requires newer version of %s",
+ tfm::format(std::cerr, "Error loading %s: Wallet requires newer version of %s",
name.c_str(), PACKAGE_NAME);
return nullptr;
} else if (load_wallet_ret == DBErrors::NEED_REWRITE) {
- fprintf(stderr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME);
+ tfm::format(std::cerr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME);
return nullptr;
} else {
- fprintf(stderr, "Error loading %s", name.c_str());
+ tfm::format(std::cerr, "Error loading %s", name.c_str());
return nullptr;
}
}
@@ -94,15 +90,14 @@ static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::pa
static void WalletShowInfo(CWallet* wallet_instance)
{
- // lock required because of some AssertLockHeld()
LOCK(wallet_instance->cs_wallet);
- fprintf(stdout, "Wallet info\n===========\n");
- fprintf(stdout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no");
- fprintf(stdout, "HD (hd seed available): %s\n", wallet_instance->GetHDChain().seed_id.IsNull() ? "no" : "yes");
- fprintf(stdout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());
- fprintf(stdout, "Transactions: %zu\n", wallet_instance->mapWallet.size());
- fprintf(stdout, "Address Book: %zu\n", wallet_instance->mapAddressBook.size());
+ tfm::format(std::cout, "Wallet info\n===========\n");
+ tfm::format(std::cout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no");
+ tfm::format(std::cout, "HD (hd seed available): %s\n", wallet_instance->GetHDChain().seed_id.IsNull() ? "no" : "yes");
+ tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());
+ tfm::format(std::cout, "Transactions: %zu\n", wallet_instance->mapWallet.size());
+ tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->mapAddressBook.size());
}
bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
@@ -113,24 +108,24 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
std::shared_ptr<CWallet> wallet_instance = CreateWallet(name, path);
if (wallet_instance) {
WalletShowInfo(wallet_instance.get());
- wallet_instance->Flush();
+ wallet_instance->Flush(true);
}
} else if (command == "info") {
if (!fs::exists(path)) {
- fprintf(stderr, "Error: no wallet file at %s\n", name.c_str());
+ tfm::format(std::cerr, "Error: no wallet file at %s\n", name.c_str());
return false;
}
std::string error;
if (!WalletBatch::VerifyEnvironment(path, error)) {
- fprintf(stderr, "Error loading %s. Is wallet being used by other process?\n", name.c_str());
+ tfm::format(std::cerr, "Error loading %s. Is wallet being used by other process?\n", name.c_str());
return false;
}
std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
if (!wallet_instance) return false;
WalletShowInfo(wallet_instance.get());
- wallet_instance->Flush();
+ wallet_instance->Flush(true);
} else {
- fprintf(stderr, "Invalid command: %s\n", command.c_str());
+ tfm::format(std::cerr, "Invalid command: %s\n", command.c_str());
return false;
}
diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h
index 5b06fd1792..da848a747b 100644
--- a/src/wallet/wallettool.h
+++ b/src/wallet/wallettool.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
index 6db4c63acb..04c2407a89 100644
--- a/src/wallet/walletutil.cpp
+++ b/src/wallet/walletutil.cpp
@@ -1,9 +1,10 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 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>
+#include <logging.h>
#include <util/system.h>
fs::path GetWalletDir()
@@ -30,12 +31,16 @@ fs::path GetWalletDir()
static bool IsBerkeleyBtree(const fs::path& path)
{
+ if (!fs::exists(path)) return false;
+
// A Berkeley DB Btree file has at least 4K.
// This check also prevents opening lock files.
boost::system::error_code ec;
- if (fs::file_size(path, ec) < 4096) return false;
+ auto size = fs::file_size(path, ec);
+ if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string());
+ if (size < 4096) return false;
- fs::ifstream file(path.string(), std::ios::binary);
+ fsbridge::ifstream file(path, std::ios::binary);
if (!file.is_open()) return false;
file.seekg(12, std::ios::beg); // Magic bytes start at offset 12
@@ -54,8 +59,14 @@ std::vector<fs::path> ListWalletDir()
const fs::path wallet_dir = GetWalletDir();
const size_t offset = wallet_dir.string().size() + 1;
std::vector<fs::path> paths;
+ boost::system::error_code ec;
+
+ for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) {
+ if (ec) {
+ LogPrintf("%s: %s %s\n", __func__, ec.message(), it->path().string());
+ continue;
+ }
- for (auto it = fs::recursive_directory_iterator(wallet_dir); it != fs::recursive_directory_iterator(); ++it) {
// Get wallet path relative to walletdir by removing walletdir from the wallet path.
// This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60.
const fs::path path = it->path().string().substr(offset);
diff --git a/src/warnings.cpp b/src/warnings.cpp
index 1c6ba13f60..5542412a7f 100644
--- a/src/warnings.cpp
+++ b/src/warnings.cpp
@@ -4,14 +4,13 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <sync.h>
-#include <clientversion.h>
#include <util/system.h>
#include <warnings.h>
-CCriticalSection cs_warnings;
-std::string strMiscWarning GUARDED_BY(cs_warnings);
-bool fLargeWorkForkFound GUARDED_BY(cs_warnings) = false;
-bool fLargeWorkInvalidChainFound GUARDED_BY(cs_warnings) = false;
+static RecursiveMutex cs_warnings;
+static std::string strMiscWarning GUARDED_BY(cs_warnings);
+static bool fLargeWorkForkFound GUARDED_BY(cs_warnings) = false;
+static bool fLargeWorkInvalidChainFound GUARDED_BY(cs_warnings) = false;
void SetMiscWarning(const std::string& strWarning)
{
diff --git a/src/zmq/zmqabstractnotifier.cpp b/src/zmq/zmqabstractnotifier.cpp
index 6a9661e3e8..a5f3be8f5b 100644
--- a/src/zmq/zmqabstractnotifier.cpp
+++ b/src/zmq/zmqabstractnotifier.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <zmq/zmqabstractnotifier.h>
-#include <util/system.h>
const int CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM;
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index 6826cf62d6..de59b71b8f 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -7,7 +7,6 @@
#include <version.h>
#include <validation.h>
-#include <streams.h>
#include <util/system.h>
void zmqError(const char *str)
diff --git a/test/README.md b/test/README.md
index a6fb9c9d73..9d4351b1de 100644
--- a/test/README.md
+++ b/test/README.md
@@ -178,6 +178,26 @@ gdb /home/example/bitcoind <pid>
Note: gdb attach step may require ptrace_scope to be modified, or `sudo` preceding the `gdb`.
See this link for considerations: https://www.kernel.org/doc/Documentation/security/Yama.txt
+##### Profiling
+
+An easy way to profile node performance during functional tests is provided
+for Linux platforms using `perf`.
+
+Perf will sample the running node and will generate profile data in the node's
+datadir. The profile data can then be presented using `perf report` or a graphical
+tool like [hotspot](https://github.com/KDAB/hotspot).
+
+To generate a profile during test suite runs, use the `--perf` flag.
+
+To see render the output to text, run
+
+```sh
+perf report -i /path/to/datadir/send-big-msgs.perf.data.xxxx --stdio | c++filt | less
+```
+
+For ways to generate more granular profiles, see the README in
+[test/functional](/test/functional).
+
### Util tests
Util tests can be run locally by running `test/util/bitcoin-util-test.py`.
diff --git a/test/config.ini.in b/test/config.ini.in
index 28abee2a3d..060c553da2 100644
--- a/test/config.ini.in
+++ b/test/config.ini.in
@@ -6,6 +6,7 @@
# test/functional/test_runner.py and test/util/bitcoin-util-test.py
[environment]
+PACKAGE_NAME=@PACKAGE_NAME@
SRCDIR=@abs_top_srcdir@
BUILDDIR=@abs_top_builddir@
EXEEXT=@EXEEXT@
@@ -16,4 +17,5 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py
@ENABLE_WALLET_TRUE@ENABLE_WALLET=true
@BUILD_BITCOIN_CLI_TRUE@ENABLE_CLI=true
@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=true
+@ENABLE_FUZZ_TRUE@ENABLE_FUZZ=true
@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=true
diff --git a/test/functional/README.md b/test/functional/README.md
index 628c77eb11..5e3009e6af 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -43,6 +43,7 @@ don't have test cases for.
- `mining` for tests for mining features, eg `mining_prioritisetransaction.py`
- `p2p` for tests that explicitly test the p2p interface, eg `p2p_disconnect_ban.py`
- `rpc` for tests for individual RPC methods or features, eg `rpc_listtransactions.py`
+ - `tool` for tests for tools, eg `tool_wallet.py`
- `wallet` for tests for wallet features, eg `wallet_keypool.py`
- use an underscore to separate words
- exception: for tests for specific RPCs or command line options which don't include underscores, name the test after the exact RPC or argument name, eg `rpc_decodescript.py`, not `rpc_decode_script.py`
@@ -122,3 +123,36 @@ Helpers for script.py
#### [test_framework/blocktools.py](test_framework/blocktools.py)
Helper functions for creating blocks and transactions.
+
+### Benchmarking with perf
+
+An easy way to profile node performance during functional tests is provided
+for Linux platforms using `perf`.
+
+Perf will sample the running node and will generate profile data in the node's
+datadir. The profile data can then be presented using `perf report` or a graphical
+tool like [hotspot](https://github.com/KDAB/hotspot).
+
+There are two ways of invoking perf: one is to use the `--perf` flag when
+running tests, which will profile each node during the entire test run: perf
+begins to profile when the node starts and ends when it shuts down. The other
+way is the use the `profile_with_perf` context manager, e.g.
+
+```python
+with node.profile_with_perf("send-big-msgs"):
+ # Perform activity on the node you're interested in profiling, e.g.:
+ for _ in range(10000):
+ node.p2p.send_message(some_large_message)
+```
+
+To see useful textual output, run
+
+```sh
+perf report -i /path/to/datadir/send-big-msgs.perf.data.xxxx --stdio | c++filt | less
+```
+
+#### See also:
+
+- [Installing perf](https://askubuntu.com/q/50145)
+- [Perf examples](http://www.brendangregg.com/perf.html)
+- [Hotspot](https://github.com/KDAB/hotspot): a GUI for perf output analysis
diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py
index 5bb3b5c094..45ecaabe14 100755
--- a/test/functional/combine_logs.py
+++ b/test/functional/combine_logs.py
@@ -11,6 +11,7 @@ from collections import defaultdict, namedtuple
import heapq
import itertools
import os
+import pathlib
import re
import sys
import tempfile
@@ -51,9 +52,23 @@ def main():
if not args.testdir:
print("Opening latest test directory: {}".format(testdir), file=sys.stderr)
+ colors = defaultdict(lambda: '')
+ if args.color:
+ colors["test"] = "\033[0;36m" # CYAN
+ colors["node0"] = "\033[0;34m" # BLUE
+ colors["node1"] = "\033[0;32m" # GREEN
+ colors["node2"] = "\033[0;31m" # RED
+ colors["node3"] = "\033[0;33m" # YELLOW
+ colors["reset"] = "\033[0m" # Reset font color
+
log_events = read_logs(testdir)
- print_logs(log_events, color=args.color, html=args.html)
+ if args.html:
+ print_logs_html(log_events)
+ else:
+ print_logs_plain(log_events, colors)
+ print_node_warnings(testdir, colors)
+
def read_logs(tmp_dir):
"""Reads log files.
@@ -71,6 +86,26 @@ def read_logs(tmp_dir):
return heapq.merge(*[get_log_events(source, f) for source, f in files])
+def print_node_warnings(tmp_dir, colors):
+ """Print nodes' errors and warnings"""
+
+ warnings = []
+ for stream in ['stdout', 'stderr']:
+ for i in itertools.count():
+ folder = "{}/node{}/{}".format(tmp_dir, i, stream)
+ if not os.path.isdir(folder):
+ break
+ for (_, _, fns) in os.walk(folder):
+ for fn in fns:
+ warning = pathlib.Path('{}/{}'.format(folder, fn)).read_text().strip()
+ if warning:
+ warnings.append(("node{} {}".format(i, stream), warning))
+
+ print()
+ for w in warnings:
+ print("{} {} {} {}".format(colors[w[0].split()[0]], w[0], w[1], colors["reset"]))
+
+
def find_latest_test_dir():
"""Returns the latest tmpfile test directory prefix."""
tmpdir = tempfile.gettempdir()
@@ -127,18 +162,9 @@ def get_log_events(source, logfile):
except FileNotFoundError:
print("File %s could not be opened. Continuing without it." % logfile, file=sys.stderr)
-def print_logs(log_events, color=False, html=False):
- """Renders the iterator of log events into text or html."""
- if not html:
- colors = defaultdict(lambda: '')
- if color:
- colors["test"] = "\033[0;36m" # CYAN
- colors["node0"] = "\033[0;34m" # BLUE
- colors["node1"] = "\033[0;32m" # GREEN
- colors["node2"] = "\033[0;31m" # RED
- colors["node3"] = "\033[0;33m" # YELLOW
- colors["reset"] = "\033[0m" # Reset font color
+def print_logs_plain(log_events, colors):
+ """Renders the iterator of log events into text."""
for event in log_events:
lines = event.event.splitlines()
print("{0} {1: <5} {2} {3}".format(colors[event.source.rstrip()], event.source, lines[0], colors["reset"]))
@@ -146,7 +172,9 @@ def print_logs(log_events, color=False, html=False):
for line in lines[1:]:
print("{0}{1}{2}".format(colors[event.source.rstrip()], line, colors["reset"]))
- else:
+
+def print_logs_html(log_events):
+ """Renders the iterator of log events into html."""
try:
import jinja2
except ImportError:
diff --git a/test/functional/data/invalid_txs.py b/test/functional/data/invalid_txs.py
index 02deae92f3..454eb583f7 100644
--- a/test/functional/data/invalid_txs.py
+++ b/test/functional/data/invalid_txs.py
@@ -58,7 +58,7 @@ class BadTxTemplate:
class OutputMissing(BadTxTemplate):
reject_reason = "bad-txns-vout-empty"
- expect_disconnect = False
+ expect_disconnect = True
def get_tx(self):
tx = CTransaction()
@@ -69,11 +69,15 @@ class OutputMissing(BadTxTemplate):
class InputMissing(BadTxTemplate):
reject_reason = "bad-txns-vin-empty"
- expect_disconnect = False
+ expect_disconnect = True
+ # We use a blank transaction here to make sure
+ # it is interpreted as a non-witness transaction.
+ # Otherwise the transaction will fail the
+ # "surpufluous witness" check during deserialization
+ # rather than the input count check.
def get_tx(self):
tx = CTransaction()
- tx.vout.append(CTxOut(0, sc.CScript([sc.OP_TRUE] * 100)))
tx.calc_sha256()
return tx
diff --git a/test/functional/data/rpc_getblockstats.json b/test/functional/data/rpc_getblockstats.json
index b8cabe1e5e..16dbc5fe60 100644
--- a/test/functional/data/rpc_getblockstats.json
+++ b/test/functional/data/rpc_getblockstats.json
@@ -1,109 +1,109 @@
{
"blocks": [
"0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff7f20020000000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000",
- "0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f1cd16b94f20a8a3dda91027c888025f2ec1a07ddcb2786bdff5916e66c00406f194ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "0000002014341131c18d3b3aa30056a0f7a97c9ac852d3fd0ec9c76f7a25e83c01e7f821bf83574fb606f25c59200c844443201faf923ef5284fd4401f3104a323c601491a4ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "0000002078616da95299bd42cd8f813c8043816ec5741de466be3162e16bfff471808461f671e694afaf534d37df484f1990fc19a65fc26964b38141b7f8ecf61b8a50241a4ae75affff7f200500000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03530101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020a08613f37d305835a3a1553e77a479eba0f34c06c52e429ece54f5973cd77a7086a1efcaf75f1cd5be2c9deb6a7850225757a2cfc3031a91cc1330b3af4acc891b4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03540101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000203b304fa1ce0505c2366982939ac148d9124c5ac747cc9aea133cea9916484966305de0e8d049f2be65c68d64d2c45746def5a9b4fcb8e298692b53b83b4690241b4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03550101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020fbdf49978ec4f0b23704b6772a614336872587e29c463f375836ffd775248837fed9f3fdfc33f076c6663ae78070fad7263c1e24161f3ee1a4857b8931815e2c1b4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03560101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020c37548b9ca256b9ff17187d4d4309cf3143845b0a5811d3ca5427b2fddf000731a10985dfd473561c070c3527c3fe3941834cf51b3dfbacb501b44c69c9745ce1b4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03570101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020383dd3766c0675440f26370ad62d687e335ea3a650dec9b02fe544107cc1823a13b98696d41562945457d655f4c6921f736068f7a72afd1ad6b335f2857d16631c4ae75affff7f200400000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03580101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000207476dd96d81f53e63934ce28c9e89022e0f24d040ec3c838443e925fc3a2f230a94d0cbcefb4a151191dd7664153944d9eee3b7b46d4ba997f397ed2b72c3afe1c4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03590101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000209e425c73eea16cce98c0d47d6070aca29f0524eab4b97af84c386aa5322dd43055002f097e929bc6ad88ce869968e1b049aab7f6e45a5b869cf4349afd5d43e01c4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff035a0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000202090e16a514fad40386413a100bbaa4fc14086a8d3501ac64c91fdef922e834a369e409444d0ec496eb0dd9a47f1fe81a7ab974bab28c50a912b994acf13b5f91c4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff035b0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020777b767e42624c52775b331f19e81ba03be2f51a0608166cd5388c1a47d5e776473570bb9bba553a7db4a9a3083533027c54af1fea3ef6ef67757ef2255d64631c4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff035c0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "0000002076bb2bf3251a51ec367a42f8584043171a5d53157394cd776ebd017e2982127653d953aca3e2217f56533c043c07b9a926a30672ebce2562f1d06a6dc5044e7b1c4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff035d0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020e6d7f02292655b73fc1f1958b09633ba07265d71d2a2784060b354cbbf1900202e9c9b02b63170002a94a0c9d8d787e2faa4c074a1ebdeb2855555347321dd101d4ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff035e0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020d61f077b0ed326e17f0a3d5af3fa876b72b434a252c9c3248d20130ed744287fcb10da470222dd29c7a07e2da7eb25d6499ed3919676df89cc630bd1b23fbb411d4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff035f0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000205efa9741cf51533ed6e07a97c71768372f53ca9c6df83894d64fe94c718eee23a207441e79ecdcf99ef3326385f5f675e2dea84c85ab8973219c63f92847ed5b1d4ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03600101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020368386b0a0f46b2a2c4648eb9cb5dd1380c4f22e437e0bd49420670993361e5b9026632c2ddbb4b31b3c3118c51e43ea4d78e05c0aca0956278ead26a263d1521d4ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401110101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020dd0a1594bbff6345a3e34f326e5ee605c855f5e0a5c363fc39615a8b1539b736200b51297dbee4aadf9b536cd2afe7617651e0a1d0f0610f436518a2a4dc54621d4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401120101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020f302a1092709dc27a32d7229d391b90824a75828692c4bb2ca8f0ca5c88b3613c2e18797ffe8b367336338f90b2cf8c3f66277eb1e1ddbe18c052977294f10691d4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401130101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000203dcc77aac703a8cd0e799384b74383c1d5f236426f77d516694607fc88fe85581276a20ceb98d02e6355c9dba4312e2fdc9832f4302cc307e1263f2df0aadd6a1e4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401140101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020dfe109704a0b2801aee4232c31fb744145a7c80dd91a7727e16d4057719d5c3730f8296243521d82d96ed75c5af800a722fc9dde2e02af95c8c9822190ba07b21e4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401150101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000201ef9ef2699bc36fd646bb9ba8629644bc98396122f6753710caf0315d7539f751382d3d85f17eb8b42cf17e54baa327886dcf6fa63207e097df8f9b84cc5422f1e4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401160101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020834c91f23cd91b727be08b892f1c1a2f33c1e66d66f35607925fa1be4bca2c25b4145a73b1c71b945f5bb9ede3d8d95c9a3b12a0a81b7b14f440ec5146dd4ec71e4ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401170101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000205743202bf1e543a9be2a59b62be6a5a494511fab96968007b8d7199ea60a524697227ba473ceaf48d4f48ee17f8ee6cd2f1f5ddff03a641642ece240e7872f8f1e4ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401180101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000207469f5c1841bf57275d82db23e5a8f0e8512af1eb10119c238519cdd6cdced34fd96dc659a874b3f5d30fbe6ea421a6b9791dfce8450f8851e4b90d80f0f794e1e4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401190101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020b6338f55fcc473b744d53d675b4a83dcd80ddec9d02ad3323cf1ff50ac0412239d986ec20885d772fdc67803273aaec43871426ac93d3815846a8cd13dea5af11f4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04011a0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020a19d9edf2d22415cf226b4e1416c8a3097e0af222efac2bfeab15fa1f07b3f24c18580c4004de6d6244a30ce431c4be3ca44731509fc6b11710c792efed5e9191f4ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04011b0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000208f9f29a27424ec01ce77485617088506ca8faeef69300f0a474ad63ca5d32972d6049609fa3588d6ffab4d9d89a90636ac94c0ca1995f7768163abeb25dbf2bc1f4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04011c0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020fbd87d530a9ec3835ab579337fd16e512cec6c4779ab4d84e7256b3333dece28de1065c8c3d3d166e057139ac59af6f4f2c0d241b6269bbea6f61c5eff3dba431f4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04011d0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020dc6ef4f436baab2d6880f242a2588313a2739ac694e30319344045ee318c9524d0ec7fbcaca30ce85392cb03b64015ece769afb50fd07db05c15ec49abf7d71c1f4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04011e0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020386bf4cbb3708ae6345b9f2459bdd99d07422b05f9b005d2d4d1d3bf87d47359ebb22b3a15c8e94ddd8129527873b9bebfa10c54d11196961376efbcaad3c4681f4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04011f0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020600e892f12ad82a23ce12684d3ffa0887eab5e3e97804fa651050b23366cc55ef2468e65c3d3cf49650657eb47d0b0b7949c71dcc0922eb824523157b7eff478204ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401200101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "0000002068e4aec52a3f4e44279e3a65cd476237bdfcc2328390bb31b8a903f89ddfa70e8d669f61b469acb31b1d4ecdc238e6616a83a30644a5d06fc2ca1aad6449d09b204ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401210101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020cf2428ae9a5014b910275807f54a8bbbeec47d462f9d284ada60329b4955ff10cf83c44ddde39a709aef54fb302c7f1cb36db8fe7c3befce20dcc3729767518c204ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401220101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000205801eef8cca082407ce4798648c4d3ba0fc4dd2d4459eccfe5300c7960760d16cb3dd78a2f22fb88717a175e45c53d34f970b94ef9f7cd1b6c279294d427d163204ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401230101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "0000002088320983a4bcb95b9f342994c6943c227f3102d3b16282f048ceb8e15748662a52d1207591a0a364bc9245a76e36530f147ec4d1b4e1917676b4071f542c3b19204ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401240101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020acd85b6d8087d3b6bd2a208a2e39b75da459c0e0eb14088075a23a2e043e8a4ed5a1754491f8180d293b42e6c04ec3f82e29c1f2600dc8607616f69a4a464e6e204ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401250101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000204873bcb379f78da4497ce1e22f6bfb63537b89c8c522257a7b7bef74e515ed1b6b235faab048fa73a76c68fdbdde6a4ee7ebe0a3b7b23df24ce75dbd2cf49c33214ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401260101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020a87f6d13bae8e2e07996c3316e8e0da6aec7d1aee6b80aa5883018e4d136db3e9a498ff7d322ad93863e0a5318af7e7d0ed683fd2e4ecf523f2b7369106dbe4a214ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401270101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020d9b0618450a51c08f43187c479e20d351f0466464409bb3071dc0af7c51d65498a198cef23dddd2c4b93d9d3288ae922584e221a9ef1ded3dba5a2ad494d9237214ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401280101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020167dff31847b8dcad472bb6bb7d0af53b245f0f1d4c9f83d4ce14a0f05d42d7f0f2638ffc0e6896230f28df1865ef133dccb1f027545c6a1177dffd1fecf8a01214ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401290101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000208236c04424573692504e777e179b9247e54622b118239311413812f13cefbf6e39a639143f599dc76208b2014de12a364716df2918af9186453e3676dca743d7214ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04012a0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020fab7b55aeb59f63315dbc10784c55e55635a7600cc4f3b94a00003007e7fc90b4af016248e9908882f8a7f0bd8743c8da82da119446e8b02e4d3b8d1d938a3aa214ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04012b0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020794b4160d8fd4ebe7611d0fc9d3e04f3038a485669f74075aa153852ed181121c577f4c0b7151f6d78a16e3c21ab291b53ced8a5c4f05a22caf24a25ed029d56224ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04012c0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000204a8acdb549d2ed922360ae0e81de6c913c3fd84b0de51abacbf97162a99f7c26c656b252d3259c33ffc7e5d403843accc6ace9b7e60e911e2347a6a0b0abd122224ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04012d0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020dee6d00c7058b3422e4273c98c16181e04ea93116496a8442de546c2ee9fd86b550013f39e004b3829dc3717b17fcfedc87de9450315fcca540963119cb264c1224ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04012e0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000202ebca471f5fb2226a790233c3d0bc323f73d935872f3e15b66bd5fdcb822101d7b68788ed61d3fb8cb746f627e09db2fc09d8a07672747709d92ae400e053e78224ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04012f0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020ee500470fc1c71a82f2cbb9f8d5723bcdf57b8051fc458a8dbd8d0dbf60d0e40d1a0be0e50f3312d4830e3900186e5a6760d44006c164b4f0758218ef2b2de8e224ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401300101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000205057e8d8a7451d79325851dc8e0f4dfdb1dfaaab637509d9e61f0e064af5ee5c185221c53c0cf43261b3c238c0e8117da5d6ac60085615f7a3d8027726cb2143224ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401310101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000200907f01d9c5c872296796ca77feb62eb414cc080e13a93e59e181528bc19c336eaabacb1ebcbd20b26f6bacdc712ffe17d4c8131e7f99b9cac309c0683737c04234ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401320101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000209bd6a4ec962d9e6199c0a2f39481a7ecc322fedbd2320cbe7dc984c6ab958421fe7a5c7f2513a3c3de9ddf7b211f5bfe85675b31183c4bb98ae79ab28cd055ac234ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401330101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000202b8a6381b7c9476fd77be379a929863db6b7edd9858d6eca0f68a430dc87cc3a9c6c8b34bfadfdacbe95cb1d4ae5ce4e4f4ccf0300171d7a91cdc97f620c7b37234ae75affff7f200400000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401340101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000203d3e9a0b4decf12b4402a2178b60599311c8a9c7d50ece365a61ca29530da7056754ddf7d77a11cc8ce680a74fae01d851bda024fc9c51712c55b4a190caef24234ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401350101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "0000002067fa05082d4f7a29a3985f30798940cd854c76a2b20e9560b2047f7753193f71ac61b8df17a8a63f099c8f55869301fc2a0aa37355a4a2f89078135ee72a1362234ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401360101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020249684272865fec3ad62ecb73dfd930500e32e475306c9e5d4b6d545e3687b0a48deaeebfafa213f0a560994b3f4000d5e2b93951d7e5be40073503877292dc7234ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401370101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000204671db1df91098669bb03c3e8504d432da99853601366c7de7585bb8f23a6e1e2996a16c11a0f9ae87d937f566c8bbd919040528c1bf8dae4e22a8f0ac5f935a244ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401380101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000203046500cabfee552ed114c505279cd75c28faa811adcb5010c53c14df5de3216d265321a4168c70fff1c85fd83bd976cc03c8c1fb6567398cc24053e343ad137244ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401390101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020591533cf8dd1fa1872c7e6d62ef714b28886f38f7921ee614e13b748eaf923282a96287277f18b1113a9c3ac384fd7b43bccd0e45114908ca5396a76cbd736fd244ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04013a0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000205286b5747d0244a55e1dce435fdbb9f300d7138fff3b16767bc09196eb00256c6795e3f372a2d5d137244a4fd72fd799e8de913f868f22269eaa628d7d2970a1244ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04013b0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000209649b55f6a5ff0d5db73aeb7089fcce605fb9dd23971e577b73747ffab586f4edf84581240223e2d8912a6eca049e06aa46ddbf271af08e9ac9605505311418b244ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04013c0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020fe7ff82c11ad3a3db3dc11a03a67e0b86b727d232c80f37209b028bc2689296c0da76fb756e1c9833f38b198cfe843ab820fbc0c38e30f8f858f6ffdbd64e834244ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04013d0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020c02e6109b6aaf6643cac109ad5b5be7f7ec47c7993335bdceea6e0e490ae9067eb1fcee49ecc40a61477f934e3b9821f2cc7ada429fcca2cd645866742854c40254ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04013e0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020956c46fce0adfc8a30d91c7b7f328f17c2a90771083884c4fdb7f24640598f6fa69c4e5971bd3b6cdc7e3ee98e03a969b28c3220fdc685cc2eb77293763ca4ba254ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04013f0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000203eec7d0ea9c539f47e684eec9dba900e27c805b8200f237924c7df8065957726c83caf659fa341bf45f733a92cf76daf2cfb6bccbf969a2753f5f7d9cda4bd1e254ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401400101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000203c022b42283bf651ee4d536fe71b7e5382e9783d4a85f8bc159f00b97f16d82d312ac4f89f1b1496de576811f2ff17de44b512db0beddae59e030e2ce3eba4df254ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401410101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000205da9709dea4bea4f3229d0eed439a4820a0e817f9fbbcd8bb355cd8052702973403358080a2881b823a740f58b6b0c922b42189e06326478cc33390f2c704743254ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401420101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020447c49f664916cc13a839e27f7cbe0b09dea990dec71dd479b537ecfb771c7159ae6241727c2645ce00817909ba97d43447fe2e146bce55b7dccd5bcd27d2e3d254ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401430101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020ae3b214b1495953eea3b99af7ade4b8d22d615d598dd6c790c6576d6453ef35d37fbd3446f8431f00052f278c3e0359beba54a2f5064bc6be4990478fcf4e086264ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401440101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000204cc68f9f571f59145e1f7505550d56da13a797fbc7e5a178dd7f6f9241d91f007f2400f7aa1b32b30bf869e4da86f75eaf2baae182efc45c42c6245f06aba675264ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401450101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "0000002096d55714a83c3d030cc72141eac3577b8a394b8366b2c93354fcecdafeab025022f5e26de7e4eba3f8e4a09f243b55ea6f08bfd013e2051cf1d5df20dcb3331b264ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401460101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000202ccf49665bd46e4915dcc9221f5fc72124bb73fb11fed8869dc5862a47950c0d654693a1d86098212d68fa9a27f9df0faafea4aecebd8b203f2ab8f3f0b86196264ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401470101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000205d0be5aea546eab7734cbee757ea5f4983ab3a3f202323c1c88590bdbc8b561974cb0de6549bfdec92322ad53d8a4192433edab0e33a10e7561d2e27c7759f8a264ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401480101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020fc3d70d4f690d7d7da90b1c832a51c2a92940cdd9fccda6a909c7256ad567160550e1089b48fe75f0f1e6f712cc2a1d4aad384a4ceacf1c71576420fe8e7de46264ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401490101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020995ab1f2293e3aa1e7bef418919bdce60032e89e60223b8c12b17488c50af83b3a360b4f89551aa0ced646a6210b0c3d3f7d0464faa248b9d252c89615babc9f274ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04014a0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020e02dd5e1201ebe7ef453e77df3021cffde1b5447b9eced017963489e005ac247e1f2b80e91180cd9744c3e126eb8a0fb34ced45587da6b0fa9fd1c6946f049bf274ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04014b0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "0000002081b265800d61feb37525c58aacc7e6d32cfc6b6579784f9952dca51c3addeb37da900ae4ec1b2075056001a361c0fc8f9ac1ca018bde2a2bd8fc587424dfe18f274ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04014c0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000207daba0dc6fccc067a19dec46770d1443fc715b50540242ff26073a6748bcd9583804c315b783d4f9aac4dc8f109fa60bcbf1c3cf98a7e1e593fcb969e88aafae274ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04014d0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000204a35ab2d9c4656c4992d2445ee566e4f5cd0468358292c94839de2b270ba0628de9148db03da59ca2eb727a4f7a03f2b9ee1e4045f37bdaa4b75a89de56b63ee274ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04014e0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020215ed7b31d118a45a06407133e41f3f9d25c913ff98f3e798144f981d9145305266efc9274f7c836aa0f8b831732aec2c1d85c5bb9176af4c889a707cca380fc274ae75affff7f200400000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04014f0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020425321a1f12ed4613b63b3cc5b4c7674242c3de67ea86984f2d9bb2766f6220a5c460ce6840832b7ef05f206c5d255bc2d8ea83753a7f5ef5c0bf3dbcbc7c74a284ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401500101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000204e088cfd312091bf589f0fc24131dba20ed83f716530a7b033356e9ec803be23a73c4af5b64fc631d20aa1f646c7c61c4cc5d4be6e2fa29570030d72219726a0284ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401510101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000204287a2bf40300e39b45d6667d6e17abba37d98c41efc7023fd21b643fb2f6b76d915d10e1f9e224d3bfdf5203da57173f0b8d8eedc92ed9ff23372927e86073b284ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401520101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "0000002083137929ca902f409a1f35385b4d31e0f5163d488d71b00ade724d7831986a280a187c13fe0db45aed5fa4f5089d544a617da9f4f80fe6ce1d0711b228f3a6bd284ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401530101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020b38c074d78cbcc8bbbf20c48e30639c2dcf444488efee59f5aa01c48322a30418009c11df7e14de6a6d0d72cc22f20ff5fdba3a7dbe409409ff92b1302340fb8284ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401540101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020a6865148220c7f5ac3259f57fa381676d6be5fdab56eed8e060808fcce4118492476fe6d5ecc681e7a18c20a04239143de1c2cddefbb08f15153b8e9b0c22f62284ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401550101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020ae146263f4087106b8c51ffb5cdddb03a4e593ea1a22a8f6580eaa374185126db2187dbe694a1b17b7c5664115307965407274c25bf6ae02049817685f923256294ae75affff7f200600000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401560101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020718abe22705f33134812b56d04c129d453da890e27caf6e77d2bfb3f9be460083634f35fc8ea4a29b0f30d6c6b4926455a31de62d2f763bc95c8cd1a7a425599294ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401570101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "0000002084537c50520f4e6b0173926320c3193f6b4259c9a724fe202337d3d5cf7da70b1104e1d6f25c411165a856a0bfdd5ddbe168238425c05271962aa6e5ebb676fb294ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401580101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000204bae65b948c9790468cfc8fbf78a81e7aec5407dbab18c18ef1ab37a13f0c257792cd1246dac5ce265a8439e539db1aebe4776535d422315bac8e57a51605aa6294ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401590101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000201e68985b2a15920524c3c2da77354c0ccfb202c3ab4005128eb2743aa33f0a5b0527a624a6d1f808c896d8cd1552c251a4ae5310e6c62493977b0965d17f580d294ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04015a0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020ab1d90854505caa1ee748c1987f4cb6674844b84d17224bc3983fdd6e44a930b2edf3e5a90be7a9080ad15014ce796b38b6c19ddd3754443243fc277bcd2368a294ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04015b0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "0000002062242f316efa083c17e2173d9f9831afc1465f3fe84431d8e52fec71ae358b243e29246e0db770708987f2cff380920321636926d9c66c2c808d2d5f0ed27d672a4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04015c0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020258c1a9b0ca17819d8d7fea5178e9c72c2e7c6769696f70098e0d5d2a9dc0e5cf039f5f686d727c6158ef5405ee8794ec2f89c641093a1dff0f5240263a441bc2a4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04015d0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020434ce26cddae99571a1d663419d715f88e6ff0e413c45f21c44b1991d041fd2745534db221054e2233052aaffe7d7232a91b3f0918c14eac74b2f704b37bfb8e2a4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04015e0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020ec94475f112ea37dddd84f066e61446b431d28d2d9b6fbb336d16295e7eece52f1dc01abb3b27e71d8a25c3ed2d6fbbc5900bf954738ed63d57689ba5e68f6532a4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04015f0101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000206188cca18a2be3b7f0b500a724f1b73312873488f04c5082abd81ca0b2250a75ec0efb2fa994ad23e1f39416e69976e66fc4ef9f101bd4f60fcee1f8c45a2b802a4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401600101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000202384b91bd0bfb1385ef42bda9b2fa43930aa8889214bb14f63c61a74f380dc7f9d547f2925ad39b9161fa55b9cd258463fd058234778a7c7b3061113d64179812a4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401610101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000204c266a3008c3bd95ae4bc8228bf878595cabf76cc2ef3fed32936777fe37833a1a13ea016868b2352a8d6d1eb35d0aee784cef06ed50655b8d84c089324351df2b4ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401620101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000020ab2c78d1249de8482cf26bfb1616ac04f7b8404eafcae3e0654f398943fd41571b4d7549862be0b06ed1408ddc3e7c01b07af24c203a18fe9744e214b14d2d652b4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401630101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "0000002031aa0fc5e0242a9c9d91024b6b7e67544da72c4773537f881ee5caacd45a3c38c650ff5fa0ffd080b3e5f9752527b718e8c6b731467028e14b0b009d4e7406ee2b4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401640101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000200418c57609fc6ea3f24f569dc3afe163538a6aab940fe8c3d73c72cd781221380482c6f8ef2c19429da2f7a842bb811496aa86247c55f7ecb3337718b694d9472b4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401650101ffffffff0200f2052a01000000232103836d350d84c3782a7579c5f542c7e70fe628d3062c6bac563d2264c21e56f70eac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
- "000000204f36abaf10d6a8b3113103f3413475db0640c89a39f02a718e8bd2190fe87f1d6ffaf2b9fdbed67ebae4b36c97f9582814c99c90d0b5123f14da4861bf3e4f742b4ae75affff7f200100000002020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401660101ffffffff02b000062a01000000232102f6869601b2b9980b07fc047e309c1fc1433e08c5bf0498115c5c0e117ef59bfdac0000000000000000266a24aa21a9ed5896cd6a40cd126b09e317cbd179f39e2bcef2f9d423751d980258396416e671012000000000000000000000000000000000000000000000000000000000000000000000000002000000011cd16b94f20a8a3dda91027c888025f2ec1a07ddcb2786bdff5916e66c00406f0000000048473044022046d00465c4508cfd02fcb878b19d120e28be28e40658b1f15458828891ed1541022036aac054f36a42666dfb7b42a20506315a0b72232ce7704406e23c7a9515178701feffffff0200286bee0000000017a91481ddd4a9708ba8088cdcfeab9583ede8d83a298c8750bb9a3b0000000017a91484e16967722289584257803688aae36cd64480688765000000",
- "00000020cc7c39992f2dae21e0ebd958f6ba77a6d0bcc568e044b9b61ca4d77536a4214e7b4ade79dd733ea72d97993aaac27f2263b91b1500467350ff35ea40c2850d392b4ae75affff7f200100000004020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401670101ffffffff0230d0062a01000000232102f58ba54b2d51c4e2a6096f9d266261b45f1026a86ba88c29ed8070dfe3f5ec6bac0000000000000000266a24aa21a9edb824d92cb231c2366f0726aedf8bfc2705239a40ae6b10a106534e8b5395a09d01200000000000000000000000000000000000000000000000000000000000000000000000000200000000010196bac2a0f6212b8e5710f8c7214dd32c393d38d20b7703cf0b7b25276bb6ab8001000000171600144f8a6c8d4c6c309b1e2b725b7496859e172ea367feffffff02781ef5050000000017a9149f00bafb542049fe32d532b0ea7494ebb7ae41398750daa4350000000017a9147794e6dc43de332ca7a095e582478c331446686d8702483045022100bd85ed3954f1151c2fde32c4021a32c96d7defa4a57c14b1a056be9b361a8e49022054947bf6fdb535c46cbee62efc1262cc0389a6eaa19afbcfa98fb1fb30c3ef230121031b2371df07fb88dddf590b246dc74defc265fdbfe258e4b168250859b806f5ce660000000200000001bf83574fb606f25c59200c844443201faf923ef5284fd4401f3104a323c601490000000049483045022100d1c4b09b488f6375ee4540a531a13b5549e88e2459bd88c84867e293c54862740220222e8af70c8d8b1139c2a7b616d9132bde94244edca7eee3c7a783b12839dadc01feffffff0250196bee0000000017a91490e5e33cfedf18d5cf911d6f853770c62e1f5d028700ca9a3b0000000017a91422311ee58518edf3a2289012461dc66bc8739d2687660000000200000000010196bac2a0f6212b8e5710f8c7214dd32c393d38d20b7703cf0b7b25276bb6ab800000000017160014b81faaafa52f7723f539f8d595147b1a112b38ecfeffffff0208bd9a3b0000000017a9146b2e611708a94d9c674dd08c7c4c1fbb97bcdba987005ed0b20000000017a914933a20f07bab1d8f341f391819466a271f0cfd648702483045022100dfa8b0052c7825e6abcea05d10fc82550a8d6538681ffc8188d48ba789e4d9b40220697371cdf527a6ecd1887f87da3c87dca3419e4a1aa2683e5c4e035199084d100121021cc37c2ec090f30ea0b49508ae8a7d65d9759903932666da56671c1faa445d5d53000000"
+ "0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f28394022bf44bff30d7399cb5a16e3b94fed67dc174c2e1d77df91bad5a51cb3194ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020e959d05cac787d7f82d1348326a4ca25ead09589befcd4b4513163e5acb5af6612d2f07672102dc6f099c4be308f598e4c4da1a7e0cb462ae14f0444525a13321a4ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000204401ebd07d42f8f18e80ede81795f728a9eb2a63073274ad92ccb9eda593ff3c5f17ca91704a014c6f68ca623ace6c542950f2e1d2d02ece08fbd440e33af53a1a4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03530101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020eab1809c3e750647f588c027df5c9d5735bb8cb2a1a5f182d7b35524b0b8595f9d59f165de689fd9a4b6954b4394d40d7899eef078e6ddb9f7eb036b7b15af2b1b4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03540101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020bbe445e86bf69865a0c816690c0e470338bf9d692d388f8186613830afe2f54c07ae38ccc6fd49e7098d6e3149e459a234f30970c6c9a9894df992e3caf97ce31b4ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03550101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020f5b84b4e3b7e84720a83dae76aad6657c06ec6bbf85d9158c575de09c34631035b263b763b955c4c6d1a97b23e6b4bc5e6ee96d75910845557aaca233fe777fe1b4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03560101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002028d52759ccb342b81f2e8d574d8cf116178949f8a595d577098bae70e6969326119c83444b75d63bbe98d8b3a937f0de3a459bda5fdc0fd66c7acd752d19496d1b4ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03570101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020f461e9c8981911f5180e9a8e28be1d34146460ae4e7583935949f43f6252bb3cb287270caf2d4e735caf0d9888f998b8d7c79443e97933976930919e6dbc0b471c4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03580101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020d3328d99edb99583b478969aea57e378c2840bfc1df0341963fde16f75636e34a6c85df88c2800c54565eb2e6579b729def99fa9b8ca347ec649b8d4f8db78de1c4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03590101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000206b88980f5b713605233c1c8806639717f753c4aad93617d5e37872a43188af6c8dabd724a42288139a21186c855f23c4fe1d12337ec7b97f87c48389983239651c4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff035a0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002030fd29b0e0e54a5eec463f93dac2fd9d73f383b7467d146a882bee4700f79832a343b1b1867b07ba30134ae555db5816ccd971232b78a9d596e2711d02251c521c4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff035b0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000201845eacaca9b23798f32c22414015655035d4918be70e26f56b56e730e195e220bf32e9a8af59ce9264884b0690bb26709616ccf4bfb85812faf87144b2fb8131c4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff035c0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020353ff17bd0d66a6ee1e784bfbb2497691f49e27c3822aced5125fda6ff09892fe72239d73e75b82916e8eef04a4963e6b500de80a7fdcd89952e23c6d249c5931c4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff035d0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020c6c359d1b891b0a6afa7e0fb685b9c21afe41c8dddd80cd6331ecd856650af3803953a9bf6fc675eb2856718bac5362a12168e7b1baa7dc6b46a4eacd2e8baf61d4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff035e0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020291a8a183e7ebb538030bc791b0f995a2ac0a766add84d83d3fa9f6fad50075a0857e76b64648e82fd4341931116efccaf147f0bc6e8c107cee68f400530c17d1d4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff035f0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002081f804c3afb976efbaf9f62d50ca43402fd5a09571cfd93ddaf77d78952ebb3e3db790f598f68da5e0514b03902de787ff0d2457c05a59bfb22547d8ee6f7ed91d4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03600101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000204d559298cc1db4c70f422519822a850603ec250f4ab705f51423d67bb2d4a03abfdfd932c91a77b0065183cf1a575e73b9d1322804f869cc640747e7fa36c0531d4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401110101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020b49a26625c5d5210a3dbd1aeba62718b828a29dab035b6e028073c3679053c02ee7b702c74ece9d04af3aca5f745aad5d4142d6a15ae12d16157c1449b62b1b31d4ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401120101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020655a431b076a153aa7d9d3731b42b4213995f92299a56e6270860be4005d584d33b5d4b94367acd8ef20e10678a75de3c02e143a3f9bb0b0f0c31ed0396454c31d4ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401130101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020a96f9ad3e058c097d63488627158e074f759a73ae3292b8d8e3e979edcb4e33f0ccd7e3208e9cf08375f36a34f3d71da039e6a33cb8621a67bb484cedc5fb4f31e4ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401140101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000202ec3fddee36cdcfa564d29eafe073ba79120ecdf8740f62600293b0aad6039419047bdc522883061665549cacc029b05713ad4ae24281eeaf0a041cec3c7d0d31e4ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401150101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020d2985cee6e78b525c61ad111b0e9a6ad8c222ca793eff2121c23cf06462fd930bfe97d3f071f45d95467db4f5d31d8b7966c26789be3d5c1a5d53c903230abe61e4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401160101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020e799bf0166a1e3e7b94d6709fa0edd5feeadac19ddb271ce8e8a6144fd52c23b1699dad3820cb2ba528c13e37550cd32a900ca14f307db9b735daddb523b1d511e4ae75affff7f200a00000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401170101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020a6e38cccd5f39851e6f1aaab690b3591ce59ed225bc4815b66ed59f9e604da4beadf823c049567a4628d7b06090ac7f51f1a854c46817a8b6fb8f069a098f8941e4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401180101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002067dc38dccecab584fccbd21e1fdcaa8ce69155053eaffe082d51fedff22c26299e57f12478cab2077ac63eecad4a342082c9976addd08ea58896ab334ddf5e6d1e4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401190101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000200b71cba7f1d43034d1584c309e04bff61a7814896b3fd170f69d8757a81b114b4769be83a7993e5214c7cbb5053a142840cc180e23366cc1d52cd2ace2347a1b1f4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04011a0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002044d38910c5362c279ee6e183c56bb2379f0d053bceea9f2c00f99adaf1a70067de8615ddd58387b2847d215fde3633242c278ef9b18ab9c70963c9060b4a3f101f4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04011b0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020b063fc1b09b6f9cd8207b0f9ca9f1549ce2b1de07b7f937275d96461ecacb26a3abf0a5d24c68c0b3df1a58b6128039eca6452810a374a306ac0cb96bd462df61f4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04011c0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002023334826cbee5a6679259a50f8e112332db9663c97a1d08d54e64319dabac73331dbae7789d23e1da14d1ee39a9dea43581ddd9c09a199253ec0bd6c819514911f4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04011d0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020911e28670d744a0cb23495711db6ac20576273eec7ee442b0377ea2d7564402fcc7db46bd4aa8fdf1209e649b3866f5cb79fe3f2cfaf8aadd39a3d43eb084ce21f4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04011e0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002038d6d41fddd3c9278884c9141096360c538d2491ffb078be006222a88a10c854366b75e0a133e1d3f4db26e1b0e9b9820db50dbfb11988e8bb8739420ce1799c1f4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04011f0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000204e501ddc49c462c4ca52283614e99e6379b9c6570c947a822b832804e39aee00b037b85318c2f997bd9edda927e85dc2c83f8aa1952dc67556565d141a246186204ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401200101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002040cb0b50f15716374c2dc627d1fa6e3eaae67b1b4f1c90a30914e3c89a74de794a98fd86cc22c32f447477016e297c61a4e48135e658e5b5be84638f3a836ee3204ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401210101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020f51dd27d82fbb9296330e7f105a69ea307303799ca986abe900f00836f181e59a34210d60eb84f3fe493845aadfe404fb4096e599a26f63453c8120257cfc8c2204ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401220101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020b696bc517ca5d36092ca1106fd8d31047552e9453a9de51d92cf9226259a491618e806cd5aa13974beb89d41c5040a48242c24bffa8e65e9fdf3bc35e2a7c1b4204ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401230101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020358daeb9eef51c07f280abf17c5321023d8cf8bae26254abf25b9268d89a424fb67a604005cd713a7df2c45fbf5f761da51b9c6f21e86a3d8eb40827bbe2764b204ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401240101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000200170975c14ec6544367490c04b06294aa57fe978a1da6e5939ee6051d7602928e3676a94ad350f04f371edfe21281df51430055242082d9704030cf5317d0b81204ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401250101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002008d02d23a387a7263f5f3475bcd56b380fde9b742bbe83790dd9751701a7923038bfe35d91a9e4e5f8cc714713acfe2d1e96646e61b29b3dc072e5b182fdb45e214ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401260101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000206bd75eab3dabdee5a35c0a64de2aa90fb54f2d12b4f37ee60aa28541b51b493f0e5915bc2be5d87850016a330fc36b62f6e40ad7a6d38e0ed6a4f62056ba22a9214ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401270101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020a42645a785e5dd28047022b724c1af64d507d372b83f3f23899c82b317168262371693330fe6d94f33a962de50891b2f5811a066830c331a240996310b5c6280214ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401280101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020a6626233cfb7d9dd7c97f6db305ca20b6a7fc32d5e8ce9b07e35aeaaa7c0af3975cbe08a458c6fc4cd748a39427e29c2b29180293359623e30e2b5639d2d0417214ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401290101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000203a5a408e1a076fdc7bfa08460932fa40aedb0471fe808e268ee5768ce173520af5dc75a6bde31efc2ba81a4ea94fe91d3b1ab1ed99975fc5b1cc725f7f20817b214ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04012a0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000204cc70837ef7c3804c04419260a2d65ce21515317991751117fb81596ef32c3161154931edf6dd92bb8a13231190f7c0b21bc527e188384770faa53b54aba04f8214ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04012b0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002070b6275e92ff0b54df08933d70b7d933c3b534b779026630dfb2f96efb860436581cf56e07b7d7e2a377ba0e698f592e464d1ed84bf464b0dd67cb85cbf69f7a224ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04012c0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020a330af8e1dc93fd74b7479c626b1a77c21dec2d240fe7b7d90249da127aaea50773fe8f12bdce2a65c375082dc3db0414e68aacea57f41504cf901a7858b8cf3224ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04012d0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002052841b806090d1d40d221788025ba6ae3a9bb32f352cb12ce4b165be58846a12ee2ad08be6b3e693e3d53a90abe88a8426b342f19f3b271818e66eed4ed1892f224ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04012e0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002095b86217b1ebf86fa345f2c605f4251b24d5a647710758cbe08340448250ef015f65e5752628c0a0131fc31fac03ed3a2ab0d2c1407414d4dfdc1680037a3b38224ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04012f0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020757dfdbb6f52e389a0ed997dff15de8682d55f9e241d3b53b254f19fd96b926860d0c537aaa527616248c9e50fd776e801eb5dc5d9ef034251b846359da84bc1224ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401300101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000204c5ff56d6188016b722ba01c7d3b74bc530820b7dff447efbb8c5e756def2d25bdb8e2a27ead1a99e184a87195f06f858b810add552f6bf0127c0a36ec101a60224ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401310101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002064c6f9651c489cb014e8d5271e1f711b71d20d865ab7561b26bea5c85fe6957b6326b1ba78a33db0f148a07e1d49437ca70ae9d73544c183d2cfdf814fd7ff37234ae75affff7f200700000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401320101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020bc955dccd950bcd6051723e18c87ba5114103eb66c136d4bde1070b99678c12f6192856637c104e86194dc91e1550447d83b7124a230871020c37454f2ec82bf234ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401330101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000200f4e7a95470e9056dda68e82b62a61cae778e88b6e35a4714bdfa3bff8b6e846c45be9fa19a24d90a3180a562bdd8b8a5b71d4c2e0a9f84b6c55c439ebf2f22a234ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401340101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002044388dd29a4f16901222a7beeb49315884c0583a2c31a2f8865536931ff8ec4442553d33ba0273b54c52e589523cde509eb117046f5e63bd5e8a5a96f467c5f1234ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401350101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020438366e968619730e26031d71e8208d31ab6cb7c242acb65f3d1769157cb971210b6afd88e0ce95bb14554c6dad04012fa8ce0c88a5932ad71da4d04a15d44c3234ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401360101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020c38296d021842f437d04f8515079f943f0a1c8437d8f2c329499c32c0448ae1d961fb68ada366165b25199757ce8a527f11d0744bbcbaad402815e09623dae04234ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401370101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020101cd76ca645831e3a55898707454b2405151a06f8c8cee7822d148ca1251d12fd945975eb3855e5f0cd2afafd20b169a7763bdc73b64d1c2f096425d9c902eb244ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401380101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020f7735468f9b54bae6399754a1ba4b5ee620af1dc6a46261f4d8ab4d872818f35507916cc69e748edc3a9feadf7b79d1d19f16140e8f3260d7fbe9d1620e55f26244ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401390101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000200bc5d881eeab42717526d45d8475fefb1940bbf03c5726174e64379e5a23bf5cbc75b97dfb5771d55e7bd108bb109705ba34952511ab96646cd2db4539fc3b2b244ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04013a0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002094a466199740a3c77035005e1fe5ae603df04ab18c429abfcc385b93681b75390e0d6b356161c1ccbe486a26b777ff01b1247cd6b4b9d4d5b0f958d2e887caab244ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04013b0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020b5e8aad67d17f4f047da10404862703a89feab21624801f785cfe77af4a71d6b87994452ce4bfdc92a07f49ed16903e68d8d48191e31d9558dad4805718311d5244ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04013c0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020b0ed4309ee1541c2dc5480b14092940b88870b9d4ca32ca448e7eaffc1d74978b9c5b6e8c1739811c932cdbecd192ae0bead4068782ca2e98805b8174d40f213244ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04013d0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000205caa7f7a1817fcf6b454b156301f44edbb71cd19de098d9261305aa5731cf073faa4b4bcef4b3ed44ddf9b2b708397114484aa262a538c81b04103a02d8c033a254ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04013e0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020c7ec1d68a2b4309b255ebc418cc4c8c1a57892bb3c6360a07d22b526ffba6a38436088fcdbf94cabbacefee23bb69b5ac30e3627f94500746630163249cc5029254ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04013f0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002014c877261aa37fc58f703b64278f1d611b6faddd582e33cc7f1e7e63ad75df6cbd9d279982bfe391b013160a66675ed11debd83c32a9efa02351eee65ac96d09254ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401400101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000202b5cbee48cac714ff220fafa0cd4304f452e5c5b63dc5cd4f27072cc4ba7e9376f1f5aac2480c10e93e13f3be00b38523dc036eb6ae49b93f815140ee2b08a27254ae75affff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401410101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020aebd16a2c6cf18789e213338ee08bcca42d89e7c9f9220cf80e803cf9b67205eb58e2687eae141c7bc9656b434bc95900a6935f00db43dc23ac155e380af8e9e254ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401420101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020984d2d236668762da13fcb8f811401c1928a1df9a9896a4cc382912669b0b13642ad8e61dd8e6c702869587acb0d09b3355a1be27cb20ea909f51287f9e1b010254ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401430101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020d8ca9b5782f98937be5967d0c34aabdcdddc0201e17b70e4071e320bbb2c06418d020f678641ba278d49d70430385ede2f830c46aa49ff7a3febe99d2a9896a9264ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401440101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002017dad2d355421342e973abef035c18398d83dceccd6372269f6bbe6e844fcd52d9c4bce498d350184aae9af97f0aef0f366356d50b926c59bc1605ff6f41f144264ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401450101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020ab1775daac96add2d038b5046be91c90b597b8ed11038b2b9da32f47537d106556d820e14b52e6f4fd113554754f3a8d65b80e7dfab2c84c5e7bb41a30c39977264ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401460101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000204dd398d0716e12f3397c9de79cc644ae3d63c6b90c579f5872d76820dd79260fc5808f07c0d1a3e6a23c9e8dc84d58c12758cd61a2f0e8ec3694a1ac08a89bda264ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401470101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020ed9c915c725956194c078ed08ad2508ec34a6b868c21226d14213904bb3c4c50f385dbbdfd18fa6f8355a89c58370c50a71f59f44e2d2e505fb861960d155705264ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401480101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020b2f8525fa7a276f4a078bdad600074c49df4c237aae27cadb60ecf91eb7b8d42c21e035455f17270ff5e2a53c57c8c663f31cdb7a3a929de1499e3117358108f264ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401490101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020897b6cc09bd1b7cb5d42c648498199aba4008875614e021898b121e8e751f34cf0b8a1a4c993ffd133335d84b9aba6159bb8129384a253131d1998f28c3d371b274ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04014a0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000206a9836bb25994ab592060389b119cb8f7a19a64acd27f9665eab93b235803170ec6385bd31ab432ed3a13f7a6cdf0a6c87fe50c614372a58a5a3716c34456e83274ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04014b0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000208202cd4938b8eef65707e3b6cbd025780f7220fdfea8bc897e802da008026d29043cd5b1662309701440c7941d61cfb95d0c98e22ff8ff8b5994149e988ab179274ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04014c0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002074eed60562b3fabf1cfbd4efd86acd62a470dc264b81b98c784dce4a57f56614d713809d3b1678f325563577d3b6dd9ae4f1e5b05b70c3b16abd67d7161d9002274ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04014d0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002072677aab7f4bb3af82841c6a7100df3ac5e8b643d9e88bb271b2da39c575222aadfd5417618dde8d0ee9191c4b110d0c76dcb65eb8adfd3f8a32a35ccceee445274ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04014e0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020508ed0a8978dd8a6a4e55bb5bc27dd8edae3838d4ddba461332da10f0fb901080c5a78ea3fb056e326e873cea75009c29c401fb77a415ee64ae4eca44bd617dd274ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04014f0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000202004607f94d91d8eca99a860ed208c53a4fd53f38889c77dc35b31a676eef1625ee8e5c95cb2105d0b8268ed13eaac6236eb3405df3099fc52d62169d218fcdf284ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401500101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000204374d19a51a307de1344af36d893991965f58779adbc4b1c1045d5d8e14d6d0b034e10cf7e5158f62fe2674c89e8fc4ee94d2da62d81c0ffc8a237e8dfcb3e21284ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401510101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002037320af6c9cc617be33ba517458ae4607b1c0e3e007268669cf5c799e93ebe67f182660cef31f84c0c6b384f084b243a77e2c271be610ac958124076306d9170284ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401520101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000201ea13062078bd8f70060208d6973bd55789cbf2f221e8690f1a78254a597a1434a16aa13a90a872e0dd3046458114dc745e37d2928bd9ff306ce7adb2567efb3284ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401530101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002052fe4aa74774077ea9a1c27fea59dfa12b75f7ad46746f8458eb67acaeb9803921a47de6874e6c2359af86d72abdee12b27bcebe0f1ce48751055005813d3f42284ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401540101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000208ee41caa2f3076233035157fde7c5c2795a81b85d5e27ce7898e301679e91c4d2d38f595981a444c7d868166ae50e371d83b5f59802bebfed056b93aad1b971e284ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401550101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000201a0eea032f4abd74aa1bf1151e2966433175ecdf2d68b750b509305bc5663e3641f55e2175bc1d50b4d8a83f167091059a2a6e7d1a24d8223f50ca41313eef1b294ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401560101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000207403c2bca2c06351f16746d3360a1a7dceb5194d57879caee03a8316ec774a608a5d44cb75516291cdf62cd51a1a9c71fd3d4879737d88216c6c4c6bffb0784a294ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401570101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020f2be7a9ba2c914d5ee97c819cb096aa9d2c715d2f74e9dcf8a12cc83ac48475e4e332a45285f3fff16e8c5cf79107ee110dc2a2f84456205f43d8f4e237a10da294ae75affff7f200400000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401580101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000205fc71ff65dd07485e26129b765937f514315487b27f0e431c5da7de97b397a4f1d984f86e81bd2c9db53c3515b6e3d6a28187b50adffb87df25608b738190bcf294ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401590101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020d7befb40f244eb69f02a3253bcc9b14f5c2697261e158288e7d0b48eab34f12eeac43d0751178d2574aa20506d1b91af13af688a8dfcdf1a48806f63e53b3245294ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04015a0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000204a0082f8508319d99c86cc074cfedcf26fb98f2e6d0c27d602a9d6ed6d04db042163e10278a5cc75003c52739df112f1ea4fe0ed250ace8e544ecde713f47bfe294ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04015b0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000203de23751fef24f5d30565808862dd364e209fc0f7bf83874ebcc8f155e65574acda81243c6a438daeb547e1b36c9a5441e6556bf16bb9bfb839628c39a3785162a4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04015c0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002045addce76c20d564ac3ff6b955e8ecf2185a5aa355a0d3ac7fd29448dcfb9c1ed184bdf283ba671c76c99173e4ec87e45dd97331c9000070edc81093bc4c7c872a4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04015d0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020a7e8079eccbca3b4a4a0136ef27478b45352270f2f8947ab83eea09b06cc826ab29d7b37989b6570510e368a20f4fee2ca0142044f96a027830abdf438ddf7592a4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04015e0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000201f28203982fc7c2fc155361d00ef7e69ac9fa9c35ff3d10e8fd1a0004e9b10528488982a149b5ac96bef6be131e0b2f0ad9269ba66c9b4216901c0b81523123e2a4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04015f0101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020435d68020c01c6088e4013cb4f349009e65c28ffa0630baf5dde1676df55481513a48742e06190847a40cabbda6d48f715802bdb3cbab7ad7a2e5c345d557b082a4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401600101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020e525dd596c4b9ca78d964ef7997063d35af98665f62e9da62ed9c7fb38c9f57f5abfb8140edec21a383e1e3e31288ee0130a86564e5c3da9764594d8365134652a4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401610101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000203d4144c4bd71aa7f71af930a98087d3786e697335fc1eb11177bedcdae72a61549aa4519391183deb58058f99a6abb7638fe81f079b31c4e090d486c49e047ac2b4ae75affff7f200500000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401620101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000020f44e7a48b9f221af95f3295c8dcefc5358934a68dc79e2933dc0794b350cad0a90fad2cd50b41d4ef45e76c2a456b98c180632bb4b44e0cd18ce90679fe54e552b4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401630101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0000002087454276cce83f4d19e0120f6e9728ac5905f7adaf6b27e3f5bbe43ab823f85db7d1f44666531483df3d67c15f2c231718ad93b63b851dce5ff4c4a67f524ffa2b4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401640101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000202cdc3e99f07a80252dd6097faa0eddf3f2dde5ae390610e0bca94ecc25931551d31fceb8fe0a682f6017ca3dbb582f3a2f06e5d99ec99c42c8a744dd4c9216b82b4ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401650101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
+ "000000209b3ace9bd510918d20e87518c0cf5976cab3e28cc7af41259a89c6dd7668a32922808b8a082be71bcd6152cb8fd223650b5579a41344ba749e4d17b9bf211a9e2b4ae75affff7f200000000002020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401660101ffffffff026c03062a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9edb85d8f3c122c43a72f1e0dd122c8f7af040aa0b0a46001621110fb37818021510120000000000000000000000000000000000000000000000000000000000000000000000000020000000128394022bf44bff30d7399cb5a16e3b94fed67dc174c2e1d77df91bad5a51cb3000000006a47304402201c16d06a5c4353168b3881071aea7d1eb4d88eedfea53a9d6af9abb56da9060002205abf3ae535f1f1b5cfe8ba955535c2b20ac003e7d7720c5b7d2640ac2a04d19001210227d85ba011276cf25b51df6a188b75e604b38770a462b2d0e9fb2fc839ef5d3ffeffffff0294b89a3b000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac00286bee0000000017a91452bab4f229415d0dc5c6d30b162f93a1a0cac5958765000000",
+ "000000200fa168b50a79ad24378a6b0f96e4c9f4ccb657a2663320d5fc1efd8ee7caa10ab42a31c444f2153387530a0979d4dc3dcc134b394c821227b8abff930c03c8412b4ae75affff7f200200000004020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401670101ffffffff02e015072a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ed20376d4bc90f9c689850eec3603cda658ba6295241730473ceb0e970b8d594150120000000000000000000000000000000000000000000000000000000000000000000000000020000000191e549a6cc852bbf1d3f11144b1a34079f64305e6971d2e685d2b40cd386e8a6000000006a47304402200bf62021c0a9a47ced8eba1e0998f5c71b2950763198d83ad284bd791241dbb00220446a05b7c35e7458924de88a8dcccab1ec6a106aa005345e55b482d8eb66337301210227d85ba011276cf25b51df6a188b75e604b38770a462b2d0e9fb2fc839ef5d3ffeffffff02acdbf405000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac94d7a4350000000017a914dfa6f0b17d2c64962c94203e744a9d4179ed22c18766000000020000000112d2f07672102dc6f099c4be308f598e4c4da1a7e0cb462ae14f0444525a1332000000006a47304402200a6a2f544f3f9d299608a7c745e2326de176fb1cac03ae3e74943f4250b8896e02205023a5b4faff99865bf91f1263605a502c723628be9240c0b7bec81d2ed106f101210227d85ba011276cf25b51df6a188b75e604b38770a462b2d0e9fb2fc839ef5d3ffeffffff0200ca9a3b000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac94166bee0000000017a914152cc82f7944f5c416de7dbffb052f7081765d7987660000000200000000010191e549a6cc852bbf1d3f11144b1a34079f64305e6971d2e685d2b40cd386e8a601000000171600147cc872ad7350c37fecab9c4c6d9f08aceb53bdb8feffffff02005ed0b20000000017a914aab1c8c53fe62e283a53efa28097709f4f2ed37b87e0bc9a3b000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0247304402201b4476f238ed5d515bfcd6927d0d008a4993770763eca73e3ee66f69971831d902200f5215a6dfd90391dd63462cfdf69804fe31224c309ec9c38d33a04dce71c0ee0121028c9d2955a95301b699db62e97d54bf0a91feb44e5cd94bbf5b62f1df57fb643966000000"
],
"mocktime": 1525107225,
"stats": [
@@ -111,7 +111,7 @@
"avgfee": 0,
"avgfeerate": 0,
"avgtxsize": 0,
- "blockhash": "1d7fe80f19d28b8e712af0399ac84006db753441f3033111b3a8d610afab364f",
+ "blockhash": "29a36876ddc6899a2541afc78ce2b3ca7659cfc01875e8208d9110d59bce3a9b",
"feerate_percentiles": [
0,
0,
@@ -142,14 +142,13 @@
"totalfee": 0,
"txs": 1,
"utxo_increase": 2,
- "utxo_size_inc": 173
+ "utxo_size_inc": 163
},
{
- "avgfee": 3760,
+ "avgfee": 4460,
"avgfeerate": 20,
- "avgtxsize": 187,
- "blockhash": "4e21a43675d7a41cb6b944e068c5bcd0a677baf658d9ebe021ae2d2f99397ccc",
- "height": 102,
+ "avgtxsize": 223,
+ "blockhash": "0aa1cae78efd1efcd5203366a257b6ccf4c9e4960f6b8a3724ad790ab568a10f",
"feerate_percentiles": [
20,
20,
@@ -157,35 +156,36 @@
20,
20
],
+ "height": 102,
"ins": 1,
- "maxfee": 3760,
+ "maxfee": 4460,
"maxfeerate": 20,
- "maxtxsize": 187,
- "medianfee": 3760,
+ "maxtxsize": 223,
+ "medianfee": 4460,
"mediantime": 1525107242,
- "mediantxsize": 187,
- "minfee": 3760,
+ "mediantxsize": 223,
+ "minfee": 4460,
"minfeerate": 20,
- "mintxsize": 187,
+ "mintxsize": 223,
"outs": 4,
"subsidy": 5000000000,
"swtotal_size": 0,
"swtotal_weight": 0,
"swtxs": 0,
"time": 1525107243,
- "total_out": 4999996240,
- "total_size": 187,
- "total_weight": 748,
- "totalfee": 3760,
+ "total_out": 4999995540,
+ "total_size": 223,
+ "total_weight": 892,
+ "totalfee": 4460,
"txs": 2,
"utxo_increase": 3,
- "utxo_size_inc": 234
+ "utxo_size_inc": 236
},
{
- "avgfee": 18960,
- "avgfeerate": 109,
- "avgtxsize": 228,
- "blockhash": "22d9b8b9c2a37c81515f3fc84f7241f6c07dbcea85ef16b00bcc33ae400a030f",
+ "avgfee": 24906,
+ "avgfeerate": 121,
+ "avgtxsize": 231,
+ "blockhash": "53e416e2538bc783c42a7aea566e884321afed893e9e58cf356d6429759dfa46",
"feerate_percentiles": [
20,
20,
@@ -195,28 +195,28 @@
],
"height": 103,
"ins": 3,
- "maxfee": 49800,
+ "maxfee": 66900,
"maxfeerate": 300,
- "maxtxsize": 248,
- "medianfee": 3760,
+ "maxtxsize": 249,
+ "medianfee": 4460,
"mediantime": 1525107243,
- "mediantxsize": 248,
- "minfee": 3320,
+ "mediantxsize": 223,
+ "minfee": 3360,
"minfeerate": 20,
- "mintxsize": 188,
+ "mintxsize": 223,
"outs": 8,
"subsidy": 5000000000,
- "swtotal_size": 496,
- "swtotal_weight": 1324,
- "swtxs": 2,
+ "swtotal_size": 249,
+ "swtotal_weight": 669,
+ "swtxs": 1,
"time": 1525107243,
- "total_out": 9999939360,
- "total_size": 684,
- "total_weight": 2076,
- "totalfee": 56880,
+ "total_out": 9999920820,
+ "total_size": 695,
+ "total_weight": 2453,
+ "totalfee": 74720,
"txs": 4,
"utxo_increase": 5,
- "utxo_size_inc": 380
+ "utxo_size_inc": 384
}
]
} \ No newline at end of file
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index f367e4fca8..a2726763d0 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""An example functional test
@@ -117,7 +117,7 @@ class ExampleTest(BitcoinTestFramework):
# sync_all() should not include node2, since we're not expecting it to
# sync.
connect_nodes(self.nodes[0], 1)
- self.sync_all([self.nodes[0:2]])
+ self.sync_all(self.nodes[0:2])
# Use setup_nodes() to customize the node start behaviour (for example if
# you don't want to start all nodes at the start of the test).
@@ -141,7 +141,7 @@ class ExampleTest(BitcoinTestFramework):
# Generating a block on one of the nodes will get us out of IBD
blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)]
- self.sync_all([self.nodes[0:2]])
+ self.sync_all(self.nodes[0:2])
# Notice above how we called an RPC by calling a method with the same
# name on the node object. Notice also how we used a keyword argument
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py
index 12a4ce9aff..b7814bf33e 100755
--- a/test/functional/feature_assumevalid.py
+++ b/test/functional/feature_assumevalid.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 skipping signature validation on old blocks.
@@ -32,7 +32,7 @@ Start three nodes:
import time
from test_framework.blocktools import (create_block, create_coinbase)
-from test_framework.key import CECKey
+from test_framework.key import ECKey
from test_framework.messages import (
CBlockHeader,
COutPoint,
@@ -104,9 +104,9 @@ class AssumeValidTest(BitcoinTestFramework):
self.blocks = []
# Get a pubkey for the coinbase TXO
- coinbase_key = CECKey()
- coinbase_key.set_secretbytes(b"horsebattery")
- coinbase_pubkey = coinbase_key.get_pubkey()
+ coinbase_key = ECKey()
+ coinbase_key.generate()
+ coinbase_pubkey = coinbase_key.get_pubkey().get_bytes()
# Create the first block with a coinbase output to our key
height = 1
@@ -180,7 +180,7 @@ class AssumeValidTest(BitcoinTestFramework):
for i in range(2202):
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.
- p2p1.sync_with_ping(150)
+ p2p1.sync_with_ping(200)
assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202)
# Send blocks to node2. Block 102 will be rejected.
diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py
index 8466f851ca..d38eca6cbe 100755
--- a/test/functional/feature_bip68_sequence.py
+++ b/test/functional/feature_bip68_sequence.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 BIP68 implementation."""
@@ -10,7 +10,13 @@ from test_framework.blocktools import create_block, create_coinbase, add_witness
from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, FromHex, ToHex
from test_framework.script import CScript
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_greater_than, assert_raises_rpc_error, bytes_to_hex_str, get_bip9_status, satoshi_round, sync_blocks
+from test_framework.util import (
+ assert_equal,
+ assert_greater_than,
+ assert_raises_rpc_error,
+ get_bip9_status,
+ satoshi_round,
+)
SEQUENCE_LOCKTIME_DISABLE_FLAG = (1<<31)
SEQUENCE_LOCKTIME_TYPE_FLAG = (1<<22) # this means use time (0 means height)
@@ -63,7 +69,7 @@ class BIP68Test(BitcoinTestFramework):
self.nodes[0].sendtoaddress(new_addr, 2) # send 2 BTC
utxos = self.nodes[0].listunspent(0, 0)
- assert(len(utxos) > 0)
+ assert len(utxos) > 0
utxo = utxos[0]
@@ -253,7 +259,7 @@ class BIP68Test(BitcoinTestFramework):
self.nodes[0].generate(1)
cur_time += 600
- assert(tx2.hash in self.nodes[0].getrawmempool())
+ assert tx2.hash in self.nodes[0].getrawmempool()
test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=True)
test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False)
@@ -264,23 +270,23 @@ class BIP68Test(BitcoinTestFramework):
# Advance the time on the node so that we can test timelocks
self.nodes[0].setmocktime(cur_time+600)
self.nodes[0].generate(1)
- assert(tx2.hash not in self.nodes[0].getrawmempool())
+ assert tx2.hash not in self.nodes[0].getrawmempool()
# Now that tx2 is not in the mempool, a sequence locked spend should
# succeed
tx3 = test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False)
- assert(tx3.hash in self.nodes[0].getrawmempool())
+ assert tx3.hash in self.nodes[0].getrawmempool()
self.nodes[0].generate(1)
- assert(tx3.hash not in self.nodes[0].getrawmempool())
+ assert tx3.hash not in self.nodes[0].getrawmempool()
# One more test, this time using height locks
tx4 = test_nonzero_locks(tx3, self.nodes[0], self.relayfee, use_height_lock=True)
- assert(tx4.hash in self.nodes[0].getrawmempool())
+ assert tx4.hash in self.nodes[0].getrawmempool()
# Now try combining confirmed and unconfirmed inputs
tx5 = test_nonzero_locks(tx4, self.nodes[0], self.relayfee, use_height_lock=True)
- assert(tx5.hash not in self.nodes[0].getrawmempool())
+ assert tx5.hash not in self.nodes[0].getrawmempool()
utxos = self.nodes[0].listunspent()
tx5.vin.append(CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]), nSequence=1))
@@ -299,8 +305,8 @@ class BIP68Test(BitcoinTestFramework):
# If we invalidate the tip, tx3 should get added to the mempool, causing
# tx4 to be removed (fails sequence-lock).
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
- assert(tx4.hash not in self.nodes[0].getrawmempool())
- assert(tx3.hash in self.nodes[0].getrawmempool())
+ assert tx4.hash not in self.nodes[0].getrawmempool()
+ assert tx3.hash in self.nodes[0].getrawmempool()
# Now mine 2 empty blocks to reorg out the current tip (labeled tip-1 in
# diagram above).
@@ -319,8 +325,8 @@ class BIP68Test(BitcoinTestFramework):
cur_time += 1
mempool = self.nodes[0].getrawmempool()
- assert(tx3.hash not in mempool)
- assert(tx2.hash in mempool)
+ assert tx3.hash not in mempool
+ assert tx2.hash in mempool
# Reset the chain and get rid of the mocktimed-blocks
self.nodes[0].setmocktime(0)
@@ -332,7 +338,7 @@ class BIP68Test(BitcoinTestFramework):
# being run, then it's possible the test has activated the soft fork, and
# this test should be moved to run earlier, or deleted.
def test_bip68_not_consensus(self):
- assert(get_bip9_status(self.nodes[0], 'csv')['status'] != 'active')
+ assert get_bip9_status(self.nodes[0], 'csv')['status'] != 'active'
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)
tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
@@ -372,7 +378,7 @@ class BIP68Test(BitcoinTestFramework):
add_witness_commitment(block)
block.solve()
- self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True)))
+ self.nodes[0].submitblock(block.serialize().hex())
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
def activateCSV(self):
@@ -385,7 +391,7 @@ class BIP68Test(BitcoinTestFramework):
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)
+ self.sync_blocks()
# Use self.nodes[1] to test that version 2 transactions are standard.
def test_version2_relay(self):
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 5253ff7aaa..3ad83cd2b3 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -14,7 +14,7 @@ from test_framework.blocktools import (
get_legacy_sigopcount_block,
MAX_BLOCK_SIGOPS,
)
-from test_framework.key import CECKey
+from test_framework.key import ECKey
from test_framework.messages import (
CBlock,
COIN,
@@ -86,9 +86,9 @@ class FullBlockTest(BitcoinTestFramework):
self.bootstrap_p2p() # Add one p2p connection to the node
self.block_heights = {}
- self.coinbase_key = CECKey()
- self.coinbase_key.set_secretbytes(b"horsebattery")
- self.coinbase_pubkey = self.coinbase_key.get_pubkey()
+ self.coinbase_key = ECKey()
+ self.coinbase_key.generate()
+ self.coinbase_pubkey = self.coinbase_key.get_pubkey().get_bytes()
self.tip = None
self.blocks = {}
self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
@@ -98,7 +98,7 @@ class FullBlockTest(BitcoinTestFramework):
# Create a new block
b0 = self.next_block(0)
self.save_spendable_output()
- self.sync_blocks([b0])
+ self.send_blocks([b0])
# These constants chosen specifically to trigger an immature coinbase spend
# at a certain time below.
@@ -110,7 +110,7 @@ class FullBlockTest(BitcoinTestFramework):
for i in range(NUM_BUFFER_BLOCKS_TO_GENERATE):
blocks.append(self.next_block("maturitybuffer.{}".format(i)))
self.save_spendable_output()
- self.sync_blocks(blocks)
+ self.send_blocks(blocks)
# collect spendable outputs now to avoid cluttering the code later on
out = []
@@ -126,7 +126,7 @@ class FullBlockTest(BitcoinTestFramework):
b2 = self.next_block(2, spend=out[1])
self.save_spendable_output()
- self.sync_blocks([b1, b2], timeout=4)
+ self.send_blocks([b1, b2], timeout=4)
# Select a txn with an output eligible for spending. This won't actually be spent,
# since we're testing submission of a series of blocks with invalid txns.
@@ -146,23 +146,9 @@ class FullBlockTest(BitcoinTestFramework):
badtx = template.get_tx()
if TxTemplate != invalid_txs.InputMissing:
self.sign_tx(badtx, attempt_spend_tx)
- else:
- # Segwit is active in regtest at this point, so to deserialize a
- # transaction without any inputs correctly, we set the outputs
- # to an empty list. This is a hack, as the serialization of an
- # empty list of outputs is deserialized as flags==0 and thus
- # deserialization of the outputs is skipped.
- # A policy check requires "loose" txs to be of a minimum size,
- # so vtx is not set to be empty in the TxTemplate class and we
- # only apply the workaround where txs are not "loose", i.e. in
- # blocks.
- #
- # The workaround has the purpose that both sides calculate
- # the same tx hash in the merkle tree
- badtx.vout = []
badtx.rehash()
badblock = self.update_block(blockname, [badtx])
- self.sync_blocks(
+ self.send_blocks(
[badblock], success=False,
reject_reason=(template.block_reject_reason or template.reject_reason),
reconnect=True, timeout=2)
@@ -179,7 +165,7 @@ class FullBlockTest(BitcoinTestFramework):
self.move_tip(1)
b3 = self.next_block(3, spend=out[1])
txout_b3 = b3.vtx[1]
- self.sync_blocks([b3], False)
+ self.send_blocks([b3], False)
# Now we add another block to make the alternative chain longer.
#
@@ -187,7 +173,7 @@ class FullBlockTest(BitcoinTestFramework):
# \-> b3 (1) -> b4 (2)
self.log.info("Reorg to a longer chain")
b4 = self.next_block(4, spend=out[2])
- self.sync_blocks([b4])
+ self.send_blocks([b4])
# ... and back to the first chain.
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@@ -195,11 +181,11 @@ class FullBlockTest(BitcoinTestFramework):
self.move_tip(2)
b5 = self.next_block(5, spend=out[2])
self.save_spendable_output()
- self.sync_blocks([b5], False)
+ self.send_blocks([b5], False)
self.log.info("Reorg back to the original chain")
b6 = self.next_block(6, spend=out[3])
- self.sync_blocks([b6], True)
+ self.send_blocks([b6], True)
# Try to create a fork that double-spends
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@@ -208,10 +194,10 @@ class FullBlockTest(BitcoinTestFramework):
self.log.info("Reject a chain with a double spend, even if it is longer")
self.move_tip(5)
b7 = self.next_block(7, spend=out[2])
- self.sync_blocks([b7], False)
+ self.send_blocks([b7], False)
b8 = self.next_block(8, spend=out[4])
- self.sync_blocks([b8], False, reconnect=True)
+ self.send_blocks([b8], False, reconnect=True)
# Try to create a block that has too much fee
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@@ -220,7 +206,7 @@ class FullBlockTest(BitcoinTestFramework):
self.log.info("Reject a block where the miner creates too much coinbase reward")
self.move_tip(6)
b9 = self.next_block(9, spend=out[4], additional_coinbase_value=1)
- self.sync_blocks([b9], success=False, reject_reason='bad-cb-amount', reconnect=True)
+ self.send_blocks([b9], success=False, reject_reason='bad-cb-amount', reconnect=True)
# Create a fork that ends in a block with too much fee (the one that causes the reorg)
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@@ -229,10 +215,10 @@ class FullBlockTest(BitcoinTestFramework):
self.log.info("Reject a chain where the miner creates too much coinbase reward, even if the chain is longer")
self.move_tip(5)
b10 = self.next_block(10, spend=out[3])
- self.sync_blocks([b10], False)
+ self.send_blocks([b10], False)
b11 = self.next_block(11, spend=out[4], additional_coinbase_value=1)
- self.sync_blocks([b11], success=False, reject_reason='bad-cb-amount', reconnect=True)
+ self.send_blocks([b11], success=False, reject_reason='bad-cb-amount', reconnect=True)
# Try again, but with a valid fork first
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@@ -245,7 +231,7 @@ class FullBlockTest(BitcoinTestFramework):
b13 = self.next_block(13, spend=out[4])
self.save_spendable_output()
b14 = self.next_block(14, spend=out[5], additional_coinbase_value=1)
- self.sync_blocks([b12, b13, b14], success=False, reject_reason='bad-cb-amount', reconnect=True)
+ self.send_blocks([b12, b13, b14], success=False, reject_reason='bad-cb-amount', reconnect=True)
# New tip should be b13.
assert_equal(node.getbestblockhash(), b13.hash)
@@ -259,12 +245,12 @@ class FullBlockTest(BitcoinTestFramework):
self.move_tip(13)
b15 = self.next_block(15, spend=out[5], script=lots_of_checksigs)
self.save_spendable_output()
- self.sync_blocks([b15], True)
+ self.send_blocks([b15], True)
self.log.info("Reject a block with too many checksigs")
too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS))
b16 = self.next_block(16, spend=out[6], script=too_many_checksigs)
- self.sync_blocks([b16], success=False, reject_reason='bad-blk-sigops', reconnect=True)
+ self.send_blocks([b16], success=False, reject_reason='bad-blk-sigops', reconnect=True)
# Attempt to spend a transaction created on a different fork
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@@ -273,7 +259,7 @@ class FullBlockTest(BitcoinTestFramework):
self.log.info("Reject a block with a spend from a re-org'ed out tx")
self.move_tip(15)
b17 = self.next_block(17, spend=txout_b3)
- self.sync_blocks([b17], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
+ self.send_blocks([b17], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
# Attempt to spend a transaction created on a different fork (on a fork this time)
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@@ -283,10 +269,10 @@ class FullBlockTest(BitcoinTestFramework):
self.log.info("Reject a block with a spend from a re-org'ed out tx (on a forked chain)")
self.move_tip(13)
b18 = self.next_block(18, spend=txout_b3)
- self.sync_blocks([b18], False)
+ self.send_blocks([b18], False)
b19 = self.next_block(19, spend=out[6])
- self.sync_blocks([b19], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
+ self.send_blocks([b19], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
# Attempt to spend a coinbase at depth too low
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@@ -295,7 +281,7 @@ class FullBlockTest(BitcoinTestFramework):
self.log.info("Reject a block spending an immature coinbase.")
self.move_tip(15)
b20 = self.next_block(20, spend=out[7])
- self.sync_blocks([b20], success=False, reject_reason='bad-txns-premature-spend-of-coinbase')
+ self.send_blocks([b20], success=False, reject_reason='bad-txns-premature-spend-of-coinbase', reconnect=True)
# Attempt to spend a coinbase at depth too low (on a fork this time)
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@@ -305,10 +291,10 @@ class FullBlockTest(BitcoinTestFramework):
self.log.info("Reject a block spending an immature coinbase (on a forked chain)")
self.move_tip(13)
b21 = self.next_block(21, spend=out[6])
- self.sync_blocks([b21], False)
+ self.send_blocks([b21], False)
b22 = self.next_block(22, spend=out[5])
- self.sync_blocks([b22], success=False, reject_reason='bad-txns-premature-spend-of-coinbase')
+ self.send_blocks([b22], success=False, reject_reason='bad-txns-premature-spend-of-coinbase', reconnect=True)
# Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@@ -326,7 +312,7 @@ class FullBlockTest(BitcoinTestFramework):
b23 = self.update_block(23, [tx])
# Make sure the math above worked out to produce a max-sized block
assert_equal(len(b23.serialize()), MAX_BLOCK_BASE_SIZE)
- self.sync_blocks([b23], True)
+ self.send_blocks([b23], True)
self.save_spendable_output()
self.log.info("Reject a block of size MAX_BLOCK_BASE_SIZE + 1")
@@ -337,10 +323,10 @@ class FullBlockTest(BitcoinTestFramework):
tx.vout = [CTxOut(0, script_output)]
b24 = self.update_block(24, [tx])
assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE + 1)
- self.sync_blocks([b24], success=False, reject_reason='bad-blk-length', reconnect=True)
+ self.send_blocks([b24], success=False, reject_reason='bad-blk-length', reconnect=True)
b25 = self.next_block(25, spend=out[7])
- self.sync_blocks([b25], False)
+ self.send_blocks([b25], False)
# Create blocks with a coinbase input script size out of range
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@@ -355,11 +341,11 @@ class FullBlockTest(BitcoinTestFramework):
# update_block causes the merkle root to get updated, even with no new
# transactions, and updates the required state.
b26 = self.update_block(26, [])
- self.sync_blocks([b26], success=False, reject_reason='bad-cb-length', reconnect=True)
+ self.send_blocks([b26], success=False, reject_reason='bad-cb-length', reconnect=True)
# Extend the b26 chain to make sure bitcoind isn't accepting b26
b27 = self.next_block(27, spend=out[7])
- self.sync_blocks([b27], False)
+ self.send_blocks([b27], False)
# Now try a too-large-coinbase script
self.move_tip(15)
@@ -367,11 +353,11 @@ class FullBlockTest(BitcoinTestFramework):
b28.vtx[0].vin[0].scriptSig = b'\x00' * 101
b28.vtx[0].rehash()
b28 = self.update_block(28, [])
- self.sync_blocks([b28], success=False, reject_reason='bad-cb-length', reconnect=True)
+ self.send_blocks([b28], success=False, reject_reason='bad-cb-length', reconnect=True)
# Extend the b28 chain to make sure bitcoind isn't accepting b28
b29 = self.next_block(29, spend=out[7])
- self.sync_blocks([b29], False)
+ self.send_blocks([b29], False)
# b30 has a max-sized coinbase scriptSig.
self.move_tip(23)
@@ -379,7 +365,7 @@ class FullBlockTest(BitcoinTestFramework):
b30.vtx[0].vin[0].scriptSig = b'\x00' * 100
b30.vtx[0].rehash()
b30 = self.update_block(30, [])
- self.sync_blocks([b30], True)
+ self.send_blocks([b30], True)
self.save_spendable_output()
# b31 - b35 - check sigops of OP_CHECKMULTISIG / OP_CHECKMULTISIGVERIFY / OP_CHECKSIGVERIFY
@@ -395,7 +381,7 @@ class FullBlockTest(BitcoinTestFramework):
lots_of_multisigs = CScript([OP_CHECKMULTISIG] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19)
b31 = self.next_block(31, spend=out[8], script=lots_of_multisigs)
assert_equal(get_legacy_sigopcount_block(b31), MAX_BLOCK_SIGOPS)
- self.sync_blocks([b31], True)
+ self.send_blocks([b31], True)
self.save_spendable_output()
# this goes over the limit because the coinbase has one sigop
@@ -403,33 +389,33 @@ class FullBlockTest(BitcoinTestFramework):
too_many_multisigs = CScript([OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS // 20))
b32 = self.next_block(32, spend=out[9], script=too_many_multisigs)
assert_equal(get_legacy_sigopcount_block(b32), MAX_BLOCK_SIGOPS + 1)
- self.sync_blocks([b32], success=False, reject_reason='bad-blk-sigops', reconnect=True)
+ self.send_blocks([b32], success=False, reject_reason='bad-blk-sigops', reconnect=True)
# CHECKMULTISIGVERIFY
self.log.info("Accept a block with the max number of OP_CHECKMULTISIGVERIFY sigops")
self.move_tip(31)
lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19)
b33 = self.next_block(33, spend=out[9], script=lots_of_multisigs)
- self.sync_blocks([b33], True)
+ self.send_blocks([b33], True)
self.save_spendable_output()
self.log.info("Reject a block with too many OP_CHECKMULTISIGVERIFY sigops")
too_many_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * (MAX_BLOCK_SIGOPS // 20))
b34 = self.next_block(34, spend=out[10], script=too_many_multisigs)
- self.sync_blocks([b34], success=False, reject_reason='bad-blk-sigops', reconnect=True)
+ self.send_blocks([b34], success=False, reject_reason='bad-blk-sigops', reconnect=True)
# CHECKSIGVERIFY
self.log.info("Accept a block with the max number of OP_CHECKSIGVERIFY sigops")
self.move_tip(33)
lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS - 1))
b35 = self.next_block(35, spend=out[10], script=lots_of_checksigs)
- self.sync_blocks([b35], True)
+ self.send_blocks([b35], True)
self.save_spendable_output()
self.log.info("Reject a block with too many OP_CHECKSIGVERIFY sigops")
too_many_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS))
b36 = self.next_block(36, spend=out[11], script=too_many_checksigs)
- self.sync_blocks([b36], success=False, reject_reason='bad-blk-sigops', reconnect=True)
+ self.send_blocks([b36], success=False, reject_reason='bad-blk-sigops', reconnect=True)
# Check spending of a transaction in a block which failed to connect
#
@@ -446,12 +432,12 @@ class FullBlockTest(BitcoinTestFramework):
txout_b37 = b37.vtx[1]
tx = self.create_and_sign_transaction(out[11], 0)
b37 = self.update_block(37, [tx])
- self.sync_blocks([b37], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
+ self.send_blocks([b37], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
# attempt to spend b37's first non-coinbase tx, at which point b37 was still considered valid
self.move_tip(35)
b38 = self.next_block(38, spend=txout_b37)
- self.sync_blocks([b38], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
+ self.send_blocks([b38], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
# Check P2SH SigOp counting
#
@@ -501,7 +487,7 @@ class FullBlockTest(BitcoinTestFramework):
b39_outputs += 1
b39 = self.update_block(39, [])
- self.sync_blocks([b39], True)
+ self.send_blocks([b39], True)
self.save_spendable_output()
# Test sigops in P2SH redeem scripts
@@ -528,7 +514,7 @@ class FullBlockTest(BitcoinTestFramework):
tx.vin.append(CTxIn(COutPoint(b39.vtx[i].sha256, 0), b''))
# Note: must pass the redeem_script (not p2sh_script) to the signature hash function
(sighash, err) = SignatureHash(redeem_script, tx, 1, SIGHASH_ALL)
- sig = self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))
+ sig = self.coinbase_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL]))
scriptSig = CScript([sig, redeem_script])
tx.vin[1].scriptSig = scriptSig
@@ -543,7 +529,7 @@ class FullBlockTest(BitcoinTestFramework):
tx.rehash()
new_txs.append(tx)
self.update_block(40, new_txs)
- self.sync_blocks([b40], success=False, reject_reason='bad-blk-sigops', reconnect=True)
+ self.send_blocks([b40], success=False, reject_reason='bad-blk-sigops', reconnect=True)
# same as b40, but one less sigop
self.log.info("Accept a block with the max number of P2SH sigops")
@@ -556,7 +542,7 @@ class FullBlockTest(BitcoinTestFramework):
tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b41_sigops_to_fill)))
tx.rehash()
self.update_block(41, [tx])
- self.sync_blocks([b41], True)
+ self.send_blocks([b41], True)
# Fork off of b39 to create a constant base again
#
@@ -569,7 +555,7 @@ class FullBlockTest(BitcoinTestFramework):
b43 = self.next_block(43, spend=out[13])
self.save_spendable_output()
- self.sync_blocks([b42, b43], True)
+ self.send_blocks([b42, b43], True)
# Test a number of really invalid scenarios
#
@@ -591,7 +577,7 @@ class FullBlockTest(BitcoinTestFramework):
self.tip = b44
self.block_heights[b44.sha256] = height
self.blocks[44] = b44
- self.sync_blocks([b44], True)
+ self.send_blocks([b44], True)
self.log.info("Reject a block with a non-coinbase as the first tx")
non_coinbase = self.create_tx(out[15], 0, 1)
@@ -606,7 +592,7 @@ class FullBlockTest(BitcoinTestFramework):
self.block_heights[b45.sha256] = self.block_heights[self.tip.sha256] + 1
self.tip = b45
self.blocks[45] = b45
- self.sync_blocks([b45], success=False, reject_reason='bad-cb-missing', reconnect=True)
+ self.send_blocks([b45], success=False, reject_reason='bad-cb-missing', reconnect=True)
self.log.info("Reject a block with no transactions")
self.move_tip(44)
@@ -621,7 +607,7 @@ class FullBlockTest(BitcoinTestFramework):
self.tip = b46
assert 46 not in self.blocks
self.blocks[46] = b46
- self.sync_blocks([b46], success=False, reject_reason='bad-blk-length', reconnect=True)
+ self.send_blocks([b46], success=False, reject_reason='bad-blk-length', reconnect=True)
self.log.info("Reject a block with invalid work")
self.move_tip(44)
@@ -630,35 +616,35 @@ class FullBlockTest(BitcoinTestFramework):
while b47.sha256 < target:
b47.nNonce += 1
b47.rehash()
- self.sync_blocks([b47], False, force_send=True, reject_reason='high-hash')
+ self.send_blocks([b47], False, force_send=True, reject_reason='high-hash', reconnect=True)
self.log.info("Reject a block with a timestamp >2 hours in the future")
self.move_tip(44)
b48 = self.next_block(48, solve=False)
b48.nTime = int(time.time()) + 60 * 60 * 3
b48.solve()
- self.sync_blocks([b48], False, force_send=True, reject_reason='time-too-new')
+ self.send_blocks([b48], False, force_send=True, reject_reason='time-too-new')
self.log.info("Reject a block with invalid merkle hash")
self.move_tip(44)
b49 = self.next_block(49)
b49.hashMerkleRoot += 1
b49.solve()
- self.sync_blocks([b49], success=False, reject_reason='bad-txnmrklroot', reconnect=True)
+ self.send_blocks([b49], success=False, reject_reason='bad-txnmrklroot', reconnect=True)
self.log.info("Reject a block with incorrect POW limit")
self.move_tip(44)
b50 = self.next_block(50)
b50.nBits = b50.nBits - 1
b50.solve()
- self.sync_blocks([b50], False, force_send=True, reject_reason='bad-diffbits', reconnect=True)
+ self.send_blocks([b50], False, force_send=True, reject_reason='bad-diffbits', reconnect=True)
self.log.info("Reject a block with two coinbase transactions")
self.move_tip(44)
b51 = self.next_block(51)
cb2 = create_coinbase(51, self.coinbase_pubkey)
b51 = self.update_block(51, [cb2])
- self.sync_blocks([b51], success=False, reject_reason='bad-cb-multiple', reconnect=True)
+ self.send_blocks([b51], success=False, reject_reason='bad-cb-multiple', reconnect=True)
self.log.info("Reject a block with duplicate transactions")
# Note: txns have to be in the right position in the merkle tree to trigger this error
@@ -666,7 +652,7 @@ class FullBlockTest(BitcoinTestFramework):
b52 = self.next_block(52, spend=out[15])
tx = self.create_tx(b52.vtx[1], 0, 1)
b52 = self.update_block(52, [tx, tx])
- self.sync_blocks([b52], success=False, reject_reason='bad-txns-duplicate', reconnect=True)
+ self.send_blocks([b52], success=False, reject_reason='bad-txns-duplicate', reconnect=True)
# Test block timestamps
# -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15)
@@ -674,21 +660,21 @@ class FullBlockTest(BitcoinTestFramework):
#
self.move_tip(43)
b53 = self.next_block(53, spend=out[14])
- self.sync_blocks([b53], False)
+ self.send_blocks([b53], False)
self.save_spendable_output()
self.log.info("Reject a block with timestamp before MedianTimePast")
b54 = self.next_block(54, spend=out[15])
b54.nTime = b35.nTime - 1
b54.solve()
- self.sync_blocks([b54], False, force_send=True, reject_reason='time-too-old')
+ self.send_blocks([b54], False, force_send=True, reject_reason='time-too-old', reconnect=True)
# valid timestamp
self.move_tip(53)
b55 = self.next_block(55, spend=out[15])
b55.nTime = b35.nTime
self.update_block(55, [])
- self.sync_blocks([b55], True)
+ self.send_blocks([b55], True)
self.save_spendable_output()
# Test Merkle tree malleability
@@ -733,7 +719,7 @@ class FullBlockTest(BitcoinTestFramework):
assert_equal(len(b56.vtx), 3)
b56 = self.update_block(56, [tx1])
assert_equal(b56.hash, b57.hash)
- self.sync_blocks([b56], success=False, reject_reason='bad-txns-duplicate', reconnect=True)
+ self.send_blocks([b56], success=False, reject_reason='bad-txns-duplicate', reconnect=True)
# b57p2 - a good block with 6 tx'es, don't submit until end
self.move_tip(55)
@@ -753,13 +739,13 @@ class FullBlockTest(BitcoinTestFramework):
assert_equal(b56p2.hash, b57p2.hash)
assert_equal(len(b56p2.vtx), 6)
b56p2 = self.update_block("b56p2", [tx3, tx4])
- self.sync_blocks([b56p2], success=False, reject_reason='bad-txns-duplicate', reconnect=True)
+ self.send_blocks([b56p2], success=False, reject_reason='bad-txns-duplicate', reconnect=True)
self.move_tip("57p2")
- self.sync_blocks([b57p2], True)
+ self.send_blocks([b57p2], True)
self.move_tip(57)
- self.sync_blocks([b57], False) # The tip is not updated because 57p2 seen first
+ self.send_blocks([b57], False) # The tip is not updated because 57p2 seen first
self.save_spendable_output()
# Test a few invalid tx types
@@ -773,12 +759,12 @@ class FullBlockTest(BitcoinTestFramework):
self.move_tip(57)
b58 = self.next_block(58, spend=out[17])
tx = CTransaction()
- assert(len(out[17].vout) < 42)
+ assert len(out[17].vout) < 42
tx.vin.append(CTxIn(COutPoint(out[17].sha256, 42), CScript([OP_TRUE]), 0xffffffff))
tx.vout.append(CTxOut(0, b""))
tx.calc_sha256()
b58 = self.update_block(58, [tx])
- self.sync_blocks([b58], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
+ self.send_blocks([b58], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
# tx with output value > input value
self.log.info("Reject a block with a transaction with outputs > inputs")
@@ -786,12 +772,12 @@ class FullBlockTest(BitcoinTestFramework):
b59 = self.next_block(59)
tx = self.create_and_sign_transaction(out[17], 51 * COIN)
b59 = self.update_block(59, [tx])
- self.sync_blocks([b59], success=False, reject_reason='bad-txns-in-belowout', reconnect=True)
+ self.send_blocks([b59], success=False, reject_reason='bad-txns-in-belowout', reconnect=True)
# reset to good chain
self.move_tip(57)
b60 = self.next_block(60, spend=out[17])
- self.sync_blocks([b60], True)
+ self.send_blocks([b60], True)
self.save_spendable_output()
# Test BIP30
@@ -810,7 +796,7 @@ class FullBlockTest(BitcoinTestFramework):
b61.vtx[0].rehash()
b61 = self.update_block(61, [])
assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize())
- self.sync_blocks([b61], success=False, reject_reason='bad-txns-BIP30', reconnect=True)
+ self.send_blocks([b61], success=False, reject_reason='bad-txns-BIP30', reconnect=True)
# Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests)
#
@@ -824,10 +810,10 @@ class FullBlockTest(BitcoinTestFramework):
tx.nLockTime = 0xffffffff # this locktime is non-final
tx.vin.append(CTxIn(COutPoint(out[18].sha256, 0))) # don't set nSequence
tx.vout.append(CTxOut(0, CScript([OP_TRUE])))
- assert(tx.vin[0].nSequence < 0xffffffff)
+ assert tx.vin[0].nSequence < 0xffffffff
tx.calc_sha256()
b62 = self.update_block(62, [tx])
- self.sync_blocks([b62], success=False, reject_reason='bad-txns-nonfinal')
+ self.send_blocks([b62], success=False, reject_reason='bad-txns-nonfinal', reconnect=True)
# Test a non-final coinbase is also rejected
#
@@ -841,7 +827,7 @@ class FullBlockTest(BitcoinTestFramework):
b63.vtx[0].vin[0].nSequence = 0xDEADBEEF
b63.vtx[0].rehash()
b63 = self.update_block(63, [])
- self.sync_blocks([b63], success=False, reject_reason='bad-txns-nonfinal')
+ self.send_blocks([b63], success=False, reject_reason='bad-txns-nonfinal', reconnect=True)
# This checks that a block with a bloated VARINT between the block_header and the array of tx such that
# the block is > MAX_BLOCK_BASE_SIZE with the bloated varint, but <= MAX_BLOCK_BASE_SIZE without the bloated varint,
@@ -875,7 +861,7 @@ class FullBlockTest(BitcoinTestFramework):
tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0)))
b64a = self.update_block("64a", [tx])
assert_equal(len(b64a.serialize()), MAX_BLOCK_BASE_SIZE + 8)
- self.sync_blocks([b64a], success=False, reject_reason='non-canonical ReadCompactSize()')
+ self.send_blocks([b64a], success=False, reject_reason='non-canonical ReadCompactSize()')
# bitcoind doesn't disconnect us for sending a bloated block, but if we subsequently
# resend the header message, it won't send us the getdata message again. Just
@@ -891,7 +877,7 @@ class FullBlockTest(BitcoinTestFramework):
assert_equal(len(b64.serialize()), MAX_BLOCK_BASE_SIZE)
self.blocks[64] = b64
b64 = self.update_block(64, [])
- self.sync_blocks([b64], True)
+ self.send_blocks([b64], True)
self.save_spendable_output()
# Spend an output created in the block itself
@@ -904,7 +890,7 @@ class FullBlockTest(BitcoinTestFramework):
tx1 = self.create_and_sign_transaction(out[19], out[19].vout[0].nValue)
tx2 = self.create_and_sign_transaction(tx1, 0)
b65 = self.update_block(65, [tx1, tx2])
- self.sync_blocks([b65], True)
+ self.send_blocks([b65], True)
self.save_spendable_output()
# Attempt to spend an output created later in the same block
@@ -917,7 +903,7 @@ class FullBlockTest(BitcoinTestFramework):
tx1 = self.create_and_sign_transaction(out[20], out[20].vout[0].nValue)
tx2 = self.create_and_sign_transaction(tx1, 1)
b66 = self.update_block(66, [tx2, tx1])
- self.sync_blocks([b66], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
+ self.send_blocks([b66], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
# Attempt to double-spend a transaction created in a block
#
@@ -932,7 +918,7 @@ class FullBlockTest(BitcoinTestFramework):
tx2 = self.create_and_sign_transaction(tx1, 1)
tx3 = self.create_and_sign_transaction(tx1, 2)
b67 = self.update_block(67, [tx1, tx2, tx3])
- self.sync_blocks([b67], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
+ self.send_blocks([b67], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
# More tests of block subsidy
#
@@ -951,14 +937,14 @@ class FullBlockTest(BitcoinTestFramework):
b68 = self.next_block(68, additional_coinbase_value=10)
tx = self.create_and_sign_transaction(out[20], out[20].vout[0].nValue - 9)
b68 = self.update_block(68, [tx])
- self.sync_blocks([b68], success=False, reject_reason='bad-cb-amount', reconnect=True)
+ self.send_blocks([b68], success=False, reject_reason='bad-cb-amount', reconnect=True)
self.log.info("Accept a block claiming the correct subsidy in the coinbase transaction")
self.move_tip(65)
b69 = self.next_block(69, additional_coinbase_value=10)
tx = self.create_and_sign_transaction(out[20], out[20].vout[0].nValue - 10)
self.update_block(69, [tx])
- self.sync_blocks([b69], True)
+ self.send_blocks([b69], True)
self.save_spendable_output()
# Test spending the outpoint of a non-existent transaction
@@ -975,7 +961,7 @@ class FullBlockTest(BitcoinTestFramework):
tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff))
tx.vout.append(CTxOut(1, b""))
b70 = self.update_block(70, [tx])
- self.sync_blocks([b70], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
+ self.send_blocks([b70], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
# Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks)
#
@@ -1000,10 +986,10 @@ class FullBlockTest(BitcoinTestFramework):
assert_equal(b72.sha256, b71.sha256)
self.move_tip(71)
- self.sync_blocks([b71], success=False, reject_reason='bad-txns-duplicate', reconnect=True)
+ self.send_blocks([b71], success=False, reject_reason='bad-txns-duplicate', reconnect=True)
self.move_tip(72)
- self.sync_blocks([b72], True)
+ self.send_blocks([b72], True)
self.save_spendable_output()
# Test some invalid scripts and MAX_BLOCK_SIGOPS
@@ -1038,7 +1024,7 @@ class FullBlockTest(BitcoinTestFramework):
tx = self.create_and_sign_transaction(out[22], 1, CScript(a))
b73 = self.update_block(73, [tx])
assert_equal(get_legacy_sigopcount_block(b73), MAX_BLOCK_SIGOPS + 1)
- self.sync_blocks([b73], success=False, reject_reason='bad-blk-sigops', reconnect=True)
+ self.send_blocks([b73], success=False, reject_reason='bad-blk-sigops', reconnect=True)
# b74/75 - if we push an invalid script element, all previous sigops are counted,
# but sigops after the element are not counted.
@@ -1062,7 +1048,7 @@ class FullBlockTest(BitcoinTestFramework):
a[MAX_BLOCK_SIGOPS + 4] = 0xff
tx = self.create_and_sign_transaction(out[22], 1, CScript(a))
b74 = self.update_block(74, [tx])
- self.sync_blocks([b74], success=False, reject_reason='bad-blk-sigops', reconnect=True)
+ self.send_blocks([b74], success=False, reject_reason='bad-blk-sigops', reconnect=True)
self.move_tip(72)
b75 = self.next_block(75)
@@ -1075,7 +1061,7 @@ class FullBlockTest(BitcoinTestFramework):
a[MAX_BLOCK_SIGOPS + 3] = 0xff
tx = self.create_and_sign_transaction(out[22], 1, CScript(a))
b75 = self.update_block(75, [tx])
- self.sync_blocks([b75], True)
+ self.send_blocks([b75], True)
self.save_spendable_output()
# Check that if we push an element filled with CHECKSIGs, they are not counted
@@ -1086,7 +1072,7 @@ class FullBlockTest(BitcoinTestFramework):
a[MAX_BLOCK_SIGOPS - 1] = 0x4e # PUSHDATA4, but leave the following bytes as just checksigs
tx = self.create_and_sign_transaction(out[23], 1, CScript(a))
b76 = self.update_block(76, [tx])
- self.sync_blocks([b76], True)
+ self.send_blocks([b76], True)
self.save_spendable_output()
# Test transaction resurrection
@@ -1111,40 +1097,40 @@ class FullBlockTest(BitcoinTestFramework):
b77 = self.next_block(77)
tx77 = self.create_and_sign_transaction(out[24], 10 * COIN)
b77 = self.update_block(77, [tx77])
- self.sync_blocks([b77], True)
+ self.send_blocks([b77], True)
self.save_spendable_output()
b78 = self.next_block(78)
tx78 = self.create_tx(tx77, 0, 9 * COIN)
b78 = self.update_block(78, [tx78])
- self.sync_blocks([b78], True)
+ self.send_blocks([b78], True)
b79 = self.next_block(79)
tx79 = self.create_tx(tx78, 0, 8 * COIN)
b79 = self.update_block(79, [tx79])
- self.sync_blocks([b79], True)
+ self.send_blocks([b79], True)
# mempool should be empty
assert_equal(len(self.nodes[0].getrawmempool()), 0)
self.move_tip(77)
b80 = self.next_block(80, spend=out[25])
- self.sync_blocks([b80], False, force_send=True)
+ self.send_blocks([b80], False, force_send=True)
self.save_spendable_output()
b81 = self.next_block(81, spend=out[26])
- self.sync_blocks([b81], False, force_send=True) # other chain is same length
+ self.send_blocks([b81], False, force_send=True) # other chain is same length
self.save_spendable_output()
b82 = self.next_block(82, spend=out[27])
- self.sync_blocks([b82], True) # now this chain is longer, triggers re-org
+ self.send_blocks([b82], True) # now this chain is longer, triggers re-org
self.save_spendable_output()
# now check that tx78 and tx79 have been put back into the peer's mempool
mempool = self.nodes[0].getrawmempool()
assert_equal(len(mempool), 2)
- assert(tx78.hash in mempool)
- assert(tx79.hash in mempool)
+ assert tx78.hash in mempool
+ assert tx79.hash in mempool
# Test invalid opcodes in dead execution paths.
#
@@ -1161,7 +1147,7 @@ class FullBlockTest(BitcoinTestFramework):
tx2.rehash()
b83 = self.update_block(83, [tx1, tx2])
- self.sync_blocks([b83], True)
+ self.send_blocks([b83], True)
self.save_spendable_output()
# Reorg on/off blocks that have OP_RETURN in them (and try to spend them)
@@ -1188,30 +1174,30 @@ class FullBlockTest(BitcoinTestFramework):
tx5 = self.create_tx(tx1, 4, 0, CScript([OP_RETURN]))
b84 = self.update_block(84, [tx1, tx2, tx3, tx4, tx5])
- self.sync_blocks([b84], True)
+ self.send_blocks([b84], True)
self.save_spendable_output()
self.move_tip(83)
b85 = self.next_block(85, spend=out[29])
- self.sync_blocks([b85], False) # other chain is same length
+ self.send_blocks([b85], False) # other chain is same length
b86 = self.next_block(86, spend=out[30])
- self.sync_blocks([b86], True)
+ self.send_blocks([b86], True)
self.move_tip(84)
b87 = self.next_block(87, spend=out[30])
- self.sync_blocks([b87], False) # other chain is same length
+ self.send_blocks([b87], False) # other chain is same length
self.save_spendable_output()
b88 = self.next_block(88, spend=out[31])
- self.sync_blocks([b88], True)
+ self.send_blocks([b88], True)
self.save_spendable_output()
# trying to spend the OP_RETURN output is rejected
b89a = self.next_block("89a", spend=out[32])
tx = self.create_tx(tx1, 0, 0, CScript([OP_TRUE]))
b89a = self.update_block("89a", [tx])
- self.sync_blocks([b89a], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
+ self.send_blocks([b89a], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
self.log.info("Test a re-org of one week's worth of blocks (1088 blocks)")
@@ -1232,7 +1218,7 @@ class FullBlockTest(BitcoinTestFramework):
self.save_spendable_output()
spend = self.get_spendable_output()
- self.sync_blocks(blocks, True, timeout=480)
+ self.send_blocks(blocks, True, timeout=480)
chain1_tip = i
# now create alt chain of same length
@@ -1240,22 +1226,22 @@ class FullBlockTest(BitcoinTestFramework):
blocks2 = []
for i in range(89, LARGE_REORG_SIZE + 89):
blocks2.append(self.next_block("alt" + str(i), version=4))
- self.sync_blocks(blocks2, False, force_send=True)
+ self.send_blocks(blocks2, False, force_send=True)
# extend alt chain to trigger re-org
block = self.next_block("alt" + str(chain1_tip + 1), version=4)
- self.sync_blocks([block], True, timeout=480)
+ self.send_blocks([block], True, timeout=480)
# ... and re-org back to the first chain
self.move_tip(chain1_tip)
block = self.next_block(chain1_tip + 1, version=4)
- self.sync_blocks([block], False, force_send=True)
+ self.send_blocks([block], False, force_send=True)
block = self.next_block(chain1_tip + 2, version=4)
- self.sync_blocks([block], True, timeout=480)
+ self.send_blocks([block], True, timeout=480)
self.log.info("Reject a block with an invalid block header version")
b_v1 = self.next_block('b_v1', version=1)
- self.sync_blocks([b_v1], success=False, force_send=True, reject_reason='bad-version(0x00000001)')
+ self.send_blocks([b_v1], success=False, force_send=True, reject_reason='bad-version(0x00000001)', reconnect=True)
self.move_tip(chain1_tip + 2)
b_cb34 = self.next_block('b_cb34', version=4)
@@ -1263,7 +1249,7 @@ class FullBlockTest(BitcoinTestFramework):
b_cb34.vtx[0].rehash()
b_cb34.hashMerkleRoot = b_cb34.calc_merkle_root()
b_cb34.solve()
- self.sync_blocks([b_cb34], success=False, reject_reason='bad-cb-height', reconnect=True)
+ self.send_blocks([b_cb34], success=False, reject_reason='bad-cb-height', reconnect=True)
# Helper methods
################
@@ -1284,7 +1270,7 @@ class FullBlockTest(BitcoinTestFramework):
tx.vin[0].scriptSig = CScript()
return
(sighash, err) = SignatureHash(spend_tx.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL)
- tx.vin[0].scriptSig = CScript([self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))])
+ tx.vin[0].scriptSig = CScript([self.coinbase_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL]))])
def create_and_sign_transaction(self, spend_tx, value, script=CScript([OP_TRUE])):
tx = self.create_tx(spend_tx, 0, value, script)
@@ -1372,7 +1358,7 @@ class FullBlockTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps()
self.bootstrap_p2p(timeout=timeout)
- def sync_blocks(self, blocks, success=True, reject_reason=None, force_send=False, reconnect=False, timeout=60):
+ def send_blocks(self, blocks, success=True, reject_reason=None, force_send=False, reconnect=False, timeout=60):
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
Call with success = False if the tip shouldn't advance to the most recent block."""
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index 302a5ec1cb..b16eafccca 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 BIP65 (CHECKLOCKTIMEVERIFY).
@@ -15,7 +15,6 @@ from test_framework.script import CScript, OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, O
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- bytes_to_hex_str,
hex_str_to_bytes,
)
@@ -60,6 +59,7 @@ class BIP65Test(BitcoinTestFramework):
self.num_nodes = 1
self.extra_args = [['-whitelist=127.0.0.1', '-par=1']] # Use only one script thread to get the exact reject reason for testing
self.setup_clean_chain = True
+ self.rpc_timeout = 120
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -113,7 +113,7 @@ class BIP65Test(BitcoinTestFramework):
# rejected from the mempool for exactly that reason.
assert_equal(
[{'txid': spendtx.hash, 'allowed': False, 'reject-reason': '64: non-mandatory-script-verify-flag (Negative locktime)'}],
- self.nodes[0].testmempoolaccept(rawtxs=[bytes_to_hex_str(spendtx.serialize())], allowhighfees=True)
+ self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0)
)
# Now we verify that a block with this transaction is also invalid.
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 4b3f6603a2..2b93e3c24d 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 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 command line arguments and configuration file parameters."""
@@ -25,6 +25,10 @@ class ConfArgsTest(BitcoinTestFramework):
conf.write('-dash=1\n')
self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 1: -dash=1, options in configuration file must be specified without leading -')
+ with open(inc_conf_file_path, 'w', encoding='utf8') as conf:
+ conf.write("wallet=foo\n")
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Config setting for -wallet only applied on regtest network when in [regtest] section.')
+
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('nono\n')
self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 1: nono, if you intended to specify a negated option, use nono=1 instead')
@@ -41,13 +45,21 @@ class ConfArgsTest(BitcoinTestFramework):
conf.write('server=1\nrpcuser=someuser\n[main]\nrpcpassword=some#pass')
self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 4, using # in rpcpassword can be ambiguous and should be avoided')
+ inc_conf_file2_path = os.path.join(self.nodes[0].datadir, 'include2.conf')
+ with open(os.path.join(self.nodes[0].datadir, 'bitcoin.conf'), 'a', encoding='utf-8') as conf:
+ conf.write('includeconf={}\n'.format(inc_conf_file2_path))
+
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
- conf.write('testnot.datadir=1\n[testnet]\n')
+ conf.write('testnot.datadir=1\n')
+ with open(inc_conf_file2_path, 'w', encoding='utf-8') as conf:
+ conf.write('[testnet]\n')
self.restart_node(0)
- self.nodes[0].stop_node(expected_stderr='Warning: Section [testnet] is not recognized.' + os.linesep + 'Warning: Section [testnot] is not recognized.')
+ self.nodes[0].stop_node(expected_stderr='Warning: ' + inc_conf_file_path + ':1 Section [testnot] is not recognized.' + os.linesep + 'Warning: ' + inc_conf_file2_path + ':1 Section [testnet] is not recognized.')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('') # clear
+ with open(inc_conf_file2_path, 'w', encoding='utf-8') as conf:
+ conf.write('') # clear
def run_test(self):
self.stop_node(0)
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index df79c4312c..887e9dafa3 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 activation of the first version bits soft fork.
@@ -106,7 +106,7 @@ def send_generic_input_tx(node, coinbases, address):
def create_bip68txs(node, bip68inputs, txversion, address, locktime_delta=0):
"""Returns a list of bip68 transactions with different bits set."""
txs = []
- assert(len(bip68inputs) >= 16)
+ assert len(bip68inputs) >= 16
for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
locktime = relative_locktime(sdf, srhb, stf, srlb)
tx = create_transaction(node, bip68inputs[i], address, amount=Decimal("49.98"))
@@ -121,7 +121,7 @@ def create_bip68txs(node, bip68inputs, txversion, address, locktime_delta=0):
def create_bip112txs(node, bip112inputs, varyOP_CSV, txversion, address, locktime_delta=0):
"""Returns a list of bip68 transactions with different bits set."""
txs = []
- assert(len(bip112inputs) >= 16)
+ assert len(bip112inputs) >= 16
for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
locktime = relative_locktime(sdf, srhb, stf, srlb)
tx = create_transaction(node, bip112inputs[i], address, amount=Decimal("49.98"))
@@ -168,7 +168,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
block.solve()
return block
- def sync_blocks(self, blocks, success=True):
+ def send_blocks(self, blocks, success=True):
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
Call with success = False if the tip shouldn't advance to the most recent block."""
@@ -190,7 +190,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.log.info("Test that the csv softfork is DEFINED")
assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'defined')
test_blocks = self.generate_blocks(61, 4)
- self.sync_blocks(test_blocks)
+ self.send_blocks(test_blocks)
self.log.info("Advance from DEFINED to STARTED, height = 143")
assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started')
@@ -202,7 +202,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
test_blocks = self.generate_blocks(20, 4, test_blocks) # 0x00000004 (signalling not)
test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready)
test_blocks = self.generate_blocks(24, 536936448, test_blocks) # 0x20010000 (signalling not)
- self.sync_blocks(test_blocks)
+ self.send_blocks(test_blocks)
self.log.info("Failed to advance past STARTED, height = 287")
assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started')
@@ -214,14 +214,14 @@ class BIP68_112_113Test(BitcoinTestFramework):
test_blocks = self.generate_blocks(26, 4, test_blocks) # 0x00000004 (signalling not)
test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready)
test_blocks = self.generate_blocks(10, 536936448, test_blocks) # 0x20010000 (signalling not)
- self.sync_blocks(test_blocks)
+ self.send_blocks(test_blocks)
self.log.info("Advanced from STARTED to LOCKED_IN, height = 431")
assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')
# Generate 140 more version 4 blocks
test_blocks = self.generate_blocks(140, 4)
- self.sync_blocks(test_blocks)
+ self.send_blocks(test_blocks)
# Inputs at height = 572
#
@@ -264,7 +264,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
# 2 more version 4 blocks
test_blocks = self.generate_blocks(2, 4)
- self.sync_blocks(test_blocks)
+ self.send_blocks(test_blocks)
self.log.info("Not yet advanced to ACTIVE, height = 574 (will activate for block 576, not 575)")
assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')
@@ -318,7 +318,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
# try BIP 112 with seq=9 txs
success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v1))
success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_9_v1))
- self.sync_blocks([self.create_test_block(success_txs)])
+ self.send_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
self.log.info("Test version 2 txs")
@@ -337,12 +337,12 @@ class BIP68_112_113Test(BitcoinTestFramework):
# try BIP 112 with seq=9 txs
success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v2))
success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_9_v2))
- self.sync_blocks([self.create_test_block(success_txs)])
+ self.send_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
# 1 more version 4 block to get us to height 575 so the fork should now be active for the next block
test_blocks = self.generate_blocks(1, 4)
- self.sync_blocks(test_blocks)
+ self.send_blocks(test_blocks)
assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'active')
self.log.info("Post-Soft Fork Tests.")
@@ -354,74 +354,74 @@ class BIP68_112_113Test(BitcoinTestFramework):
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
for bip113tx in [bip113signed1, bip113signed2]:
- self.sync_blocks([self.create_test_block([bip113tx])], success=False)
+ self.send_blocks([self.create_test_block([bip113tx])], success=False)
# BIP 113 tests should now pass if the locktime is < MTP
bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
for bip113tx in [bip113signed1, bip113signed2]:
- self.sync_blocks([self.create_test_block([bip113tx])])
+ self.send_blocks([self.create_test_block([bip113tx])])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
# Next block height = 580 after 4 blocks of random version
test_blocks = self.generate_blocks(4, 1234)
- self.sync_blocks(test_blocks)
+ self.send_blocks(test_blocks)
self.log.info("BIP 68 tests")
self.log.info("Test version 1 txs - all should still pass")
success_txs = []
success_txs.extend(all_rlt_txs(bip68txs_v1))
- self.sync_blocks([self.create_test_block(success_txs)])
+ self.send_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
self.log.info("Test version 2 txs")
# All txs with SEQUENCE_LOCKTIME_DISABLE_FLAG set pass
bip68success_txs = [tx['tx'] for tx in bip68txs_v2 if tx['sdf']]
- self.sync_blocks([self.create_test_block(bip68success_txs)])
+ self.send_blocks([self.create_test_block(bip68success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
# All txs without flag fail as we are at delta height = 8 < 10 and delta time = 8 * 600 < 10 * 512
bip68timetxs = [tx['tx'] for tx in bip68txs_v2 if not tx['sdf'] and tx['stf']]
for tx in bip68timetxs:
- self.sync_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False)
bip68heighttxs = [tx['tx'] for tx in bip68txs_v2 if not tx['sdf'] and not tx['stf']]
for tx in bip68heighttxs:
- self.sync_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False)
# Advance one block to 581
test_blocks = self.generate_blocks(1, 1234)
- self.sync_blocks(test_blocks)
+ self.send_blocks(test_blocks)
# Height txs should fail and time txs should now pass 9 * 600 > 10 * 512
bip68success_txs.extend(bip68timetxs)
- self.sync_blocks([self.create_test_block(bip68success_txs)])
+ self.send_blocks([self.create_test_block(bip68success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
for tx in bip68heighttxs:
- self.sync_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False)
# Advance one block to 582
test_blocks = self.generate_blocks(1, 1234)
- self.sync_blocks(test_blocks)
+ self.send_blocks(test_blocks)
# All BIP 68 txs should pass
bip68success_txs.extend(bip68heighttxs)
- self.sync_blocks([self.create_test_block(bip68success_txs)])
+ self.send_blocks([self.create_test_block(bip68success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
self.log.info("BIP 112 tests")
self.log.info("Test version 1 txs")
# -1 OP_CSV tx should fail
- self.sync_blocks([self.create_test_block([bip112tx_special_v1])], success=False)
+ self.send_blocks([self.create_test_block([bip112tx_special_v1])], success=False)
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 1 txs should still pass
success_txs = [tx['tx'] for tx in bip112txs_vary_OP_CSV_v1 if tx['sdf']]
success_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if tx['sdf']]
- self.sync_blocks([self.create_test_block(success_txs)])
+ self.send_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is unset in argument to OP_CSV, version 1 txs should now fail
@@ -430,18 +430,18 @@ class BIP68_112_113Test(BitcoinTestFramework):
fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if not tx['sdf']]
fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if not tx['sdf']]
for tx in fail_txs:
- self.sync_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False)
self.log.info("Test version 2 txs")
# -1 OP_CSV tx should fail
- self.sync_blocks([self.create_test_block([bip112tx_special_v2])], success=False)
+ self.send_blocks([self.create_test_block([bip112tx_special_v2])], success=False)
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 2 txs should pass (all sequence locks are met)
success_txs = [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if tx['sdf']]
success_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v2 if tx['sdf']]
- self.sync_blocks([self.create_test_block(success_txs)])
+ self.send_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
# SEQUENCE_LOCKTIME_DISABLE_FLAG is unset in argument to OP_CSV for all remaining txs ##
@@ -450,23 +450,23 @@ class BIP68_112_113Test(BitcoinTestFramework):
fail_txs = all_rlt_txs(bip112txs_vary_nSequence_9_v2)
fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v2 if not tx['sdf']]
for tx in fail_txs:
- self.sync_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False)
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in nSequence, tx should fail
fail_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if tx['sdf']]
for tx in fail_txs:
- self.sync_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False)
# If sequencelock types mismatch, tx should fail
fail_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if not tx['sdf'] and tx['stf']]
fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and tx['stf']]
for tx in fail_txs:
- self.sync_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False)
# Remaining txs should pass, just test masking works properly
success_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if not tx['sdf'] and not tx['stf']]
success_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and not tx['stf']]
- self.sync_blocks([self.create_test_block(success_txs)])
+ self.send_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
# Additional test, of checking that comparison of two time types works properly
@@ -476,7 +476,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
signtx = sign_transaction(self.nodes[0], tx)
time_txs.append(signtx)
- self.sync_blocks([self.create_test_block(time_txs)])
+ self.send_blocks([self.create_test_block(time_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
# TODO: Test empty stack fails
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index 8b06cc7372..62062926a6 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 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 recovery from a crash during chainstate writing.
@@ -28,18 +28,12 @@
import errno
import http.client
import random
-import sys
import time
from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, ToHex
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, create_confirmed_utxos, hex_str_to_bytes
-HTTP_DISCONNECT_ERRORS = [http.client.CannotSendRequest]
-try:
- HTTP_DISCONNECT_ERRORS.append(http.client.RemoteDisconnected)
-except AttributeError:
- pass
class ChainstateWriteCrashTest(BitcoinTestFramework):
def set_test_params(self):
@@ -110,14 +104,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
try:
self.nodes[node_index].submitblock(block)
return True
- except http.client.BadStatusLine as e:
- # Prior to 3.5 BadStatusLine('') was raised for a remote disconnect error.
- if sys.version_info[0] == 3 and sys.version_info[1] < 5 and e.line == "''":
- self.log.debug("node %d submitblock raised exception: %s", node_index, e)
- return False
- else:
- raise
- except tuple(HTTP_DISCONNECT_ERRORS) as e:
+ except (http.client.CannotSendRequest, http.client.RemoteDisconnected) as e:
self.log.debug("node %d submitblock raised exception: %s", node_index, e)
return False
except OSError as e:
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index 9cbc1b39bd..7480e5c5ba 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 BIP66 (DER SIG).
@@ -14,7 +14,6 @@ from test_framework.script import CScript
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- bytes_to_hex_str,
wait_until,
)
@@ -47,6 +46,7 @@ class BIP66Test(BitcoinTestFramework):
self.num_nodes = 1
self.extra_args = [['-whitelist=127.0.0.1', '-par=1', '-enablebip61']] # Use only one script thread to get the exact reject reason for testing
self.setup_clean_chain = True
+ self.rpc_timeout = 120
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -102,7 +102,7 @@ class BIP66Test(BitcoinTestFramework):
# rejected from the mempool for exactly that reason.
assert_equal(
[{'txid': spendtx.hash, 'allowed': False, 'reject-reason': '64: non-mandatory-script-verify-flag (Non-canonical DER signature)'}],
- self.nodes[0].testmempoolaccept(rawtxs=[bytes_to_hex_str(spendtx.serialize())], allowhighfees=True)
+ self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0)
)
# Now we verify that a block with this transaction is also invalid.
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index b68e46adbc..a4b9f213a1 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 fee estimation code."""
@@ -15,8 +15,6 @@ from test_framework.util import (
assert_greater_than_or_equal,
connect_nodes,
satoshi_round,
- sync_blocks,
- sync_mempools,
)
# Construct 2 trivial P2SH's and the ScriptSigs that spend them
@@ -65,7 +63,7 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee
# the ScriptSig that will satisfy the ScriptPubKey.
for inp in tx.vin:
inp.scriptSig = SCRIPT_SIG[inp.prevout.n]
- txid = from_node.sendrawtransaction(ToHex(tx), True)
+ txid = from_node.sendrawtransaction(hexstring=ToHex(tx), maxfeerate=0)
unconflist.append({"txid": txid, "vout": 0, "amount": total_in - amount - fee})
unconflist.append({"txid": txid, "vout": 1, "amount": amount})
@@ -95,7 +93,7 @@ def split_inputs(from_node, txins, txouts, initial_split=False):
else:
tx.vin[0].scriptSig = SCRIPT_SIG[prevtxout["vout"]]
completetx = ToHex(tx)
- txid = from_node.sendrawtransaction(completetx, True)
+ txid = from_node.sendrawtransaction(hexstring=completetx, maxfeerate=0)
txouts.append({"txid": txid, "vout": 0, "amount": half_change})
txouts.append({"txid": txid, "vout": 1, "amount": rem_change})
@@ -162,9 +160,9 @@ class EstimateFeeTest(BitcoinTestFramework):
self.memutxo, Decimal("0.005"), min_fee, min_fee)
tx_kbytes = (len(txhex) // 2) / 1000.0
self.fees_per_kb.append(float(fee) / tx_kbytes)
- sync_mempools(self.nodes[0:3], wait=.1)
+ self.sync_mempools(self.nodes[0:3], wait=.1)
mined = mining_node.getblock(mining_node.generate(1)[0], True)["tx"]
- sync_blocks(self.nodes[0:3], wait=.1)
+ self.sync_blocks(self.nodes[0:3], wait=.1)
# update which txouts are confirmed
newmem = []
for utx in self.memutxo:
@@ -237,7 +235,7 @@ class EstimateFeeTest(BitcoinTestFramework):
while len(self.nodes[1].getrawmempool()) > 0:
self.nodes[1].generate(1)
- sync_blocks(self.nodes[0:3], wait=.1)
+ self.sync_blocks(self.nodes[0:3], wait=.1)
self.log.info("Final estimates after emptying mempools")
check_estimates(self.nodes[1], self.fees_per_kb)
diff --git a/test/functional/feature_filelock.py b/test/functional/feature_filelock.py
index 9fb0d35a68..ba116e41f5 100755
--- a/test/functional/feature_filelock.py
+++ b/test/functional/feature_filelock.py
@@ -23,7 +23,7 @@ class FilelockTest(BitcoinTestFramework):
self.log.info("Using datadir {}".format(datadir))
self.log.info("Check that we can't start a second bitcoind instance using the same datadir")
- expected_msg = "Error: Cannot obtain a lock on data directory {}. Bitcoin Core is probably already running.".format(datadir)
+ expected_msg = "Error: Cannot obtain a lock on data directory {0}. {1} is probably already running.".format(datadir, self.config['environment']['PACKAGE_NAME'])
self.nodes[1].assert_start_raises_init_error(extra_args=['-datadir={}'.format(self.nodes[0].datadir), '-noserver'], expected_msg=expected_msg)
if self.is_wallet_compiled():
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index d8083b2840..13cf951550 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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."""
@@ -66,23 +66,7 @@ class NotificationsTest(BitcoinTestFramework):
txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir)))
- # Mine another 41 up-version blocks. -alertnotify should trigger on the 51st.
- self.log.info("test -alertnotify")
- self.nodes[1].generatetoaddress(41, ADDRESS_BCRT1_UNSPENDABLE)
- self.sync_all()
-
- # Give bitcoind 10 seconds to write the alert notification
- wait_until(lambda: len(os.listdir(self.alertnotify_dir)), timeout=10)
-
- for notify_file in os.listdir(self.alertnotify_dir):
- os.remove(os.path.join(self.alertnotify_dir, notify_file))
-
- # Mine more up-version blocks, should not get more alerts:
- self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_UNSPENDABLE)
- self.sync_all()
-
- self.log.info("-alertnotify should not continue notifying for more unknown version blocks")
- assert_equal(len(os.listdir(self.alertnotify_dir)), 0)
+ # TODO: add test for `-alertnotify` large fork notifications
if __name__ == '__main__':
NotificationsTest().main()
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index eb76089d9c..60a703c48f 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2018 The Bitcoin Core developers
+# Copyright (c) 2016-2019 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 NULLDUMMY softfork.
@@ -18,7 +18,7 @@ from test_framework.blocktools import create_coinbase, create_block, create_tran
from test_framework.messages import CTransaction
from test_framework.script import CScript
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, bytes_to_hex_str
+from test_framework.util import assert_equal, assert_raises_rpc_error
NULLDUMMY_ERROR = "non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero) (code 64)"
@@ -27,7 +27,7 @@ def trueDummy(tx):
newscript = []
for i in scriptSig:
if (len(newscript) == 0):
- assert(len(i) == 0)
+ assert len(i) == 0
newscript.append(b'\x51')
else:
newscript.append(i)
@@ -64,17 +64,17 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.log.info("Test 1: NULLDUMMY compliant base transactions should be accepted to mempool and mined before activation [430]")
test1txs = [create_transaction(self.nodes[0], coinbase_txid[0], self.ms_address, amount=49)]
- txid1 = self.nodes[0].sendrawtransaction(bytes_to_hex_str(test1txs[0].serialize_with_witness()), True)
+ txid1 = self.nodes[0].sendrawtransaction(test1txs[0].serialize_with_witness().hex(), 0)
test1txs.append(create_transaction(self.nodes[0], txid1, self.ms_address, amount=48))
- txid2 = self.nodes[0].sendrawtransaction(bytes_to_hex_str(test1txs[1].serialize_with_witness()), True)
+ txid2 = self.nodes[0].sendrawtransaction(test1txs[1].serialize_with_witness().hex(), 0)
test1txs.append(create_transaction(self.nodes[0], coinbase_txid[1], self.wit_ms_address, amount=49))
- txid3 = self.nodes[0].sendrawtransaction(bytes_to_hex_str(test1txs[2].serialize_with_witness()), True)
+ txid3 = self.nodes[0].sendrawtransaction(test1txs[2].serialize_with_witness().hex(), 0)
self.block_submit(self.nodes[0], test1txs, False, True)
self.log.info("Test 2: Non-NULLDUMMY base multisig transaction should not be accepted to mempool before activation")
test2tx = create_transaction(self.nodes[0], txid2, self.ms_address, amount=47)
trueDummy(test2tx)
- assert_raises_rpc_error(-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, test2tx.serialize_with_witness().hex(), 0)
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)
@@ -83,19 +83,19 @@ class NULLDUMMYTest(BitcoinTestFramework):
test4tx = create_transaction(self.nodes[0], test2tx.hash, self.address, amount=46)
test6txs = [CTransaction(test4tx)]
trueDummy(test4tx)
- assert_raises_rpc_error(-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, test4tx.serialize_with_witness().hex(), 0)
self.block_submit(self.nodes[0], [test4tx])
self.log.info("Test 5: Non-NULLDUMMY P2WSH multisig transaction invalid after activation")
test5tx = create_transaction(self.nodes[0], txid3, self.wit_address, amount=48)
test6txs.append(CTransaction(test5tx))
test5tx.wit.vtxinwit[0].scriptWitness.stack[0] = b'\x01'
- assert_raises_rpc_error(-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, test5tx.serialize_with_witness().hex(), 0)
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]")
for i in test6txs:
- self.nodes[0].sendrawtransaction(bytes_to_hex_str(i.serialize_with_witness()), True)
+ self.nodes[0].sendrawtransaction(i.serialize_with_witness().hex(), 0)
self.block_submit(self.nodes[0], test6txs, True, True)
def block_submit(self, node, txs, witness=False, accept=False):
@@ -108,7 +108,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
witness and add_witness_commitment(block)
block.rehash()
block.solve()
- node.submitblock(bytes_to_hex_str(block.serialize(True)))
+ node.submitblock(block.serialize().hex())
if (accept):
assert_equal(node.getbestblockhash(), block.hash)
self.tip = block.sha256
diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py
index 31d2ee8e13..be323d355e 100755
--- a/test/functional/feature_proxy.py
+++ b/test/functional/feature_proxy.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 bitcoind with different proxy configuration.
@@ -44,6 +44,7 @@ RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports
class ProxyTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
+ self.setup_clean_chain = True
def setup_nodes(self):
self.have_ipv6 = test_ipv6_local()
@@ -94,7 +95,7 @@ class ProxyTest(BitcoinTestFramework):
# Test: outgoing IPv4 connection through node
node.addnode("15.61.23.23:1234", "onetry")
cmd = proxies[0].queue.get()
- assert(isinstance(cmd, Socks5Command))
+ assert isinstance(cmd, Socks5Command)
# Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
assert_equal(cmd.addr, b"15.61.23.23")
@@ -108,7 +109,7 @@ class ProxyTest(BitcoinTestFramework):
# Test: outgoing IPv6 connection through node
node.addnode("[1233:3432:2434:2343:3234:2345:6546:4534]:5443", "onetry")
cmd = proxies[1].queue.get()
- assert(isinstance(cmd, Socks5Command))
+ assert isinstance(cmd, Socks5Command)
# Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
assert_equal(cmd.addr, b"1233:3432:2434:2343:3234:2345:6546:4534")
@@ -122,7 +123,7 @@ class ProxyTest(BitcoinTestFramework):
# Test: outgoing onion connection through node
node.addnode("bitcoinostk4e4re.onion:8333", "onetry")
cmd = proxies[2].queue.get()
- assert(isinstance(cmd, Socks5Command))
+ assert isinstance(cmd, Socks5Command)
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
assert_equal(cmd.addr, b"bitcoinostk4e4re.onion")
assert_equal(cmd.port, 8333)
@@ -134,7 +135,7 @@ class ProxyTest(BitcoinTestFramework):
# Test: outgoing DNS name connection through node
node.addnode("node.noumenon:8333", "onetry")
cmd = proxies[3].queue.get()
- assert(isinstance(cmd, Socks5Command))
+ assert isinstance(cmd, Socks5Command)
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
assert_equal(cmd.addr, b"node.noumenon")
assert_equal(cmd.port, 8333)
@@ -198,4 +199,3 @@ class ProxyTest(BitcoinTestFramework):
if __name__ == '__main__':
ProxyTest().main()
-
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 3e1ba88f0a..66c395d7a2 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 pruning code.
@@ -8,32 +8,80 @@ WARNING:
This test uses 4GB of disk space.
This test takes 30 mins or more (up to 2 hours)
"""
-
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_greater_than, assert_raises_rpc_error, connect_nodes, mine_large_block, sync_blocks, wait_until
-
import os
-MIN_BLOCKS_TO_KEEP = 288
+from test_framework.blocktools import create_coinbase
+from test_framework.messages import CBlock, ToHex
+from test_framework.script import CScript, OP_RETURN, OP_NOP
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_greater_than,
+ assert_raises_rpc_error,
+ connect_nodes,
+ disconnect_nodes,
+ wait_until,
+)
# Rescans start at the earliest block up to 2 hours before a key timestamp, so
# the manual prune RPC avoids pruning blocks in the same window to be
# compatible with pruning based on key creation time.
TIMESTAMP_WINDOW = 2 * 60 * 60
+def mine_large_blocks(node, n):
+ # Make a large scriptPubKey for the coinbase transaction. This is OP_RETURN
+ # followed by 950k of OP_NOP. This would be non-standard in a non-coinbase
+ # transaction but is consensus valid.
+
+ # Set the nTime if this is the first time this function has been called.
+ # A static variable ensures that time is monotonicly increasing and is therefore
+ # different for each block created => blockhash is unique.
+ if "nTimes" not in mine_large_blocks.__dict__:
+ mine_large_blocks.nTime = 0
+
+ # Get the block parameters for the first block
+ big_script = CScript([OP_RETURN] + [OP_NOP] * 950000)
+ best_block = node.getblock(node.getbestblockhash())
+ height = int(best_block["height"]) + 1
+ mine_large_blocks.nTime = max(mine_large_blocks.nTime, int(best_block["time"])) + 1
+ previousblockhash = int(best_block["hash"], 16)
+
+ for _ in range(n):
+ # Build the coinbase transaction (with large scriptPubKey)
+ coinbase_tx = create_coinbase(height)
+ coinbase_tx.vin[0].nSequence = 2 ** 32 - 1
+ coinbase_tx.vout[0].scriptPubKey = big_script
+ coinbase_tx.rehash()
+
+ # Build the block
+ block = CBlock()
+ block.nVersion = best_block["version"]
+ block.hashPrevBlock = previousblockhash
+ block.nTime = mine_large_blocks.nTime
+ block.nBits = int('207fffff', 16)
+ block.nNonce = 0
+ block.vtx = [coinbase_tx]
+ block.hashMerkleRoot = block.calc_merkle_root()
+ block.solve()
+
+ # Submit to the node
+ node.submitblock(ToHex(block))
+
+ previousblockhash = block.sha256
+ height += 1
+ mine_large_blocks.nTime += 1
def calc_usage(blockdir):
- return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(os.path.join(blockdir, f))) / (1024. * 1024.)
+ return sum(os.path.getsize(blockdir + f) for f in os.listdir(blockdir) if os.path.isfile(os.path.join(blockdir, f))) / (1024. * 1024.)
class PruneTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 6
- self.rpc_timeout = 900
# Create nodes 0 and 1 to mine.
# Create node 2 to test pruning.
- self.full_node_default_args = ["-maxreceivebuffer=20000", "-checkblocks=5", "-limitdescendantcount=100", "-limitdescendantsize=5000", "-limitancestorcount=100", "-limitancestorsize=5000"]
+ self.full_node_default_args = ["-maxreceivebuffer=20000", "-checkblocks=5"]
# Create nodes 3 and 4 to test manual pruning (they will be re-started with manual pruning later)
# Create nodes 5 to test wallet in prune mode, but do not connect
self.extra_args = [
@@ -55,10 +103,10 @@ class PruneTest(BitcoinTestFramework):
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 2)
- connect_nodes(self.nodes[2], 0)
+ connect_nodes(self.nodes[0], 2)
connect_nodes(self.nodes[0], 3)
connect_nodes(self.nodes[0], 4)
- sync_blocks(self.nodes[0:5])
+ self.sync_blocks(self.nodes[0:5])
def setup_nodes(self):
self.add_nodes(self.num_nodes, self.extra_args)
@@ -69,23 +117,21 @@ class PruneTest(BitcoinTestFramework):
def create_big_chain(self):
# Start by creating some coinbases we can spend later
self.nodes[1].generate(200)
- sync_blocks(self.nodes[0:2])
+ self.sync_blocks(self.nodes[0:2])
self.nodes[0].generate(150)
+
# Then mine enough full blocks to create more than 550MiB of data
- for i in range(645):
- mine_large_block(self.nodes[0], self.utxo_cache_0)
+ mine_large_blocks(self.nodes[0], 645)
- sync_blocks(self.nodes[0:5])
+ self.sync_blocks(self.nodes[0:5])
def test_height_min(self):
- if not os.path.isfile(os.path.join(self.prunedir, "blk00000.dat")):
- raise AssertionError("blk00000.dat is missing, pruning too early")
+ assert os.path.isfile(os.path.join(self.prunedir, "blk00000.dat")), "blk00000.dat is missing, pruning too early"
self.log.info("Success")
self.log.info("Though we're already using more than 550MiB, current usage: %d" % calc_usage(self.prunedir))
self.log.info("Mining 25 more blocks should cause the first block file to be pruned")
# Pruning doesn't run until we're allocating another chunk, 20 full blocks past the height cutoff will ensure this
- for i in range(25):
- mine_large_block(self.nodes[0], self.utxo_cache_0)
+ mine_large_blocks(self.nodes[0], 25)
# Wait for blk00000.dat to be pruned
wait_until(lambda: not os.path.isfile(os.path.join(self.prunedir, "blk00000.dat")), timeout=30)
@@ -93,8 +139,7 @@ class PruneTest(BitcoinTestFramework):
self.log.info("Success")
usage = calc_usage(self.prunedir)
self.log.info("Usage should be below target: %d" % usage)
- if (usage > 550):
- raise AssertionError("Pruning target not being met")
+ assert_greater_than(550, usage)
def create_chain_with_staleblocks(self):
# Create stale blocks in manageable sized chunks
@@ -103,90 +148,66 @@ class PruneTest(BitcoinTestFramework):
for j in range(12):
# Disconnect node 0 so it can mine a longer reorg chain without knowing about node 1's soon-to-be-stale chain
# Node 2 stays connected, so it hears about the stale blocks and then reorg's when node0 reconnects
- # Stopping node 0 also clears its mempool, so it doesn't have node1's transactions to accidentally mine
- self.stop_node(0)
- self.start_node(0, extra_args=self.full_node_default_args)
+ disconnect_nodes(self.nodes[0], 1)
+ disconnect_nodes(self.nodes[0], 2)
# Mine 24 blocks in node 1
- for i in range(24):
- if j == 0:
- mine_large_block(self.nodes[1], self.utxo_cache_1)
- else:
- # Add node1's wallet transactions back to the mempool, to
- # avoid the mined blocks from being too small.
- self.nodes[1].resendwallettransactions()
- self.nodes[1].generate(1) #tx's already in mempool from previous disconnects
+ mine_large_blocks(self.nodes[1], 24)
# Reorg back with 25 block chain from node 0
- for i in range(25):
- mine_large_block(self.nodes[0], self.utxo_cache_0)
+ mine_large_blocks(self.nodes[0], 25)
# Create connections in the order so both nodes can see the reorg at the same time
- connect_nodes(self.nodes[1], 0)
- connect_nodes(self.nodes[2], 0)
- sync_blocks(self.nodes[0:3])
+ connect_nodes(self.nodes[0], 1)
+ connect_nodes(self.nodes[0], 2)
+ self.sync_blocks(self.nodes[0:3])
self.log.info("Usage can be over target because of high stale rate: %d" % calc_usage(self.prunedir))
def reorg_test(self):
# Node 1 will mine a 300 block chain starting 287 blocks back from Node 0 and Node 2's tip
# This will cause Node 2 to do a reorg requiring 288 blocks of undo data to the reorg_test chain
- # Reboot node 1 to clear its mempool (hopefully make the invalidate faster)
- # Lower the block max size so we don't keep mining all our big mempool transactions (from disconnected blocks)
- self.stop_node(1)
- self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxweight=20000", "-checkblocks=5"])
height = self.nodes[1].getblockcount()
self.log.info("Current block height: %d" % height)
- invalidheight = height-287
- badhash = self.nodes[1].getblockhash(invalidheight)
- self.log.info("Invalidating block %s at height %d" % (badhash,invalidheight))
- self.nodes[1].invalidateblock(badhash)
+ self.forkheight = height - 287
+ self.forkhash = self.nodes[1].getblockhash(self.forkheight)
+ self.log.info("Invalidating block %s at height %d" % (self.forkhash, self.forkheight))
+ self.nodes[1].invalidateblock(self.forkhash)
# We've now switched to our previously mined-24 block fork on node 1, but that's not what we want
# So invalidate that fork as well, until we're on the same chain as node 0/2 (but at an ancestor 288 blocks ago)
- mainchainhash = self.nodes[0].getblockhash(invalidheight - 1)
- curhash = self.nodes[1].getblockhash(invalidheight - 1)
+ mainchainhash = self.nodes[0].getblockhash(self.forkheight - 1)
+ curhash = self.nodes[1].getblockhash(self.forkheight - 1)
while curhash != mainchainhash:
self.nodes[1].invalidateblock(curhash)
- curhash = self.nodes[1].getblockhash(invalidheight - 1)
+ curhash = self.nodes[1].getblockhash(self.forkheight - 1)
- assert(self.nodes[1].getblockcount() == invalidheight - 1)
+ assert self.nodes[1].getblockcount() == self.forkheight - 1
self.log.info("New best height: %d" % self.nodes[1].getblockcount())
- # Reboot node1 to clear those giant tx's from mempool
- self.stop_node(1)
- self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxweight=20000", "-checkblocks=5"])
+ # Disconnect node1 and generate the new chain
+ disconnect_nodes(self.nodes[0], 1)
+ disconnect_nodes(self.nodes[1], 2)
self.log.info("Generating new longer chain of 300 more blocks")
self.nodes[1].generate(300)
self.log.info("Reconnect nodes")
connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[2], 1)
- sync_blocks(self.nodes[0:3], timeout=120)
+ connect_nodes(self.nodes[1], 2)
+ self.sync_blocks(self.nodes[0:3], timeout=120)
self.log.info("Verify height on node 2: %d" % self.nodes[2].getblockcount())
- self.log.info("Usage possibly still high bc of stale blocks in block files: %d" % calc_usage(self.prunedir))
+ self.log.info("Usage possibly still high because of stale blocks in block files: %d" % calc_usage(self.prunedir))
- self.log.info("Mine 220 more blocks so we have requisite history (some blocks will be big and cause pruning of previous chain)")
+ self.log.info("Mine 220 more large blocks so we have requisite history")
- # Get node0's wallet transactions back in its mempool, to avoid the
- # mined blocks from being too small.
- self.nodes[0].resendwallettransactions()
-
- for i in range(22):
- # This can be slow, so do this in multiple RPC calls to avoid
- # RPC timeouts.
- self.nodes[0].generate(10) #node 0 has many large tx's in its mempool from the disconnects
- sync_blocks(self.nodes[0:3], timeout=300)
+ mine_large_blocks(self.nodes[0], 220)
usage = calc_usage(self.prunedir)
self.log.info("Usage should be below target: %d" % usage)
- if (usage > 550):
- raise AssertionError("Pruning target not being met")
-
- return invalidheight,badhash
+ assert_greater_than(550, usage)
def reorg_back(self):
# Verify that a block on the old main chain fork has been pruned away
@@ -219,17 +240,17 @@ class PruneTest(BitcoinTestFramework):
blocks_to_mine = first_reorg_height + 1 - self.mainchainheight
self.log.info("Rewind node 0 to prev main chain to mine longer chain to trigger redownload. Blocks needed: %d" % blocks_to_mine)
self.nodes[0].invalidateblock(curchainhash)
- assert(self.nodes[0].getblockcount() == self.mainchainheight)
- assert(self.nodes[0].getbestblockhash() == self.mainchainhash2)
+ assert_equal(self.nodes[0].getblockcount(), self.mainchainheight)
+ assert_equal(self.nodes[0].getbestblockhash(), self.mainchainhash2)
goalbesthash = self.nodes[0].generate(blocks_to_mine)[-1]
goalbestheight = first_reorg_height + 1
self.log.info("Verify node 2 reorged back to the main chain, some blocks of which it had to redownload")
# Wait for Node 2 to reorg to proper height
wait_until(lambda: self.nodes[2].getblockcount() >= goalbestheight, timeout=900)
- assert(self.nodes[2].getbestblockhash() == goalbesthash)
+ assert_equal(self.nodes[2].getbestblockhash(), goalbesthash)
# Verify we can now have the data for a block previously pruned
- assert(self.nodes[2].getblock(self.forkhash)["height"] == self.forkheight)
+ assert_equal(self.nodes[2].getblock(self.forkhash)["height"], self.forkheight)
def manual_test(self, node_number, use_timestamp):
# at this point, node has 995 blocks and has not yet run in prune mode
@@ -250,20 +271,9 @@ class PruneTest(BitcoinTestFramework):
else:
return index
- def prune(index, expected_ret=None):
+ def prune(index):
ret = node.pruneblockchain(height=height(index))
- # Check the return value. When use_timestamp is True, just check
- # that the return value is less than or equal to the expected
- # value, because when more than one block is generated per second,
- # a timestamp will not be granular enough to uniquely identify an
- # individual block.
- if expected_ret is None:
- expected_ret = index
- if use_timestamp:
- assert_greater_than(ret, 0)
- assert_greater_than(expected_ret + 1, ret)
- else:
- assert_equal(ret, expected_ret)
+ assert_equal(ret, node.getblockchaininfo()['pruneheight'])
def has_block(index):
return os.path.isfile(os.path.join(self.nodes[node_number].datadir, "regtest", "blocks", "blk{:05}.dat".format(index)))
@@ -287,38 +297,30 @@ class PruneTest(BitcoinTestFramework):
# height=100 too low to prune first block file so this is a no-op
prune(100)
- if not has_block(0):
- raise AssertionError("blk00000.dat is missing when should still be there")
+ assert has_block(0), "blk00000.dat is missing when should still be there"
# Does nothing
node.pruneblockchain(height(0))
- if not has_block(0):
- raise AssertionError("blk00000.dat is missing when should still be there")
+ assert has_block(0), "blk00000.dat is missing when should still be there"
# height=500 should prune first file
prune(500)
- if has_block(0):
- raise AssertionError("blk00000.dat is still there, should be pruned by now")
- if not has_block(1):
- raise AssertionError("blk00001.dat is missing when should still be there")
+ assert not has_block(0), "blk00000.dat is still there, should be pruned by now"
+ assert has_block(1), "blk00001.dat is missing when should still be there"
# height=650 should prune second file
prune(650)
- if has_block(1):
- raise AssertionError("blk00001.dat is still there, should be pruned by now")
+ assert not has_block(1), "blk00001.dat is still there, should be pruned by now"
# height=1000 should not prune anything more, because tip-288 is in blk00002.dat.
- prune(1000, 1001 - MIN_BLOCKS_TO_KEEP)
- if not has_block(2):
- raise AssertionError("blk00002.dat is still there, should be pruned by now")
+ prune(1000)
+ assert has_block(2), "blk00002.dat is still there, should be pruned by now"
# advance the tip so blk00002.dat and blk00003.dat can be pruned (the last 288 blocks should now be in blk00004.dat)
node.generate(288)
prune(1000)
- if has_block(2):
- raise AssertionError("blk00002.dat is still there, should be pruned by now")
- if has_block(3):
- raise AssertionError("blk00003.dat is still there, should be pruned by now")
+ assert not has_block(2), "blk00002.dat is still there, should be pruned by now"
+ assert not has_block(3), "blk00003.dat is still there, should be pruned by now"
# stop node, start back up with auto-prune at 550 MiB, make sure still runs
self.stop_node(node_number)
@@ -338,22 +340,15 @@ class PruneTest(BitcoinTestFramework):
self.log.info("Syncing node 5 to test wallet")
connect_nodes(self.nodes[0], 5)
nds = [self.nodes[0], self.nodes[5]]
- sync_blocks(nds, wait=5, timeout=300)
- self.stop_node(5) #stop and start to trigger rescan
+ self.sync_blocks(nds, wait=5, timeout=300)
+ self.stop_node(5) # stop and start to trigger rescan
self.start_node(5, extra_args=["-prune=550"])
self.log.info("Success")
def run_test(self):
- self.log.info("Warning! This test requires 4GB of disk space and takes over 30 mins (up to 2 hours)")
- self.log.info("Mining a big blockchain of 995 blocks")
-
- # Determine default relay fee
- self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
-
- # Cache for utxos, as the listunspent may take a long time later in the test
- self.utxo_cache_0 = []
- self.utxo_cache_1 = []
+ self.log.info("Warning! This test requires 4GB of disk space")
+ self.log.info("Mining a big blockchain of 995 blocks")
self.create_big_chain()
# Chain diagram key:
# * blocks on main chain
@@ -394,11 +389,11 @@ class PruneTest(BitcoinTestFramework):
# +...+(1044) &.. $...$(1319)
# Save some current chain state for later use
- self.mainchainheight = self.nodes[2].getblockcount() #1320
+ self.mainchainheight = self.nodes[2].getblockcount() # 1320
self.mainchainhash2 = self.nodes[2].getblockhash(self.mainchainheight)
self.log.info("Check that we can survive a 288 block reorg still")
- (self.forkheight,self.forkhash) = self.reorg_test() #(1033, )
+ self.reorg_test() # (1033, )
# Now create a 288 block reorg by mining a longer chain on N1
# First disconnect N1
# Then invalidate 1033 on main chain and 1032 on fork so height is 1032 on main chain
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index ff7c2b23bf..1496c5d958 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 RBF code."""
@@ -9,12 +9,12 @@ from decimal import Decimal
from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut
from test_framework.script import CScript, OP_DROP
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, bytes_to_hex_str, satoshi_round
+from test_framework.util import assert_equal, assert_raises_rpc_error, satoshi_round
MAX_REPLACEMENT_LIMIT = 100
def txToHex(tx):
- return bytes_to_hex_str(tx.serialize())
+ return tx.serialize().hex()
def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])):
"""Create a txout with a given amount and scriptPubKey
@@ -46,7 +46,7 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])):
signed_tx = node.signrawtransactionwithwallet(txToHex(tx2))
- txid = node.sendrawtransaction(signed_tx['hex'], True)
+ txid = node.sendrawtransaction(signed_tx['hex'], 0)
# If requested, ensure txouts are confirmed.
if confirmed:
@@ -56,7 +56,7 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])):
new_size = len(node.getrawmempool())
# Error out if we have something stuck in the mempool, as this
# would likely be a bug.
- assert(new_size < mempool_size)
+ assert new_size < mempool_size
mempool_size = new_size
return COutPoint(int(txid, 16), 0)
@@ -64,7 +64,7 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])):
class ReplaceByFeeTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 2
+ self.num_nodes = 1
self.extra_args = [
[
"-maxorphantx=1000",
@@ -74,9 +74,6 @@ class ReplaceByFeeTest(BitcoinTestFramework):
"-limitdescendantcount=200",
"-limitdescendantsize=101",
],
- [
- "-mempoolreplacement=0",
- ],
]
def skip_test_if_missing_module(self):
@@ -136,7 +133,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx1a_hex = txToHex(tx1a)
- tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
+ tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
self.sync_all()
@@ -147,32 +144,23 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1b_hex = txToHex(tx1b)
# This will raise an exception due to insufficient fee
- 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)
+ assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
# Extra 0.1 BTC fee
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))]
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)
+ tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
mempool = self.nodes[0].getrawmempool()
- assert (tx1a_txid not in mempool)
- assert (tx1b_txid in mempool)
+ assert tx1a_txid not in mempool
+ assert tx1b_txid in mempool
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"""
@@ -188,7 +176,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx.vin = [CTxIn(prevout, nSequence=0)]
tx.vout = [CTxOut(remaining_value, CScript([1, OP_DROP] * 15 + [1]))]
tx_hex = txToHex(tx)
- txid = self.nodes[0].sendrawtransaction(tx_hex, True)
+ txid = self.nodes[0].sendrawtransaction(tx_hex, 0)
chain_txids.append(txid)
prevout = COutPoint(int(txid, 16), 0)
@@ -200,18 +188,18 @@ class ReplaceByFeeTest(BitcoinTestFramework):
dbl_tx_hex = txToHex(dbl_tx)
# This will raise an exception due to insufficient fee
- assert_raises_rpc_error(-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, 0)
# Accepted with sufficient fee
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
dbl_tx.vout = [CTxOut(1 * COIN, CScript([1] * 35))]
dbl_tx_hex = txToHex(dbl_tx)
- self.nodes[0].sendrawtransaction(dbl_tx_hex, True)
+ self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
mempool = self.nodes[0].getrawmempool()
for doublespent_txid in chain_txids:
- assert(doublespent_txid not in mempool)
+ assert doublespent_txid not in mempool
def test_doublespend_tree(self):
"""Doublespend of a big tree of transactions"""
@@ -236,8 +224,8 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx.vout = vout
tx_hex = txToHex(tx)
- assert(len(tx.serialize()) < 100000)
- txid = self.nodes[0].sendrawtransaction(tx_hex, True)
+ assert len(tx.serialize()) < 100000
+ txid = self.nodes[0].sendrawtransaction(tx_hex, 0)
yield tx
_total_txs[0] += 1
@@ -261,20 +249,20 @@ class ReplaceByFeeTest(BitcoinTestFramework):
dbl_tx.vout = [CTxOut(initial_nValue - fee * n, CScript([1] * 35))]
dbl_tx_hex = txToHex(dbl_tx)
# This will raise an exception due to insufficient fee
- assert_raises_rpc_error(-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, 0)
# 1 BTC fee is enough
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
dbl_tx.vout = [CTxOut(initial_nValue - fee * n - 1 * COIN, CScript([1] * 35))]
dbl_tx_hex = txToHex(dbl_tx)
- self.nodes[0].sendrawtransaction(dbl_tx_hex, True)
+ self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
mempool = self.nodes[0].getrawmempool()
for tx in tree_txs:
tx.rehash()
- assert (tx.hash not in mempool)
+ assert tx.hash not in mempool
# Try again, but with more total transactions than the "max txs
# double-spent at once" anti-DoS limit.
@@ -289,7 +277,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
dbl_tx.vout = [CTxOut(initial_nValue - 2 * fee * n, CScript([1] * 35))]
dbl_tx_hex = txToHex(dbl_tx)
# This will raise an exception
- assert_raises_rpc_error(-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, 0)
for tx in tree_txs:
tx.rehash()
@@ -303,7 +291,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx1a_hex = txToHex(tx1a)
- self.nodes[0].sendrawtransaction(tx1a_hex, True)
+ self.nodes[0].sendrawtransaction(tx1a_hex, 0)
# Higher fee, but the fee per KB is much lower, so the replacement is
# rejected.
@@ -313,7 +301,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1b_hex = txToHex(tx1b)
# This will raise an exception due to insufficient fee
- assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
+ assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
def test_spends_of_conflicting_outputs(self):
"""Replacements that spend conflicting tx outputs are rejected"""
@@ -324,7 +312,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1a.vin = [CTxIn(utxo1, nSequence=0)]
tx1a.vout = [CTxOut(int(1.1 * COIN), CScript([b'a' * 35]))]
tx1a_hex = txToHex(tx1a)
- tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
+ tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
tx1a_txid = int(tx1a_txid, 16)
@@ -336,14 +324,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2_hex = txToHex(tx2)
# This will raise an exception
- assert_raises_rpc_error(-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, 0)
# Spend tx1a's output to test the indirect case.
tx1b = CTransaction()
tx1b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)]
tx1b.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx1b_hex = txToHex(tx1b)
- tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
+ tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
tx1b_txid = int(tx1b_txid, 16)
tx2 = CTransaction()
@@ -353,7 +341,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2_hex = txToHex(tx2)
# This will raise an exception
- assert_raises_rpc_error(-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, 0)
def test_new_unconfirmed_inputs(self):
"""Replacements that add new unconfirmed inputs are rejected"""
@@ -364,7 +352,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1.vin = [CTxIn(confirmed_utxo)]
tx1.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx1_hex = txToHex(tx1)
- self.nodes[0].sendrawtransaction(tx1_hex, True)
+ self.nodes[0].sendrawtransaction(tx1_hex, 0)
tx2 = CTransaction()
tx2.vin = [CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)]
@@ -372,7 +360,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2_hex = txToHex(tx2)
# This will raise an exception
- assert_raises_rpc_error(-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, 0)
def test_too_many_replacements(self):
"""Replacements that evict too many transactions are rejected"""
@@ -394,7 +382,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
splitting_tx.vout = outputs
splitting_tx_hex = txToHex(splitting_tx)
- txid = self.nodes[0].sendrawtransaction(splitting_tx_hex, True)
+ txid = self.nodes[0].sendrawtransaction(splitting_tx_hex, 0)
txid = int(txid, 16)
# Now spend each of those outputs individually
@@ -403,7 +391,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx_i.vin = [CTxIn(COutPoint(txid, i), nSequence=0)]
tx_i.vout = [CTxOut(split_value - fee, CScript([b'a' * 35]))]
tx_i_hex = txToHex(tx_i)
- self.nodes[0].sendrawtransaction(tx_i_hex, True)
+ self.nodes[0].sendrawtransaction(tx_i_hex, 0)
# Now create doublespend of the whole lot; should fail.
# Need a big enough fee to cover all spending transactions and have
@@ -418,14 +406,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
double_tx_hex = txToHex(double_tx)
# This will raise an exception
- assert_raises_rpc_error(-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, 0)
# If we remove an input, it should pass
double_tx = CTransaction()
double_tx.vin = inputs[0:-1]
double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))]
double_tx_hex = txToHex(double_tx)
- self.nodes[0].sendrawtransaction(double_tx_hex, True)
+ self.nodes[0].sendrawtransaction(double_tx_hex, 0)
def test_opt_in(self):
"""Replacing should only work if orig tx opted in"""
@@ -436,7 +424,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0xffffffff)]
tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx1a_hex = txToHex(tx1a)
- tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
+ tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
# This transaction isn't shown as replaceable
assert_equal(self.nodes[0].getmempoolentry(tx1a_txid)['bip125-replaceable'], False)
@@ -448,7 +436,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1b_hex = txToHex(tx1b)
# This will raise an exception
- assert_raises_rpc_error(-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, 0)
tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
@@ -457,7 +445,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0xfffffffe)]
tx2a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx2a_hex = txToHex(tx2a)
- tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True)
+ tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, 0)
# Still shouldn't be able to double-spend
tx2b = CTransaction()
@@ -466,7 +454,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2b_hex = txToHex(tx2b)
# This will raise an exception
- assert_raises_rpc_error(-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, 0)
# Now create a new transaction that spends from tx1a and tx2a
# opt-in on one of the inputs
@@ -481,7 +469,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx3a.vout = [CTxOut(int(0.9*COIN), CScript([b'c'])), CTxOut(int(0.9*COIN), CScript([b'd']))]
tx3a_hex = txToHex(tx3a)
- tx3a_txid = self.nodes[0].sendrawtransaction(tx3a_hex, True)
+ tx3a_txid = self.nodes[0].sendrawtransaction(tx3a_hex, 0)
# This transaction is shown as replaceable
assert_equal(self.nodes[0].getmempoolentry(tx3a_txid)['bip125-replaceable'], True)
@@ -496,10 +484,10 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx3c.vout = [CTxOut(int(0.5 * COIN), CScript([b'f' * 35]))]
tx3c_hex = txToHex(tx3c)
- self.nodes[0].sendrawtransaction(tx3b_hex, True)
+ self.nodes[0].sendrawtransaction(tx3b_hex, 0)
# If tx3b was accepted, tx3c won't look like a replacement,
# but make sure it is accepted anyway
- self.nodes[0].sendrawtransaction(tx3c_hex, True)
+ self.nodes[0].sendrawtransaction(tx3c_hex, 0)
def test_prioritised_transactions(self):
# Ensure that fee deltas used via prioritisetransaction are
@@ -512,7 +500,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx1a_hex = txToHex(tx1a)
- tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
+ tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
# Higher fee, but the actual fee per KB is much lower.
tx1b = CTransaction()
@@ -521,15 +509,15 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1b_hex = txToHex(tx1b)
# Verify tx1b cannot replace tx1a.
- assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
+ assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
# Use prioritisetransaction to set tx1a's fee to 0.
self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1*COIN))
# Now tx1b should be able to replace tx1a
- tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
+ tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
- assert(tx1b_txid in self.nodes[0].getrawmempool())
+ assert tx1b_txid in self.nodes[0].getrawmempool()
# 2. Check that absolute fee checks use modified fee.
tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
@@ -538,7 +526,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)]
tx2a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx2a_hex = txToHex(tx2a)
- self.nodes[0].sendrawtransaction(tx2a_hex, True)
+ self.nodes[0].sendrawtransaction(tx2a_hex, 0)
# Lower fee, but we'll prioritise it
tx2b = CTransaction()
@@ -548,15 +536,15 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2b_hex = txToHex(tx2b)
# Verify tx2b cannot replace tx2a.
- assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, True)
+ assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, 0)
# Now prioritise tx2b to have a higher modified fee
self.nodes[0].prioritisetransaction(txid=tx2b.hash, fee_delta=int(0.1*COIN))
# tx2b should now be accepted
- tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True)
+ tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, 0)
- assert(tx2b_txid in self.nodes[0].getrawmempool())
+ assert tx2b_txid in self.nodes[0].getrawmempool()
def test_rpc(self):
us0 = self.nodes[0].listunspent()[0]
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 1efc50e71f..2d4dd96a1d 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2018 The Bitcoin Core developers
+# Copyright (c) 2016-2019 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 SegWit changeover logic."""
@@ -18,7 +18,13 @@ from test_framework.blocktools import witness_script, send_to_witness
from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, FromHex, sha256, ToHex
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, OP_DROP
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, bytes_to_hex_str, connect_nodes, hex_str_to_bytes, sync_blocks, try_rpc
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ connect_nodes,
+ hex_str_to_bytes,
+ try_rpc,
+)
NODE_0 = 0
NODE_2 = 2
@@ -38,31 +44,29 @@ def find_spendable_utxo(node, min_value):
raise AssertionError("Unspent output equal or higher than %s not found" % min_value)
+txs_mined = {} # txindex from txid to blockhash
+
class SegWitTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
- # TODO: remove -txindex. Currently required for getrawtransaction call.
self.extra_args = [
[
"-rpcserialversion=0",
"-vbparams=segwit:0:999999999999",
"-addresstype=legacy",
- "-txindex"
],
[
"-blockversion=4",
"-rpcserialversion=1",
"-vbparams=segwit:0:999999999999",
"-addresstype=legacy",
- "-txindex"
],
[
"-blockversion=536870915",
"-vbparams=segwit:0:999999999999",
"-addresstype=legacy",
- "-txindex"
],
]
@@ -78,13 +82,13 @@ class SegWitTest(BitcoinTestFramework):
send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script)
block = node.generate(1)
assert_equal(len(node.getblock(block[0])["tx"]), 2)
- sync_blocks(self.nodes)
+ self.sync_blocks()
def skip_mine(self, node, txid, sign, redeem_script=""):
send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script)
block = node.generate(1)
assert_equal(len(node.getblock(block[0])["tx"]), 1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
def fail_accept(self, node, error_msg, txid, sign, redeem_script=""):
assert_raises_rpc_error(-26, error_msg, send_to_witness, use_p2wsh=1, node=node, utxo=getutxo(txid), pubkey=self.pubkey[0], encode_p2sh=False, amount=Decimal("49.998"), sign=sign, insert_redeem_script=redeem_script)
@@ -95,17 +99,17 @@ class SegWitTest(BitcoinTestFramework):
self.log.info("Verify sigops are counted in GBT with pre-BIP141 rules before the fork")
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']})
- assert(tmpl['sizelimit'] == 1000000)
- assert('weightlimit' not in tmpl)
- assert(tmpl['sigoplimit'] == 20000)
- assert(tmpl['transactions'][0]['hash'] == txid)
- assert(tmpl['transactions'][0]['sigops'] == 2)
+ assert tmpl['sizelimit'] == 1000000
+ assert 'weightlimit' not in tmpl
+ assert tmpl['sigoplimit'] == 20000
+ assert tmpl['transactions'][0]['hash'] == txid
+ assert tmpl['transactions'][0]['sigops'] == 2
tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']})
- assert(tmpl['sizelimit'] == 1000000)
- assert('weightlimit' not in tmpl)
- assert(tmpl['sigoplimit'] == 20000)
- assert(tmpl['transactions'][0]['hash'] == txid)
- assert(tmpl['transactions'][0]['sigops'] == 2)
+ assert tmpl['sizelimit'] == 1000000
+ assert 'weightlimit' not in tmpl
+ assert tmpl['sigoplimit'] == 20000
+ assert tmpl['transactions'][0]['hash'] == txid
+ assert tmpl['transactions'][0]['sigops'] == 2
self.nodes[0].generate(1) # block 162
balance_presetup = self.nodes[0].getbalance()
@@ -133,7 +137,7 @@ class SegWitTest(BitcoinTestFramework):
p2sh_ids[n][v].append(send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], True, Decimal("49.999")))
self.nodes[0].generate(1) # block 163
- sync_blocks(self.nodes)
+ self.sync_blocks()
# Make sure all nodes recognize the transactions as theirs
assert_equal(self.nodes[0].getbalance(), balance_presetup - 60 * 50 + 20 * Decimal("49.999") + 50)
@@ -141,7 +145,7 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(self.nodes[2].getbalance(), 20 * Decimal("49.999"))
self.nodes[0].generate(260) # block 423
- sync_blocks(self.nodes)
+ self.sync_blocks()
self.log.info("Verify witness txs are skipped for mining before the fork")
self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][0], True) # block 424
@@ -157,10 +161,10 @@ class SegWitTest(BitcoinTestFramework):
self.log.info("Verify previous witness txs skipped for mining can now be mined")
assert_equal(len(self.nodes[2].getrawmempool()), 4)
- block = self.nodes[2].generate(1) # block 432 (first block with new rules; 432 = 144 * 3)
- sync_blocks(self.nodes)
+ blockhash = self.nodes[2].generate(1)[0] # block 432 (first block with new rules; 432 = 144 * 3)
+ self.sync_blocks()
assert_equal(len(self.nodes[2].getrawmempool()), 0)
- segwit_tx_list = self.nodes[2].getblock(block[0])["tx"]
+ segwit_tx_list = self.nodes[2].getblock(blockhash)["tx"]
assert_equal(len(segwit_tx_list), 5)
self.log.info("Verify default node can't accept txs with missing witness")
@@ -174,15 +178,16 @@ class SegWitTest(BitcoinTestFramework):
self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V1][0], False, witness_script(True, self.pubkey[0]))
self.log.info("Verify block and transaction serialization rpcs return differing serializations depending on rpc serialization flag")
- assert(self.nodes[2].getblock(block[0], False) != self.nodes[0].getblock(block[0], False))
- assert(self.nodes[1].getblock(block[0], False) == self.nodes[2].getblock(block[0], False))
- for i in range(len(segwit_tx_list)):
- tx = FromHex(CTransaction(), self.nodes[2].gettransaction(segwit_tx_list[i])["hex"])
- assert(self.nodes[2].getrawtransaction(segwit_tx_list[i]) != self.nodes[0].getrawtransaction(segwit_tx_list[i]))
- assert(self.nodes[1].getrawtransaction(segwit_tx_list[i], 0) == self.nodes[2].getrawtransaction(segwit_tx_list[i]))
- assert(self.nodes[0].getrawtransaction(segwit_tx_list[i]) != self.nodes[2].gettransaction(segwit_tx_list[i])["hex"])
- assert(self.nodes[1].getrawtransaction(segwit_tx_list[i]) == self.nodes[2].gettransaction(segwit_tx_list[i])["hex"])
- assert(self.nodes[0].getrawtransaction(segwit_tx_list[i]) == bytes_to_hex_str(tx.serialize_without_witness()))
+ assert self.nodes[2].getblock(blockhash, False) != self.nodes[0].getblock(blockhash, False)
+ assert self.nodes[1].getblock(blockhash, False) == self.nodes[2].getblock(blockhash, False)
+
+ for tx_id in segwit_tx_list:
+ tx = FromHex(CTransaction(), self.nodes[2].gettransaction(tx_id)["hex"])
+ assert self.nodes[2].getrawtransaction(tx_id, False, blockhash) != self.nodes[0].getrawtransaction(tx_id, False, blockhash)
+ assert self.nodes[1].getrawtransaction(tx_id, False, blockhash) == self.nodes[2].getrawtransaction(tx_id, False, blockhash)
+ assert self.nodes[0].getrawtransaction(tx_id, False, blockhash) != self.nodes[2].gettransaction(tx_id)["hex"]
+ assert self.nodes[1].getrawtransaction(tx_id, False, blockhash) == self.nodes[2].gettransaction(tx_id)["hex"]
+ assert self.nodes[0].getrawtransaction(tx_id, False, blockhash) == tx.serialize_without_witness().hex()
self.log.info("Verify witness txs without witness data are invalid after the fork")
self.fail_accept(self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch) (code 64)', wit_ids[NODE_2][WIT_V0][2], sign=False)
@@ -199,11 +204,11 @@ class SegWitTest(BitcoinTestFramework):
self.log.info("Verify sigops are counted in GBT with BIP141 rules after the fork")
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']})
- assert(tmpl['sizelimit'] >= 3999577) # actual maximum size is lower due to minimum mandatory non-witness data
- assert(tmpl['weightlimit'] == 4000000)
- assert(tmpl['sigoplimit'] == 80000)
- assert(tmpl['transactions'][0]['txid'] == txid)
- assert(tmpl['transactions'][0]['sigops'] == 8)
+ assert tmpl['sizelimit'] >= 3999577 # actual maximum size is lower due to minimum mandatory non-witness data
+ assert tmpl['weightlimit'] == 4000000
+ assert tmpl['sigoplimit'] == 80000
+ assert tmpl['transactions'][0]['txid'] == txid
+ assert tmpl['transactions'][0]['sigops'] == 8
self.nodes[0].generate(1) # Mine a block to clear the gbt cache
@@ -215,8 +220,8 @@ class SegWitTest(BitcoinTestFramework):
txid1 = send_to_witness(1, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[0], False, Decimal("49.996"))
hex_tx = self.nodes[0].gettransaction(txid)['hex']
tx = FromHex(CTransaction(), hex_tx)
- assert(tx.wit.is_null()) # This should not be a segwit input
- assert(txid1 in self.nodes[0].getrawmempool())
+ assert tx.wit.is_null() # This should not be a segwit input
+ assert txid1 in self.nodes[0].getrawmempool()
# Now create tx2, which will spend from txid1.
tx = CTransaction()
@@ -225,7 +230,7 @@ class SegWitTest(BitcoinTestFramework):
tx2_hex = self.nodes[0].signrawtransactionwithwallet(ToHex(tx))['hex']
txid2 = self.nodes[0].sendrawtransaction(tx2_hex)
tx = FromHex(CTransaction(), tx2_hex)
- assert(not tx.wit.is_null())
+ assert not tx.wit.is_null()
# Now create tx3, which will spend from txid2
tx = CTransaction()
@@ -233,15 +238,15 @@ class SegWitTest(BitcoinTestFramework):
tx.vout.append(CTxOut(int(49.95 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) # Huge fee
tx.calc_sha256()
txid3 = self.nodes[0].sendrawtransaction(ToHex(tx))
- assert(tx.wit.is_null())
- assert(txid3 in self.nodes[0].getrawmempool())
+ assert tx.wit.is_null()
+ assert txid3 in self.nodes[0].getrawmempool()
# Check that getblocktemplate includes all transactions.
template = self.nodes[0].getblocktemplate({"rules": ["segwit"]})
template_txids = [t['txid'] for t in template['transactions']]
- assert(txid1 in template_txids)
- assert(txid2 in template_txids)
- assert(txid3 in template_txids)
+ assert txid1 in template_txids
+ 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))
@@ -393,22 +398,22 @@ class SegWitTest(BitcoinTestFramework):
v = self.nodes[0].getaddressinfo(i)
if (v['isscript']):
bare = hex_str_to_bytes(v['hex'])
- importlist.append(bytes_to_hex_str(bare))
- importlist.append(bytes_to_hex_str(CScript([OP_0, sha256(bare)])))
+ importlist.append(bare.hex())
+ importlist.append(CScript([OP_0, sha256(bare)]).hex())
else:
pubkey = hex_str_to_bytes(v['pubkey'])
p2pk = CScript([pubkey, OP_CHECKSIG])
p2pkh = CScript([OP_DUP, OP_HASH160, hash160(pubkey), OP_EQUALVERIFY, OP_CHECKSIG])
- importlist.append(bytes_to_hex_str(p2pk))
- importlist.append(bytes_to_hex_str(p2pkh))
- importlist.append(bytes_to_hex_str(CScript([OP_0, hash160(pubkey)])))
- importlist.append(bytes_to_hex_str(CScript([OP_0, sha256(p2pk)])))
- importlist.append(bytes_to_hex_str(CScript([OP_0, sha256(p2pkh)])))
+ importlist.append(p2pk.hex())
+ importlist.append(p2pkh.hex())
+ importlist.append(CScript([OP_0, hash160(pubkey)]).hex())
+ importlist.append(CScript([OP_0, sha256(p2pk)]).hex())
+ importlist.append(CScript([OP_0, sha256(p2pkh)]).hex())
- importlist.append(bytes_to_hex_str(unsolvablep2pkh))
- importlist.append(bytes_to_hex_str(unsolvablep2wshp2pkh))
- importlist.append(bytes_to_hex_str(op1))
- importlist.append(bytes_to_hex_str(p2wshop1))
+ importlist.append(unsolvablep2pkh.hex())
+ importlist.append(unsolvablep2wshp2pkh.hex())
+ importlist.append(op1.hex())
+ importlist.append(p2wshop1.hex())
for i in importlist:
# import all generated addresses. The wallet already has the private keys for some of these, so catch JSON RPC
@@ -536,10 +541,10 @@ class SegWitTest(BitcoinTestFramework):
for i in script_list:
tx.vout.append(CTxOut(10000000, i))
tx.rehash()
- signresults = self.nodes[0].signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize_without_witness()))['hex']
- txid = self.nodes[0].sendrawtransaction(signresults, True)
- self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ signresults = self.nodes[0].signrawtransactionwithwallet(tx.serialize_without_witness().hex())['hex']
+ txid = self.nodes[0].sendrawtransaction(signresults, 0)
+ txs_mined[txid] = self.nodes[0].generate(1)[0]
+ self.sync_blocks()
watchcount = 0
spendcount = 0
for i in self.nodes[0].listunspent():
@@ -581,17 +586,17 @@ class SegWitTest(BitcoinTestFramework):
tx = CTransaction()
for i in txids:
txtmp = CTransaction()
- txraw = self.nodes[0].getrawtransaction(i)
+ txraw = self.nodes[0].getrawtransaction(i, 0, txs_mined[i])
f = BytesIO(hex_str_to_bytes(txraw))
txtmp.deserialize(f)
for j in range(len(txtmp.vout)):
tx.vin.append(CTxIn(COutPoint(int('0x' + i, 0), j)))
tx.vout.append(CTxOut(0, CScript()))
tx.rehash()
- signresults = self.nodes[0].signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize_without_witness()))['hex']
- self.nodes[0].sendrawtransaction(signresults, True)
+ signresults = self.nodes[0].signrawtransactionwithwallet(tx.serialize_without_witness().hex())['hex']
+ self.nodes[0].sendrawtransaction(signresults, 0)
self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
if __name__ == '__main__':
diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py
index 88df61cabc..0713925141 100755
--- a/test/functional/feature_versionbits_warning.py
+++ b/test/functional/feature_versionbits_warning.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2018 The Bitcoin Core developers
+# Copyright (c) 2016-2019 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 version bits warning system.
@@ -22,7 +22,6 @@ VB_TOP_BITS = 0x20000000
VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment
VB_UNKNOWN_VERSION = VB_TOP_BITS | (1 << VB_UNKNOWN_BIT)
-WARN_UNKNOWN_RULES_MINED = "Unknown block versions being mined! It's possible unknown rules are in effect"
WARN_UNKNOWN_RULES_ACTIVE = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT)
VB_PATTERN = re.compile("Warning: unknown new rules activated.*versionbit")
@@ -75,18 +74,13 @@ class VersionBitsWarningTest(BitcoinTestFramework):
node.generatetoaddress(VB_PERIOD - VB_THRESHOLD + 1, node_deterministic_address)
# Check that we're not getting any versionbit-related errors in get*info()
- assert(not VB_PATTERN.match(node.getmininginfo()["warnings"]))
- assert(not VB_PATTERN.match(node.getnetworkinfo()["warnings"]))
+ assert not VB_PATTERN.match(node.getmininginfo()["warnings"])
+ assert not VB_PATTERN.match(node.getnetworkinfo()["warnings"])
- self.log.info("Check that there is a warning if >50 blocks in the last 100 were an unknown version")
# Build one period of blocks with VB_THRESHOLD blocks signaling some unknown bit
self.send_blocks_with_version(node.p2p, VB_THRESHOLD, VB_UNKNOWN_VERSION)
node.generatetoaddress(VB_PERIOD - VB_THRESHOLD, node_deterministic_address)
- # Check that get*info() shows the 51/100 unknown block version error.
- assert(WARN_UNKNOWN_RULES_MINED in node.getmininginfo()["warnings"])
- assert(WARN_UNKNOWN_RULES_MINED in node.getnetworkinfo()["warnings"])
-
self.log.info("Check that there is a warning if previous VB_BLOCKS have >=VB_THRESHOLD blocks with unknown versionbits version.")
# Mine a period worth of expected blocks so the generic block-version warning
# is cleared. This will move the versionbit state to ACTIVE.
@@ -101,8 +95,8 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# Generating one more block will be enough to generate an error.
node.generatetoaddress(1, node_deterministic_address)
# Check that get*info() shows the versionbits unknown rules warning
- assert(WARN_UNKNOWN_RULES_ACTIVE in node.getmininginfo()["warnings"])
- assert(WARN_UNKNOWN_RULES_ACTIVE in node.getnetworkinfo()["warnings"])
+ assert WARN_UNKNOWN_RULES_ACTIVE in node.getmininginfo()["warnings"]
+ assert WARN_UNKNOWN_RULES_ACTIVE in node.getnetworkinfo()["warnings"]
# Check that the alert file shows the versionbits unknown rules warning
wait_until(lambda: self.versionbits_in_alert_file(), timeout=60)
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index aed439339f..7bb7044cc0 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 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 bitcoin-cli"""
@@ -16,7 +16,7 @@ class TestBitcoinCli(BitcoinTestFramework):
"""Main test logic"""
cli_response = self.nodes[0].cli("-version").send_cli()
- assert("Bitcoin Core RPC client version" in cli_response)
+ assert "{} RPC client version".format(self.config['environment']['PACKAGE_NAME']) in cli_response
self.log.info("Compare responses from getwalletinfo RPC and `bitcoin-cli getwalletinfo`")
if self.is_wallet_compiled():
@@ -57,16 +57,14 @@ class TestBitcoinCli(BitcoinTestFramework):
assert_equal(cli_get_info['version'], network_info['version'])
assert_equal(cli_get_info['protocolversion'], network_info['protocolversion'])
- if self.is_wallet_compiled():
- 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['chain'], blockchain_info['chain'])
if self.is_wallet_compiled():
+ assert_equal(cli_get_info['walletversion'], wallet_info['walletversion'])
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'])
diff --git a/test/functional/interface_http.py b/test/functional/interface_http.py
index 20889366e5..bb868d7115 100755
--- a/test/functional/interface_http.py
+++ b/test/functional/interface_http.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 RPC HTTP basics."""
@@ -30,14 +30,14 @@ class HTTPBasicsTest (BitcoinTestFramework):
conn.connect()
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
- assert(b'"error":null' in out1)
- assert(conn.sock is not None) #according to http/1.1 connection must still be open!
+ assert b'"error":null' in out1
+ assert conn.sock is not None #according to http/1.1 connection must still be open!
#send 2nd request without closing connection
conn.request('POST', '/', '{"method": "getchaintips"}', headers)
out1 = conn.getresponse().read()
- assert(b'"error":null' in out1) #must also response with a correct json-rpc message
- assert(conn.sock is not None) #according to http/1.1 connection must still be open!
+ assert b'"error":null' in out1 #must also response with a correct json-rpc message
+ assert conn.sock is not None #according to http/1.1 connection must still be open!
conn.close()
#same should be if we add keep-alive because this should be the std. behaviour
@@ -47,14 +47,14 @@ class HTTPBasicsTest (BitcoinTestFramework):
conn.connect()
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
- assert(b'"error":null' in out1)
- assert(conn.sock is not None) #according to http/1.1 connection must still be open!
+ assert b'"error":null' in out1
+ assert conn.sock is not None #according to http/1.1 connection must still be open!
#send 2nd request without closing connection
conn.request('POST', '/', '{"method": "getchaintips"}', headers)
out1 = conn.getresponse().read()
- assert(b'"error":null' in out1) #must also response with a correct json-rpc message
- assert(conn.sock is not None) #according to http/1.1 connection must still be open!
+ assert b'"error":null' in out1 #must also response with a correct json-rpc message
+ assert conn.sock is not None #according to http/1.1 connection must still be open!
conn.close()
#now do the same with "Connection: close"
@@ -64,8 +64,8 @@ class HTTPBasicsTest (BitcoinTestFramework):
conn.connect()
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
- assert(b'"error":null' in out1)
- assert(conn.sock is None) #now the connection must be closed after the response
+ assert b'"error":null' in out1
+ assert conn.sock is None #now the connection must be closed after the response
#node1 (2nd node) is running with disabled keep-alive option
urlNode1 = urllib.parse.urlparse(self.nodes[1].url)
@@ -76,7 +76,7 @@ class HTTPBasicsTest (BitcoinTestFramework):
conn.connect()
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
- assert(b'"error":null' in out1)
+ assert b'"error":null' in out1
#node2 (third node) is running with standard keep-alive parameters which means keep-alive is on
urlNode2 = urllib.parse.urlparse(self.nodes[2].url)
@@ -87,8 +87,8 @@ class HTTPBasicsTest (BitcoinTestFramework):
conn.connect()
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
- assert(b'"error":null' in out1)
- assert(conn.sock is not None) #connection must be closed because bitcoind should use keep-alive by default
+ assert b'"error":null' in out1
+ assert conn.sock is not None #connection must be closed because bitcoind should use keep-alive by default
# Check excessive request size
conn = http.client.HTTPConnection(urlNode2.hostname, urlNode2.port)
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index f850a54462..a036dfc790 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 REST API."""
@@ -22,6 +22,8 @@ from test_framework.util import (
hex_str_to_bytes,
)
+from test_framework.messages import BLOCK_HEADER_SIZE
+
class ReqType(Enum):
JSON = 1
BIN = 2
@@ -41,8 +43,7 @@ class RESTTest (BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- # TODO: remove -txindex. Currently required for getrawtransaction call.
- self.extra_args = [["-rest", "-txindex"], []]
+ self.extra_args = [["-rest"], []]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -89,15 +90,17 @@ class RESTTest (BitcoinTestFramework):
txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
self.sync_all()
- self.nodes[1].generatetoaddress(1, not_related_address)
- self.sync_all()
- bb_hash = self.nodes[0].getbestblockhash()
- assert_equal(self.nodes[1].getbalance(), Decimal("0.1"))
-
- self.log.info("Load the transaction using the /tx URI")
+ self.log.info("Test the /tx URI")
json_obj = self.test_rest_request("/tx/{}".format(txid))
+ assert_equal(json_obj['txid'], txid)
+
+ # Check hex format response
+ hex_response = self.test_rest_request("/tx/{}".format(txid), req_type=ReqType.HEX, ret_type=RetType.OBJ)
+ assert_greater_than_or_equal(int(hex_response.getheader('content-length')),
+ json_obj['size']*2)
+
spent = (json_obj['vin'][0]['txid'], json_obj['vin'][0]['vout']) # get the vin to later check for utxo (should be spent by then)
# get n of 0.1 outpoint
n, = filter_output_indices_by_value(json_obj['vout'], Decimal('0.1'))
@@ -105,9 +108,14 @@ class RESTTest (BitcoinTestFramework):
self.log.info("Query an unspent TXO using the /getutxos URI")
- json_obj = self.test_rest_request("/getutxos/{}-{}".format(*spending))
+ self.nodes[1].generatetoaddress(1, not_related_address)
+ self.sync_all()
+ bb_hash = self.nodes[0].getbestblockhash()
+
+ assert_equal(self.nodes[1].getbalance(), Decimal("0.1"))
# Check chainTip response
+ json_obj = self.test_rest_request("/getutxos/{}-{}".format(*spending))
assert_equal(json_obj['chaintipHash'], bb_hash)
# Make sure there is one utxo
@@ -144,7 +152,7 @@ class RESTTest (BitcoinTestFramework):
bin_response = self.test_rest_request("/getutxos", http_method='POST', req_type=ReqType.BIN, body=bin_request, ret_type=RetType.BYTES)
output = BytesIO(bin_response)
chain_height, = unpack("i", output.read(4))
- response_hash = binascii.hexlify(output.read(32)[::-1]).decode('ascii')
+ response_hash = output.read(32)[::-1].hex()
assert_equal(bb_hash, response_hash) # check if getutxo's chaintip during calculation was fine
assert_equal(chain_height, 102) # chain height must be 102
@@ -214,26 +222,26 @@ class RESTTest (BitcoinTestFramework):
# Check binary format
response = self.test_rest_request("/block/{}".format(bb_hash), req_type=ReqType.BIN, ret_type=RetType.OBJ)
- assert_greater_than(int(response.getheader('content-length')), 80)
+ assert_greater_than(int(response.getheader('content-length')), BLOCK_HEADER_SIZE)
response_bytes = response.read()
# Compare with block header
response_header = self.test_rest_request("/headers/1/{}".format(bb_hash), req_type=ReqType.BIN, ret_type=RetType.OBJ)
- assert_equal(int(response_header.getheader('content-length')), 80)
+ assert_equal(int(response_header.getheader('content-length')), BLOCK_HEADER_SIZE)
response_header_bytes = response_header.read()
- assert_equal(response_bytes[:80], response_header_bytes)
+ assert_equal(response_bytes[:BLOCK_HEADER_SIZE], response_header_bytes)
# Check block hex format
response_hex = self.test_rest_request("/block/{}".format(bb_hash), req_type=ReqType.HEX, ret_type=RetType.OBJ)
- assert_greater_than(int(response_hex.getheader('content-length')), 160)
+ assert_greater_than(int(response_hex.getheader('content-length')), BLOCK_HEADER_SIZE*2)
response_hex_bytes = response_hex.read().strip(b'\n')
assert_equal(binascii.hexlify(response_bytes), response_hex_bytes)
# Compare with hex block header
response_header_hex = self.test_rest_request("/headers/1/{}".format(bb_hash), req_type=ReqType.HEX, ret_type=RetType.OBJ)
- assert_greater_than(int(response_header_hex.getheader('content-length')), 160)
- response_header_hex_bytes = response_header_hex.read(160)
- assert_equal(binascii.hexlify(response_bytes[:80]), response_header_hex_bytes)
+ assert_greater_than(int(response_header_hex.getheader('content-length')), BLOCK_HEADER_SIZE*2)
+ response_header_hex_bytes = response_header_hex.read(BLOCK_HEADER_SIZE*2)
+ assert_equal(binascii.hexlify(response_bytes[:BLOCK_HEADER_SIZE]), response_header_hex_bytes)
# Check json format
block_json_obj = self.test_rest_request("/block/{}".format(bb_hash))
@@ -244,7 +252,7 @@ class RESTTest (BitcoinTestFramework):
resp_hex = self.test_rest_request("/blockhashbyheight/{}".format(block_json_obj['height']), req_type=ReqType.HEX, ret_type=RetType.OBJ)
assert_equal(resp_hex.read().decode('utf-8').rstrip(), bb_hash)
resp_bytes = self.test_rest_request("/blockhashbyheight/{}".format(block_json_obj['height']), req_type=ReqType.BIN, ret_type=RetType.BYTES)
- blockhash = binascii.hexlify(resp_bytes[::-1]).decode('utf-8')
+ blockhash = resp_bytes[::-1].hex()
assert_equal(blockhash, bb_hash)
# Check invalid blockhashbyheight requests
@@ -272,17 +280,6 @@ class RESTTest (BitcoinTestFramework):
json_obj = self.test_rest_request("/headers/5/{}".format(bb_hash))
assert_equal(len(json_obj), 5) # now we should have 5 header objects
- self.log.info("Test the /tx URI")
-
- tx_hash = block_json_obj['tx'][0]['txid']
- json_obj = self.test_rest_request("/tx/{}".format(tx_hash))
- assert_equal(json_obj['txid'], tx_hash)
-
- # Check hex format response
- hex_response = self.test_rest_request("/tx/{}".format(tx_hash), req_type=ReqType.HEX, ret_type=RetType.OBJ)
- assert_greater_than_or_equal(int(hex_response.getheader('content-length')),
- json_obj['size']*2)
-
self.log.info("Test tx inclusion in the /mempool and /block URIs")
# Make 3 tx and mine them on node 1
diff --git a/test/functional/interface_rpc.py b/test/functional/interface_rpc.py
index b6955d4492..49ae0fb1a9 100755
--- a/test/functional/interface_rpc.py
+++ b/test/functional/interface_rpc.py
@@ -1,12 +1,22 @@
#!/usr/bin/env python3
-# Copyright (c) 2018 The Bitcoin Core developers
+# Copyright (c) 2018-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Tests some generic aspects of the RPC interface."""
+from test_framework.authproxy import JSONRPCException
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_greater_than_or_equal
+def expect_http_status(expected_http_status, expected_rpc_code,
+ fcn, *args):
+ try:
+ fcn(*args)
+ raise AssertionError("Expected RPC error %d, got none" % expected_rpc_code)
+ except JSONRPCException as exc:
+ assert_equal(exc.error["code"], expected_rpc_code)
+ assert_equal(exc.http_status, expected_http_status)
+
class RPCInterfaceTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
@@ -48,9 +58,16 @@ class RPCInterfaceTest(BitcoinTestFramework):
assert_equal(result_by_id[3]['error'], None)
assert result_by_id[3]['result'] is not None
+ def test_http_status_codes(self):
+ self.log.info("Testing HTTP status codes for JSON-RPC requests...")
+
+ expect_http_status(404, -32601, self.nodes[0].invalidmethod)
+ expect_http_status(500, -8, self.nodes[0].getblockhash, 42)
+
def run_test(self):
self.test_getrpcinfo()
self.test_batch_request()
+ self.test_http_status_codes()
if __name__ == '__main__':
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 94fea37090..8e58c85c15 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 notification interface."""
@@ -10,7 +10,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import CTransaction
from test_framework.util import (
assert_equal,
- bytes_to_hex_str,
hash256,
)
from io import BytesIO
@@ -94,17 +93,17 @@ class ZMQTest (BitcoinTestFramework):
tx = CTransaction()
tx.deserialize(BytesIO(hex))
tx.calc_sha256()
- assert_equal(tx.hash, bytes_to_hex_str(txid))
+ assert_equal(tx.hash, txid.hex())
# Should receive the generated block hash.
- hash = bytes_to_hex_str(self.hashblock.receive())
+ hash = self.hashblock.receive().hex()
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"])
+ assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"])
# Should receive the generated raw block.
block = self.rawblock.receive()
- assert_equal(genhashes[x], bytes_to_hex_str(hash256(block[:80])))
+ assert_equal(genhashes[x], hash256(block[:80]).hex())
if self.is_wallet_compiled():
self.log.info("Wait for tx from second node")
@@ -113,11 +112,11 @@ class ZMQTest (BitcoinTestFramework):
# Should receive the broadcasted txid.
txid = self.hashtx.receive()
- assert_equal(payment_txid, bytes_to_hex_str(txid))
+ assert_equal(payment_txid, txid.hex())
# Should receive the broadcasted raw transaction.
hex = self.rawtx.receive()
- assert_equal(payment_txid, bytes_to_hex_str(hash256(hex)))
+ assert_equal(payment_txid, hash256(hex).hex())
self.log.info("Test the getzmqnotifications RPC")
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index e2a219b85a..2bb5d8ab7d 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 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 mempool acceptance of raw transactions."""
@@ -27,9 +27,7 @@ from test_framework.script import (
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- bytes_to_hex_str,
hex_str_to_bytes,
- wait_until,
)
@@ -38,7 +36,6 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
self.num_nodes = 1
self.extra_args = [[
'-txindex',
- '-reindex', # Need reindex for txindex
'-acceptnonstdtxn=0', # Try to mimic main-net
]] * self.num_nodes
@@ -56,7 +53,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
self.log.info('Start with empty mempool, and 200 blocks')
self.mempool_size = 0
- wait_until(lambda: node.getblockcount() == 200)
+ assert_equal(node.getblockcount(), 200)
assert_equal(node.getmempoolinfo()['size'], self.mempool_size)
coins = node.listunspent()
@@ -71,7 +68,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
inputs=[{'txid': coin['txid'], 'vout': coin['vout']}],
outputs=[{node.getnewaddress(): 0.3}, {node.getnewaddress(): 49}],
))['hex']
- txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, allowhighfees=True)
+ txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, maxfeerate=0)
node.generate(1)
self.mempool_size = 0
self.check_mempool_result(
@@ -103,10 +100,10 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_final)))
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': True}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
- allowhighfees=True,
+ rawtxs=[tx.serialize().hex()],
+ maxfeerate=0,
)
- node.sendrawtransaction(hexstring=raw_tx_final, allowhighfees=True)
+ node.sendrawtransaction(hexstring=raw_tx_final, maxfeerate=0)
self.mempool_size += 1
self.log.info('A transaction in the mempool')
@@ -121,7 +118,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
tx.vout[0].nValue -= int(fee * COIN) # Double the fee
tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER + 1 # Now, opt out of RBF
- raw_tx_0 = node.signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize()))['hex']
+ raw_tx_0 = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
txid_0 = tx.rehash()
self.check_mempool_result(
@@ -131,15 +128,15 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
self.log.info('A transaction that conflicts with an unconfirmed tx')
# Send the transaction that replaces the mempool transaction and opts out of replaceability
- node.sendrawtransaction(hexstring=bytes_to_hex_str(tx.serialize()), allowhighfees=True)
+ node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)
# take original raw_tx_0
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee
# skip re-signing the tx
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '18: txn-mempool-conflict'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
- allowhighfees=True,
+ rawtxs=[tx.serialize().hex()],
+ maxfeerate=0,
)
self.log.info('A transaction with missing inputs, that never existed')
@@ -148,14 +145,14 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
# skip re-signing the tx
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'missing-inputs'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
self.log.info('A transaction with missing inputs, that existed once in the past')
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
tx.vin[0].prevout.n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend
- raw_tx_1 = node.signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize()))['hex']
- txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, allowhighfees=True)
+ raw_tx_1 = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
+ txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, maxfeerate=0)
# Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them
raw_tx_spend_both = node.signrawtransactionwithwallet(node.createrawtransaction(
inputs=[
@@ -164,7 +161,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
],
outputs=[{node.getnewaddress(): 0.1}]
))['hex']
- txid_spend_both = node.sendrawtransaction(hexstring=raw_tx_spend_both, allowhighfees=True)
+ txid_spend_both = node.sendrawtransaction(hexstring=raw_tx_spend_both, maxfeerate=0)
node.generate(1)
self.mempool_size = 0
# Now see if we can add the coins back to the utxo set by sending the exact txs again
@@ -186,17 +183,17 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
# Reference tx should be valid on itself
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': True}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
self.log.info('A transaction with no outputs')
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
tx.vout = []
# Skip re-signing the transaction for context independent checks from now on
- # tx.deserialize(BytesIO(hex_str_to_bytes(node.signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize()))['hex'])))
+ # tx.deserialize(BytesIO(hex_str_to_bytes(node.signrawtransactionwithwallet(tx.serialize().hex())['hex'])))
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-empty'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
self.log.info('A really large transaction')
@@ -204,7 +201,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.vin = [tx.vin[0]] * math.ceil(MAX_BLOCK_BASE_SIZE / len(tx.vin[0].serialize()))
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-oversize'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
self.log.info('A transaction with negative output value')
@@ -212,7 +209,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.vout[0].nValue *= -1
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-negative'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
self.log.info('A transaction with too large output value')
@@ -220,7 +217,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.vout[0].nValue = 21000000 * COIN + 1
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-toolarge'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
self.log.info('A transaction with too large sum of output values')
@@ -229,7 +226,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.vout[0].nValue = 21000000 * COIN
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-txouttotal-toolarge'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
self.log.info('A transaction with duplicate inputs')
@@ -237,7 +234,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.vin = [tx.vin[0]] * 2
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-inputs-duplicate'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
self.log.info('A coinbase transaction')
@@ -246,7 +243,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_coinbase_spent)))
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: coinbase'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
self.log.info('Some nonstandard transactions')
@@ -254,19 +251,19 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.nVersion = 3 # A version currently non-standard
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: version'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
tx.vout[0].scriptPubKey = CScript([OP_0]) # Some non-standard script
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptpubkey'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
tx.vin[0].scriptSig = CScript([OP_HASH160]) # Some not-pushonly scriptSig
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptsig-not-pushonly'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript([OP_HASH160, hash160(b'burn'), OP_EQUAL]))
@@ -274,21 +271,21 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.vout = [output_p2sh_burn] * num_scripts
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: tx-size'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
tx.vout[0] = output_p2sh_burn
tx.vout[0].nValue -= 1 # Make output smaller, such that it is dust for our policy
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: dust'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff'])
tx.vout = [tx.vout[0]] * 2
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: multi-op-return'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
self.log.info('A timelocked transaction')
@@ -297,7 +294,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.nLockTime = node.getblockcount() + 1
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-final'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
+ rawtxs=[tx.serialize().hex()],
)
self.log.info('A transaction that is locked by BIP68 sequence logic')
@@ -306,8 +303,8 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
# Can skip re-signing the tx because of early rejection
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-BIP68-final'}],
- rawtxs=[bytes_to_hex_str(tx.serialize())],
- allowhighfees=True,
+ rawtxs=[tx.serialize().hex()],
+ maxfeerate=0,
)
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index c0918893cd..351b27e94a 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 mempool limiting together/eviction with the wallet."""
@@ -47,9 +47,9 @@ class MempoolLimitTest(BitcoinTestFramework):
txids[i] = create_lots_of_big_transactions(self.nodes[0], txouts, utxos[30*i:30*i+30], 30, (i+1)*base_fee)
self.log.info('The tx should be evicted by now')
- assert(txid not in self.nodes[0].getrawmempool())
+ assert txid not in self.nodes[0].getrawmempool()
txdata = self.nodes[0].gettransaction(txid)
- assert(txdata['confirmations'] == 0) #confirmation should still be 0
+ assert txdata['confirmations'] == 0 #confirmation should still be 0
self.log.info('Check that mempoolminfee is larger than minrelytxfee')
assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 9336547a6b..c7d241503a 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 descendant package tracking code."""
@@ -8,7 +8,11 @@ from decimal import Decimal
from test_framework.messages import COIN
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, satoshi_round, sync_blocks, sync_mempools
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ satoshi_round,
+)
MAX_ANCESTORS = 25
MAX_DESCENDANTS = 25
@@ -33,7 +37,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
signedtx = node.signrawtransactionwithwallet(rawtx)
txid = node.sendrawtransaction(signedtx['hex'])
fulltx = node.getrawtransaction(txid, 1)
- assert(len(fulltx['vout']) == num_outputs) # make sure we didn't generate a change output
+ assert len(fulltx['vout']) == num_outputs # make sure we didn't generate a change output
return (txid, send_value)
def run_test(self):
@@ -58,9 +62,9 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(len(mempool), MAX_ANCESTORS)
descendant_count = 1
descendant_fees = 0
- descendant_size = 0
+ descendant_vsize = 0
- ancestor_size = sum([mempool[tx]['size'] for tx in mempool])
+ ancestor_vsize = sum([mempool[tx]['vsize'] for tx in mempool])
ancestor_count = MAX_ANCESTORS
ancestor_fees = sum([mempool[tx]['fee'] for tx in mempool])
@@ -79,15 +83,15 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(mempool[x]['fees']['modified'], mempool[x]['modifiedfee'])
assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN)
assert_equal(mempool[x]['fees']['descendant'], descendant_fees)
- descendant_size += mempool[x]['size']
- assert_equal(mempool[x]['descendantsize'], descendant_size)
+ descendant_vsize += mempool[x]['vsize']
+ assert_equal(mempool[x]['descendantsize'], descendant_vsize)
descendant_count += 1
# Check that ancestor calculations are correct
assert_equal(mempool[x]['ancestorcount'], ancestor_count)
assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN)
- assert_equal(mempool[x]['ancestorsize'], ancestor_size)
- ancestor_size -= mempool[x]['size']
+ assert_equal(mempool[x]['ancestorsize'], ancestor_vsize)
+ ancestor_vsize -= mempool[x]['vsize']
ancestor_fees -= mempool[x]['fee']
ancestor_count -= 1
@@ -125,13 +129,13 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(len(v_ancestors), len(chain)-1)
for x in v_ancestors.keys():
assert_equal(mempool[x], v_ancestors[x])
- assert(chain[-1] not in v_ancestors.keys())
+ assert chain[-1] not in v_ancestors.keys()
v_descendants = self.nodes[0].getmempooldescendants(chain[0], True)
assert_equal(len(v_descendants), len(chain)-1)
for x in v_descendants.keys():
assert_equal(mempool[x], v_descendants[x])
- assert(chain[0] not in v_descendants.keys())
+ assert chain[0] not in v_descendants.keys()
# Check that ancestor modified fees includes fee deltas from
# prioritisetransaction
@@ -163,7 +167,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
# Check that prioritising a tx before it's added to the mempool works
# First clear the mempool by mining a block.
self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
assert_equal(len(self.nodes[0].getrawmempool()), 0)
# Prioritise a transaction that has been mined, then add it back to the
# mempool by using invalidateblock.
@@ -228,7 +232,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
# Test reorg handling
# First, the basics:
self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash())
self.nodes[1].reconsiderblock(self.nodes[0].getbestblockhash())
@@ -283,12 +287,12 @@ class MempoolPackagesTest(BitcoinTestFramework):
rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
- sync_mempools(self.nodes)
+ self.sync_mempools()
# Now try to disconnect the tip on each node...
self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
- sync_blocks(self.nodes)
+ self.sync_blocks()
if __name__ == '__main__':
MempoolPackagesTest().main()
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index d74d4eaaf1..bb0169ee52 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -37,7 +37,6 @@ Test is as follows:
"""
from decimal import Decimal
import os
-import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, wait_until
@@ -83,9 +82,10 @@ class MempoolPersistTest(BitcoinTestFramework):
self.start_node(1, extra_args=["-persistmempool=0"])
self.start_node(0)
self.start_node(2)
- # Give bitcoind a second to reload the mempool
- wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5, timeout=1)
- wait_until(lambda: len(self.nodes[2].getrawmempool()) == 5, timeout=1)
+ wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"], timeout=1)
+ wait_until(lambda: self.nodes[2].getmempoolinfo()["loaded"], timeout=1)
+ assert_equal(len(self.nodes[0].getrawmempool()), 5)
+ assert_equal(len(self.nodes[2].getrawmempool()), 5)
# The others have loaded their mempool. If node_1 loaded anything, we'd probably notice by now:
assert_equal(len(self.nodes[1].getrawmempool()), 0)
@@ -100,14 +100,14 @@ class MempoolPersistTest(BitcoinTestFramework):
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"])
- # Give bitcoind a second to reload the mempool
- time.sleep(1)
+ wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"])
assert_equal(len(self.nodes[0].getrawmempool()), 0)
self.log.debug("Stop-start node0. Verify that it has the transactions in its mempool.")
self.stop_nodes()
self.start_node(0)
- wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5)
+ wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"])
+ assert_equal(len(self.nodes[0].getrawmempool()), 5)
mempooldat0 = os.path.join(self.nodes[0].datadir, 'regtest', 'mempool.dat')
mempooldat1 = os.path.join(self.nodes[1].datadir, 'regtest', 'mempool.dat')
@@ -120,7 +120,8 @@ class MempoolPersistTest(BitcoinTestFramework):
os.rename(mempooldat0, mempooldat1)
self.stop_nodes()
self.start_node(1, extra_args=[])
- wait_until(lambda: len(self.nodes[1].getrawmempool()) == 5)
+ wait_until(lambda: self.nodes[1].getmempoolinfo()["loaded"])
+ assert_equal(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 creating a tmp folder called mempool.dat.new
diff --git a/test/functional/mempool_resurrect.py b/test/functional/mempool_resurrect.py
index 845beb551e..187c9026f6 100755
--- a/test/functional/mempool_resurrect.py
+++ b/test/functional/mempool_resurrect.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 resurrection of mined transactions when the blockchain is re-organized."""
@@ -45,7 +45,7 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
assert_equal(set(self.nodes[0].getrawmempool()), set())
for txid in spends1_id+spends2_id:
tx = self.nodes[0].gettransaction(txid)
- assert(tx["confirmations"] > 0)
+ assert tx["confirmations"] > 0
# Use invalidateblock to re-org back
for node in self.nodes:
@@ -55,7 +55,7 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
assert_equal(set(self.nodes[0].getrawmempool()), set(spends1_id+spends2_id))
for txid in spends1_id+spends2_id:
tx = self.nodes[0].gettransaction(txid)
- assert(tx["confirmations"] == 0)
+ assert tx["confirmations"] == 0
# Generate another block, they should all get mined
self.nodes[0].generate(1)
@@ -63,7 +63,7 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
assert_equal(set(self.nodes[0].getrawmempool()), set())
for txid in spends1_id+spends2_id:
tx = self.nodes[0].gettransaction(txid)
- assert(tx["confirmations"] > 0)
+ assert tx["confirmations"] > 0
if __name__ == '__main__':
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index 661d9f4c97..788aabc192 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 mining RPCs
@@ -11,10 +11,14 @@
import copy
from decimal import Decimal
-from test_framework.blocktools import create_coinbase
+from test_framework.blocktools import (
+ create_coinbase,
+ TIME_GENESIS_BLOCK,
+)
from test_framework.messages import (
CBlock,
CBlockHeader,
+ BLOCK_HEADER_SIZE
)
from test_framework.mininode import (
P2PDataStore,
@@ -23,37 +27,51 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- bytes_to_hex_str as b2x,
+ connect_nodes_bi,
)
from test_framework.script import CScriptNum
+
def assert_template(node, block, expect, rehash=True):
if rehash:
block.hashMerkleRoot = block.calc_merkle_root()
- rsp = node.getblocktemplate(template_request={'data': b2x(block.serialize()), 'mode': 'proposal', 'rules': ['segwit']})
+ rsp = node.getblocktemplate(template_request={'data': block.serialize().hex(), 'mode': 'proposal', 'rules': ['segwit']})
assert_equal(rsp, expect)
class MiningTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- self.setup_clean_chain = False
+ self.setup_clean_chain = True
+
+ def mine_chain(self):
+ self.log.info('Create some old blocks')
+ for t in range(TIME_GENESIS_BLOCK, TIME_GENESIS_BLOCK + 200 * 600, 600):
+ self.nodes[0].setmocktime(t)
+ self.nodes[0].generate(1)
+ mining_info = self.nodes[0].getmininginfo()
+ assert_equal(mining_info['blocks'], 200)
+ assert_equal(mining_info['currentblocktx'], 0)
+ assert_equal(mining_info['currentblockweight'], 4000)
+ self.restart_node(0)
+ connect_nodes_bi(self.nodes, 0, 1)
def run_test(self):
+ self.mine_chain()
node = self.nodes[0]
def assert_submitblock(block, result_str_1, result_str_2=None):
block.solve()
result_str_2 = result_str_2 or 'duplicate-invalid'
- assert_equal(result_str_1, node.submitblock(hexdata=b2x(block.serialize())))
- assert_equal(result_str_2, node.submitblock(hexdata=b2x(block.serialize())))
+ assert_equal(result_str_1, node.submitblock(hexdata=block.serialize().hex()))
+ assert_equal(result_str_2, node.submitblock(hexdata=block.serialize().hex()))
self.log.info('getmininginfo')
mining_info = node.getmininginfo()
assert_equal(mining_info['blocks'], 200)
assert_equal(mining_info['chain'], 'regtest')
- assert_equal(mining_info['currentblocktx'], 0)
- assert_equal(mining_info['currentblockweight'], 0)
+ assert 'currentblocktx' not in mining_info
+ assert 'currentblockweight' not in mining_info
assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10'))
assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334'))
assert_equal(mining_info['pooledtx'], 0)
@@ -93,7 +111,7 @@ class MiningTest(BitcoinTestFramework):
assert_template(node, block, None)
self.log.info("submitblock: Test block decode failure")
- assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, b2x(block.serialize()[:-15]))
+ assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, block.serialize()[:-15].hex())
self.log.info("getblocktemplate: Test bad input hash for coinbase transaction")
bad_block = copy.deepcopy(block)
@@ -102,10 +120,10 @@ class MiningTest(BitcoinTestFramework):
assert_template(node, bad_block, 'bad-cb-missing')
self.log.info("submitblock: Test invalid coinbase transaction")
- assert_raises_rpc_error(-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, bad_block.serialize().hex())
self.log.info("getblocktemplate: Test truncated final transaction")
- assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal', 'rules': ['segwit']})
+ assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': block.serialize()[:-1].hex(), 'mode': 'proposal', 'rules': ['segwit']})
self.log.info("getblocktemplate: Test duplicate transaction")
bad_block = copy.deepcopy(block)
@@ -131,11 +149,10 @@ class MiningTest(BitcoinTestFramework):
self.log.info("getblocktemplate: Test bad tx count")
# The tx count is immediately after the block header
- TX_COUNT_OFFSET = 80
bad_block_sn = bytearray(block.serialize())
- assert_equal(bad_block_sn[TX_COUNT_OFFSET], 1)
- bad_block_sn[TX_COUNT_OFFSET] += 1
- assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal', 'rules': ['segwit']})
+ assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1)
+ bad_block_sn[BLOCK_HEADER_SIZE] += 1
+ assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': bad_block_sn.hex(), 'mode': 'proposal', 'rules': ['segwit']})
self.log.info("getblocktemplate: Test bad bits")
bad_block = copy.deepcopy(block)
@@ -164,9 +181,9 @@ class MiningTest(BitcoinTestFramework):
assert_submitblock(bad_block, 'prev-blk-not-found', 'prev-blk-not-found')
self.log.info('submitheader tests')
- assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * 80))
- assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * 78))
- assert_raises_rpc_error(-25, 'Must submit previous header', lambda: node.submitheader(hexdata='ff' * 80))
+ assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE))
+ assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE-2)))
+ assert_raises_rpc_error(-25, 'Must submit previous header', lambda: node.submitheader(hexdata=super(CBlock, bad_block).serialize().hex()))
block.nTime += 1
block.solve()
@@ -175,23 +192,23 @@ class MiningTest(BitcoinTestFramework):
return {'hash': b_hash, 'height': 202, 'branchlen': branchlen, 'status': status}
assert chain_tip(block.hash) not in node.getchaintips()
- node.submitheader(hexdata=b2x(block.serialize()))
+ node.submitheader(hexdata=block.serialize().hex())
assert chain_tip(block.hash) in node.getchaintips()
- node.submitheader(hexdata=b2x(CBlockHeader(block).serialize())) # Noop
+ node.submitheader(hexdata=CBlockHeader(block).serialize().hex()) # Noop
assert chain_tip(block.hash) in node.getchaintips()
bad_block_root = copy.deepcopy(block)
bad_block_root.hashMerkleRoot += 2
bad_block_root.solve()
assert chain_tip(bad_block_root.hash) not in node.getchaintips()
- node.submitheader(hexdata=b2x(CBlockHeader(bad_block_root).serialize()))
+ node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
assert chain_tip(bad_block_root.hash) in node.getchaintips()
# Should still reject invalid blocks, even if we have the header:
- assert_equal(node.submitblock(hexdata=b2x(bad_block_root.serialize())), 'bad-txnmrklroot')
- assert_equal(node.submitblock(hexdata=b2x(bad_block_root.serialize())), 'bad-txnmrklroot')
+ assert_equal(node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot')
+ assert_equal(node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot')
assert chain_tip(bad_block_root.hash) in node.getchaintips()
# We know the header for this invalid block, so should just return early without error:
- node.submitheader(hexdata=b2x(CBlockHeader(bad_block_root).serialize()))
+ node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
assert chain_tip(bad_block_root.hash) in node.getchaintips()
bad_block_lock = copy.deepcopy(block)
@@ -199,19 +216,19 @@ class MiningTest(BitcoinTestFramework):
bad_block_lock.vtx[0].rehash()
bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root()
bad_block_lock.solve()
- assert_equal(node.submitblock(hexdata=b2x(bad_block_lock.serialize())), 'bad-txns-nonfinal')
- assert_equal(node.submitblock(hexdata=b2x(bad_block_lock.serialize())), 'duplicate-invalid')
+ assert_equal(node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'bad-txns-nonfinal')
+ assert_equal(node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'duplicate-invalid')
# Build a "good" block on top of the submitted bad block
bad_block2 = copy.deepcopy(block)
bad_block2.hashPrevBlock = bad_block_lock.sha256
bad_block2.solve()
- assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=b2x(CBlockHeader(bad_block2).serialize())))
+ assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(bad_block2).serialize().hex()))
# Should reject invalid header right away
bad_block_time = copy.deepcopy(block)
bad_block_time.nTime = 1
bad_block_time.solve()
- assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=b2x(CBlockHeader(bad_block_time).serialize())))
+ assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
# Should ask for the block from a p2p node, if they announce the header as well:
node.add_p2p_connection(P2PDataStore())
@@ -222,11 +239,11 @@ class MiningTest(BitcoinTestFramework):
# Building a few blocks should give the same results
node.generatetoaddress(10, node.get_deterministic_priv_key().address)
- assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=b2x(CBlockHeader(bad_block_time).serialize())))
- assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=b2x(CBlockHeader(bad_block2).serialize())))
- node.submitheader(hexdata=b2x(CBlockHeader(block).serialize()))
- node.submitheader(hexdata=b2x(CBlockHeader(bad_block_root).serialize()))
- assert_equal(node.submitblock(hexdata=b2x(block.serialize())), 'duplicate') # valid
+ assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
+ assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(bad_block2).serialize().hex()))
+ node.submitheader(hexdata=CBlockHeader(block).serialize().hex())
+ node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
+ assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
if __name__ == '__main__':
diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py
index 72cde8e811..445ec124ce 100755
--- a/test/functional/mining_getblocktemplate_longpoll.py
+++ b/test/functional/mining_getblocktemplate_longpoll.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 longpolling with getblocktemplate."""
@@ -38,27 +38,27 @@ class GetBlockTemplateLPTest(BitcoinTestFramework):
longpollid = template['longpollid']
# longpollid should not change between successive invocations if nothing else happens
template2 = self.nodes[0].getblocktemplate({'rules': ['segwit']})
- assert(template2['longpollid'] == longpollid)
+ assert template2['longpollid'] == longpollid
# Test 1: test that the longpolling wait if we do nothing
thr = LongpollThread(self.nodes[0])
thr.start()
# check that thread still lives
thr.join(5) # wait 5 seconds or until thread exits
- assert(thr.is_alive())
+ assert thr.is_alive()
# Test 2: test that longpoll will terminate if another node generates a block
self.nodes[1].generate(1) # generate a block on another node
# check that thread will exit now that new transaction entered mempool
thr.join(5) # wait 5 seconds or until thread exits
- assert(not thr.is_alive())
+ assert not thr.is_alive()
# Test 3: test that longpoll will terminate if we generate a block ourselves
thr = LongpollThread(self.nodes[0])
thr.start()
self.nodes[0].generate(1) # generate a block on another node
thr.join(5) # wait 5 seconds or until thread exits
- assert(not thr.is_alive())
+ assert not thr.is_alive()
# Test 4: test that introducing a new transaction into the mempool will terminate the longpoll
thr = LongpollThread(self.nodes[0])
@@ -69,7 +69,7 @@ class GetBlockTemplateLPTest(BitcoinTestFramework):
(txid, txhex, fee) = random_transaction(self.nodes, Decimal("1.1"), min_relay_fee, Decimal("0.001"), 20)
# after one minute, every 10 seconds the mempool is probed, so in 80 seconds it should have returned
thr.join(60 + 20)
- assert(not thr.is_alive())
+ assert not thr.is_alive()
if __name__ == '__main__':
GetBlockTemplateLPTest().main()
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index ca4b621a78..b0a069be81 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 prioritisetransaction mining RPC."""
@@ -63,9 +63,9 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
sizes = [0, 0, 0]
for i in range(3):
for j in txids[i]:
- assert(j in mempool)
- sizes[i] += mempool[j]['size']
- assert(sizes[i] > MAX_BLOCK_BASE_SIZE) # Fail => raise utxo_count
+ assert j in mempool
+ sizes[i] += mempool[j]['vsize']
+ assert sizes[i] > MAX_BLOCK_BASE_SIZE # Fail => raise utxo_count
# add a fee delta to something in the cheapest bucket and make sure it gets mined
# also check that a different entry in the cheapest bucket is NOT mined
@@ -75,8 +75,8 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
mempool = self.nodes[0].getrawmempool()
self.log.info("Assert that prioritised transaction was mined")
- assert(txids[0][0] not in mempool)
- assert(txids[0][1] in mempool)
+ assert txids[0][0] not in mempool
+ assert txids[0][1] in mempool
high_fee_tx = None
for x in txids[2]:
@@ -84,7 +84,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
high_fee_tx = x
# Something high-fee should have been mined!
- assert(high_fee_tx is not None)
+ assert high_fee_tx is not None
# Add a prioritisation before a tx is in the mempool (de-prioritising a
# high-fee transaction so that it's now low fee).
@@ -95,7 +95,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
# Check to make sure our high fee rate tx is back in the mempool
mempool = self.nodes[0].getrawmempool()
- assert(high_fee_tx in mempool)
+ assert high_fee_tx in mempool
# Now verify the modified-high feerate transaction isn't mined before
# the other high fee transactions. Keep mining until our mempool has
@@ -107,14 +107,14 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
# transactions should have been.
mempool = self.nodes[0].getrawmempool()
self.log.info("Assert that de-prioritised transaction is still in mempool")
- assert(high_fee_tx in mempool)
+ assert high_fee_tx in mempool
for x in txids[2]:
if (x != high_fee_tx):
- assert(x not in mempool)
+ assert x not in mempool
# Create a free transaction. Should be rejected.
utxo_list = self.nodes[0].listunspent()
- assert(len(utxo_list) > 0)
+ assert len(utxo_list) > 0
utxo = utxo_list[0]
inputs = []
@@ -127,7 +127,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
# This will raise an exception due to min relay fee not being met
assert_raises_rpc_error(-26, "min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex)
- assert(tx_id not in self.nodes[0].getrawmempool())
+ assert tx_id not in self.nodes[0].getrawmempool()
# This is a less than 1000-byte transaction, so just set the fee
# to be the minimum for a 1000-byte transaction and check that it is
@@ -136,7 +136,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
self.log.info("Assert that prioritised free transaction is accepted to mempool")
assert_equal(self.nodes[0].sendrawtransaction(tx_hex), tx_id)
- assert(tx_id in self.nodes[0].getrawmempool())
+ assert tx_id in self.nodes[0].getrawmempool()
# Test that calling prioritisetransaction is sufficient to trigger
# getblocktemplate to (eventually) return a new block.
@@ -147,7 +147,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
self.nodes[0].setmocktime(mock_time+10)
new_template = self.nodes[0].getblocktemplate({'rules': ['segwit']})
- assert(template != new_template)
+ assert template != new_template
if __name__ == '__main__':
PrioritiseTransactionTest().main()
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
new file mode 100755
index 0000000000..12cb06a407
--- /dev/null
+++ b/test/functional/p2p_blocksonly.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 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 p2p blocksonly"""
+
+from test_framework.messages import msg_tx, CTransaction, FromHex
+from test_framework.mininode import P2PInterface
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+
+class P2PBlocksOnly(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 1
+ self.extra_args = [["-blocksonly"]]
+
+ def run_test(self):
+ self.nodes[0].add_p2p_connection(P2PInterface())
+
+ self.log.info('Check that txs from p2p are rejected')
+ prevtx = self.nodes[0].getblock(self.nodes[0].getblockhash(1), 2)['tx'][0]
+ rawtx = self.nodes[0].createrawtransaction(
+ inputs=[{
+ 'txid': prevtx['txid'],
+ 'vout': 0
+ }],
+ outputs=[{
+ self.nodes[0].get_deterministic_priv_key().address: 50 - 0.00125
+ }],
+ )
+ sigtx = self.nodes[0].signrawtransactionwithkey(
+ hexstring=rawtx,
+ privkeys=[self.nodes[0].get_deterministic_priv_key().key],
+ prevtxs=[{
+ 'txid': prevtx['txid'],
+ 'vout': 0,
+ 'scriptPubKey': prevtx['vout'][0]['scriptPubKey']['hex'],
+ }],
+ )['hex']
+ assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
+ with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']):
+ self.nodes[0].p2p.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
+ self.nodes[0].p2p.sync_with_ping()
+ assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
+
+ self.log.info('Check that txs from rpc are not rejected and relayed to other peers')
+ assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)
+ txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
+ with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=0'.format(txid)]):
+ self.nodes[0].sendrawtransaction(sigtx)
+ self.nodes[0].p2p.wait_for_tx(txid)
+ assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
+
+
+if __name__ == '__main__':
+ P2PBlocksOnly().main()
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index f0dee59b5c..0994857912 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2018 The Bitcoin Core developers
+# Copyright (c) 2016-2019 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 compact blocks (BIP 152).
@@ -7,19 +7,18 @@
Version 1 compact blocks are pre-segwit (txids)
Version 2 compact blocks are post-segwit (wtxids)
"""
-from decimal import Decimal
import random
from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment
-from test_framework.messages import BlockTransactions, BlockTransactionsRequest, calculate_shortid, CBlock, CBlockHeader, CInv, COutPoint, CTransaction, CTxIn, CTxInWitness, CTxOut, FromHex, HeaderAndShortIDs, msg_block, msg_blocktxn, msg_cmpctblock, msg_getblocktxn, msg_getdata, msg_getheaders, msg_headers, msg_inv, msg_sendcmpct, msg_sendheaders, msg_tx, msg_witness_block, msg_witness_blocktxn, MSG_WITNESS_FLAG, NODE_NETWORK, NODE_WITNESS, P2PHeaderAndShortIDs, PrefilledTransaction, ser_uint256, ToHex
+from test_framework.messages import BlockTransactions, BlockTransactionsRequest, calculate_shortid, CBlock, CBlockHeader, CInv, COutPoint, CTransaction, CTxIn, CTxInWitness, CTxOut, FromHex, HeaderAndShortIDs, msg_no_witness_block, msg_no_witness_blocktxn, msg_cmpctblock, msg_getblocktxn, msg_getdata, msg_getheaders, msg_headers, msg_inv, msg_sendcmpct, msg_sendheaders, msg_tx, msg_block, msg_blocktxn, MSG_WITNESS_FLAG, NODE_NETWORK, P2PHeaderAndShortIDs, PrefilledTransaction, ser_uint256, ToHex
from test_framework.mininode import mininode_lock, P2PInterface
from test_framework.script import CScript, OP_TRUE, OP_DROP
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, get_bip9_status, satoshi_round, sync_blocks, wait_until
+from test_framework.util import assert_equal, get_bip9_status, wait_until
# TestP2PConn: A peer we use to send messages to bitcoind, and store responses.
class TestP2PConn(P2PInterface):
- def __init__(self):
+ def __init__(self, cmpct_version):
super().__init__()
self.last_sendcmpct = []
self.block_announced = False
@@ -27,6 +26,7 @@ class TestP2PConn(P2PInterface):
# This is for synchronizing the p2p message traffic,
# so we can eg wait until a particular block is announced.
self.announced_blockhashes = set()
+ self.cmpct_version = cmpct_version
def on_sendcmpct(self, message):
self.last_sendcmpct.append(message)
@@ -94,11 +94,7 @@ class TestP2PConn(P2PInterface):
class CompactBlocksTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- # Node0 = pre-segwit, node1 = segwit-aware
- self.num_nodes = 2
- # 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.num_nodes = 1
self.utxos = []
def skip_test_if_missing_module(self):
@@ -117,11 +113,10 @@ class CompactBlocksTest(BitcoinTestFramework):
# Create 10 more anyone-can-spend utxo's for testing.
def make_utxos(self):
- # Doesn't matter which node we use, just use node0.
block = self.build_block_on_tip(self.nodes[0])
- self.test_node.send_and_ping(msg_block(block))
- assert(int(self.nodes[0].getbestblockhash(), 16) == block.sha256)
- self.nodes[0].generate(100)
+ self.segwit_node.send_and_ping(msg_no_witness_block(block))
+ assert int(self.nodes[0].getbestblockhash(), 16) == block.sha256
+ self.nodes[0].generatetoaddress(100, self.nodes[0].getnewaddress(address_type="bech32"))
total_value = block.vtx[0].vout[0].nValue
out_value = total_value // 10
@@ -135,10 +130,10 @@ class CompactBlocksTest(BitcoinTestFramework):
block2.vtx.append(tx)
block2.hashMerkleRoot = block2.calc_merkle_root()
block2.solve()
- self.test_node.send_and_ping(msg_block(block2))
+ self.segwit_node.send_and_ping(msg_no_witness_block(block2))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block2.sha256)
self.utxos.extend([[tx.sha256, i, out_value] for i in range(10)])
- return
+
# Test "sendcmpct" (between peers preferring the same version):
# - No compact block announcements unless sendcmpct is sent.
@@ -149,7 +144,10 @@ class CompactBlocksTest(BitcoinTestFramework):
# are made with compact blocks.
# If old_node is passed in, request compact blocks with version=preferred-1
# and verify that it receives block announcements via compact block.
- def test_sendcmpct(self, node, test_node, preferred_version, old_node=None):
+ def test_sendcmpct(self, test_node, old_node=None):
+ preferred_version = test_node.cmpct_version
+ node = self.nodes[0]
+
# Make sure we get a SENDCMPCT message from our peer
def received_sendcmpct():
return (len(test_node.last_sendcmpct) > 0)
@@ -167,7 +165,7 @@ class CompactBlocksTest(BitcoinTestFramework):
peer.clear_block_announcement()
block_hash = int(node.generate(1)[0], 16)
peer.wait_for_block_announcement(block_hash, timeout=30)
- assert(peer.block_announced)
+ assert peer.block_announced
with mininode_lock:
assert predicate(peer), (
@@ -251,23 +249,18 @@ class CompactBlocksTest(BitcoinTestFramework):
# This index will be too high
prefilled_txn = PrefilledTransaction(1, block.vtx[0])
cmpct_block.prefilled_txn = [prefilled_txn]
- self.test_node.send_await_disconnect(msg_cmpctblock(cmpct_block))
+ self.segwit_node.send_await_disconnect(msg_cmpctblock(cmpct_block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.hashPrevBlock)
# Compare the generated shortids to what we expect based on BIP 152, given
# bitcoind's choice of nonce.
- def test_compactblock_construction(self, node, test_node, version, use_witness_address):
+ def test_compactblock_construction(self, test_node, use_witness_address=True):
+ version = test_node.cmpct_version
+ node = self.nodes[0]
# Generate a bunch of transactions.
node.generate(101)
num_transactions = 25
address = node.getnewaddress()
- if use_witness_address:
- # Want at least one segwit spend, so move all funds to
- # a witness address.
- address = node.getnewaddress(address_type='bech32')
- value_to_send = node.getbalance()
- node.sendtoaddress(address, satoshi_round(value_to_send - Decimal(0.1)))
- node.generate(1)
segwit_tx_generated = False
for i in range(num_transactions):
@@ -285,7 +278,7 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.wait_for_block_announcement(tip)
# Make sure we will receive a fast-announce compact block
- self.request_cb_announcements(test_node, node, version)
+ self.request_cb_announcements(test_node)
# Now mine a block, and look at the resulting compact block.
test_node.clear_block_announcement()
@@ -303,7 +296,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Now fetch and check the compact block
header_and_shortids = None
with mininode_lock:
- assert("cmpctblock" in test_node.last_message)
+ assert "cmpctblock" in test_node.last_message
# Convert the on-the-wire representation to absolute indexes
header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)
@@ -319,7 +312,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Now fetch and check the compact block
header_and_shortids = None
with mininode_lock:
- assert("cmpctblock" in test_node.last_message)
+ assert "cmpctblock" in test_node.last_message
# Convert the on-the-wire representation to absolute indexes
header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)
@@ -330,7 +323,7 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(header_and_shortids.header.sha256, block_hash)
# Make sure the prefilled_txn appears to have included the coinbase
- assert(len(header_and_shortids.prefilled_txn) >= 1)
+ assert len(header_and_shortids.prefilled_txn) >= 1
assert_equal(header_and_shortids.prefilled_txn[0].index, 0)
# Check that all prefilled_txn entries match what's in the block.
@@ -345,7 +338,7 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(wtxid, block.vtx[entry.index].calc_sha256(True))
else:
# Shouldn't have received a witness
- assert(entry.tx.wit.is_null())
+ assert entry.tx.wit.is_null()
# Check that the cmpctblock message announced all the transactions.
assert_equal(len(header_and_shortids.prefilled_txn) + len(header_and_shortids.shortids), len(block.vtx))
@@ -375,7 +368,9 @@ class CompactBlocksTest(BitcoinTestFramework):
# Post-segwit: upgraded nodes would only make this request of cb-version-2,
# NODE_WITNESS peers. Unupgraded nodes would still make this request of
# any cb-version-1-supporting peer.
- def test_compactblock_requests(self, node, test_node, version, segwit):
+ def test_compactblock_requests(self, test_node, segwit=True):
+ version = test_node.cmpct_version
+ node = self.nodes[0]
# Try announcing a block with an inv or header, expect a compactblock
# request
for announce in ["inv", "header"]:
@@ -407,15 +402,15 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
# Expect a getblocktxn message.
with mininode_lock:
- assert("getblocktxn" in test_node.last_message)
+ assert "getblocktxn" in test_node.last_message
absolute_indexes = test_node.last_message["getblocktxn"].block_txn_request.to_absolute()
assert_equal(absolute_indexes, [0]) # should be a coinbase request
# Send the coinbase, and verify that the tip advances.
if version == 2:
- msg = msg_witness_blocktxn()
- else:
msg = msg_blocktxn()
+ else:
+ msg = msg_no_witness_blocktxn()
msg.block_transactions.blockhash = block.sha256
msg.block_transactions.transactions = [block.vtx[0]]
test_node.send_and_ping(msg)
@@ -440,14 +435,16 @@ class CompactBlocksTest(BitcoinTestFramework):
# Test that we only receive getblocktxn requests for transactions that the
# node needs, and that responding to them causes the block to be
# reconstructed.
- def test_getblocktxn_requests(self, node, test_node, version):
+ def test_getblocktxn_requests(self, test_node):
+ version = test_node.cmpct_version
+ node = self.nodes[0]
with_witness = (version == 2)
def test_getblocktxn_response(compact_block, peer, expected_result):
msg = msg_cmpctblock(compact_block.to_p2p())
peer.send_and_ping(msg)
with mininode_lock:
- assert("getblocktxn" in peer.last_message)
+ assert "getblocktxn" in peer.last_message
absolute_indexes = peer.last_message["getblocktxn"].block_txn_request.to_absolute()
assert_equal(absolute_indexes, expected_result)
@@ -466,9 +463,9 @@ class CompactBlocksTest(BitcoinTestFramework):
test_getblocktxn_response(comp_block, test_node, [1, 2, 3, 4, 5])
- msg_bt = msg_blocktxn()
+ msg_bt = msg_no_witness_blocktxn()
if with_witness:
- msg_bt = msg_witness_blocktxn() # serialize with witnesses
+ msg_bt = msg_blocktxn() # serialize with witnesses
msg_bt.block_transactions = BlockTransactions(block.sha256, block.vtx[1:])
test_tip_after_message(node, test_node, msg_bt, block.sha256)
@@ -487,7 +484,7 @@ class CompactBlocksTest(BitcoinTestFramework):
block = self.build_block_with_transactions(node, utxo, 5)
self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
test_node.send_and_ping(msg_tx(block.vtx[1]))
- assert(block.vtx[1].hash in node.getrawmempool())
+ assert block.vtx[1].hash in node.getrawmempool()
# Prefill 4 out of the 6 transactions, and verify that only the one
# that was not in the mempool is requested.
@@ -508,7 +505,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Make sure all transactions were accepted.
mempool = node.getrawmempool()
for tx in block.vtx[1:]:
- assert(tx.hash in mempool)
+ assert tx.hash in mempool
# Clear out last request.
with mininode_lock:
@@ -519,13 +516,13 @@ class CompactBlocksTest(BitcoinTestFramework):
test_tip_after_message(node, test_node, msg_cmpctblock(comp_block.to_p2p()), block.sha256)
with mininode_lock:
# Shouldn't have gotten a request for any transaction
- assert("getblocktxn" not in test_node.last_message)
+ assert "getblocktxn" not in test_node.last_message
# Incorrectly responding to a getblocktxn shouldn't cause the block to be
# permanently failed.
- def test_incorrect_blocktxn_response(self, node, test_node, version):
- if (len(self.utxos) == 0):
- self.make_utxos()
+ def test_incorrect_blocktxn_response(self, test_node):
+ version = test_node.cmpct_version
+ node = self.nodes[0]
utxo = self.utxos.pop(0)
block = self.build_block_with_transactions(node, utxo, 10)
@@ -537,7 +534,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Make sure all transactions were accepted.
mempool = node.getrawmempool()
for tx in block.vtx[1:6]:
- assert(tx.hash in mempool)
+ assert tx.hash in mempool
# Send compact block
comp_block = HeaderAndShortIDs()
@@ -545,7 +542,7 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
absolute_indexes = []
with mininode_lock:
- assert("getblocktxn" in test_node.last_message)
+ assert "getblocktxn" in test_node.last_message
absolute_indexes = test_node.last_message["getblocktxn"].block_txn_request.to_absolute()
assert_equal(absolute_indexes, [6, 7, 8, 9, 10])
@@ -557,9 +554,9 @@ class CompactBlocksTest(BitcoinTestFramework):
# different peer provide the block further down, so that we're still
# verifying that the block isn't marked bad permanently. This is good
# enough for now.
- msg = msg_blocktxn()
+ msg = msg_no_witness_blocktxn()
if version == 2:
- msg = msg_witness_blocktxn()
+ msg = msg_blocktxn()
msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]] + block.vtx[7:])
test_node.send_and_ping(msg)
@@ -569,17 +566,19 @@ class CompactBlocksTest(BitcoinTestFramework):
# We should receive a getdata request
wait_until(lambda: "getdata" in test_node.last_message, timeout=10, lock=mininode_lock)
assert_equal(len(test_node.last_message["getdata"].inv), 1)
- assert(test_node.last_message["getdata"].inv[0].type == 2 or test_node.last_message["getdata"].inv[0].type == 2 | MSG_WITNESS_FLAG)
+ assert test_node.last_message["getdata"].inv[0].type == 2 or test_node.last_message["getdata"].inv[0].type == 2 | MSG_WITNESS_FLAG
assert_equal(test_node.last_message["getdata"].inv[0].hash, block.sha256)
# Deliver the block
if version == 2:
- test_node.send_and_ping(msg_witness_block(block))
- else:
test_node.send_and_ping(msg_block(block))
+ else:
+ test_node.send_and_ping(msg_no_witness_block(block))
assert_equal(int(node.getbestblockhash(), 16), block.sha256)
- def test_getblocktxn_handler(self, node, test_node, version):
+ def test_getblocktxn_handler(self, test_node):
+ version = test_node.cmpct_version
+ node = self.nodes[0]
# bitcoind will not send blocktxn responses for blocks whose height is
# more than 10 blocks deep.
MAX_GETBLOCKTXN_DEPTH = 10
@@ -606,7 +605,7 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(tx.sha256, block.vtx[index].sha256)
if version == 1:
# Witnesses should have been stripped
- assert(tx.wit.is_null())
+ assert tx.wit.is_null()
else:
# Check that the witness matches
assert_equal(tx.calc_sha256(True), block.vtx[index].calc_sha256(True))
@@ -626,7 +625,8 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(test_node.last_message["block"].block.sha256, int(block_hash, 16))
assert "blocktxn" not in test_node.last_message
- def test_compactblocks_not_at_tip(self, node, test_node):
+ def test_compactblocks_not_at_tip(self, test_node):
+ node = self.nodes[0]
# Test that requesting old compactblocks doesn't work.
MAX_CMPCTBLOCK_DEPTH = 5
new_blocks = []
@@ -669,7 +669,7 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(x["status"], "headers-only")
found = True
break
- assert(found)
+ assert found
# Requesting this block via getblocktxn should silently fail
# (to avoid fingerprinting attacks).
@@ -681,11 +681,8 @@ class CompactBlocksTest(BitcoinTestFramework):
with mininode_lock:
assert "blocktxn" not in test_node.last_message
- def activate_segwit(self, node):
- node.generate(144 * 3)
- assert_equal(get_bip9_status(node, "segwit")["status"], 'active')
-
- def test_end_to_end_block_relay(self, node, listeners):
+ def test_end_to_end_block_relay(self, listeners):
+ node = self.nodes[0]
utxo = self.utxos.pop(0)
block = self.build_block_with_transactions(node, utxo, 10)
@@ -706,8 +703,9 @@ class CompactBlocksTest(BitcoinTestFramework):
# Test that we don't get disconnected if we relay a compact block with valid header,
# but invalid transactions.
- def test_invalid_tx_in_compactblock(self, node, test_node, use_segwit):
- assert(len(self.utxos))
+ def test_invalid_tx_in_compactblock(self, test_node, use_segwit=True):
+ node = self.nodes[0]
+ assert len(self.utxos)
utxo = self.utxos[0]
block = self.build_block_with_transactions(node, utxo, 5)
@@ -728,22 +726,24 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.send_and_ping(msg)
# Check that the tip didn't advance
- assert(int(node.getbestblockhash(), 16) is not block.sha256)
+ assert int(node.getbestblockhash(), 16) is not block.sha256
test_node.sync_with_ping()
# Helper for enabling cb announcements
# Send the sendcmpct request and sync headers
- def request_cb_announcements(self, peer, node, version):
+ def request_cb_announcements(self, peer):
+ node = self.nodes[0]
tip = node.getbestblockhash()
peer.get_headers(locator=[int(tip, 16)], hashstop=0)
msg = msg_sendcmpct()
- msg.version = version
+ msg.version = peer.cmpct_version
msg.announce = True
peer.send_and_ping(msg)
- def test_compactblock_reconstruction_multiple_peers(self, node, stalling_peer, delivery_peer):
- assert(len(self.utxos))
+ def test_compactblock_reconstruction_multiple_peers(self, stalling_peer, delivery_peer):
+ node = self.nodes[0]
+ assert len(self.utxos)
def announce_cmpct_block(node, peer):
utxo = self.utxos.pop(0)
@@ -764,7 +764,7 @@ class CompactBlocksTest(BitcoinTestFramework):
delivery_peer.sync_with_ping()
mempool = node.getrawmempool()
for tx in block.vtx[1:]:
- assert(tx.hash in mempool)
+ assert tx.hash in mempool
delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
assert_equal(int(node.getbestblockhash(), 16), block.sha256)
@@ -783,9 +783,9 @@ class CompactBlocksTest(BitcoinTestFramework):
cmpct_block.use_witness = True
delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
- assert(int(node.getbestblockhash(), 16) != block.sha256)
+ assert int(node.getbestblockhash(), 16) != block.sha256
- msg = msg_blocktxn()
+ msg = msg_no_witness_blocktxn()
msg.block_transactions.blockhash = block.sha256
msg.block_transactions.transactions = block.vtx[1:]
stalling_peer.send_and_ping(msg)
@@ -793,126 +793,55 @@ class CompactBlocksTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections
- self.test_node = self.nodes[0].add_p2p_connection(TestP2PConn())
- self.segwit_node = self.nodes[1].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK | NODE_WITNESS)
- self.old_node = self.nodes[1].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK)
+ self.segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=2))
+ self.old_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=1), services=NODE_NETWORK)
+ self.additional_segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=2))
# We will need UTXOs to construct transactions in later tests.
self.make_utxos()
- self.log.info("Running tests, pre-segwit activation:")
+ assert_equal(get_bip9_status(self.nodes[0], "segwit")["status"], 'active')
self.log.info("Testing SENDCMPCT p2p message... ")
- self.test_sendcmpct(self.nodes[0], self.test_node, 1)
- sync_blocks(self.nodes)
- self.test_sendcmpct(self.nodes[1], self.segwit_node, 2, old_node=self.old_node)
- sync_blocks(self.nodes)
+ self.test_sendcmpct(self.segwit_node, old_node=self.old_node)
+ self.test_sendcmpct(self.additional_segwit_node)
self.log.info("Testing compactblock construction...")
- self.test_compactblock_construction(self.nodes[0], self.test_node, 1, False)
- sync_blocks(self.nodes)
- self.test_compactblock_construction(self.nodes[1], self.segwit_node, 2, False)
- sync_blocks(self.nodes)
-
- self.log.info("Testing compactblock requests... ")
- self.test_compactblock_requests(self.nodes[0], self.test_node, 1, False)
- sync_blocks(self.nodes)
- self.test_compactblock_requests(self.nodes[1], self.segwit_node, 2, False)
- sync_blocks(self.nodes)
-
- self.log.info("Testing getblocktxn requests...")
- self.test_getblocktxn_requests(self.nodes[0], self.test_node, 1)
- sync_blocks(self.nodes)
- self.test_getblocktxn_requests(self.nodes[1], self.segwit_node, 2)
- sync_blocks(self.nodes)
-
- self.log.info("Testing getblocktxn handler...")
- self.test_getblocktxn_handler(self.nodes[0], self.test_node, 1)
- sync_blocks(self.nodes)
- self.test_getblocktxn_handler(self.nodes[1], self.segwit_node, 2)
- self.test_getblocktxn_handler(self.nodes[1], self.old_node, 1)
- sync_blocks(self.nodes)
-
- self.log.info("Testing compactblock requests/announcements not at chain tip...")
- self.test_compactblocks_not_at_tip(self.nodes[0], self.test_node)
- sync_blocks(self.nodes)
- self.test_compactblocks_not_at_tip(self.nodes[1], self.segwit_node)
- self.test_compactblocks_not_at_tip(self.nodes[1], self.old_node)
- sync_blocks(self.nodes)
-
- self.log.info("Testing handling of incorrect blocktxn responses...")
- self.test_incorrect_blocktxn_response(self.nodes[0], self.test_node, 1)
- sync_blocks(self.nodes)
- self.test_incorrect_blocktxn_response(self.nodes[1], self.segwit_node, 2)
- sync_blocks(self.nodes)
-
- # End-to-end block relay tests
- self.log.info("Testing end-to-end block relay...")
- self.request_cb_announcements(self.test_node, self.nodes[0], 1)
- self.request_cb_announcements(self.old_node, self.nodes[1], 1)
- self.request_cb_announcements(self.segwit_node, self.nodes[1], 2)
- self.test_end_to_end_block_relay(self.nodes[0], [self.segwit_node, self.test_node, self.old_node])
- self.test_end_to_end_block_relay(self.nodes[1], [self.segwit_node, self.test_node, self.old_node])
-
- self.log.info("Testing handling of invalid compact blocks...")
- self.test_invalid_tx_in_compactblock(self.nodes[0], self.test_node, False)
- self.test_invalid_tx_in_compactblock(self.nodes[1], self.segwit_node, False)
- self.test_invalid_tx_in_compactblock(self.nodes[1], self.old_node, False)
-
- self.log.info("Testing reconstructing compact blocks from all peers...")
- self.test_compactblock_reconstruction_multiple_peers(self.nodes[1], self.segwit_node, self.old_node)
- sync_blocks(self.nodes)
-
- # Advance to segwit activation
- self.log.info("Advancing to segwit activation")
- self.activate_segwit(self.nodes[1])
- self.log.info("Running tests, post-segwit activation...")
-
- self.log.info("Testing compactblock construction...")
- self.test_compactblock_construction(self.nodes[1], self.old_node, 1, True)
- self.test_compactblock_construction(self.nodes[1], self.segwit_node, 2, True)
- sync_blocks(self.nodes)
-
- self.log.info("Testing compactblock requests (unupgraded node)... ")
- self.test_compactblock_requests(self.nodes[0], self.test_node, 1, True)
-
- self.log.info("Testing getblocktxn requests (unupgraded node)...")
- self.test_getblocktxn_requests(self.nodes[0], self.test_node, 1)
-
- # Need to manually sync node0 and node1, because post-segwit activation,
- # node1 will not download blocks from node0.
- self.log.info("Syncing nodes...")
- assert(self.nodes[0].getbestblockhash() != self.nodes[1].getbestblockhash())
- while (self.nodes[0].getblockcount() > self.nodes[1].getblockcount()):
- block_hash = self.nodes[0].getblockhash(self.nodes[1].getblockcount() + 1)
- self.nodes[1].submitblock(self.nodes[0].getblock(block_hash, False))
- assert_equal(self.nodes[0].getbestblockhash(), self.nodes[1].getbestblockhash())
+ self.test_compactblock_construction(self.old_node)
+ self.test_compactblock_construction(self.segwit_node)
self.log.info("Testing compactblock requests (segwit node)... ")
- self.test_compactblock_requests(self.nodes[1], self.segwit_node, 2, True)
+ self.test_compactblock_requests(self.segwit_node)
self.log.info("Testing getblocktxn requests (segwit node)...")
- self.test_getblocktxn_requests(self.nodes[1], self.segwit_node, 2)
- sync_blocks(self.nodes)
+ self.test_getblocktxn_requests(self.segwit_node)
self.log.info("Testing getblocktxn handler (segwit node should return witnesses)...")
- self.test_getblocktxn_handler(self.nodes[1], self.segwit_node, 2)
- self.test_getblocktxn_handler(self.nodes[1], self.old_node, 1)
+ self.test_getblocktxn_handler(self.segwit_node)
+ self.test_getblocktxn_handler(self.old_node)
+
+ self.log.info("Testing compactblock requests/announcements not at chain tip...")
+ self.test_compactblocks_not_at_tip(self.segwit_node)
+ self.test_compactblocks_not_at_tip(self.old_node)
+
+ self.log.info("Testing handling of incorrect blocktxn responses...")
+ self.test_incorrect_blocktxn_response(self.segwit_node)
+
+ self.log.info("Testing reconstructing compact blocks from all peers...")
+ self.test_compactblock_reconstruction_multiple_peers(self.segwit_node, self.additional_segwit_node)
# Test that if we submitblock to node1, we'll get a compact block
# announcement to all peers.
# (Post-segwit activation, blocks won't propagate from node0 to node1
# automatically, so don't bother testing a block announced to node0.)
self.log.info("Testing end-to-end block relay...")
- self.request_cb_announcements(self.test_node, self.nodes[0], 1)
- self.request_cb_announcements(self.old_node, self.nodes[1], 1)
- self.request_cb_announcements(self.segwit_node, self.nodes[1], 2)
- self.test_end_to_end_block_relay(self.nodes[1], [self.segwit_node, self.test_node, self.old_node])
+ self.request_cb_announcements(self.old_node)
+ self.request_cb_announcements(self.segwit_node)
+ self.test_end_to_end_block_relay([self.segwit_node, self.old_node])
self.log.info("Testing handling of invalid compact blocks...")
- self.test_invalid_tx_in_compactblock(self.nodes[0], self.test_node, False)
- self.test_invalid_tx_in_compactblock(self.nodes[1], self.segwit_node, True)
- self.test_invalid_tx_in_compactblock(self.nodes[1], self.old_node, True)
+ self.test_invalid_tx_in_compactblock(self.segwit_node)
+ self.test_invalid_tx_in_compactblock(self.old_node)
self.log.info("Testing invalid index in cmpctblock message...")
self.test_invalid_cmpctblock_message()
diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py
index d589519e45..7f901b1886 100755
--- a/test/functional/p2p_feefilter.py
+++ b/test/functional/p2p_feefilter.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2018 The Bitcoin Core developers
+# Copyright (c) 2016-2019 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 processing of feefilter messages."""
@@ -10,7 +10,7 @@ import time
from test_framework.messages import msg_feefilter
from test_framework.mininode import mininode_lock, P2PInterface
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import sync_blocks, sync_mempools
+
def hashToHex(hash):
return format(hash, '064x')
@@ -50,14 +50,14 @@ class FeeFilterTest(BitcoinTestFramework):
node0 = self.nodes[0]
# Get out of IBD
node1.generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
self.nodes[0].add_p2p_connection(TestP2PConn())
# 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, self.nodes[0].p2p))
+ assert allInvsMatch(txids, self.nodes[0].p2p)
self.nodes[0].p2p.clear_invs()
# Set a filter of 15 sat/byte
@@ -65,13 +65,13 @@ class FeeFilterTest(BitcoinTestFramework):
# 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, self.nodes[0].p2p))
+ 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"))
[node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
- sync_mempools(self.nodes) # must be sure node 0 has received all txs
+ self.sync_mempools() # must be sure node 0 has received all txs
# Send one transaction from node0 that should be received, so that we
# we can sync the test on receipt (if node1's txs were relayed, they'd
@@ -82,13 +82,13 @@ class FeeFilterTest(BitcoinTestFramework):
# as well.
node0.settxfee(Decimal("0.00020000"))
txids = [node0.sendtoaddress(node0.getnewaddress(), 1)]
- assert(allInvsMatch(txids, self.nodes[0].p2p))
+ assert allInvsMatch(txids, self.nodes[0].p2p)
self.nodes[0].p2p.clear_invs()
# Remove fee filter and check that txs are received again
self.nodes[0].p2p.send_and_ping(msg_feefilter(0))
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
- assert(allInvsMatch(txids, self.nodes[0].p2p))
+ assert allInvsMatch(txids, self.nodes[0].p2p)
self.nodes[0].p2p.clear_invs()
if __name__ == '__main__':
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index dcc0d1d235..481d697e63 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -1,13 +1,14 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 node responses to invalid network messages."""
+import asyncio
import os
import struct
from test_framework import messages
-from test_framework.mininode import P2PDataStore
+from test_framework.mininode import P2PDataStore, NetworkThread
from test_framework.test_framework import BitcoinTestFramework
@@ -143,7 +144,15 @@ class InvalidMessagesTest(BitcoinTestFramework):
def test_magic_bytes(self):
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
- conn.magic_bytes = b'\x00\x11\x22\x32'
+
+ def swap_magic_bytes():
+ conn._on_data = lambda: None # Need to ignore all incoming messages from now, since they come with "invalid" magic bytes
+ conn.magic_bytes = b'\x00\x11\x22\x32'
+
+ # Call .result() to block until the atomic swap is complete, otherwise
+ # we might run into races later on
+ asyncio.run_coroutine_threadsafe(asyncio.coroutine(swap_magic_bytes)(), NetworkThread.network_event_loop).result()
+
with self.nodes[0].assert_debug_log(['PROCESSMESSAGE: INVALID MESSAGESTART ping']):
conn.send_message(messages.msg_ping(nonce=0xff))
conn.wait_for_disconnect(timeout=1)
@@ -211,6 +220,5 @@ class InvalidMessagesTest(BitcoinTestFramework):
return raw_msg_with_wrong_size
-
if __name__ == '__main__':
InvalidMessagesTest().main()
diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py
index 336d34a81d..06049db54c 100755
--- a/test/functional/p2p_leak.py
+++ b/test/functional/p2p_leak.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 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 message sending before handshake completion.
@@ -117,9 +117,9 @@ class P2PLeakTest(BitcoinTestFramework):
wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 0)
# Make sure no unexpected messages came in
- assert(no_version_bannode.unexpected_msg == False)
- assert(no_version_idlenode.unexpected_msg == False)
- assert(no_verack_idlenode.unexpected_msg == False)
+ assert no_version_bannode.unexpected_msg == False
+ assert no_version_idlenode.unexpected_msg == False
+ assert no_verack_idlenode.unexpected_msg == False
if __name__ == '__main__':
diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py
index 359880506e..573d5f5a5f 100755
--- a/test/functional/p2p_node_network_limited.py
+++ b/test/functional/p2p_node_network_limited.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Tests NODE_NETWORK_LIMITED.
@@ -11,7 +11,13 @@ and that it responds to getdata requests for blocks correctly:
from test_framework.messages import CInv, msg_getdata, msg_verack, NODE_BLOOM, NODE_NETWORK_LIMITED, NODE_WITNESS
from test_framework.mininode import P2PInterface, mininode_lock
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, disconnect_nodes, connect_nodes_bi, sync_blocks, wait_until
+from test_framework.util import (
+ assert_equal,
+ disconnect_nodes,
+ connect_nodes_bi,
+ wait_until,
+)
+
class P2PIgnoreInv(P2PInterface):
firstAddrnServices = 0
@@ -60,7 +66,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
self.log.info("Mine enough blocks to reach the NODE_NETWORK_LIMITED range.")
connect_nodes_bi(self.nodes, 0, 1)
blocks = self.nodes[1].generatetoaddress(292, self.nodes[1].get_deterministic_priv_key().address)
- sync_blocks([self.nodes[0], self.nodes[1]])
+ self.sync_blocks([self.nodes[0], self.nodes[1]])
self.log.info("Make sure we can max retrieve block at tip-288.")
node.send_getdata_for_block(blocks[1]) # last block in valid range
@@ -86,7 +92,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
# because node 2 is in IBD and node 0 is a NODE_NETWORK_LIMITED peer, sync must not be possible
connect_nodes_bi(self.nodes, 0, 2)
try:
- sync_blocks([self.nodes[0], self.nodes[2]], timeout=5)
+ self.sync_blocks([self.nodes[0], self.nodes[2]], timeout=5)
except:
pass
# node2 must remain at height 0
@@ -96,7 +102,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
connect_nodes_bi(self.nodes, 1, 2)
# sync must be possible
- sync_blocks(self.nodes)
+ self.sync_blocks()
# disconnect all peers
self.disconnect_all()
@@ -108,7 +114,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
connect_nodes_bi(self.nodes, 0, 1)
# sync must be possible, node 1 is no longer in IBD and should therefore connect to node 0 (NODE_NETWORK_LIMITED)
- sync_blocks([self.nodes[0], self.nodes[1]])
+ self.sync_blocks([self.nodes[0], self.nodes[1]])
if __name__ == '__main__':
NodeNetworkLimitedTest().main()
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 8f8e89cf15..b7fa42f593 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -1,16 +1,15 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2018 The Bitcoin Core developers
+# Copyright (c) 2016-2019 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 segwit transactions and blocks on P2P network."""
-from binascii import hexlify
import math
import random
import struct
import time
from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, get_witness_script, WITNESS_COMMITMENT_HEADER
-from test_framework.key import CECKey, CPubKey
+from test_framework.key import ECKey
from test_framework.messages import (
BIP125_SEQUENCE_NUMBER,
CBlock,
@@ -26,17 +25,18 @@ from test_framework.messages import (
MSG_WITNESS_FLAG,
NODE_NETWORK,
NODE_WITNESS,
- msg_block,
+ msg_no_witness_block,
msg_getdata,
msg_headers,
msg_inv,
msg_tx,
- msg_witness_block,
+ msg_block,
msg_witness_tx,
ser_uint256,
ser_vector,
sha256,
uint256_from_str,
+ FromHex,
)
from test_framework.mininode import (
P2PInterface,
@@ -74,13 +74,11 @@ from test_framework.script import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- bytes_to_hex_str,
connect_nodes,
disconnect_nodes,
get_bip9_status,
hex_str_to_bytes,
- sync_blocks,
- sync_mempools,
+ assert_raises_rpc_error,
)
# The versionbit bit used to signal activation of SegWit
@@ -104,7 +102,7 @@ def get_p2pkh_script(pubkeyhash):
def sign_p2pk_witness_input(script, tx_to, in_idx, hashtype, value, key):
"""Add signature for a P2PK witness program."""
tx_hash = SegwitVersion1SignatureHash(script, tx_to, in_idx, hashtype, value)
- signature = key.sign(tx_hash) + chr(hashtype).encode('latin-1')
+ signature = key.sign_ecdsa(tx_hash) + chr(hashtype).encode('latin-1')
tx_to.wit.vtxinwit[in_idx].scriptWitness.stack = [signature, script]
tx_to.rehash()
@@ -113,7 +111,7 @@ def get_virtual_size(witness_block):
Virtual size is base + witness/4."""
base_size = len(witness_block.serialize(with_witness=False))
- total_size = len(witness_block.serialize(with_witness=True))
+ total_size = len(witness_block.serialize())
# the "+3" is so we round up
vsize = int((3 * base_size + total_size + 3) / 4)
return vsize
@@ -136,7 +134,7 @@ def test_witness_block(node, p2p, block, accepted, with_witness=True, reason=Non
- use the getbestblockhash rpc to check for acceptance."""
reason = [reason] if reason else []
with node.assert_debug_log(expected_msgs=reason):
- p2p.send_message(msg_witness_block(block) if with_witness else msg_block(block))
+ p2p.send_message(msg_block(block) if with_witness else msg_no_witness_block(block))
p2p.sync_with_ping()
assert_equal(node.getbestblockhash() == block.hash, accepted)
@@ -273,6 +271,7 @@ class SegWitTest(BitcoinTestFramework):
self.test_non_standard_witness()
self.test_upgrade_after_activation()
self.test_witness_sigops()
+ self.test_superfluous_witness()
# Individual tests
@@ -285,7 +284,7 @@ class SegWitTest(BitcoinTestFramework):
func(self, *args, **kwargs)
# Each subtest should leave some utxos for the next subtest
assert self.utxo
- sync_blocks(self.nodes)
+ self.sync_blocks()
# Assert segwit status is as expected at end of subtest
assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], self.segwit_status)
@@ -299,7 +298,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block(version=1)
block.solve()
- self.test_node.send_message(msg_block(block))
+ self.test_node.send_message(msg_no_witness_block(block))
self.test_node.sync_with_ping() # make sure the block was processed
txid = block.vtx[0].sha256
@@ -317,7 +316,7 @@ class SegWitTest(BitcoinTestFramework):
self.test_node.send_message(msg_witness_tx(tx))
self.test_node.sync_with_ping() # make sure the tx was processed
- assert(tx.hash in self.nodes[0].getrawmempool())
+ assert tx.hash in self.nodes[0].getrawmempool()
# Save this transaction for later
self.utxo.append(UTXO(tx.sha256, 0, 49 * 100000000))
self.nodes[0].generate(1)
@@ -335,7 +334,7 @@ class SegWitTest(BitcoinTestFramework):
# Verify the hash with witness differs from the txid
# (otherwise our testing framework must be broken!)
tx.rehash()
- assert(tx.sha256 != tx.calc_sha256(with_witness=True))
+ assert tx.sha256 != tx.calc_sha256(with_witness=True)
# Construct a segwit-signaling block that includes the transaction.
block = self.build_next_block(version=(VB_TOP_BITS | (1 << VB_WITNESS_BIT)))
@@ -346,7 +345,7 @@ class SegWitTest(BitcoinTestFramework):
# But it should not be permanently marked bad...
# Resend without witness information.
- self.test_node.send_message(msg_block(block))
+ self.test_node.send_message(msg_no_witness_block(block))
self.test_node.sync_with_ping()
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
@@ -371,20 +370,20 @@ class SegWitTest(BitcoinTestFramework):
block1.solve()
self.test_node.announce_block_and_wait_for_getdata(block1, use_header=False)
- assert(self.test_node.last_message["getdata"].inv[0].type == blocktype)
+ assert self.test_node.last_message["getdata"].inv[0].type == blocktype
test_witness_block(self.nodes[0], self.test_node, block1, True)
block2 = self.build_next_block(version=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)
+ assert self.test_node.last_message["getdata"].inv[0].type == blocktype
test_witness_block(self.nodes[0], self.test_node, block2, True)
block3 = self.build_next_block(version=(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)
+ assert self.test_node.last_message["getdata"].inv[0].type == blocktype
test_witness_block(self.nodes[0], self.test_node, block3, True)
# Check that we can getdata for witness blocks or regular blocks,
@@ -404,7 +403,7 @@ class SegWitTest(BitcoinTestFramework):
block_hash = int(block_hash, 16)
block = self.test_node.request_block(block_hash, 2)
wit_block = self.test_node.request_block(block_hash, 2 | MSG_WITNESS_FLAG)
- assert_equal(block.serialize(True), wit_block.serialize(True))
+ assert_equal(block.serialize(), wit_block.serialize())
assert_equal(block.serialize(), hex_str_to_bytes(rpc_block))
else:
# After activation, witness blocks and non-witness blocks should
@@ -413,22 +412,22 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [])
# 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)
+ assert len(block.vtx[0].wit.vtxinwit) == 1
+ assert len(block.vtx[0].wit.vtxinwit[0].scriptWitness.stack) == 1
test_witness_block(self.nodes[0], 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)
wit_block = self.test_node.request_block(block.sha256, 2 | MSG_WITNESS_FLAG)
- assert_equal(wit_block.serialize(True), hex_str_to_bytes(rpc_block))
+ assert_equal(wit_block.serialize(), hex_str_to_bytes(rpc_block))
assert_equal(wit_block.serialize(False), non_wit_block.serialize())
- assert_equal(wit_block.serialize(True), block.serialize(True))
+ assert_equal(wit_block.serialize(), block.serialize())
# Test size, vsize, weight
rpc_details = self.nodes[0].getblock(block.hash, True)
- assert_equal(rpc_details["size"], len(block.serialize(True)))
+ assert_equal(rpc_details["size"], len(block.serialize()))
assert_equal(rpc_details["strippedsize"], len(block.serialize(False)))
- weight = 3 * len(block.serialize(False)) + len(block.serialize(True))
+ weight = 3 * len(block.serialize(False)) + len(block.serialize())
assert_equal(rpc_details["weight"], weight)
# Upgraded node should not ask for blocks from unupgraded
@@ -448,7 +447,7 @@ class SegWitTest(BitcoinTestFramework):
msg.headers = [CBlockHeader(block4)]
self.old_node.send_message(msg)
self.old_node.announce_tx_and_wait_for_getdata(block4.vtx[0])
- assert(block4.sha256 not in self.old_node.getdataset)
+ assert block4.sha256 not in self.old_node.getdataset
@subtest
def test_v0_outputs_arent_spendable(self):
@@ -537,7 +536,7 @@ class SegWitTest(BitcoinTestFramework):
"""Mine enough blocks for segwit's vb state to be 'started'."""
height = self.nodes[0].getblockcount()
# Will need to rewrite the tests here if we are past the first period
- assert(height < VB_PERIOD - 1)
+ assert height < VB_PERIOD - 1
# Advance to end of period, status should now be 'started'
self.nodes[0].generate(VB_PERIOD - height - 1)
assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started')
@@ -554,19 +553,19 @@ class SegWitTest(BitcoinTestFramework):
# If this is a non-segwit node, we should not get a witness
# commitment, nor a version bit signalling segwit.
assert_equal(block_version & (1 << VB_WITNESS_BIT), 0)
- assert('default_witness_commitment' not in gbt_results)
+ assert 'default_witness_commitment' not in gbt_results
else:
# For segwit-aware nodes, check the version bit and the witness
# commitment are correct.
- assert(block_version & (1 << VB_WITNESS_BIT) != 0)
- assert('default_witness_commitment' in gbt_results)
+ assert block_version & (1 << VB_WITNESS_BIT) != 0
+ assert 'default_witness_commitment' in gbt_results
witness_commitment = gbt_results['default_witness_commitment']
# Check that default_witness_commitment is present.
witness_root = CBlock.get_merkle_root([ser_uint256(0),
ser_uint256(txid)])
script = get_witness_script(witness_root, 0)
- assert_equal(witness_commitment, bytes_to_hex_str(script))
+ assert_equal(witness_commitment, script.hex())
@subtest
def advance_to_segwit_lockin(self):
@@ -575,7 +574,7 @@ class SegWitTest(BitcoinTestFramework):
# Advance to end of period, and verify lock-in happens at the end
self.nodes[0].generate(VB_PERIOD - 1)
height = self.nodes[0].getblockcount()
- assert((height % VB_PERIOD) == VB_PERIOD - 2)
+ assert (height % VB_PERIOD) == VB_PERIOD - 2
assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started')
self.nodes[0].generate(1)
assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in')
@@ -600,7 +599,7 @@ class SegWitTest(BitcoinTestFramework):
# Verify that if a peer doesn't set nServices to include NODE_WITNESS,
# the getdata is just for the non-witness portion.
self.old_node.announce_tx_and_wait_for_getdata(tx)
- assert(self.old_node.last_message["getdata"].inv[0].type == 1)
+ assert self.old_node.last_message["getdata"].inv[0].type == 1
# Since we haven't delivered the tx yet, inv'ing the same tx from
# a witness transaction ought not result in a getdata.
@@ -646,7 +645,7 @@ class SegWitTest(BitcoinTestFramework):
# Mine it on test_node to create the confirmed output.
test_transaction_acceptance(self.nodes[0], self.test_node, p2sh_tx, with_witness=True, accepted=True)
self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
# Now test standardness of v0 P2WSH outputs.
# Start by creating a transaction with two outputs.
@@ -677,7 +676,7 @@ class SegWitTest(BitcoinTestFramework):
tx3 = CTransaction()
# tx and tx2 were both accepted. Don't bother trying to reclaim the
# P2PKH output; just send tx's first output back to an anyone-can-spend.
- sync_mempools([self.nodes[0], self.nodes[1]])
+ self.sync_mempools([self.nodes[0], self.nodes[1]])
tx3.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")]
tx3.vout = [CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))]
tx3.wit.vtxinwit.append(CTxInWitness())
@@ -686,17 +685,17 @@ class SegWitTest(BitcoinTestFramework):
if self.segwit_status != 'active':
# Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed
# in blocks and the tx is impossible to mine right now.
- assert_equal(self.nodes[0].testmempoolaccept([bytes_to_hex_str(tx3.serialize_with_witness())]), [{'txid': tx3.hash, 'allowed': True}])
+ assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True}])
# Create the same output as tx3, but by replacing tx
tx3_out = tx3.vout[0]
tx3 = tx
tx3.vout = [tx3_out]
tx3.rehash()
- assert_equal(self.nodes[0].testmempoolaccept([bytes_to_hex_str(tx3.serialize_with_witness())]), [{'txid': tx3.hash, 'allowed': True}])
+ assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True}])
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=True)
self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
self.utxo.pop(0)
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
assert_equal(len(self.nodes[1].getrawmempool()), 0)
@@ -734,7 +733,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
test_witness_block(self.nodes[0], self.test_node, block, accepted=True, with_witness=True)
- sync_blocks(self.nodes)
+ self.sync_blocks()
# Now test attempts to spend the output.
spend_tx = CTransaction()
@@ -792,7 +791,7 @@ class SegWitTest(BitcoinTestFramework):
block.solve()
# Test the test -- witness serialization should be different
- assert(msg_witness_block(block).serialize() != msg_block(block).serialize())
+ assert msg_block(block).serialize() != msg_no_witness_block(block).serialize()
# This empty block should be valid.
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
@@ -803,7 +802,7 @@ class SegWitTest(BitcoinTestFramework):
block_2.solve()
# The commitment should have changed!
- assert(block_2.vtx[0].vout[-1] != block.vtx[0].vout[-1])
+ assert block_2.vtx[0].vout[-1] != block.vtx[0].vout[-1]
# This should also be valid.
test_witness_block(self.nodes[0], self.test_node, block_2, accepted=True)
@@ -850,7 +849,7 @@ class SegWitTest(BitcoinTestFramework):
block_3.vtx[0].rehash()
block_3.hashMerkleRoot = block_3.calc_merkle_root()
block_3.rehash()
- assert(len(block_3.vtx[0].vout) == 4) # 3 OP_returns
+ assert len(block_3.vtx[0].vout) == 4 # 3 OP_returns
block_3.solve()
test_witness_block(self.nodes[0], self.test_node, block_3, accepted=True)
@@ -881,19 +880,19 @@ class SegWitTest(BitcoinTestFramework):
block.solve()
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.append(b'a' * 5000000)
- assert(get_virtual_size(block) > MAX_BLOCK_BASE_SIZE)
+ assert get_virtual_size(block) > MAX_BLOCK_BASE_SIZE
# We can't send over the p2p network, because this is too big to relay
# TODO: repeat this test with a block that can be relayed
- self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True)))
+ self.nodes[0].submitblock(block.serialize().hex())
- assert(self.nodes[0].getbestblockhash() != block.hash)
+ assert self.nodes[0].getbestblockhash() != block.hash
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.pop()
- assert(get_virtual_size(block) < MAX_BLOCK_BASE_SIZE)
- self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True)))
+ assert get_virtual_size(block) < MAX_BLOCK_BASE_SIZE
+ self.nodes[0].submitblock(block.serialize().hex())
- assert(self.nodes[0].getbestblockhash() == block.hash)
+ assert self.nodes[0].getbestblockhash() == block.hash
# Now make sure that malleating the witness reserved value doesn't
# result in a block permanently marked bad.
@@ -918,7 +917,7 @@ class SegWitTest(BitcoinTestFramework):
# Test that witness-bearing blocks are limited at ceil(base + wit/4) <= 1MB.
block = self.build_next_block()
- assert(len(self.utxo) > 0)
+ assert len(self.utxo) > 0
# Create a P2WSH transaction.
# The witness program will be a bunch of OP_2DROP's, followed by OP_TRUE.
@@ -940,7 +939,7 @@ class SegWitTest(BitcoinTestFramework):
for i in range(NUM_OUTPUTS):
parent_tx.vout.append(CTxOut(child_value, script_pubkey))
parent_tx.vout[0].nValue -= 50000
- assert(parent_tx.vout[0].nValue > 0)
+ assert parent_tx.vout[0].nValue > 0
parent_tx.rehash()
child_tx = CTransaction()
@@ -970,7 +969,7 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(vsize, MAX_BLOCK_BASE_SIZE + 1)
# Make sure that our test case would exceed the old max-network-message
# limit
- assert(len(block.serialize(True)) > 2 * 1024 * 1024)
+ assert len(block.serialize()) > 2 * 1024 * 1024
test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
@@ -980,7 +979,7 @@ class SegWitTest(BitcoinTestFramework):
block.vtx[0].vout.pop()
add_witness_commitment(block)
block.solve()
- assert(get_virtual_size(block) == MAX_BLOCK_BASE_SIZE)
+ assert get_virtual_size(block) == MAX_BLOCK_BASE_SIZE
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
@@ -998,14 +997,14 @@ class SegWitTest(BitcoinTestFramework):
add_witness_commitment(block, nonce=1)
block.vtx[0].wit = CTxWitness() # drop the nonce
block.solve()
- self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True)))
- assert(self.nodes[0].getbestblockhash() != block.hash)
+ self.nodes[0].submitblock(block.serialize().hex())
+ assert self.nodes[0].getbestblockhash() != block.hash
# Now redo commitment with the standard nonce, but let bitcoind fill it in.
add_witness_commitment(block, nonce=0)
block.vtx[0].wit = CTxWitness()
block.solve()
- self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True)))
+ self.nodes[0].submitblock(block.serialize().hex())
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
# This time, add a tx with non-empty witness, but don't supply
@@ -1020,9 +1019,9 @@ class SegWitTest(BitcoinTestFramework):
block_2.vtx[0].vout.pop()
block_2.vtx[0].wit = CTxWitness()
- self.nodes[0].submitblock(bytes_to_hex_str(block_2.serialize(True)))
+ self.nodes[0].submitblock(block_2.serialize().hex())
# Tip should not advance!
- assert(self.nodes[0].getbestblockhash() != block_2.hash)
+ assert self.nodes[0].getbestblockhash() != block_2.hash
@subtest
def test_extra_witness_data(self):
@@ -1142,7 +1141,7 @@ class SegWitTest(BitcoinTestFramework):
# This program is 19 max pushes (9937 bytes), then 64 more opcode-bytes.
long_witness_program = CScript([b'a' * 520] * 19 + [OP_DROP] * 63 + [OP_TRUE])
- assert(len(long_witness_program) == MAX_PROGRAM_LENGTH + 1)
+ assert len(long_witness_program) == MAX_PROGRAM_LENGTH + 1
long_witness_hash = sha256(long_witness_program)
long_script_pubkey = CScript([OP_0, long_witness_hash])
@@ -1166,7 +1165,7 @@ class SegWitTest(BitcoinTestFramework):
# Try again with one less byte in the witness program
witness_program = CScript([b'a' * 520] * 19 + [OP_DROP] * 62 + [OP_TRUE])
- assert(len(witness_program) == MAX_PROGRAM_LENGTH)
+ assert len(witness_program) == MAX_PROGRAM_LENGTH
witness_hash = sha256(witness_program)
script_pubkey = CScript([OP_0, witness_hash])
@@ -1197,7 +1196,7 @@ class SegWitTest(BitcoinTestFramework):
for i in range(10):
tx.vout.append(CTxOut(int(value / 10), script_pubkey))
tx.vout[0].nValue -= 1000
- assert(tx.vout[0].nValue >= 0)
+ assert tx.vout[0].nValue >= 0
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
@@ -1347,8 +1346,8 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(raw_tx["vsize"], vsize)
assert_equal(raw_tx["weight"], weight)
assert_equal(len(raw_tx["vin"][0]["txinwitness"]), 1)
- assert_equal(raw_tx["vin"][0]["txinwitness"][0], hexlify(witness_program).decode('ascii'))
- assert(vsize != raw_tx["size"])
+ assert_equal(raw_tx["vin"][0]["txinwitness"][0], witness_program.hex())
+ assert vsize != raw_tx["size"]
# Cleanup: mine the transactions and update utxo for next test
self.nodes[0].generate(1)
@@ -1361,7 +1360,8 @@ class SegWitTest(BitcoinTestFramework):
def test_segwit_versions(self):
"""Test validity of future segwit version transactions.
- Future segwit version transactions are non-standard, but valid in blocks.
+ Future segwit versions are non-standard to spend, but valid in blocks.
+ Sending to future segwit versions is always allowed.
Can run this before and after segwit activation."""
NUM_SEGWIT_VERSIONS = 17 # will test OP_0, OP1, ..., OP_16
@@ -1379,7 +1379,7 @@ class SegWitTest(BitcoinTestFramework):
for i in range(NUM_SEGWIT_VERSIONS):
self.utxo.append(UTXO(tx.sha256, i, split_value))
- sync_blocks(self.nodes)
+ self.sync_blocks()
temp_utxo = []
tx = CTransaction()
witness_program = CScript([OP_TRUE])
@@ -1397,11 +1397,11 @@ class SegWitTest(BitcoinTestFramework):
temp_utxo.append(UTXO(tx.sha256, 0, tx.vout[0].nValue))
self.nodes[0].generate(1) # Mine all the transactions
- sync_blocks(self.nodes)
- assert(len(self.nodes[0].getrawmempool()) == 0)
+ self.sync_blocks()
+ assert len(self.nodes[0].getrawmempool()) == 0
# Finally, verify that version 0 -> version 1 transactions
- # are non-standard
+ # are standard
script_pubkey = CScript([CScriptOp(OP_1), witness_hash])
tx2 = CTransaction()
tx2.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")]
@@ -1409,10 +1409,9 @@ class SegWitTest(BitcoinTestFramework):
tx2.wit.vtxinwit.append(CTxInWitness())
tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
tx2.rehash()
- # Gets accepted to test_node, because standardness of outputs isn't
- # checked with fRequireStandard
+ # Gets accepted to both policy-enforcing nodes and others.
test_transaction_acceptance(self.nodes[0], self.test_node, tx2, with_witness=True, accepted=True)
- test_transaction_acceptance(self.nodes[1], self.std_node, tx2, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[1], self.std_node, tx2, with_witness=True, accepted=True)
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))
@@ -1434,7 +1433,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx2, tx3])
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
- sync_blocks(self.nodes)
+ self.sync_blocks()
# Add utxo to our list
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
@@ -1462,7 +1461,7 @@ class SegWitTest(BitcoinTestFramework):
# Now test a premature spend.
self.nodes[0].generate(98)
- sync_blocks(self.nodes)
+ self.sync_blocks()
block2 = self.build_next_block()
self.update_witness_block_with_transactions(block2, [spend_tx])
test_witness_block(self.nodes[0], self.test_node, block2, accepted=False)
@@ -1472,7 +1471,7 @@ class SegWitTest(BitcoinTestFramework):
block2 = self.build_next_block()
self.update_witness_block_with_transactions(block2, [spend_tx])
test_witness_block(self.nodes[0], self.test_node, block2, accepted=True)
- sync_blocks(self.nodes)
+ self.sync_blocks()
@subtest
def test_uncompressed_pubkey(self):
@@ -1483,10 +1482,9 @@ class SegWitTest(BitcoinTestFramework):
# Segwit transactions using uncompressed pubkeys are not accepted
# under default policy, but should still pass consensus.
- key = CECKey()
- key.set_secretbytes(b"9")
- key.set_compressed(False)
- pubkey = CPubKey(key.get_pubkey())
+ key = ECKey()
+ key.generate(False)
+ pubkey = key.get_pubkey().get_bytes()
assert_equal(len(pubkey), 65) # This should be an uncompressed pubkey
utxo = self.utxo.pop(0)
@@ -1516,7 +1514,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_wsh))
script = get_p2pkh_script(pubkeyhash)
sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
- signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
+ signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
tx2.wit.vtxinwit.append(CTxInWitness())
tx2.wit.vtxinwit[0].scriptWitness.stack = [signature, pubkey]
tx2.rehash()
@@ -1570,7 +1568,7 @@ class SegWitTest(BitcoinTestFramework):
tx5.vin.append(CTxIn(COutPoint(tx4.sha256, 0), b""))
tx5.vout.append(CTxOut(tx4.vout[0].nValue - 1000, CScript([OP_TRUE])))
(sig_hash, err) = SignatureHash(script_pubkey, tx5, 0, SIGHASH_ALL)
- signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
+ signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
tx5.vin[0].scriptSig = CScript([signature, pubkey])
tx5.rehash()
# Should pass policy and consensus.
@@ -1583,9 +1581,9 @@ class SegWitTest(BitcoinTestFramework):
@subtest
def test_signature_version_1(self):
- key = CECKey()
- key.set_secretbytes(b"9")
- pubkey = CPubKey(key.get_pubkey())
+ key = ECKey()
+ key.generate()
+ pubkey = key.get_pubkey().get_bytes()
witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)])
witness_hash = sha256(witness_program)
@@ -1602,7 +1600,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
- sync_blocks(self.nodes)
+ self.sync_blocks()
self.utxo.pop(0)
# Test each hashtype
@@ -1667,7 +1665,7 @@ class SegWitTest(BitcoinTestFramework):
# Create a slight bias for producing more utxos
num_outputs = random.randint(1, 11)
random.shuffle(temp_utxos)
- assert(len(temp_utxos) > num_inputs)
+ assert len(temp_utxos) > num_inputs
tx = CTransaction()
total_value = 0
for i in range(num_inputs):
@@ -1720,7 +1718,7 @@ class SegWitTest(BitcoinTestFramework):
script = get_p2pkh_script(pubkeyhash)
sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
- signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
+ signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
# Check that we can't have a scriptSig
tx2.vin[0].scriptSig = CScript([signature, pubkey])
@@ -1781,7 +1779,7 @@ class SegWitTest(BitcoinTestFramework):
tx.rehash()
test_transaction_acceptance(self.nodes[0], self.test_node, tx, False, True)
self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
# We'll add an unnecessary witness to this transaction that would cause
# it to be non-standard, to test that violating policy with a witness
@@ -1810,7 +1808,7 @@ class SegWitTest(BitcoinTestFramework):
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, False, True)
self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
# Update our utxo list; we spent the first entry.
self.utxo.pop(0)
@@ -1846,7 +1844,7 @@ class SegWitTest(BitcoinTestFramework):
test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=False, accepted=True)
self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
# Creating transactions for tests
p2wsh_txs = []
@@ -1910,7 +1908,7 @@ class SegWitTest(BitcoinTestFramework):
self.nodes[0].generate(1) # Mine and clean up the mempool of non-standard node
# Valid but non-standard transactions in a block should be accepted by standard node
- sync_blocks(self.nodes)
+ self.sync_blocks()
assert_equal(len(self.nodes[0].getrawmempool()), 0)
assert_equal(len(self.nodes[1].getrawmempool()), 0)
@@ -1925,10 +1923,10 @@ class SegWitTest(BitcoinTestFramework):
self.start_node(2, extra_args=["-vbparams=segwit:0:999999999999"])
connect_nodes(self.nodes[0], 2)
- sync_blocks(self.nodes)
+ self.sync_blocks()
# Make sure that this peer thinks segwit has activated.
- assert(get_bip9_status(self.nodes[2], 'segwit')['status'] == "active")
+ assert get_bip9_status(self.nodes[2], 'segwit')['status'] == "active"
# Make sure this peer's blocks match those of node0.
height = self.nodes[2].getblockcount()
@@ -1955,7 +1953,7 @@ class SegWitTest(BitcoinTestFramework):
extra_sigops_available = MAX_SIGOP_COST % sigops_per_script
# We chose the number of checkmultisigs/checksigs to make this work:
- assert(extra_sigops_available < 100) # steer clear of MAX_OPS_PER_SCRIPT
+ assert extra_sigops_available < 100 # steer clear of MAX_OPS_PER_SCRIPT
# This script, when spent with the first
# N(=MAX_SIGOP_COST//sigops_per_script) outputs of our transaction,
@@ -2022,7 +2020,7 @@ class SegWitTest(BitcoinTestFramework):
test_witness_block(self.nodes[0], self.test_node, block_4, accepted=True)
# Reset the tip back down for the next test
- sync_blocks(self.nodes)
+ self.sync_blocks()
for x in self.nodes:
x.invalidateblock(block_4.hash)
@@ -2039,5 +2037,51 @@ class SegWitTest(BitcoinTestFramework):
# TODO: test p2sh sigop counting
+ def test_superfluous_witness(self):
+ # Serialization of tx that puts witness flag to 3 always
+ def serialize_with_bogus_witness(tx):
+ flags = 3
+ r = b""
+ r += struct.pack("<i", tx.nVersion)
+ if flags:
+ dummy = []
+ r += ser_vector(dummy)
+ r += struct.pack("<B", flags)
+ r += ser_vector(tx.vin)
+ r += ser_vector(tx.vout)
+ if flags & 1:
+ if (len(tx.wit.vtxinwit) != len(tx.vin)):
+ # vtxinwit must have the same length as vin
+ tx.wit.vtxinwit = tx.wit.vtxinwit[:len(tx.vin)]
+ for i in range(len(tx.wit.vtxinwit), len(tx.vin)):
+ tx.wit.vtxinwit.append(CTxInWitness())
+ r += tx.wit.serialize()
+ r += struct.pack("<I", tx.nLockTime)
+ return r
+
+ class msg_bogus_tx(msg_tx):
+ def serialize(self):
+ return serialize_with_bogus_witness(self.tx)
+
+ self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(address_type='bech32'), 5)
+ self.nodes[0].generate(1)
+ unspent = next(u for u in self.nodes[0].listunspent() if u['spendable'] and u['address'].startswith('bcrt'))
+
+ raw = self.nodes[0].createrawtransaction([{"txid": unspent['txid'], "vout": unspent['vout']}], {self.nodes[0].getnewaddress(): 1})
+ tx = FromHex(CTransaction(), raw)
+ assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, serialize_with_bogus_witness(tx).hex())
+ with self.nodes[0].assert_debug_log(['Superfluous witness record']):
+ self.nodes[0].p2p.send_message(msg_bogus_tx(tx))
+ self.nodes[0].p2p.sync_with_ping()
+ raw = self.nodes[0].signrawtransactionwithwallet(raw)
+ assert raw['complete']
+ raw = raw['hex']
+ tx = FromHex(CTransaction(), raw)
+ assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, serialize_with_bogus_witness(tx).hex())
+ with self.nodes[0].assert_debug_log(['Unknown transaction optional data']):
+ self.nodes[0].p2p.send_message(msg_bogus_tx(tx))
+ self.nodes[0].p2p.sync_with_ping()
+
+
if __name__ == '__main__':
SegWitTest().main()
diff --git a/test/functional/p2p_sendheaders.py b/test/functional/p2p_sendheaders.py
index c7ae57de86..161b67e6d0 100755
--- a/test/functional/p2p_sendheaders.py
+++ b/test/functional/p2p_sendheaders.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 behavior of headers messages to announce blocks.
@@ -103,7 +103,6 @@ from test_framework.mininode import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- sync_blocks,
wait_until,
)
@@ -225,7 +224,7 @@ class SendHeadersTest(BitcoinTestFramework):
# make sure all invalidated blocks are node0's
self.nodes[0].generatetoaddress(length, self.nodes[0].get_deterministic_priv_key().address)
- sync_blocks(self.nodes, wait=0.1)
+ self.sync_blocks(self.nodes, wait=0.1)
for x in self.nodes[0].p2ps:
x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16))
x.clear_block_announcements()
@@ -234,7 +233,7 @@ class SendHeadersTest(BitcoinTestFramework):
hash_to_invalidate = self.nodes[1].getblockhash(tip_height - (length - 1))
self.nodes[1].invalidateblock(hash_to_invalidate)
all_hashes = self.nodes[1].generatetoaddress(length + 1, self.nodes[1].get_deterministic_priv_key().address) # Must be longer than the orig chain
- sync_blocks(self.nodes, wait=0.1)
+ self.sync_blocks(self.nodes, wait=0.1)
return [int(x, 16) for x in all_hashes]
def run_test(self):
@@ -490,7 +489,7 @@ class SendHeadersTest(BitcoinTestFramework):
# Now announce a header that forks the last two blocks
tip = blocks[0].sha256
- height -= 1
+ height -= 2
blocks = []
# Create extra blocks for later
diff --git a/test/functional/p2p_unrequested_blocks.py b/test/functional/p2p_unrequested_blocks.py
index 11299cbc00..534d275c28 100755
--- a/test/functional/p2p_unrequested_blocks.py
+++ b/test/functional/p2p_unrequested_blocks.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 processing of unrequested blocks.
@@ -57,7 +57,11 @@ from test_framework.blocktools import create_block, create_coinbase, create_tx_w
from test_framework.messages import CBlockHeader, CInv, msg_block, msg_headers, msg_inv
from test_framework.mininode import mininode_lock, P2PInterface
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, connect_nodes, sync_blocks
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ connect_nodes,
+)
class AcceptBlockTest(BitcoinTestFramework):
@@ -114,7 +118,7 @@ class AcceptBlockTest(BitcoinTestFramework):
if x['hash'] == block_h1f.hash:
assert_equal(x['status'], "headers-only")
tip_entry_found = True
- assert(tip_entry_found)
+ assert tip_entry_found
assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_h1f.hash)
# 4. Send another two block that build on the fork.
@@ -131,7 +135,7 @@ class AcceptBlockTest(BitcoinTestFramework):
if x['hash'] == block_h2f.hash:
assert_equal(x['status'], "headers-only")
tip_entry_found = True
- assert(tip_entry_found)
+ assert tip_entry_found
# But this block should be accepted by node since it has equal work.
self.nodes[0].getblock(block_h2f.hash)
@@ -150,7 +154,7 @@ class AcceptBlockTest(BitcoinTestFramework):
if x['hash'] == block_h3.hash:
assert_equal(x['status'], "headers-only")
tip_entry_found = True
- assert(tip_entry_found)
+ assert tip_entry_found
self.nodes[0].getblock(block_h3.hash)
# But this block should be accepted by node since it has more work.
@@ -263,7 +267,7 @@ class AcceptBlockTest(BitcoinTestFramework):
if x['hash'] == block_292.hash:
assert_equal(x['status'], "headers-only")
tip_entry_found = True
- assert(tip_entry_found)
+ 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))
@@ -302,7 +306,7 @@ class AcceptBlockTest(BitcoinTestFramework):
# 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.sync_blocks([self.nodes[0], self.nodes[1]])
self.log.info("Successfully synced nodes 1 and 0")
if __name__ == '__main__':
diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py
index 3938ca98dd..acc7cd811c 100755
--- a/test/functional/rpc_bind.py
+++ b/test/functional/rpc_bind.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 running bitcoind with the -rpcbind and -rpcallowip options."""
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 31e60f1cea..facb05b54c 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 RPCs related to blockchainstate.
@@ -35,6 +35,7 @@ from test_framework.util import (
from test_framework.blocktools import (
create_block,
create_coinbase,
+ TIME_GENESIS_BLOCK,
)
from test_framework.messages import (
msg_block,
@@ -46,9 +47,11 @@ from test_framework.mininode import (
class BlockchainTest(BitcoinTestFramework):
def set_test_params(self):
+ self.setup_clean_chain = True
self.num_nodes = 1
def run_test(self):
+ self.mine_chain()
self.restart_node(0, extra_args=['-stopatheight=207', '-prune=1']) # Set extra args with pruning after rescan is complete
self._test_getblockchaininfo()
@@ -61,6 +64,15 @@ class BlockchainTest(BitcoinTestFramework):
self._test_waitforblockheight()
assert self.nodes[0].verifychain(4, 0)
+ def mine_chain(self):
+ self.log.info('Create some old blocks')
+ address = self.nodes[0].get_deterministic_priv_key().address
+ for t in range(TIME_GENESIS_BLOCK, TIME_GENESIS_BLOCK + 200 * 600, 600):
+ # ten-minute steps from genesis block time
+ self.nodes[0].setmocktime(t)
+ self.nodes[0].generatetoaddress(1, address)
+ assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
+
def _test_getblockchaininfo(self):
self.log.info("Test getblockchaininfo")
@@ -160,9 +172,9 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(chaintxstats['txcount'], 2)
assert_equal(chaintxstats['window_final_block_hash'], b1_hash)
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 'window_tx_count' not in chaintxstats
+ assert 'window_interval' not in chaintxstats
+ assert 'txrate' not in chaintxstats
def _test_gettxoutsetinfo(self):
node = self.nodes[0]
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 3cc35a7b9a..7abcd71bb8 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -1,12 +1,16 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 transaction signing using the signrawtransaction* RPCs."""
+"""Test multisig RPCs"""
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_raises_rpc_error,
+)
import decimal
+
class RpcCreateMultiSigTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@@ -17,29 +21,40 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
def get_keys(self):
node0, node1, node2 = self.nodes
- self.add = [node1.getnewaddress() for _ in range(self.nkeys)]
- self.pub = [node1.getaddressinfo(a)["pubkey"] for a in self.add]
- self.priv = [node1.dumpprivkey(a) for a in self.add]
+ add = [node1.getnewaddress() for _ in range(self.nkeys)]
+ self.pub = [node1.getaddressinfo(a)["pubkey"] for a in add]
+ self.priv = [node1.dumpprivkey(a) for a in add]
self.final = node2.getnewaddress()
def run_test(self):
- node0,node1,node2 = self.nodes
+ node0, node1, node2 = self.nodes
- # 50 BTC each, rest will be 25 BTC each
+ self.check_addmultisigaddress_errors()
+
+ self.log.info('Generating blocks ...')
node0.generate(149)
self.sync_all()
self.moved = 0
- for self.nkeys in [3,5]:
- for self.nsigs in [2,3]:
+ for self.nkeys in [3, 5]:
+ for self.nsigs in [2, 3]:
for self.output_type in ["bech32", "p2sh-segwit", "legacy"]:
self.get_keys()
self.do_multisig()
self.checkbalances()
+ def check_addmultisigaddress_errors(self):
+ self.log.info('Check that addmultisigaddress fails when the private keys are missing')
+ addresses = [self.nodes[1].getnewaddress(address_type='legacy') for _ in range(2)]
+ assert_raises_rpc_error(-5, 'no full public key for address', lambda: self.nodes[0].addmultisigaddress(nrequired=1, keys=addresses))
+ for a in addresses:
+ # Importing all addresses should not change the result
+ self.nodes[0].importaddress(a)
+ assert_raises_rpc_error(-5, 'no full public key for address', lambda: self.nodes[0].addmultisigaddress(nrequired=1, keys=addresses))
+
def checkbalances(self):
- node0,node1,node2 = self.nodes
+ node0, node1, node2 = self.nodes
node0.generate(100)
self.sync_all()
@@ -49,13 +64,13 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
height = node0.getblockchaininfo()["blocks"]
assert 150 < height < 350
- total = 149*50 + (height-149-100)*25
+ total = 149 * 50 + (height - 149 - 100) * 25
assert bal1 == 0
assert bal2 == self.moved
- assert bal0+bal1+bal2 == total
+ assert bal0 + bal1 + bal2 == total
def do_multisig(self):
- node0,node1,node2 = self.nodes
+ node0, node1, node2 = self.nodes
msig = node2.createmultisig(self.nsigs, self.pub, self.output_type)
madd = msig["address"]
@@ -74,7 +89,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
txid = node0.sendtoaddress(madd, 40)
tx = node0.getrawtransaction(txid, True)
- vout = [v["n"] for v in tx["vout"] if madd in v["scriptPubKey"].get("addresses",[])]
+ vout = [v["n"] for v in tx["vout"] if madd in v["scriptPubKey"].get("addresses", [])]
assert len(vout) == 1
vout = vout[0]
scriptPubKey = tx["vout"][vout]["scriptPubKey"]["hex"]
@@ -86,16 +101,17 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
outval = value - decimal.Decimal("0.00001000")
rawtx = node2.createrawtransaction([{"txid": txid, "vout": vout}], [{self.final: outval}])
- rawtx2 = node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs-1], prevtxs)
+ rawtx2 = node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs - 1], prevtxs)
rawtx3 = node2.signrawtransactionwithkey(rawtx2["hex"], [self.priv[-1]], prevtxs)
self.moved += outval
- tx = node0.sendrawtransaction(rawtx3["hex"], True)
+ tx = node0.sendrawtransaction(rawtx3["hex"], 0)
blk = node0.generate(1)[0]
assert tx in node0.getblock(blk)["tx"]
txinfo = node0.getrawtransaction(tx, True, blk)
self.log.info("n/m=%d/%d %s size=%d vsize=%d weight=%d" % (self.nsigs, self.nkeys, self.output_type, txinfo["size"], txinfo["vsize"], txinfo["weight"]))
+
if __name__ == '__main__':
RpcCreateMultiSigTest().main()
diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py
index 940386eee7..01b8cb1854 100755
--- a/test/functional/rpc_decodescript.py
+++ b/test/functional/rpc_decodescript.py
@@ -1,12 +1,12 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 decoding scripts via decodescript RPC command."""
from test_framework.messages import CTransaction, sha256
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, bytes_to_hex_str, hex_str_to_bytes
+from test_framework.util import assert_equal, hex_str_to_bytes
from io import BytesIO
@@ -81,7 +81,7 @@ class DecodeScriptTest(BitcoinTestFramework):
rpc_result = self.nodes[0].decodescript(multisig_script)
assert_equal('2 ' + public_key + ' ' + public_key + ' ' + public_key + ' 3 OP_CHECKMULTISIG', rpc_result['asm'])
# multisig in P2WSH
- multisig_script_hash = bytes_to_hex_str(sha256(hex_str_to_bytes(multisig_script)))
+ multisig_script_hash = sha256(hex_str_to_bytes(multisig_script)).hex()
assert_equal('0 ' + multisig_script_hash, rpc_result['segwit']['asm'])
# 4) P2SH scriptPubKey
@@ -119,7 +119,7 @@ class DecodeScriptTest(BitcoinTestFramework):
rpc_result = self.nodes[0].decodescript(cltv_script)
assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm'])
# CLTV script in P2WSH
- cltv_script_hash = bytes_to_hex_str(sha256(hex_str_to_bytes(cltv_script)))
+ cltv_script_hash = sha256(hex_str_to_bytes(cltv_script)).hex()
assert_equal('0 ' + cltv_script_hash, rpc_result['segwit']['asm'])
# 7) P2PK scriptPubKey
@@ -196,7 +196,7 @@ class DecodeScriptTest(BitcoinTestFramework):
# some more full transaction tests of varying specific scriptSigs. used instead of
# tests in decodescript_script_sig because the decodescript RPC is specifically
# for working on scriptPubKeys (argh!).
- push_signature = bytes_to_hex_str(txSave.vin[0].scriptSig)[2:(0x48*2+4)]
+ push_signature = txSave.vin[0].scriptSig.hex()[2:(0x48*2+4)]
signature = push_signature[2:]
der_signature = signature[:-2]
signature_sighash_decoded = der_signature + '[ALL]'
@@ -206,23 +206,23 @@ class DecodeScriptTest(BitcoinTestFramework):
# 1) P2PK scriptSig
txSave.vin[0].scriptSig = hex_str_to_bytes(push_signature)
- rpc_result = self.nodes[0].decoderawtransaction(bytes_to_hex_str(txSave.serialize()))
+ rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex())
assert_equal(signature_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm'])
# make sure that the sighash decodes come out correctly for a more complex / lesser used case.
txSave.vin[0].scriptSig = hex_str_to_bytes(push_signature_2)
- rpc_result = self.nodes[0].decoderawtransaction(bytes_to_hex_str(txSave.serialize()))
+ rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex())
assert_equal(signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm'])
# 2) multisig scriptSig
txSave.vin[0].scriptSig = hex_str_to_bytes('00' + push_signature + push_signature_2)
- rpc_result = self.nodes[0].decoderawtransaction(bytes_to_hex_str(txSave.serialize()))
+ rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex())
assert_equal('0 ' + signature_sighash_decoded + ' ' + signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm'])
# 3) test a scriptSig that contains more than push operations.
# in fact, it contains an OP_RETURN with data specially crafted to cause improper decode if the code does not catch it.
txSave.vin[0].scriptSig = hex_str_to_bytes('6a143011020701010101010101020601010101010101')
- rpc_result = self.nodes[0].decoderawtransaction(bytes_to_hex_str(txSave.serialize()))
+ rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex())
assert_equal('OP_RETURN 3011020701010101010101020601010101010101', rpc_result['vin'][0]['scriptSig']['asm'])
def run_test(self):
diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py
index 588bfbe083..9a21998d11 100755
--- a/test/functional/rpc_deprecated.py
+++ b/test/functional/rpc_deprecated.py
@@ -1,32 +1,29 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 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
+# 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=generate"]]
-
- def skip_test_if_missing_module(self):
- # The generate RPC method requires the wallet to be compiled
- self.skip_if_no_wallet()
+ self.extra_args = [[], []]
def run_test(self):
# This test should be used to verify correct behaviour of deprecated
# RPC methods with and without the -deprecatedrpc flags. For example:
#
- # self.log.info("Make sure that -deprecatedrpc=createmultisig allows it to take addresses")
- # assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, [self.nodes[0].getnewaddress()])
- # self.nodes[1].createmultisig(1, [self.nodes[1].getnewaddress()])
-
- self.log.info("Test generate RPC")
- assert_raises_rpc_error(-32, 'The wallet generate rpc method is deprecated', self.nodes[0].rpc.generate, 1)
- self.nodes[1].generate(1)
+ # In set_test_params:
+ # self.extra_args = [[], ["-deprecatedrpc=generate"]]
+ #
+ # In run_test:
+ # self.log.info("Test generate RPC")
+ # assert_raises_rpc_error(-32, 'The wallet generate rpc method is deprecated', self.nodes[0].rpc.generate, 1)
+ # self.nodes[1].generate(1)
+ self.log.info("No tested deprecated RPC methods")
if __name__ == '__main__':
DeprecatedRpcTest().main()
diff --git a/test/functional/rpc_deriveaddresses.py b/test/functional/rpc_deriveaddresses.py
new file mode 100755
index 0000000000..1984694692
--- /dev/null
+++ b/test/functional/rpc_deriveaddresses.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018-2019 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 deriveaddresses rpc call."""
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.descriptors import descsum_create
+from test_framework.util import assert_equal, assert_raises_rpc_error
+
+class DeriveaddressesTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.supports_cli = 1
+
+ def run_test(self):
+ assert_raises_rpc_error(-5, "Invalid descriptor", self.nodes[0].deriveaddresses, "a")
+
+ descriptor = "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)#t6wfjs64"
+ address = "bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5"
+ assert_equal(self.nodes[0].deriveaddresses(descriptor), [address])
+
+ descriptor = descriptor[:-9]
+ assert_raises_rpc_error(-5, "Invalid descriptor", self.nodes[0].deriveaddresses, descriptor)
+
+ descriptor_pubkey = "wpkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0)#s9ga3alw"
+ address = "bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5"
+ assert_equal(self.nodes[0].deriveaddresses(descriptor_pubkey), [address])
+
+ ranged_descriptor = "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)#kft60nuy"
+ assert_equal(self.nodes[0].deriveaddresses(ranged_descriptor, [1, 2]), ["bcrt1qhku5rq7jz8ulufe2y6fkcpnlvpsta7rq4442dy", "bcrt1qpgptk2gvshyl0s9lqshsmx932l9ccsv265tvaq"])
+ assert_equal(self.nodes[0].deriveaddresses(ranged_descriptor, 2), [address, "bcrt1qhku5rq7jz8ulufe2y6fkcpnlvpsta7rq4442dy", "bcrt1qpgptk2gvshyl0s9lqshsmx932l9ccsv265tvaq"])
+
+ assert_raises_rpc_error(-8, "Range should not be specified for an un-ranged descriptor", self.nodes[0].deriveaddresses, descsum_create("wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)"), [0, 2])
+
+ assert_raises_rpc_error(-8, "Range must be specified for a ranged descriptor", self.nodes[0].deriveaddresses, descsum_create("wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)"))
+
+ assert_raises_rpc_error(-8, "End of range is too high", self.nodes[0].deriveaddresses, descsum_create("wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)"), 10000000000)
+
+ assert_raises_rpc_error(-8, "Range is too large", self.nodes[0].deriveaddresses, descsum_create("wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)"), [1000000000, 2000000000])
+
+ assert_raises_rpc_error(-8, "Range specified as [begin,end] must not have begin after end", self.nodes[0].deriveaddresses, descsum_create("wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)"), [2, 0])
+
+ assert_raises_rpc_error(-8, "Range should be greater or equal than 0", self.nodes[0].deriveaddresses, descsum_create("wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)"), [-1, 0])
+
+ combo_descriptor = descsum_create("combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)")
+ assert_equal(self.nodes[0].deriveaddresses(combo_descriptor), ["mtfUoUax9L4tzXARpw1oTGxWyoogp52KhJ", "mtfUoUax9L4tzXARpw1oTGxWyoogp52KhJ", address, "2NDvEwGfpEqJWfybzpKPHF2XH3jwoQV3D7x"])
+
+ hardened_without_privkey_descriptor = descsum_create("wpkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1'/1/0)")
+ assert_raises_rpc_error(-5, "Cannot derive script without private keys", self.nodes[0].deriveaddresses, hardened_without_privkey_descriptor)
+
+ bare_multisig_descriptor = descsum_create("multi(1,tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0,tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/1)")
+ assert_raises_rpc_error(-5, "Descriptor does not have a corresponding address", self.nodes[0].deriveaddresses, bare_multisig_descriptor)
+
+if __name__ == '__main__':
+ DeriveaddressesTest().main()
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 4f350953b2..d89fd6461f 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 fundrawtransaction RPC."""
@@ -94,7 +94,7 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- assert(len(dec_tx['vin']) > 0) #test that we have enough inputs
+ assert len(dec_tx['vin']) > 0 #test that we have enough inputs
##############################
# simple test with two coins #
@@ -107,7 +107,7 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- assert(len(dec_tx['vin']) > 0) #test if we have enough inputs
+ assert len(dec_tx['vin']) > 0 #test if we have enough inputs
##############################
# simple test with two coins #
@@ -120,7 +120,7 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- assert(len(dec_tx['vin']) > 0)
+ assert len(dec_tx['vin']) > 0
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
@@ -139,7 +139,7 @@ class RawTransactionsTest(BitcoinTestFramework):
for out in dec_tx['vout']:
totalOut += out['value']
- assert(len(dec_tx['vin']) > 0)
+ assert len(dec_tx['vin']) > 0
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
@@ -363,7 +363,7 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert(feeDelta >= 0 and feeDelta <= feeTolerance)
+ assert feeDelta >= 0 and feeDelta <= feeTolerance
############################################################
############################################################
@@ -378,7 +378,7 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert(feeDelta >= 0 and feeDelta <= feeTolerance)
+ assert feeDelta >= 0 and feeDelta <= feeTolerance
############################################################
@@ -405,7 +405,7 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert(feeDelta >= 0 and feeDelta <= feeTolerance)
+ assert feeDelta >= 0 and feeDelta <= feeTolerance
############################################################
@@ -438,7 +438,7 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert(feeDelta >= 0 and feeDelta <= feeTolerance)
+ assert feeDelta >= 0 and feeDelta <= feeTolerance
############################################################
@@ -558,7 +558,7 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert(feeDelta >= 0 and feeDelta <= feeTolerance*19) #~19 inputs
+ assert feeDelta >= 0 and feeDelta <= feeTolerance*19 #~19 inputs
#############################################
@@ -620,7 +620,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(len(res_dec["vin"]), 1)
assert_equal(res_dec["vin"][0]["txid"], watchonly_txid)
- assert("fee" in result.keys())
+ assert "fee" in result.keys()
assert_greater_than(result["changepos"], -1)
###############################################################
@@ -635,16 +635,16 @@ class RawTransactionsTest(BitcoinTestFramework):
result = self.nodes[3].fundrawtransaction(rawtx, True)
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
assert_equal(len(res_dec["vin"]), 2)
- assert(res_dec["vin"][0]["txid"] == watchonly_txid or res_dec["vin"][1]["txid"] == watchonly_txid)
+ assert res_dec["vin"][0]["txid"] == watchonly_txid or res_dec["vin"][1]["txid"] == watchonly_txid
assert_greater_than(result["fee"], 0)
assert_greater_than(result["changepos"], -1)
assert_equal(result["fee"] + res_dec["vout"][result["changepos"]]["value"], watchonly_amount / 10)
signedtx = self.nodes[3].signrawtransactionwithwallet(result["hex"])
- assert(not signedtx["complete"])
+ assert not signedtx["complete"]
signedtx = self.nodes[0].signrawtransactionwithwallet(signedtx["hex"])
- assert(signedtx["complete"])
+ assert signedtx["complete"]
self.nodes[0].sendrawtransaction(signedtx["hex"])
self.nodes[0].generate(1)
self.sync_all()
@@ -676,10 +676,10 @@ class RawTransactionsTest(BitcoinTestFramework):
for out in res_dec['vout']:
if out['value'] > 1.0:
changeaddress += out['scriptPubKey']['addresses'][0]
- assert(changeaddress != "")
+ assert changeaddress != ""
nextaddr = self.nodes[3].getnewaddress()
# Now the change address key should be removed from the keypool
- assert(changeaddress != nextaddr)
+ assert changeaddress != nextaddr
######################################
# Test subtractFeeFromOutputs option #
diff --git a/test/functional/rpc_getblockfilter.py b/test/functional/rpc_getblockfilter.py
new file mode 100755
index 0000000000..bd93b6f7a4
--- /dev/null
+++ b/test/functional/rpc_getblockfilter.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 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 getblockfilter RPC."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal, assert_is_hex_string, assert_raises_rpc_error,
+ connect_nodes, disconnect_nodes, sync_blocks
+ )
+
+FILTER_TYPES = ["basic"]
+
+class GetBlockFilterTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+ self.extra_args = [["-blockfilterindex"], []]
+
+ def run_test(self):
+ # Create two chains by disconnecting nodes 0 & 1, mining, then reconnecting
+ disconnect_nodes(self.nodes[0], 1)
+
+ self.nodes[0].generate(3)
+ self.nodes[1].generate(4)
+
+ assert_equal(self.nodes[0].getblockcount(), 3)
+ chain0_hashes = [self.nodes[0].getblockhash(block_height) for block_height in range(4)]
+
+ # Reorg node 0 to a new chain
+ connect_nodes(self.nodes[0], 1)
+ sync_blocks(self.nodes)
+
+ assert_equal(self.nodes[0].getblockcount(), 4)
+ chain1_hashes = [self.nodes[0].getblockhash(block_height) for block_height in range(4)]
+
+ # Test getblockfilter returns a filter for all blocks and filter types on active chain
+ for block_hash in chain1_hashes:
+ for filter_type in FILTER_TYPES:
+ result = self.nodes[0].getblockfilter(block_hash, filter_type)
+ assert_is_hex_string(result['filter'])
+
+ # Test getblockfilter returns a filter for all blocks and filter types on stale chain
+ for block_hash in chain0_hashes:
+ for filter_type in FILTER_TYPES:
+ result = self.nodes[0].getblockfilter(block_hash, filter_type)
+ assert_is_hex_string(result['filter'])
+
+ # Test getblockfilter with unknown block
+ bad_block_hash = "0123456789abcdef" * 4
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblockfilter, bad_block_hash, "basic")
+
+ # Test getblockfilter with undefined filter type
+ genesis_hash = self.nodes[0].getblockhash(0)
+ assert_raises_rpc_error(-5, "Unknown filtertype", self.nodes[0].getblockfilter, genesis_hash, "unknown")
+
+if __name__ == '__main__':
+ GetBlockFilterTest().main()
diff --git a/test/functional/rpc_getblockstats.py b/test/functional/rpc_getblockstats.py
index ca9e24367a..efab69ac26 100755
--- a/test/functional/rpc_getblockstats.py
+++ b/test/functional/rpc_getblockstats.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -13,7 +13,6 @@ from test_framework.util import (
)
import json
import os
-import time
TESTSDIR = os.path.dirname(os.path.realpath(__file__))
@@ -21,18 +20,6 @@ class GetblockstatsTest(BitcoinTestFramework):
start_height = 101
max_stat_pos = 2
- STATS_NEED_TXINDEX = [
- 'avgfee',
- 'avgfeerate',
- 'maxfee',
- 'maxfeerate',
- 'medianfee',
- 'feerate_percentiles',
- 'minfee',
- 'minfeerate',
- 'totalfee',
- 'utxo_size_inc',
- ]
def add_options(self, parser):
parser.add_argument('--gen-test-data', dest='gen_test_data',
@@ -44,24 +31,26 @@ class GetblockstatsTest(BitcoinTestFramework):
help='Test data file')
def set_test_params(self):
- self.num_nodes = 2
- self.extra_args = [['-txindex'], ['-paytxfee=0.003']]
+ self.num_nodes = 1
self.setup_clean_chain = True
def get_stats(self):
return [self.nodes[0].getblockstats(hash_or_height=self.start_height + i) for i in range(self.max_stat_pos+1)]
def generate_test_data(self, filename):
- mocktime = time.time()
+ mocktime = 1525107225
+ self.nodes[0].setmocktime(mocktime)
self.nodes[0].generate(101)
- self.nodes[0].sendtoaddress(address=self.nodes[1].getnewaddress(), amount=10, subtractfeefromamount=True)
+ address = self.nodes[0].get_deterministic_priv_key().address
+ self.nodes[0].sendtoaddress(address=address, amount=10, subtractfeefromamount=True)
self.nodes[0].generate(1)
self.sync_all()
- self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=10, subtractfeefromamount=True)
- self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=10, subtractfeefromamount=False)
- self.nodes[1].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1, subtractfeefromamount=True)
+ self.nodes[0].sendtoaddress(address=address, amount=10, subtractfeefromamount=True)
+ self.nodes[0].sendtoaddress(address=address, amount=10, subtractfeefromamount=False)
+ self.nodes[0].settxfee(amount=0.003)
+ self.nodes[0].sendtoaddress(address=address, amount=1, subtractfeefromamount=True)
self.sync_all()
self.nodes[0].generate(1)
@@ -93,11 +82,12 @@ class GetblockstatsTest(BitcoinTestFramework):
# Set the timestamps from the file so that the nodes can get out of Initial Block Download
self.nodes[0].setmocktime(mocktime)
- self.nodes[1].setmocktime(mocktime)
+ self.sync_all()
for b in blocks:
self.nodes[0].submitblock(b)
+
def run_test(self):
test_data = os.path.join(TESTSDIR, self.options.test_data)
if self.options.gen_test_data:
@@ -107,9 +97,6 @@ class GetblockstatsTest(BitcoinTestFramework):
self.sync_all()
stats = self.get_stats()
- expected_stats_noindex = []
- for stat_row in stats:
- expected_stats_noindex.append({k: v for k, v in stat_row.items() if k not in self.STATS_NEED_TXINDEX})
# Make sure all valid statistics are included but nothing else is
expected_keys = self.expected_stats[0].keys()
@@ -127,10 +114,6 @@ class GetblockstatsTest(BitcoinTestFramework):
stats_by_hash = self.nodes[0].getblockstats(hash_or_height=blockhash)
assert_equal(stats_by_hash, self.expected_stats[i])
- # Check with the node that has no txindex
- stats_no_txindex = self.nodes[1].getblockstats(hash_or_height=blockhash, stats=list(expected_stats_noindex[i].keys()))
- assert_equal(stats_no_txindex, expected_stats_noindex[i])
-
# Make sure each stat can be queried on its own
for stat in expected_keys:
for i in range(self.max_stat_pos+1):
@@ -168,15 +151,14 @@ class GetblockstatsTest(BitcoinTestFramework):
# Make sure we aren't always returning inv_sel_stat as the culprit stat
assert_raises_rpc_error(-8, 'Invalid selected statistic aaa%s' % inv_sel_stat,
self.nodes[0].getblockstats, hash_or_height=1, stats=['minfee' , 'aaa%s' % inv_sel_stat])
-
- assert_raises_rpc_error(-8, 'One or more of the selected stats requires -txindex enabled',
- self.nodes[1].getblockstats, hash_or_height=1)
- assert_raises_rpc_error(-8, 'One or more of the selected stats requires -txindex enabled',
- self.nodes[1].getblockstats, hash_or_height=self.start_height + self.max_stat_pos)
-
# Mainchain's genesis block shouldn't be found on regtest
assert_raises_rpc_error(-5, 'Block not found', self.nodes[0].getblockstats,
hash_or_height='000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')
+ # Invalid number of args
+ assert_raises_rpc_error(-1, 'getblockstats hash_or_height ( stats )', self.nodes[0].getblockstats, '00', 1, 2)
+ assert_raises_rpc_error(-1, 'getblockstats hash_or_height ( stats )', self.nodes[0].getblockstats)
+
+
if __name__ == '__main__':
GetblockstatsTest().main()
diff --git a/test/functional/rpc_getchaintips.py b/test/functional/rpc_getchaintips.py
index c869c7262f..8dc8474374 100755
--- a/test/functional/rpc_getchaintips.py
+++ b/test/functional/rpc_getchaintips.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 getchaintips RPC.
@@ -28,7 +28,8 @@ class GetChainTipsTest (BitcoinTestFramework):
self.split_network()
self.nodes[0].generatetoaddress(10, self.nodes[0].get_deterministic_priv_key().address)
self.nodes[2].generatetoaddress(20, self.nodes[2].get_deterministic_priv_key().address)
- self.sync_all([self.nodes[:2], self.nodes[2:]])
+ self.sync_all(self.nodes[:2])
+ self.sync_all(self.nodes[2:])
tips = self.nodes[1].getchaintips ()
assert_equal (len (tips), 1)
diff --git a/test/functional/rpc_invalidateblock.py b/test/functional/rpc_invalidateblock.py
index d8a1deb2a3..3d3f694fd3 100755
--- a/test/functional/rpc_invalidateblock.py
+++ b/test/functional/rpc_invalidateblock.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 invalidateblock RPC."""
@@ -9,7 +9,6 @@ from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
from test_framework.util import (
assert_equal,
connect_nodes_bi,
- sync_blocks,
wait_until,
)
@@ -35,7 +34,7 @@ class InvalidateTest(BitcoinTestFramework):
self.log.info("Connect nodes to force a reorg")
connect_nodes_bi(self.nodes, 0, 1)
- sync_blocks(self.nodes[0:2])
+ self.sync_blocks(self.nodes[0:2])
assert_equal(self.nodes[0].getblockcount(), 6)
badhash = self.nodes[1].getblockhash(2)
@@ -47,7 +46,7 @@ class InvalidateTest(BitcoinTestFramework):
self.log.info("Make sure we won't reorg to a lower work chain:")
connect_nodes_bi(self.nodes, 1, 2)
self.log.info("Sync node 2 to node 1 so both have 6 blocks")
- sync_blocks(self.nodes[1:3])
+ self.sync_blocks(self.nodes[1:3])
assert_equal(self.nodes[2].getblockcount(), 6)
self.log.info("Invalidate block 5 on node 1 so its tip is now at 4")
self.nodes[1].invalidateblock(self.nodes[1].getblockhash(5))
diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py
new file mode 100755
index 0000000000..8a3f8c6f06
--- /dev/null
+++ b/test/functional/rpc_misc.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 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 RPC misc output."""
+import xml.etree.ElementTree as ET
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_raises_rpc_error,
+ assert_equal,
+ assert_greater_than,
+ assert_greater_than_or_equal,
+)
+
+from test_framework.authproxy import JSONRPCException
+
+
+class RpcMiscTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def run_test(self):
+ node = self.nodes[0]
+
+ self.log.info("test getmemoryinfo")
+ memory = node.getmemoryinfo()['locked']
+ assert_greater_than(memory['used'], 0)
+ assert_greater_than(memory['free'], 0)
+ assert_greater_than(memory['total'], 0)
+ # assert_greater_than_or_equal() for locked in case locking pages failed at some point
+ assert_greater_than_or_equal(memory['locked'], 0)
+ assert_greater_than(memory['chunks_used'], 0)
+ assert_greater_than(memory['chunks_free'], 0)
+ assert_equal(memory['used'] + memory['free'], memory['total'])
+
+ self.log.info("test mallocinfo")
+ try:
+ mallocinfo = node.getmemoryinfo(mode="mallocinfo")
+ self.log.info('getmemoryinfo(mode="mallocinfo") call succeeded')
+ tree = ET.fromstring(mallocinfo)
+ assert_equal(tree.tag, 'malloc')
+ except JSONRPCException:
+ self.log.info('getmemoryinfo(mode="mallocinfo") not available')
+ assert_raises_rpc_error(-8, 'mallocinfo is only available when compiled with glibc 2.10+', node.getmemoryinfo, mode="mallocinfo")
+
+ assert_raises_rpc_error(-8, "unknown mode foobar", node.getmemoryinfo, mode="foobar")
+
+ self.log.info("test logging")
+ assert_equal(node.logging()['qt'], True)
+ node.logging(exclude=['qt'])
+ assert_equal(node.logging()['qt'], False)
+ node.logging(include=['qt'])
+ assert_equal(node.logging()['qt'], True)
+
+
+if __name__ == '__main__':
+ RpcMiscTest().main()
diff --git a/test/functional/rpc_named_arguments.py b/test/functional/rpc_named_arguments.py
index 6c5c29dfdf..ecac9c2f82 100755
--- a/test/functional/rpc_named_arguments.py
+++ b/test/functional/rpc_named_arguments.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2018 The Bitcoin Core developers
+# Copyright (c) 2016-2019 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 using named arguments for RPCs."""
@@ -17,7 +17,7 @@ class NamedArgumentTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0]
h = node.help(command='getblockchaininfo')
- assert(h.startswith('getblockchaininfo\n'))
+ assert h.startswith('getblockchaininfo\n')
assert_raises_rpc_error(-8, 'Unknown named parameter', node.help, random='getblockchaininfo')
diff --git a/test/functional/rpc_preciousblock.py b/test/functional/rpc_preciousblock.py
index 72e6e6329f..2d5631bb27 100755
--- a/test/functional/rpc_preciousblock.py
+++ b/test/functional/rpc_preciousblock.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 preciousblock RPC."""
@@ -8,7 +8,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes_bi,
- sync_blocks,
)
def unidirectional_node_sync_via_rpc(node_src, node_dest):
@@ -16,7 +15,7 @@ def unidirectional_node_sync_via_rpc(node_src, node_dest):
blockhash = node_src.getbestblockhash()
while True:
try:
- assert(len(node_dest.getblock(blockhash, False)) > 0)
+ assert len(node_dest.getblock(blockhash, False)) > 0
break
except:
blocks_to_copy.append(blockhash)
@@ -24,7 +23,7 @@ def unidirectional_node_sync_via_rpc(node_src, node_dest):
blocks_to_copy.reverse()
for blockhash in blocks_to_copy:
blockdata = node_src.getblock(blockhash, False)
- assert(node_dest.submitblock(blockdata) in (None, 'inconclusive'))
+ assert node_dest.submitblock(blockdata) in (None, 'inconclusive')
def node_sync_via_rpc(nodes):
for node_src in nodes:
@@ -57,7 +56,7 @@ class PreciousTest(BitcoinTestFramework):
self.log.info("Mine competing blocks E-F-G on Node 1")
hashG = self.nodes[1].generatetoaddress(3, gen_address(1))[-1]
assert_equal(self.nodes[1].getblockcount(), 5)
- assert(hashC != hashG)
+ assert hashC != hashG
self.log.info("Connect nodes and check no reorg occurs")
# Submit competing blocks via RPC so any reorg should occur before we proceed (no way to wait on inaction for p2p sync)
node_sync_via_rpc(self.nodes[0:2])
@@ -72,7 +71,7 @@ class PreciousTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getbestblockhash(), hashC)
self.log.info("Make Node1 prefer block C")
self.nodes[1].preciousblock(hashC)
- sync_blocks(self.nodes[0:2]) # wait because node 1 may not have downloaded hashC
+ self.sync_blocks(self.nodes[0:2]) # wait because node 1 may not have downloaded hashC
assert_equal(self.nodes[1].getbestblockhash(), hashC)
self.log.info("Make Node1 prefer block G again")
self.nodes[1].preciousblock(hashG)
@@ -86,7 +85,7 @@ class PreciousTest(BitcoinTestFramework):
self.log.info("Mine another block (E-F-G-)H on Node 0 and reorg Node 1")
self.nodes[0].generatetoaddress(1, gen_address(0))
assert_equal(self.nodes[0].getblockcount(), 6)
- sync_blocks(self.nodes[0:2])
+ self.sync_blocks(self.nodes[0:2])
hashH = self.nodes[0].getbestblockhash()
assert_equal(self.nodes[1].getbestblockhash(), hashH)
self.log.info("Node1 should not be able to prefer block C anymore")
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 1e10280e60..430be6dd37 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -1,12 +1,19 @@
#!/usr/bin/env python3
-# Copyright (c) 2018 The Bitcoin Core developers
+# Copyright (c) 2018-2019 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 Partially Signed Transaction RPCs.
"""
+from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, find_output, disconnect_nodes, connect_nodes_bi, sync_blocks
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ connect_nodes_bi,
+ disconnect_nodes,
+ find_output,
+)
import json
import os
@@ -19,8 +26,6 @@ class PSBTTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = False
self.num_nodes = 3
- # TODO: remove -txindex. Currently required for getrawtransaction call.
- self.extra_args = [[], ["-txindex"], ["-txindex"]]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -42,22 +47,22 @@ class PSBTTest(BitcoinTestFramework):
online_node.importaddress(offline_addr, "", False)
mining_node.sendtoaddress(address=offline_addr, amount=1.0)
mining_node.generate(nblocks=1)
- sync_blocks([mining_node, online_node])
+ self.sync_blocks([mining_node, online_node])
# Construct an unsigned PSBT on the online node (who doesn't know the output is Segwit, so will include a non-witness UTXO)
utxos = online_node.listunspent(addresses=[offline_addr])
raw = online_node.createrawtransaction([{"txid":utxos[0]["txid"], "vout":utxos[0]["vout"]}],[{online_addr:0.9999}])
psbt = online_node.walletprocesspsbt(online_node.converttopsbt(raw))["psbt"]
- assert("non_witness_utxo" in mining_node.decodepsbt(psbt)["inputs"][0])
+ assert "non_witness_utxo" in mining_node.decodepsbt(psbt)["inputs"][0]
# Have the offline node sign the PSBT (which will update the UTXO to segwit)
signed_psbt = offline_node.walletprocesspsbt(psbt)["psbt"]
- assert("witness_utxo" in mining_node.decodepsbt(signed_psbt)["inputs"][0])
+ assert "witness_utxo" in mining_node.decodepsbt(signed_psbt)["inputs"][0]
# Make sure we can mine the resulting transaction
txid = mining_node.sendrawtransaction(mining_node.finalizepsbt(signed_psbt)["hex"])
mining_node.generate(1)
- sync_blocks([mining_node, online_node])
+ self.sync_blocks([mining_node, online_node])
assert_equal(online_node.gettxout(txid,0)["confirmations"], 1)
# Reconnect
@@ -145,10 +150,12 @@ class PSBTTest(BitcoinTestFramework):
new_psbt = self.nodes[0].converttopsbt(rawtx['hex'])
self.nodes[0].decodepsbt(new_psbt)
- # Make sure that a psbt with signatures cannot be converted
+ # Make sure that a non-psbt with signatures cannot be converted
+ # Error could be either "TX decode failed" (segwit inputs causes parsing to fail) or "Inputs must not have scriptSigs and scriptWitnesses"
+ # We must set iswitness=True because the serialized transaction has inputs and is therefore a witness transaction
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex'])
- assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].converttopsbt, signedtx['hex'])
- assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].converttopsbt, signedtx['hex'], False)
+ assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, hexstring=signedtx['hex'], iswitness=True)
+ assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, hexstring=signedtx['hex'], permitsigdata=False, iswitness=True)
# Unless we allow it to convert and strip signatures
self.nodes[0].converttopsbt(signedtx['hex'], True)
@@ -160,11 +167,11 @@ class PSBTTest(BitcoinTestFramework):
node1_addr = self.nodes[1].getnewaddress()
node2_addr = self.nodes[2].getnewaddress()
txid1 = self.nodes[0].sendtoaddress(node1_addr, 13)
- txid2 =self.nodes[0].sendtoaddress(node2_addr, 13)
- self.nodes[0].generate(6)
+ txid2 = self.nodes[0].sendtoaddress(node2_addr, 13)
+ blockhash = self.nodes[0].generate(6)[0]
self.sync_all()
- vout1 = find_output(self.nodes[1], txid1, 13)
- vout2 = find_output(self.nodes[2], txid2, 13)
+ vout1 = find_output(self.nodes[1], txid1, 13, blockhash=blockhash)
+ vout2 = find_output(self.nodes[2], txid2, 13, blockhash=blockhash)
# Create a psbt spending outputs from nodes 1 and 2
psbt_orig = self.nodes[0].createpsbt([{"txid":txid1, "vout":vout1}, {"txid":txid2, "vout":vout2}], {self.nodes[0].getnewaddress():25.999})
@@ -193,8 +200,8 @@ class PSBTTest(BitcoinTestFramework):
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable":True}, False)
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
- assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
- assert "bip32_derivs" not in psbt_in
+ assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
+ assert "bip32_derivs" not in psbt_in
assert_equal(decoded_psbt["tx"]["locktime"], block_height+2)
# Same construction with only locktime set
@@ -269,6 +276,9 @@ class PSBTTest(BitcoinTestFramework):
combined = self.nodes[2].combinepsbt(combiner['combine'])
assert_equal(combined, combiner['result'])
+ # Empty combiner test
+ assert_raises_rpc_error(-8, "Parameter 'txs' cannot be empty", self.nodes[0].combinepsbt, [])
+
# Finalizer test
for finalizer in finalizers:
finalized = self.nodes[2].finalizepsbt(finalizer['finalize'], False)['psbt']
@@ -290,5 +300,75 @@ class PSBTTest(BitcoinTestFramework):
psbt = self.nodes[1].walletcreatefundedpsbt([], [{p2pkh : 1}], 0, {"includeWatching" : True}, True)
self.nodes[0].decodepsbt(psbt['psbt'])
+ # Test decoding error: invalid base64
+ assert_raises_rpc_error(-22, "TX decode failed invalid base64", self.nodes[0].decodepsbt, ";definitely not base64;")
+
+ # Send to all types of addresses
+ addr1 = self.nodes[1].getnewaddress("", "bech32")
+ txid1 = self.nodes[0].sendtoaddress(addr1, 11)
+ vout1 = find_output(self.nodes[0], txid1, 11)
+ addr2 = self.nodes[1].getnewaddress("", "legacy")
+ txid2 = self.nodes[0].sendtoaddress(addr2, 11)
+ vout2 = find_output(self.nodes[0], txid2, 11)
+ addr3 = self.nodes[1].getnewaddress("", "p2sh-segwit")
+ txid3 = self.nodes[0].sendtoaddress(addr3, 11)
+ vout3 = find_output(self.nodes[0], txid3, 11)
+ self.sync_all()
+
+ # Update a PSBT with UTXOs from the node
+ # Bech32 inputs should be filled with witness UTXO. Other inputs should not be filled because they are non-witness
+ psbt = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1},{"txid":txid2, "vout":vout2},{"txid":txid3, "vout":vout3}], {self.nodes[0].getnewaddress():32.999})
+ decoded = self.nodes[1].decodepsbt(psbt)
+ assert "witness_utxo" not in decoded['inputs'][0] and "non_witness_utxo" not in decoded['inputs'][0]
+ assert "witness_utxo" not in decoded['inputs'][1] and "non_witness_utxo" not in decoded['inputs'][1]
+ assert "witness_utxo" not in decoded['inputs'][2] and "non_witness_utxo" not in decoded['inputs'][2]
+ updated = self.nodes[1].utxoupdatepsbt(psbt)
+ decoded = self.nodes[1].decodepsbt(updated)
+ assert "witness_utxo" in decoded['inputs'][0] and "non_witness_utxo" not in decoded['inputs'][0]
+ assert "witness_utxo" not in decoded['inputs'][1] and "non_witness_utxo" not in decoded['inputs'][1]
+ assert "witness_utxo" not in decoded['inputs'][2] and "non_witness_utxo" not in decoded['inputs'][2]
+
+ # Two PSBTs with a common input should not be joinable
+ psbt1 = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1}], {self.nodes[0].getnewaddress():Decimal('10.999')})
+ assert_raises_rpc_error(-8, "exists in multiple PSBTs", self.nodes[1].joinpsbts, [psbt1, updated])
+
+ # Join two distinct PSBTs
+ addr4 = self.nodes[1].getnewaddress("", "p2sh-segwit")
+ txid4 = self.nodes[0].sendtoaddress(addr4, 5)
+ vout4 = find_output(self.nodes[0], txid4, 5)
+ self.nodes[0].generate(6)
+ self.sync_all()
+ psbt2 = self.nodes[1].createpsbt([{"txid":txid4, "vout":vout4}], {self.nodes[0].getnewaddress():Decimal('4.999')})
+ psbt2 = self.nodes[1].walletprocesspsbt(psbt2)['psbt']
+ psbt2_decoded = self.nodes[0].decodepsbt(psbt2)
+ assert "final_scriptwitness" in psbt2_decoded['inputs'][0] and "final_scriptSig" in psbt2_decoded['inputs'][0]
+ joined = self.nodes[0].joinpsbts([psbt, psbt2])
+ joined_decoded = self.nodes[0].decodepsbt(joined)
+ assert len(joined_decoded['inputs']) == 4 and len(joined_decoded['outputs']) == 2 and "final_scriptwitness" not in joined_decoded['inputs'][3] and "final_scriptSig" not in joined_decoded['inputs'][3]
+
+ # Newly created PSBT needs UTXOs and updating
+ addr = self.nodes[1].getnewaddress("", "p2sh-segwit")
+ txid = self.nodes[0].sendtoaddress(addr, 7)
+ addrinfo = self.nodes[1].getaddressinfo(addr)
+ blockhash = self.nodes[0].generate(6)[0]
+ self.sync_all()
+ vout = find_output(self.nodes[0], txid, 7, blockhash=blockhash)
+ psbt = self.nodes[1].createpsbt([{"txid":txid, "vout":vout}], {self.nodes[0].getnewaddress("", "p2sh-segwit"):Decimal('6.999')})
+ analyzed = self.nodes[0].analyzepsbt(psbt)
+ assert not analyzed['inputs'][0]['has_utxo'] and not analyzed['inputs'][0]['is_final'] and analyzed['inputs'][0]['next'] == 'updater' and analyzed['next'] == 'updater'
+
+ # After update with wallet, only needs signing
+ updated = self.nodes[1].walletprocesspsbt(psbt, False, 'ALL', True)['psbt']
+ analyzed = self.nodes[0].analyzepsbt(updated)
+ assert analyzed['inputs'][0]['has_utxo'] and not analyzed['inputs'][0]['is_final'] and analyzed['inputs'][0]['next'] == 'signer' and analyzed['next'] == 'signer' and analyzed['inputs'][0]['missing']['signatures'][0] == addrinfo['embedded']['witness_program']
+
+ # Check fee and size things
+ assert analyzed['fee'] == Decimal('0.001') and analyzed['estimated_vsize'] == 134 and analyzed['estimated_feerate'] == Decimal('0.00746268')
+
+ # After signing and finalizing, needs extracting
+ signed = self.nodes[1].walletprocesspsbt(updated)['psbt']
+ analyzed = self.nodes[0].analyzepsbt(signed)
+ assert analyzed['inputs'][0]['has_utxo'] and analyzed['inputs'][0]['is_final'] and analyzed['next'] == 'extractor'
+
if __name__ == '__main__':
PSBTTest().main()
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index a97d753626..4338675270 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 rawtransaction RPCs.
@@ -17,7 +17,7 @@ from decimal import Decimal
from io import BytesIO
from test_framework.messages import CTransaction, ToHex
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, bytes_to_hex_str, connect_nodes_bi, hex_str_to_bytes
+from test_framework.util import assert_equal, assert_raises_rpc_error, connect_nodes_bi, hex_str_to_bytes
class multidict(dict):
"""Dictionary that allows duplicate keys.
@@ -42,8 +42,11 @@ class RawTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
- # TODO: remove -txindex. Currently required for getrawtransaction call.
- self.extra_args = [["-addresstype=legacy", "-txindex"], ["-addresstype=legacy", "-txindex"], ["-addresstype=legacy", "-txindex"]]
+ self.extra_args = [
+ ["-txindex"],
+ ["-txindex"],
+ ["-txindex"],
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -120,21 +123,21 @@ class RawTransactionsTest(BitcoinTestFramework):
tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs={address: 99}))))
assert_equal(len(tx.vout), 1)
assert_equal(
- bytes_to_hex_str(tx.serialize()),
+ tx.serialize().hex(),
self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}]),
)
# Two outputs
tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=OrderedDict([(address, 99), (address2, 99)])))))
assert_equal(len(tx.vout), 2)
assert_equal(
- bytes_to_hex_str(tx.serialize()),
+ tx.serialize().hex(),
self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {address2: 99}]),
)
# Multiple mixed outputs
tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([(address, 99), (address2, 99), ('data', '99')])))))
assert_equal(len(tx.vout), 3)
assert_equal(
- bytes_to_hex_str(tx.serialize()),
+ tx.serialize().hex(),
self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {address2: 99}, {'data': '99'}]),
)
@@ -286,11 +289,7 @@ class RawTransactionsTest(BitcoinTestFramework):
txDetails = self.nodes[0].gettransaction(txId, True)
rawTx = self.nodes[0].decoderawtransaction(txDetails['hex'])
- vout = False
- for outpoint in rawTx['vout']:
- if outpoint['value'] == Decimal('2.20000000'):
- vout = outpoint
- break
+ vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('2.20000000'))
bal = self.nodes[0].getbalance()
inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "amount" : vout['value']}]
@@ -331,11 +330,7 @@ class RawTransactionsTest(BitcoinTestFramework):
txDetails = self.nodes[0].gettransaction(txId, True)
rawTx2 = self.nodes[0].decoderawtransaction(txDetails['hex'])
- vout = False
- for outpoint in rawTx2['vout']:
- if outpoint['value'] == Decimal('2.20000000'):
- vout = outpoint
- break
+ vout = next(o for o in rawTx2['vout'] if o['value'] == Decimal('2.20000000'))
bal = self.nodes[0].getbalance()
inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "redeemScript" : mSigObjValid['hex'], "amount" : vout['value']}]
@@ -370,30 +365,30 @@ class RawTransactionsTest(BitcoinTestFramework):
# getrawtransaction tests
# 1. valid parameters - only supply txid
- txHash = rawTx["hash"]
- assert_equal(self.nodes[0].getrawtransaction(txHash), rawTxSigned['hex'])
+ txId = rawTx["txid"]
+ assert_equal(self.nodes[0].getrawtransaction(txId), rawTxSigned['hex'])
# 2. valid parameters - supply txid and 0 for non-verbose
- assert_equal(self.nodes[0].getrawtransaction(txHash, 0), rawTxSigned['hex'])
+ assert_equal(self.nodes[0].getrawtransaction(txId, 0), rawTxSigned['hex'])
# 3. valid parameters - supply txid and False for non-verbose
- assert_equal(self.nodes[0].getrawtransaction(txHash, False), rawTxSigned['hex'])
+ assert_equal(self.nodes[0].getrawtransaction(txId, False), rawTxSigned['hex'])
# 4. valid parameters - supply txid and 1 for verbose.
# We only check the "hex" field of the output so we don't need to update this test every time the output format changes.
- assert_equal(self.nodes[0].getrawtransaction(txHash, 1)["hex"], rawTxSigned['hex'])
+ assert_equal(self.nodes[0].getrawtransaction(txId, 1)["hex"], rawTxSigned['hex'])
# 5. valid parameters - supply txid and True for non-verbose
- assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex'])
+ assert_equal(self.nodes[0].getrawtransaction(txId, True)["hex"], rawTxSigned['hex'])
# 6. invalid parameters - supply txid and string "Flase"
- assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, "Flase")
+ assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txId, "Flase")
# 7. invalid parameters - supply txid and empty array
- assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, [])
+ assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txId, [])
# 8. invalid parameters - supply txid and empty dict
- assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, {})
+ assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txId, {})
inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 1000}]
outputs = { self.nodes[0].getnewaddress() : 1 }
@@ -435,5 +430,30 @@ class RawTransactionsTest(BitcoinTestFramework):
decrawtx = self.nodes[0].decoderawtransaction(rawtx)
assert_equal(decrawtx['version'], 0x7fffffff)
+ self.log.info('sendrawtransaction/testmempoolaccept with maxfeerate')
+
+ txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0)
+ rawTx = self.nodes[0].getrawtransaction(txId, True)
+ vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('1.00000000'))
+
+ self.sync_all()
+ inputs = [{ "txid" : txId, "vout" : vout['n'] }]
+ outputs = { self.nodes[0].getnewaddress() : Decimal("0.99999000") } # 1000 sat fee
+ rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
+ rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx)
+ assert_equal(rawTxSigned['complete'], True)
+ # 1000 sat fee, ~100 b transaction, fee rate should land around 10 sat/b = 0.00010000 BTC/kB
+ # Thus, testmempoolaccept should reject
+ testres = self.nodes[2].testmempoolaccept([rawTxSigned['hex']], 0.00001000)[0]
+ assert_equal(testres['allowed'], False)
+ assert_equal(testres['reject-reason'], '256: absurdly-high-fee')
+ # and sendrawtransaction should throw
+ assert_raises_rpc_error(-26, "absurdly-high-fee", self.nodes[2].sendrawtransaction, rawTxSigned['hex'], 0.00001000)
+ # And below calls should both succeed
+ testres = self.nodes[2].testmempoolaccept(rawtxs=[rawTxSigned['hex']], maxfeerate='0.00070000')[0]
+ assert_equal(testres['allowed'], True)
+ self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'], maxfeerate='0.00070000')
+
+
if __name__ == '__main__':
RawTransactionsTest().main()
diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py
index 11b4db6ec5..a1cd33ad54 100755
--- a/test/functional/rpc_scantxoutset.py
+++ b/test/functional/rpc_scantxoutset.py
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
-# Copyright (c) 2018 The Bitcoin Core developers
+# Copyright (c) 2018-2019 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 scantxoutset rpc call."""
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
from decimal import Decimal
import shutil
@@ -67,6 +67,13 @@ class ScantxoutsetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr_P2SH_SEGWIT + ")", "addr(" + addr_LEGACY + ")", "addr(" + addr_BECH32 + ")"])['total_amount'], Decimal("0.007"))
assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr_P2SH_SEGWIT + ")", "addr(" + addr_LEGACY + ")", "combo(" + pubk3 + ")"])['total_amount'], Decimal("0.007"))
+ self.log.info("Test range validation.")
+ assert_raises_rpc_error(-8, "End of range is too high", self.nodes[0].scantxoutset, "start", [ {"desc": "desc", "range": -1}])
+ assert_raises_rpc_error(-8, "Range should be greater or equal than 0", self.nodes[0].scantxoutset, "start", [ {"desc": "desc", "range": [-1, 10]}])
+ assert_raises_rpc_error(-8, "End of range is too high", self.nodes[0].scantxoutset, "start", [ {"desc": "desc", "range": [(2 << 31 + 1) - 1000000, (2 << 31 + 1)]}])
+ assert_raises_rpc_error(-8, "Range specified as [begin,end] must not have begin after end", self.nodes[0].scantxoutset, "start", [ {"desc": "desc", "range": [2, 1]}])
+ assert_raises_rpc_error(-8, "Range is too large", self.nodes[0].scantxoutset, "start", [ {"desc": "desc", "range": [0, 1000001]}])
+
self.log.info("Test extended key derivation.")
# Run various scans, and verify that the sum of the amounts of the matches corresponds to the expected subset.
# Note that all amounts in the UTXO set are powers of 2 multiplied by 0.001 BTC, so each amounts uniquely identifies a subset.
@@ -95,11 +102,12 @@ class ScantxoutsetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)", "range": 1500}])['total_amount'], Decimal("28.672"))
assert_equal(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": 1499}])['total_amount'], Decimal("12.288"))
assert_equal(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": 1500}])['total_amount'], Decimal("28.672"))
+ assert_equal(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": [1500,1500]}])['total_amount'], Decimal("16.384"))
# Test the reported descriptors for a few matches
- assert_equal(descriptors(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/*)", "range": 1499}])), ["pkh([0c5f9a1e/0'/0'/0]026dbd8b2315f296d36e6b6920b1579ca75569464875c7ebe869b536a7d9503c8c)", "pkh([0c5f9a1e/0'/0'/1]033e6f25d76c00bedb3a8993c7d5739ee806397f0529b1b31dda31ef890f19a60c)"])
- assert_equal(descriptors(self.nodes[0].scantxoutset("start", [ "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)"])), ["pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)"])
- assert_equal(descriptors(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": 1500}])), ['pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)', 'pkh([0c5f9a1e/1/1/1500]03832901c250025da2aebae2bfb38d5c703a57ab66ad477f9c578bfbcd78abca6f)', 'pkh([0c5f9a1e/1/1/1]030d820fc9e8211c4169be8530efbc632775d8286167afd178caaf1089b77daba7)'])
+ assert_equal(descriptors(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/*)", "range": 1499}])), ["pkh([0c5f9a1e/0'/0'/0]026dbd8b2315f296d36e6b6920b1579ca75569464875c7ebe869b536a7d9503c8c)#dzxw429x", "pkh([0c5f9a1e/0'/0'/1]033e6f25d76c00bedb3a8993c7d5739ee806397f0529b1b31dda31ef890f19a60c)#43rvceed"])
+ assert_equal(descriptors(self.nodes[0].scantxoutset("start", [ "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)"])), ["pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)#cxmct4w8"])
+ assert_equal(descriptors(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": 1500}])), ['pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)#cxmct4w8', 'pkh([0c5f9a1e/1/1/1500]03832901c250025da2aebae2bfb38d5c703a57ab66ad477f9c578bfbcd78abca6f)#vchwd07g', 'pkh([0c5f9a1e/1/1/1]030d820fc9e8211c4169be8530efbc632775d8286167afd178caaf1089b77daba7)#z2t3ypsa'])
if __name__ == '__main__':
ScantxoutsetTest().main()
diff --git a/test/functional/rpc_signmessage.py b/test/functional/rpc_signmessage.py
index ad0e29b451..0cb3ce4215 100755
--- a/test/functional/rpc_signmessage.py
+++ b/test/functional/rpc_signmessage.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2018 The Bitcoin Core developers
+# Copyright (c) 2016-2019 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 RPC commands for signing and verifying messages."""
@@ -25,18 +25,18 @@ class SignMessagesTest(BitcoinTestFramework):
expected_signature = 'INbVnW4e6PeRmsv2Qgu8NuopvrVjkcxob+sX8OcZG0SALhWybUjzMLPdAsXI46YZGb0KQTRii+wWIQzRpG/U+S0='
signature = self.nodes[0].signmessagewithprivkey(priv_key, message)
assert_equal(expected_signature, signature)
- assert(self.nodes[0].verifymessage(address, signature, message))
+ assert self.nodes[0].verifymessage(address, signature, message)
self.log.info('test signing with an address with wallet')
address = self.nodes[0].getnewaddress()
signature = self.nodes[0].signmessage(address, message)
- assert(self.nodes[0].verifymessage(address, signature, message))
+ assert self.nodes[0].verifymessage(address, signature, message)
self.log.info('test verifying with another address should not work')
other_address = self.nodes[0].getnewaddress()
other_signature = self.nodes[0].signmessage(other_address, message)
- assert(not self.nodes[0].verifymessage(other_address, signature, message))
- assert(not self.nodes[0].verifymessage(address, other_signature, message))
+ assert not self.nodes[0].verifymessage(other_address, signature, message)
+ assert not self.nodes[0].verifymessage(address, other_signature, message)
if __name__ == '__main__':
SignMessagesTest().main()
diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py
index 291538df64..780758e219 100755
--- a/test/functional/rpc_signrawtransaction.py
+++ b/test/functional/rpc_signrawtransaction.py
@@ -1,18 +1,20 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 transaction signing using the signrawtransaction* RPCs."""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error
+from test_framework.util import assert_equal, assert_raises_rpc_error, hex_str_to_bytes
+from test_framework.messages import sha256
+from test_framework.script import CScript, OP_0
+from decimal import Decimal
class SignRawTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 1
- self.extra_args = [["-deprecatedrpc=signrawtransaction"]]
+ self.num_nodes = 2
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -143,9 +145,33 @@ class SignRawTransactionsTest(BitcoinTestFramework):
assert_equal(rawTxSigned['errors'][1]['witness'], ["304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee01", "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357"])
assert not rawTxSigned['errors'][0]['witness']
+ def witness_script_test(self):
+ # Now test signing transaction to P2SH-P2WSH addresses without wallet
+ # Create a new P2SH-P2WSH 1-of-1 multisig address:
+ embedded_address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())
+ embedded_privkey = self.nodes[1].dumpprivkey(embedded_address["address"])
+ p2sh_p2wsh_address = self.nodes[1].addmultisigaddress(1, [embedded_address["pubkey"]], "", "p2sh-segwit")
+ # send transaction to P2SH-P2WSH 1-of-1 multisig address
+ self.nodes[0].generate(101)
+ self.nodes[0].sendtoaddress(p2sh_p2wsh_address["address"], 49.999)
+ self.nodes[0].generate(1)
+ self.sync_all()
+ # Find the UTXO for the transaction node[1] should have received, check witnessScript matches
+ unspent_output = self.nodes[1].listunspent(0, 999999, [p2sh_p2wsh_address["address"]])[0]
+ assert_equal(unspent_output["witnessScript"], p2sh_p2wsh_address["redeemScript"])
+ p2sh_redeemScript = CScript([OP_0, sha256(hex_str_to_bytes(p2sh_p2wsh_address["redeemScript"]))])
+ assert_equal(unspent_output["redeemScript"], p2sh_redeemScript.hex())
+ # Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys
+ spending_tx = self.nodes[0].createrawtransaction([unspent_output], {self.nodes[1].getnewaddress(): Decimal("49.998")})
+ spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [embedded_privkey], [unspent_output])
+ # Check the signing completed successfully
+ assert 'complete' in spending_tx_signed
+ assert_equal(spending_tx_signed['complete'], True)
+
def run_test(self):
self.successful_signing_test()
self.script_verification_error_test()
+ self.witness_script_test()
self.test_with_lock_outputs()
diff --git a/test/functional/rpc_uptime.py b/test/functional/rpc_uptime.py
index 20b6341550..e86f91b1d0 100755
--- a/test/functional/rpc_uptime.py
+++ b/test/functional/rpc_uptime.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 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 RPC call related to the uptime command.
@@ -23,7 +23,7 @@ class UptimeTest(BitcoinTestFramework):
def _test_uptime(self):
wait_time = 10
self.nodes[0].setmocktime(int(time.time() + wait_time))
- assert(self.nodes[0].uptime() >= wait_time)
+ assert self.nodes[0].uptime() >= wait_time
if __name__ == '__main__':
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index 456d43aa2e..f36cffe957 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -1,11 +1,11 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2018 The Bitcoin Core developers
+# Copyright (c) 2016-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Encode and decode BASE58, P2PKH and P2SH addresses."""
from .script import hash256, hash160, sha256, CScript, OP_0
-from .util import bytes_to_hex_str, hex_str_to_bytes
+from .util import hex_str_to_bytes
from . import segwit_addr
@@ -16,9 +16,9 @@ chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def byte_to_base58(b, version):
result = ''
- str = bytes_to_hex_str(b)
- str = bytes_to_hex_str(chr(version).encode('latin-1')) + str
- checksum = bytes_to_hex_str(hash256(hex_str_to_bytes(str)))
+ str = b.hex()
+ str = chr(version).encode('latin-1').hex() + str
+ checksum = hash256(hex_str_to_bytes(str)).hex()
str += checksum[:8]
value = int('0x'+str,0)
while value > 0:
@@ -32,12 +32,12 @@ def byte_to_base58(b, version):
# TODO: def base58_decode
def keyhash_to_p2pkh(hash, main = False):
- assert (len(hash) == 20)
+ assert len(hash) == 20
version = 0 if main else 111
return byte_to_base58(hash, version)
def scripthash_to_p2sh(hash, main = False):
- assert (len(hash) == 20)
+ assert len(hash) == 20
version = 5 if main else 196
return byte_to_base58(hash, version)
@@ -80,11 +80,11 @@ def check_key(key):
key = hex_str_to_bytes(key) # Assuming this is hex string
if (type(key) is bytes and (len(key) == 33 or len(key) == 65)):
return key
- assert(False)
+ assert False
def check_script(script):
if (type(script) is str):
script = hex_str_to_bytes(script) # Assuming this is hex string
if (type(script) is bytes or type(script) is CScript):
return script
- assert(False)
+ assert False
diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py
index 1140fe9b3e..4ba6ac1db2 100644
--- a/test/functional/test_framework/authproxy.py
+++ b/test/functional/test_framework/authproxy.py
@@ -35,6 +35,7 @@ ServiceProxy class:
import base64
import decimal
+from http import HTTPStatus
import http.client
import json
import logging
@@ -49,13 +50,14 @@ USER_AGENT = "AuthServiceProxy/0.1"
log = logging.getLogger("BitcoinRPC")
class JSONRPCException(Exception):
- def __init__(self, rpc_error):
+ def __init__(self, rpc_error, http_status=None):
try:
errmsg = '%(message)s (%(code)i)' % rpc_error
except (KeyError, TypeError):
errmsg = ''
super().__init__(errmsg)
self.error = rpc_error
+ self.http_status = http_status
def EncodeDecimal(o):
@@ -120,8 +122,11 @@ class AuthServiceProxy():
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("-{}-> {} {}".format(
+ AuthServiceProxy.__id_count,
+ self._service_name,
+ json.dumps(args or argsn, default=EncodeDecimal, ensure_ascii=self.ensure_ascii),
+ ))
if args and argsn:
raise ValueError('Cannot handle both named and positional arguments')
return {'version': '1.1',
@@ -131,19 +136,26 @@ class AuthServiceProxy():
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'))
+ response, status = self._request('POST', self.__url.path, postdata.encode('utf-8'))
if response['error'] is not None:
- raise JSONRPCException(response['error'])
+ raise JSONRPCException(response['error'], status)
elif 'result' not in response:
raise JSONRPCException({
- 'code': -343, 'message': 'missing JSON-RPC result'})
+ 'code': -343, 'message': 'missing JSON-RPC result'}, status)
+ elif status != HTTPStatus.OK:
+ raise JSONRPCException({
+ 'code': -342, 'message': 'non-200 HTTP status code but no JSON-RPC error'}, status)
else:
return response['result']
def batch(self, rpc_call_list):
postdata = json.dumps(list(rpc_call_list), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
log.debug("--> " + postdata)
- return self._request('POST', self.__url.path, postdata.encode('utf-8'))
+ response, status = self._request('POST', self.__url.path, postdata.encode('utf-8'))
+ if status != HTTPStatus.OK:
+ raise JSONRPCException({
+ 'code': -342, 'message': 'non-200 HTTP status code but no JSON-RPC error'}, status)
+ return response
def _get_response(self):
req_start_time = time.time()
@@ -162,8 +174,9 @@ class AuthServiceProxy():
content_type = http_response.getheader('Content-Type')
if content_type != 'application/json':
- raise JSONRPCException({
- 'code': -342, 'message': 'non-JSON HTTP response with \'%i %s\' from server' % (http_response.status, http_response.reason)})
+ raise JSONRPCException(
+ {'code': -342, 'message': 'non-JSON HTTP response with \'%i %s\' from server' % (http_response.status, http_response.reason)},
+ http_response.status)
responsedata = http_response.read().decode('utf8')
response = json.loads(responsedata, parse_float=decimal.Decimal)
@@ -172,7 +185,7 @@ class AuthServiceProxy():
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))
- return response
+ return response, http_response.status
def __truediv__(self, relative_uri):
return AuthServiceProxy("{}/{}".format(self.__service_url, relative_uri), self._service_name, connection=self.__conn)
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 6b47cae4c3..7ac044d0d0 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Utilities for manipulating blocks and transactions."""
@@ -20,7 +20,6 @@ from .messages import (
CTxOut,
FromHex,
ToHex,
- bytes_to_hex_str,
hash256,
hex_str_to_bytes,
ser_string,
@@ -43,9 +42,13 @@ from io import BytesIO
MAX_BLOCK_SIGOPS = 20000
+# Genesis block time (regtest)
+TIME_GENESIS_BLOCK = 1296688602
+
# From BIP141
WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
+
def create_block(hashprev, coinbase, ntime=None, *, version=1):
"""Create a block (with regtest difficulty)."""
block = CBlock()
@@ -128,7 +131,7 @@ def create_tx_with_script(prevtx, n, script_sig=b"", *, amount, script_pub_key=C
Can optionally pass scriptPubKey and scriptSig, default is anyone-can-spend output.
"""
tx = CTransaction()
- assert(n < len(prevtx.vout))
+ assert n < len(prevtx.vout)
tx.vin.append(CTxIn(COutPoint(prevtx.sha256, n), script_sig, 0xffffffff))
tx.vout.append(CTxOut(amount, script_pub_key))
tx.calc_sha256()
@@ -186,7 +189,7 @@ def witness_script(use_p2wsh, pubkey):
witness_program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
scripthash = sha256(witness_program)
pkscript = CScript([OP_0, scripthash])
- return bytes_to_hex_str(pkscript)
+ return pkscript.hex()
def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
"""Return a transaction (in hex) that spends the given utxo to a segwit output.
@@ -211,7 +214,7 @@ def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=Tru
tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount)
if (sign):
signed = node.signrawtransactionwithwallet(tx_to_witness)
- assert("errors" not in signed or len(["errors"]) == 0)
+ assert "errors" not in signed or len(["errors"]) == 0
return node.sendrawtransaction(signed["hex"])
else:
if (insert_redeem_script):
diff --git a/test/functional/test_framework/descriptors.py b/test/functional/test_framework/descriptors.py
new file mode 100644
index 0000000000..29482ce01e
--- /dev/null
+++ b/test/functional/test_framework/descriptors.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 Pieter Wuille
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Utility functions related to output descriptors"""
+
+INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "
+CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
+GENERATOR = [0xf5dee51989, 0xa9fdca3312, 0x1bab10e32d, 0x3706b1677a, 0x644d626ffd]
+
+def descsum_polymod(symbols):
+ """Internal function that computes the descriptor checksum."""
+ chk = 1
+ for value in symbols:
+ top = chk >> 35
+ chk = (chk & 0x7ffffffff) << 5 ^ value
+ for i in range(5):
+ chk ^= GENERATOR[i] if ((top >> i) & 1) else 0
+ return chk
+
+def descsum_expand(s):
+ """Internal function that does the character to symbol expansion"""
+ groups = []
+ symbols = []
+ for c in s:
+ if not c in INPUT_CHARSET:
+ return None
+ v = INPUT_CHARSET.find(c)
+ symbols.append(v & 31)
+ groups.append(v >> 5)
+ if len(groups) == 3:
+ symbols.append(groups[0] * 9 + groups[1] * 3 + groups[2])
+ groups = []
+ if len(groups) == 1:
+ symbols.append(groups[0])
+ elif len(groups) == 2:
+ symbols.append(groups[0] * 3 + groups[1])
+ return symbols
+
+def descsum_create(s):
+ """Add a checksum to a descriptor without"""
+ symbols = descsum_expand(s) + [0, 0, 0, 0, 0, 0, 0, 0]
+ checksum = descsum_polymod(symbols) ^ 1
+ return s + '#' + ''.join(CHECKSUM_CHARSET[(checksum >> (5 * (7 - i))) & 31] for i in range(8))
+
+def descsum_check(s, require=True):
+ """Verify that the checksum is correct in a descriptor"""
+ if not '#' in s:
+ return not require
+ if s[-9] != '#':
+ return False
+ if not all(x in CHECKSUM_CHARSET for x in s[-8:]):
+ return False
+ symbols = descsum_expand(s[:-9]) + [CHECKSUM_CHARSET.find(x) for x in s[-8:]]
+ return descsum_polymod(symbols) == 1
diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py
index 1b3e510dc4..912c0ca978 100644
--- a/test/functional/test_framework/key.py
+++ b/test/functional/test_framework/key.py
@@ -1,226 +1,386 @@
-# Copyright (c) 2011 Sam Rushing
-"""ECC secp256k1 OpenSSL wrapper.
+# Copyright (c) 2019 Pieter Wuille
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test-only secp256k1 elliptic curve implementation
-WARNING: This module does not mlock() secrets; your private keys may end up on
-disk in swap! Use with caution!
+WARNING: This code is slow, uses bad randomness, does not properly protect
+keys, and is trivially vulnerable to side channel attacks. Do not use for
+anything but tests."""
+import random
-This file is modified from python-bitcoinlib.
-"""
-
-import ctypes
-import ctypes.util
-import hashlib
-
-ssl = ctypes.cdll.LoadLibrary(ctypes.util.find_library ('ssl') or 'libeay32')
-
-ssl.BN_new.restype = ctypes.c_void_p
-ssl.BN_new.argtypes = []
-
-ssl.BN_bin2bn.restype = ctypes.c_void_p
-ssl.BN_bin2bn.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.c_void_p]
-
-ssl.BN_CTX_free.restype = None
-ssl.BN_CTX_free.argtypes = [ctypes.c_void_p]
-
-ssl.BN_CTX_new.restype = ctypes.c_void_p
-ssl.BN_CTX_new.argtypes = []
-
-ssl.ECDH_compute_key.restype = ctypes.c_int
-ssl.ECDH_compute_key.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p]
-
-ssl.ECDSA_sign.restype = ctypes.c_int
-ssl.ECDSA_sign.argtypes = [ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
-
-ssl.ECDSA_verify.restype = ctypes.c_int
-ssl.ECDSA_verify.argtypes = [ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p]
-
-ssl.EC_KEY_free.restype = None
-ssl.EC_KEY_free.argtypes = [ctypes.c_void_p]
-
-ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
-ssl.EC_KEY_new_by_curve_name.argtypes = [ctypes.c_int]
-
-ssl.EC_KEY_get0_group.restype = ctypes.c_void_p
-ssl.EC_KEY_get0_group.argtypes = [ctypes.c_void_p]
-
-ssl.EC_KEY_get0_public_key.restype = ctypes.c_void_p
-ssl.EC_KEY_get0_public_key.argtypes = [ctypes.c_void_p]
-
-ssl.EC_KEY_set_private_key.restype = ctypes.c_int
-ssl.EC_KEY_set_private_key.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
-
-ssl.EC_KEY_set_conv_form.restype = None
-ssl.EC_KEY_set_conv_form.argtypes = [ctypes.c_void_p, ctypes.c_int]
-
-ssl.EC_KEY_set_public_key.restype = ctypes.c_int
-ssl.EC_KEY_set_public_key.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
-
-ssl.i2o_ECPublicKey.restype = ctypes.c_void_p
-ssl.i2o_ECPublicKey.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
-
-ssl.EC_POINT_new.restype = ctypes.c_void_p
-ssl.EC_POINT_new.argtypes = [ctypes.c_void_p]
-
-ssl.EC_POINT_free.restype = None
-ssl.EC_POINT_free.argtypes = [ctypes.c_void_p]
-
-ssl.EC_POINT_mul.restype = ctypes.c_int
-ssl.EC_POINT_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
-
-# this specifies the curve used with ECDSA.
-NID_secp256k1 = 714 # from openssl/obj_mac.h
+def modinv(a, n):
+ """Compute the modular inverse of a modulo n
+ See https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers.
+ """
+ t1, t2 = 0, 1
+ r1, r2 = n, a
+ while r2 != 0:
+ q = r1 // r2
+ t1, t2 = t2, t1 - q * t2
+ r1, r2 = r2, r1 - q * r2
+ if r1 > 1:
+ return None
+ if t1 < 0:
+ t1 += n
+ return t1
+
+def jacobi_symbol(n, k):
+ """Compute the Jacobi symbol of n modulo k
+
+ See http://en.wikipedia.org/wiki/Jacobi_symbol
+
+ For our application k is always prime, so this is the same as the Legendre symbol."""
+ assert k > 0 and k & 1, "jacobi symbol is only defined for positive odd k"
+ n %= k
+ t = 0
+ while n != 0:
+ while n & 1 == 0:
+ n >>= 1
+ r = k & 7
+ t ^= (r == 3 or r == 5)
+ n, k = k, n
+ t ^= (n & k & 3 == 3)
+ n = n % k
+ if k == 1:
+ return -1 if t else 1
+ return 0
+
+def modsqrt(a, p):
+ """Compute the square root of a modulo p when p % 4 = 3.
+
+ The Tonelli-Shanks algorithm can be used. See https://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm
+
+ Limiting this function to only work for p % 4 = 3 means we don't need to
+ iterate through the loop. The highest n such that p - 1 = 2^n Q with Q odd
+ is n = 1. Therefore Q = (p-1)/2 and sqrt = a^((Q+1)/2) = a^((p+1)/4)
+
+ secp256k1's is defined over field of size 2**256 - 2**32 - 977, which is 3 mod 4.
+ """
+ if p % 4 != 3:
+ raise NotImplementedError("modsqrt only implemented for p % 4 = 3")
+ sqrt = pow(a, (p + 1)//4, p)
+ if pow(sqrt, 2, p) == a % p:
+ return sqrt
+ return None
+
+class EllipticCurve:
+ def __init__(self, p, a, b):
+ """Initialize elliptic curve y^2 = x^3 + a*x + b over GF(p)."""
+ self.p = p
+ self.a = a % p
+ self.b = b % p
+
+ def affine(self, p1):
+ """Convert a Jacobian point tuple p1 to affine form, or None if at infinity.
+
+ An affine point is represented as the Jacobian (x, y, 1)"""
+ x1, y1, z1 = p1
+ if z1 == 0:
+ return None
+ inv = modinv(z1, self.p)
+ inv_2 = (inv**2) % self.p
+ inv_3 = (inv_2 * inv) % self.p
+ return ((inv_2 * x1) % self.p, (inv_3 * y1) % self.p, 1)
+
+ def negate(self, p1):
+ """Negate a Jacobian point tuple p1."""
+ x1, y1, z1 = p1
+ return (x1, (self.p - y1) % self.p, z1)
+
+ def on_curve(self, p1):
+ """Determine whether a Jacobian tuple p is on the curve (and not infinity)"""
+ x1, y1, z1 = p1
+ z2 = pow(z1, 2, self.p)
+ z4 = pow(z2, 2, self.p)
+ return z1 != 0 and (pow(x1, 3, self.p) + self.a * x1 * z4 + self.b * z2 * z4 - pow(y1, 2, self.p)) % self.p == 0
+
+ def is_x_coord(self, x):
+ """Test whether x is a valid X coordinate on the curve."""
+ x_3 = pow(x, 3, self.p)
+ return jacobi_symbol(x_3 + self.a * x + self.b, self.p) != -1
+
+ def lift_x(self, x):
+ """Given an X coordinate on the curve, return a corresponding affine point."""
+ x_3 = pow(x, 3, self.p)
+ v = x_3 + self.a * x + self.b
+ y = modsqrt(v, self.p)
+ if y is None:
+ return None
+ return (x, y, 1)
+
+ def double(self, p1):
+ """Double a Jacobian tuple p1
+
+ See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Doubling"""
+ x1, y1, z1 = p1
+ if z1 == 0:
+ return (0, 1, 0)
+ y1_2 = (y1**2) % self.p
+ y1_4 = (y1_2**2) % self.p
+ x1_2 = (x1**2) % self.p
+ s = (4*x1*y1_2) % self.p
+ m = 3*x1_2
+ if self.a:
+ m += self.a * pow(z1, 4, self.p)
+ m = m % self.p
+ x2 = (m**2 - 2*s) % self.p
+ y2 = (m*(s - x2) - 8*y1_4) % self.p
+ z2 = (2*y1*z1) % self.p
+ return (x2, y2, z2)
+
+ def add_mixed(self, p1, p2):
+ """Add a Jacobian tuple p1 and an affine tuple p2
+
+ See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition (with affine point)"""
+ x1, y1, z1 = p1
+ x2, y2, z2 = p2
+ assert(z2 == 1)
+ # Adding to the point at infinity is a no-op
+ if z1 == 0:
+ return p2
+ z1_2 = (z1**2) % self.p
+ z1_3 = (z1_2 * z1) % self.p
+ u2 = (x2 * z1_2) % self.p
+ s2 = (y2 * z1_3) % self.p
+ if x1 == u2:
+ if (y1 != s2):
+ # p1 and p2 are inverses. Return the point at infinity.
+ return (0, 1, 0)
+ # p1 == p2. The formulas below fail when the two points are equal.
+ return self.double(p1)
+ h = u2 - x1
+ r = s2 - y1
+ h_2 = (h**2) % self.p
+ h_3 = (h_2 * h) % self.p
+ u1_h_2 = (x1 * h_2) % self.p
+ x3 = (r**2 - h_3 - 2*u1_h_2) % self.p
+ y3 = (r*(u1_h_2 - x3) - y1*h_3) % self.p
+ z3 = (h*z1) % self.p
+ return (x3, y3, z3)
+
+ def add(self, p1, p2):
+ """Add two Jacobian tuples p1 and p2
+
+ See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition"""
+ x1, y1, z1 = p1
+ x2, y2, z2 = p2
+ # Adding the point at infinity is a no-op
+ if z1 == 0:
+ return p2
+ if z2 == 0:
+ return p1
+ # Adding an Affine to a Jacobian is more efficient since we save field multiplications and squarings when z = 1
+ if z1 == 1:
+ return self.add_mixed(p2, p1)
+ if z2 == 1:
+ return self.add_mixed(p1, p2)
+ z1_2 = (z1**2) % self.p
+ z1_3 = (z1_2 * z1) % self.p
+ z2_2 = (z2**2) % self.p
+ z2_3 = (z2_2 * z2) % self.p
+ u1 = (x1 * z2_2) % self.p
+ u2 = (x2 * z1_2) % self.p
+ s1 = (y1 * z2_3) % self.p
+ s2 = (y2 * z1_3) % self.p
+ if u1 == u2:
+ if (s1 != s2):
+ # p1 and p2 are inverses. Return the point at infinity.
+ return (0, 1, 0)
+ # p1 == p2. The formulas below fail when the two points are equal.
+ return self.double(p1)
+ h = u2 - u1
+ r = s2 - s1
+ h_2 = (h**2) % self.p
+ h_3 = (h_2 * h) % self.p
+ u1_h_2 = (u1 * h_2) % self.p
+ x3 = (r**2 - h_3 - 2*u1_h_2) % self.p
+ y3 = (r*(u1_h_2 - x3) - s1*h_3) % self.p
+ z3 = (h*z1*z2) % self.p
+ return (x3, y3, z3)
+
+ def mul(self, ps):
+ """Compute a (multi) point multiplication
+
+ ps is a list of (Jacobian tuple, scalar) pairs.
+ """
+ r = (0, 1, 0)
+ for i in range(255, -1, -1):
+ r = self.double(r)
+ for (p, n) in ps:
+ if ((n >> i) & 1):
+ r = self.add(r, p)
+ return r
+
+SECP256K1 = EllipticCurve(2**256 - 2**32 - 977, 0, 7)
+SECP256K1_G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, 1)
SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2
-# Thx to Sam Devlin for the ctypes magic 64-bit fix.
-def _check_result(val, func, args):
- if val == 0:
- raise ValueError
- else:
- return ctypes.c_void_p (val)
-
-ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
-ssl.EC_KEY_new_by_curve_name.errcheck = _check_result
-
-class CECKey():
- """Wrapper around OpenSSL's EC_KEY"""
-
- POINT_CONVERSION_COMPRESSED = 2
- POINT_CONVERSION_UNCOMPRESSED = 4
+class ECPubKey():
+ """A secp256k1 public key"""
def __init__(self):
- self.k = ssl.EC_KEY_new_by_curve_name(NID_secp256k1)
-
- def __del__(self):
- if ssl:
- ssl.EC_KEY_free(self.k)
- self.k = None
-
- def set_secretbytes(self, secret):
- priv_key = ssl.BN_bin2bn(secret, 32, ssl.BN_new())
- group = ssl.EC_KEY_get0_group(self.k)
- pub_key = ssl.EC_POINT_new(group)
- ctx = ssl.BN_CTX_new()
- if not ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx):
- raise ValueError("Could not derive public key from the supplied secret.")
- ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx)
- ssl.EC_KEY_set_private_key(self.k, priv_key)
- ssl.EC_KEY_set_public_key(self.k, pub_key)
- ssl.EC_POINT_free(pub_key)
- ssl.BN_CTX_free(ctx)
- return self.k
-
- def set_privkey(self, key):
- self.mb = ctypes.create_string_buffer(key)
- return ssl.d2i_ECPrivateKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key))
-
- def set_pubkey(self, key):
- self.mb = ctypes.create_string_buffer(key)
- return ssl.o2i_ECPublicKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key))
-
- def get_privkey(self):
- size = ssl.i2d_ECPrivateKey(self.k, 0)
- mb_pri = ctypes.create_string_buffer(size)
- ssl.i2d_ECPrivateKey(self.k, ctypes.byref(ctypes.pointer(mb_pri)))
- return mb_pri.raw
-
- def get_pubkey(self):
- size = ssl.i2o_ECPublicKey(self.k, 0)
- mb = ctypes.create_string_buffer(size)
- ssl.i2o_ECPublicKey(self.k, ctypes.byref(ctypes.pointer(mb)))
- return mb.raw
-
- def get_raw_ecdh_key(self, other_pubkey):
- ecdh_keybuffer = ctypes.create_string_buffer(32)
- r = ssl.ECDH_compute_key(ctypes.pointer(ecdh_keybuffer), 32,
- ssl.EC_KEY_get0_public_key(other_pubkey.k),
- self.k, 0)
- if r != 32:
- raise Exception('CKey.get_ecdh_key(): ECDH_compute_key() failed')
- return ecdh_keybuffer.raw
-
- def get_ecdh_key(self, other_pubkey, kdf=lambda k: hashlib.sha256(k).digest()):
- # FIXME: be warned it's not clear what the kdf should be as a default
- r = self.get_raw_ecdh_key(other_pubkey)
- return kdf(r)
-
- def sign(self, hash, low_s = True):
- # FIXME: need unit tests for below cases
- if not isinstance(hash, bytes):
- raise TypeError('Hash must be bytes instance; got %r' % hash.__class__)
- if len(hash) != 32:
- raise ValueError('Hash must be exactly 32 bytes long')
-
- sig_size0 = ctypes.c_uint32()
- sig_size0.value = ssl.ECDSA_size(self.k)
- mb_sig = ctypes.create_string_buffer(sig_size0.value)
- result = ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k)
- assert 1 == result
- assert mb_sig.raw[0] == 0x30
- assert mb_sig.raw[1] == sig_size0.value - 2
- total_size = mb_sig.raw[1]
- assert mb_sig.raw[2] == 2
- r_size = mb_sig.raw[3]
- assert mb_sig.raw[4 + r_size] == 2
- s_size = mb_sig.raw[5 + r_size]
- s_value = int.from_bytes(mb_sig.raw[6+r_size:6+r_size+s_size], byteorder='big')
- if (not low_s) or s_value <= SECP256K1_ORDER_HALF:
- return mb_sig.raw[:sig_size0.value]
- else:
- low_s_value = SECP256K1_ORDER - s_value
- low_s_bytes = (low_s_value).to_bytes(33, byteorder='big')
- while len(low_s_bytes) > 1 and low_s_bytes[0] == 0 and low_s_bytes[1] < 0x80:
- low_s_bytes = low_s_bytes[1:]
- new_s_size = len(low_s_bytes)
- new_total_size_byte = (total_size + new_s_size - s_size).to_bytes(1,byteorder='big')
- new_s_size_byte = (new_s_size).to_bytes(1,byteorder='big')
- return b'\x30' + new_total_size_byte + mb_sig.raw[2:5+r_size] + new_s_size_byte + low_s_bytes
-
- def verify(self, hash, sig):
- """Verify a DER signature"""
- return ssl.ECDSA_verify(0, hash, len(hash), sig, len(sig), self.k) == 1
-
- def set_compressed(self, compressed):
- if compressed:
- form = self.POINT_CONVERSION_COMPRESSED
+ """Construct an uninitialized public key"""
+ self.valid = False
+
+ def set(self, data):
+ """Construct a public key from a serialization in compressed or uncompressed format"""
+ if (len(data) == 65 and data[0] == 0x04):
+ p = (int.from_bytes(data[1:33], 'big'), int.from_bytes(data[33:65], 'big'), 1)
+ self.valid = SECP256K1.on_curve(p)
+ if self.valid:
+ self.p = p
+ self.compressed = False
+ elif (len(data) == 33 and (data[0] == 0x02 or data[0] == 0x03)):
+ x = int.from_bytes(data[1:33], 'big')
+ if SECP256K1.is_x_coord(x):
+ p = SECP256K1.lift_x(x)
+ # if the oddness of the y co-ord isn't correct, find the other
+ # valid y
+ if (p[1] & 1) != (data[0] & 1):
+ p = SECP256K1.negate(p)
+ self.p = p
+ self.valid = True
+ self.compressed = True
+ else:
+ self.valid = False
else:
- form = self.POINT_CONVERSION_UNCOMPRESSED
- ssl.EC_KEY_set_conv_form(self.k, form)
-
+ self.valid = False
-class CPubKey(bytes):
- """An encapsulated public key
-
- Attributes:
+ @property
+ def is_compressed(self):
+ return self.compressed
- is_valid - Corresponds to CPubKey.IsValid()
- is_fullyvalid - Corresponds to CPubKey.IsFullyValid()
- is_compressed - Corresponds to CPubKey.IsCompressed()
- """
+ @property
+ def is_valid(self):
+ return self.valid
+
+ def get_bytes(self):
+ assert(self.valid)
+ p = SECP256K1.affine(self.p)
+ if p is None:
+ return None
+ if self.compressed:
+ return bytes([0x02 + (p[1] & 1)]) + p[0].to_bytes(32, 'big')
+ else:
+ return bytes([0x04]) + p[0].to_bytes(32, 'big') + p[1].to_bytes(32, 'big')
+
+ def verify_ecdsa(self, sig, msg, low_s=True):
+ """Verify a strictly DER-encoded ECDSA signature against this pubkey.
+
+ See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
+ ECDSA verifier algorithm"""
+ assert(self.valid)
+
+ # Extract r and s from the DER formatted signature. Return false for
+ # any DER encoding errors.
+ if (sig[1] + 2 != len(sig)):
+ return False
+ if (len(sig) < 4):
+ return False
+ if (sig[0] != 0x30):
+ return False
+ if (sig[2] != 0x02):
+ return False
+ rlen = sig[3]
+ if (len(sig) < 6 + rlen):
+ return False
+ if rlen < 1 or rlen > 33:
+ return False
+ if sig[4] >= 0x80:
+ return False
+ if (rlen > 1 and (sig[4] == 0) and not (sig[5] & 0x80)):
+ return False
+ r = int.from_bytes(sig[4:4+rlen], 'big')
+ if (sig[4+rlen] != 0x02):
+ return False
+ slen = sig[5+rlen]
+ if slen < 1 or slen > 33:
+ return False
+ if (len(sig) != 6 + rlen + slen):
+ return False
+ if sig[6+rlen] >= 0x80:
+ return False
+ if (slen > 1 and (sig[6+rlen] == 0) and not (sig[7+rlen] & 0x80)):
+ return False
+ s = int.from_bytes(sig[6+rlen:6+rlen+slen], 'big')
+
+ # Verify that r and s are within the group order
+ if r < 1 or s < 1 or r >= SECP256K1_ORDER or s >= SECP256K1_ORDER:
+ return False
+ if low_s and s >= SECP256K1_ORDER_HALF:
+ return False
+ z = int.from_bytes(msg, 'big')
+
+ # Run verifier algorithm on r, s
+ w = modinv(s, SECP256K1_ORDER)
+ u1 = z*w % SECP256K1_ORDER
+ u2 = r*w % SECP256K1_ORDER
+ R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, u1), (self.p, u2)]))
+ if R is None or R[0] != r:
+ return False
+ return True
+
+class ECKey():
+ """A secp256k1 private key"""
- def __new__(cls, buf, _cec_key=None):
- self = super(CPubKey, cls).__new__(cls, buf)
- if _cec_key is None:
- _cec_key = CECKey()
- self._cec_key = _cec_key
- self.is_fullyvalid = _cec_key.set_pubkey(self) != 0
- return self
+ def __init__(self):
+ self.valid = False
+
+ def set(self, secret, compressed):
+ """Construct a private key object with given 32-byte secret and compressed flag."""
+ assert(len(secret) == 32)
+ secret = int.from_bytes(secret, 'big')
+ self.valid = (secret > 0 and secret < SECP256K1_ORDER)
+ if self.valid:
+ self.secret = secret
+ self.compressed = compressed
+
+ def generate(self, compressed=True):
+ """Generate a random private key (compressed or uncompressed)."""
+ self.set(random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big'), compressed)
+
+ def get_bytes(self):
+ """Retrieve the 32-byte representation of this key."""
+ assert(self.valid)
+ return self.secret.to_bytes(32, 'big')
@property
def is_valid(self):
- return len(self) > 0
+ return self.valid
@property
def is_compressed(self):
- return len(self) == 33
-
- def verify(self, hash, sig):
- return self._cec_key.verify(hash, sig)
-
- def __str__(self):
- return repr(self)
-
- def __repr__(self):
- return '%s(%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__())
+ return self.compressed
+ def get_pubkey(self):
+ """Compute an ECPubKey object for this secret key."""
+ assert(self.valid)
+ ret = ECPubKey()
+ p = SECP256K1.mul([(SECP256K1_G, self.secret)])
+ ret.p = p
+ ret.valid = True
+ ret.compressed = self.compressed
+ return ret
+
+ def sign_ecdsa(self, msg, low_s=True):
+ """Construct a DER-encoded ECDSA signature with this key.
+
+ See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
+ ECDSA signer algorithm."""
+ assert(self.valid)
+ z = int.from_bytes(msg, 'big')
+ # Note: no RFC6979, but a simple random nonce (some tests rely on distinct transactions for the same operation)
+ k = random.randrange(1, SECP256K1_ORDER)
+ R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, k)]))
+ r = R[0] % SECP256K1_ORDER
+ s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER
+ if low_s and s > SECP256K1_ORDER_HALF:
+ s = SECP256K1_ORDER - s
+ # Represent in DER format. The byte representations of r and s have
+ # length rounded up (255 bits becomes 32 bytes and 256 bits becomes 33
+ # bytes).
+ rb = r.to_bytes((r.bit_length() + 8) // 8, 'big')
+ sb = s.to_bytes((s.bit_length() + 8) // 8, 'big')
+ return b'\x30' + bytes([4 + len(rb) + len(sb), 2, len(rb)]) + rb + bytes([2, len(sb)]) + sb
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 356a45d6d0..e454ed5987 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# Copyright (c) 2010 ArtForz -- public domain half-a-node
# Copyright (c) 2012 Jeff Garzik
-# Copyright (c) 2010-2018 The Bitcoin Core developers
+# Copyright (c) 2010-2019 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 structures
@@ -28,7 +28,7 @@ import struct
import time
from test_framework.siphash import siphash256
-from test_framework.util import hex_str_to_bytes, bytes_to_hex_str
+from test_framework.util import hex_str_to_bytes, assert_equal
MIN_VERSION_SUPPORTED = 60001
MY_VERSION = 70014 # past bip-31 for ping/pong
@@ -181,7 +181,7 @@ def FromHex(obj, hex_string):
# Convert a binary-serializable object to hex (eg for submission via RPC)
def ToHex(obj):
- return bytes_to_hex_str(obj.serialize())
+ return obj.serialize().hex()
# Objects that map to bitcoind objects, which can be serialized/deserialized
@@ -319,7 +319,7 @@ class CTxIn:
def __repr__(self):
return "CTxIn(prevout=%s scriptSig=%s nSequence=%i)" \
- % (repr(self.prevout), bytes_to_hex_str(self.scriptSig),
+ % (repr(self.prevout), self.scriptSig.hex(),
self.nSequence)
@@ -343,7 +343,7 @@ class CTxOut:
def __repr__(self):
return "CTxOut(nValue=%i.%08i scriptPubKey=%s)" \
% (self.nValue // COIN, self.nValue % COIN,
- bytes_to_hex_str(self.scriptPubKey))
+ self.scriptPubKey.hex())
class CScriptWitness:
@@ -355,7 +355,7 @@ class CScriptWitness:
def __repr__(self):
return "CScriptWitness(%s)" % \
- (",".join([bytes_to_hex_str(x) for x in self.stack]))
+ (",".join([x.hex() for x in self.stack]))
def is_null(self):
if self.stack:
@@ -591,6 +591,8 @@ class CBlockHeader:
% (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
time.ctime(self.nTime), self.nBits, self.nNonce)
+BLOCK_HEADER_SIZE = len(CBlockHeader().serialize())
+assert_equal(BLOCK_HEADER_SIZE, 80)
class CBlock(CBlockHeader):
__slots__ = ("vtx",)
@@ -603,7 +605,7 @@ class CBlock(CBlockHeader):
super(CBlock, self).deserialize(f)
self.vtx = deser_vector(f, CTransaction)
- def serialize(self, with_witness=False):
+ def serialize(self, with_witness=True):
r = b""
r += super(CBlock, self).serialize()
if with_witness:
@@ -1128,7 +1130,7 @@ class msg_block:
self.block.deserialize(f)
def serialize(self):
- return self.block.serialize(with_witness=False)
+ return self.block.serialize()
def __repr__(self):
return "msg_block(block=%s)" % (repr(self.block))
@@ -1150,11 +1152,10 @@ class msg_generic:
return "msg_generic()"
-class msg_witness_block(msg_block):
+class msg_no_witness_block(msg_block):
__slots__ = ()
def serialize(self):
- r = self.block.serialize(with_witness=True)
- return r
+ return self.block.serialize(with_witness=False)
class msg_getaddr:
@@ -1440,17 +1441,15 @@ class msg_blocktxn:
def serialize(self):
r = b""
- r += self.block_transactions.serialize(with_witness=False)
+ 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):
+class msg_no_witness_blocktxn(msg_blocktxn):
__slots__ = ()
def serialize(self):
- r = b""
- r += self.block_transactions.serialize(with_witness=True)
- return r
+ return self.block_transactions.serialize(with_witness=False)
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index ac7cc068bd..cc3a4cc72a 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# Copyright (c) 2010 ArtForz -- public domain half-a-node
# Copyright (c) 2012 Jeff Garzik
-# Copyright (c) 2010-2018 The Bitcoin Core developers
+# Copyright (c) 2010-2019 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 P2P network half-a-node.
@@ -171,7 +171,7 @@ class P2PConnection(asyncio.Protocol):
if len(self.recvbuf) < 4:
return
if self.recvbuf[:4] != self.magic_bytes:
- raise ValueError("got garbage %s" % repr(self.recvbuf))
+ raise ValueError("magic bytes mismatch: {} != {}".format(repr(self.magic_bytes), repr(self.recvbuf)))
if len(self.recvbuf) < 4 + 12 + 4 + 4:
return
command = self.recvbuf[4:4+12].split(b"\x00", 1)[0]
@@ -218,10 +218,7 @@ class P2PConnection(asyncio.Protocol):
def maybe_write():
if not self._transport:
return
- # Python <3.4.4 does not have is_closing, so we have to check for
- # its existence explicitly as long as Bitcoin Core supports all
- # Python 3.4 versions.
- if hasattr(self._transport, 'is_closing') and self._transport.is_closing():
+ if self._transport.is_closing():
return
self._transport.write(raw_message_bytes)
NetworkThread.network_event_loop.call_soon_threadsafe(maybe_write)
@@ -364,6 +361,14 @@ class P2PInterface(P2PConnection):
# Message receiving helper methods
+ def wait_for_tx(self, txid, timeout=60):
+ def test_function():
+ if not self.last_message.get('tx'):
+ return False
+ return self.last_message['tx'].tx.rehash() == txid
+
+ wait_until(test_function, timeout=timeout, lock=mininode_lock)
+
def wait_for_block(self, blockhash, timeout=60):
test_function = lambda: self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash
wait_until(test_function, timeout=timeout, lock=mininode_lock)
@@ -534,7 +539,7 @@ class P2PDataStore(P2PInterface):
for b in blocks:
self.send_message(msg_block(block=b))
else:
- self.send_message(msg_headers([CBlockHeader(blocks[-1])]))
+ self.send_message(msg_headers([CBlockHeader(block) for block in blocks]))
wait_until(lambda: blocks[-1].sha256 in self.getdata_requests, timeout=timeout, lock=mininode_lock)
if expect_disconnect:
diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py
index 45c182f630..c98424e8e2 100644
--- a/test/functional/test_framework/netutil.py
+++ b/test/functional/test_framework/netutil.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Linux network utilities.
@@ -12,7 +12,7 @@ import socket
import struct
import array
import os
-from binascii import unhexlify, hexlify
+from binascii import unhexlify
# STATE_ESTABLISHED = '01'
# STATE_SYN_SENT = '02'
@@ -129,17 +129,17 @@ def addr_to_hex(addr):
if i == 0 or i == (len(addr)-1): # skip empty component at beginning or end
continue
x += 1 # :: skips to suffix
- assert(x < 2)
+ assert x < 2
else: # two bytes per component
val = int(comp, 16)
sub[x].append(val >> 8)
sub[x].append(val & 0xff)
nullbytes = 16 - len(sub[0]) - len(sub[1])
- assert((x == 0 and nullbytes == 0) or (x == 1 and nullbytes > 0))
+ assert (x == 0 and nullbytes == 0) or (x == 1 and nullbytes > 0)
addr = sub[0] + ([0] * nullbytes) + sub[1]
else:
raise ValueError('Could not parse address %s' % addr)
- return hexlify(bytearray(addr)).decode('ascii')
+ return bytearray(addr).hex()
def test_ipv6_local():
'''
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index 012c80a1be..384062b808 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Functionality to build scripts, as well as SignatureHash().
@@ -9,7 +9,6 @@ This file is modified from python-bitcoinlib.
from .messages import CTransaction, CTxOut, sha256, hash256, uint256_from_str, ser_uint256, ser_string
-from binascii import hexlify
import hashlib
import struct
@@ -450,10 +449,6 @@ class CScript(bytes):
# join makes no sense for a CScript()
raise NotImplementedError
- # Python 3.4 compatibility
- def hex(self):
- return hexlify(self).decode('ascii')
-
def __new__(cls, value=b''):
if isinstance(value, bytes) or isinstance(value, bytearray):
return super(CScript, cls).__new__(cls, value)
@@ -545,7 +540,7 @@ class CScript(bytes):
def __repr__(self):
def _repr(o):
if isinstance(o, bytes):
- return "x('%s')" % hexlify(o).decode('ascii')
+ return "x('%s')" % o.hex()
else:
return repr(o)
diff --git a/test/functional/test_framework/socks5.py b/test/functional/test_framework/socks5.py
index a21c864e75..799b1c74b8 100644
--- a/test/functional/test_framework/socks5.py
+++ b/test/functional/test_framework/socks5.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Dummy Socks5 server for testing."""
@@ -144,7 +144,7 @@ class Socks5Server():
thread.start()
def start(self):
- assert(not self.running)
+ assert not self.running
self.running = True
self.thread = threading.Thread(None, self.run)
self.thread.daemon = True
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 6dcaff0696..aa674f9ebe 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Base class for RPC testing."""
@@ -10,6 +10,7 @@ import logging
import argparse
import os
import pdb
+import random
import shutil
import sys
import tempfile
@@ -28,12 +29,11 @@ from .util import (
disconnect_nodes,
get_datadir_path,
initialize_datadir,
- p2p_port,
- set_node_times,
sync_blocks,
sync_mempools,
)
+
class TestStatus(Enum):
PASSED = 1
FAILED = 2
@@ -94,7 +94,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.setup_clean_chain = False
self.nodes = []
self.network_thread = None
- self.mocktime = 0
self.rpc_timeout = 60 # Wait for up to 60 seconds for the RPC server to respond
self.supports_cli = False
self.bind_to_localhost_only = True
@@ -128,6 +127,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
help="Attach a python debugger if test fails")
parser.add_argument("--usecli", dest="usecli", default=False, action="store_true",
help="use bitcoin-cli instead of RPC for all commands")
+ parser.add_argument("--perf", dest="perf", default=False, action="store_true",
+ help="profile running nodes with perf for the duration of the test")
+ parser.add_argument("--randomseed", type=int,
+ help="set a random seed for deterministically reproducing a previous test run")
self.add_options(parser)
self.options = parser.parse_args()
@@ -157,6 +160,22 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.options.tmpdir = tempfile.mkdtemp(prefix=TMPDIR_PREFIX)
self._start_logging()
+ # Seed the PRNG. Note that test runs are reproducible if and only if
+ # a single thread accesses the PRNG. For more information, see
+ # https://docs.python.org/3/library/random.html#notes-on-reproducibility.
+ # The network thread shouldn't access random. If we need to change the
+ # network thread to access randomness, it should instantiate its own
+ # random.Random object.
+ seed = self.options.randomseed
+
+ if seed is None:
+ seed = random.randrange(sys.maxsize)
+ else:
+ self.log.debug("User supplied random seed {}".format(seed))
+
+ random.seed(seed)
+ self.log.debug("PRNG seed is: {}".format(seed))
+
self.log.debug('Setting up network thread')
self.network_thread = NetworkThread()
self.network_thread.start()
@@ -202,11 +221,20 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
node.cleanup_on_exit = False
self.log.info("Note: bitcoinds were not stopped and may still be running")
- if not self.options.nocleanup and not self.options.noshutdown and success != TestStatus.FAILED:
+ should_clean_up = (
+ not self.options.nocleanup and
+ not self.options.noshutdown and
+ success != TestStatus.FAILED and
+ not self.options.perf
+ )
+ if should_clean_up:
self.log.info("Cleaning up {} on exit".format(self.options.tmpdir))
cleanup_tree_on_exit = True
+ elif self.options.perf:
+ self.log.warning("Not cleaning up dir {} due to perf data".format(self.options.tmpdir))
+ cleanup_tree_on_exit = False
else:
- self.log.warning("Not cleaning up dir %s" % self.options.tmpdir)
+ self.log.warning("Not cleaning up dir {}".format(self.options.tmpdir))
cleanup_tree_on_exit = False
if success == TestStatus.PASSED:
@@ -264,6 +292,19 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.add_nodes(self.num_nodes, extra_args)
self.start_nodes()
self.import_deterministic_coinbase_privkeys()
+ if not self.setup_clean_chain:
+ for n in self.nodes:
+ assert_equal(n.getblockchaininfo()["blocks"], 199)
+ # To ensure that all nodes are out of IBD, the most recent block
+ # must have a timestamp not too old (see IsInitialBlockDownload()).
+ self.log.debug('Generate a block with current time')
+ block_hash = self.nodes[0].generate(1)[0]
+ block = self.nodes[0].getblock(blockhash=block_hash, verbosity=0)
+ for n in self.nodes:
+ n.submitblock(block)
+ chain_info = n.getblockchaininfo()
+ assert_equal(chain_info["blocks"], 200)
+ assert_equal(chain_info["initialblockdownload"], False)
def import_deterministic_coinbase_privkeys(self):
for n in self.nodes:
@@ -305,11 +346,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
timewait=self.rpc_timeout,
bitcoind=binary[i],
bitcoin_cli=self.options.bitcoincli,
- mocktime=self.mocktime,
coverage_dir=self.options.coveragedir,
+ cwd=self.options.tmpdir,
extra_conf=extra_confs[i],
extra_args=extra_args[i],
use_cli=self.options.usecli,
+ start_perf=self.options.perf,
))
def start_node(self, i, *args, **kwargs):
@@ -372,7 +414,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
"""
disconnect_nodes(self.nodes[1], 2)
disconnect_nodes(self.nodes[2], 1)
- self.sync_all([self.nodes[:2], self.nodes[2:]])
+ self.sync_all(self.nodes[:2])
+ self.sync_all(self.nodes[2:])
def join_network(self):
"""
@@ -381,13 +424,15 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
connect_nodes_bi(self.nodes, 1, 2)
self.sync_all()
- def sync_all(self, node_groups=None):
- if not node_groups:
- node_groups = [self.nodes]
+ def sync_blocks(self, nodes=None, **kwargs):
+ sync_blocks(nodes or self.nodes, **kwargs)
- for group in node_groups:
- sync_blocks(group)
- sync_mempools(group)
+ def sync_mempools(self, nodes=None, **kwargs):
+ sync_mempools(nodes or self.nodes, **kwargs)
+
+ def sync_all(self, nodes=None, **kwargs):
+ self.sync_blocks(nodes, **kwargs)
+ self.sync_mempools(nodes, **kwargs)
# Private helper methods. These should not be accessed by the subclass test scripts.
@@ -422,89 +467,65 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def _initialize_chain(self):
"""Initialize a pre-mined blockchain for use by the test.
- Create a cache of a 200-block-long chain (with wallet) for MAX_NODES
+ Create a cache of a 199-block-long chain
Afterward, create num_nodes copies from the cache."""
+ CACHE_NODE_ID = 0 # Use node 0 to create the cache for all other nodes
+ cache_node_dir = get_datadir_path(self.options.cachedir, CACHE_NODE_ID)
assert self.num_nodes <= MAX_NODES
- create_cache = False
- for i in range(MAX_NODES):
- if not os.path.isdir(get_datadir_path(self.options.cachedir, i)):
- create_cache = True
- break
-
- if create_cache:
- self.log.debug("Creating data directories from cached datadir")
-
- # find and delete old cache directories if any exist
- for i in range(MAX_NODES):
- if os.path.isdir(get_datadir_path(self.options.cachedir, i)):
- shutil.rmtree(get_datadir_path(self.options.cachedir, i))
-
- # Create cache directories, run bitcoinds:
- for i in range(MAX_NODES):
- datadir = initialize_datadir(self.options.cachedir, i)
- args = [self.options.bitcoind, "-datadir=" + datadir, '-disablewallet']
- if i > 0:
- args.append("-connect=127.0.0.1:" + str(p2p_port(0)))
- self.nodes.append(TestNode(
- i,
- get_datadir_path(self.options.cachedir, i),
+
+ if not os.path.isdir(cache_node_dir):
+ self.log.debug("Creating cache directory {}".format(cache_node_dir))
+
+ initialize_datadir(self.options.cachedir, CACHE_NODE_ID)
+ self.nodes.append(
+ TestNode(
+ CACHE_NODE_ID,
+ cache_node_dir,
extra_conf=["bind=127.0.0.1"],
- extra_args=[],
+ extra_args=['-disablewallet'],
rpchost=None,
timewait=self.rpc_timeout,
bitcoind=self.options.bitcoind,
bitcoin_cli=self.options.bitcoincli,
- mocktime=self.mocktime,
coverage_dir=None,
+ cwd=self.options.tmpdir,
))
- self.nodes[i].args = args
- self.start_node(i)
+ self.start_node(CACHE_NODE_ID)
# Wait for RPC connections to be ready
- for node in self.nodes:
- node.wait_for_rpc_connection()
+ self.nodes[CACHE_NODE_ID].wait_for_rpc_connection()
- # For backward compatibility of the python scripts with previous
- # versions of the cache, set mocktime to Jan 1,
- # 2014 + (201 * 10 * 60)"""
- self.mocktime = 1388534400 + (201 * 10 * 60)
-
- # Create a 200-block-long chain; each of the 4 first nodes
+ # Create a 199-block-long chain; each of the 4 first nodes
# gets 25 mature blocks and 25 immature.
- # Note: To preserve compatibility with older versions of
- # initialize_chain, only 4 nodes will generate coins.
- #
- # blocks are created with timestamps 10 minutes apart
- # starting from 2010 minutes in the past
- block_time = self.mocktime - (201 * 10 * 60)
- for i in range(2):
- for peer in range(4):
- for j in range(25):
- set_node_times(self.nodes, block_time)
- self.nodes[peer].generatetoaddress(1, self.nodes[peer].get_deterministic_priv_key().address)
- block_time += 10 * 60
- # Must sync before next peer starts generating blocks
- sync_blocks(self.nodes)
-
- # Shut them down, and clean up cache directories:
+ # The 4th node gets only 24 immature blocks so that the very last
+ # block in the cache does not age too much (have an old tip age).
+ # This is needed so that we are out of IBD when the test starts,
+ # see the tip age check in IsInitialBlockDownload().
+ for i in range(8):
+ self.nodes[CACHE_NODE_ID].generatetoaddress(
+ nblocks=25 if i != 7 else 24,
+ address=TestNode.PRIV_KEYS[i % 4].address,
+ )
+
+ assert_equal(self.nodes[CACHE_NODE_ID].getblockchaininfo()["blocks"], 199)
+
+ # Shut it down, and clean up cache directories:
self.stop_nodes()
self.nodes = []
- self.mocktime = 0
- def cache_path(n, *paths):
- return os.path.join(get_datadir_path(self.options.cachedir, n), "regtest", *paths)
+ def cache_path(*paths):
+ return os.path.join(cache_node_dir, "regtest", *paths)
- for i in range(MAX_NODES):
- os.rmdir(cache_path(i, 'wallets')) # Remove empty wallets dir
- for entry in os.listdir(cache_path(i)):
- if entry not in ['chainstate', 'blocks']:
- os.remove(cache_path(i, entry))
+ os.rmdir(cache_path('wallets')) # Remove empty wallets dir
+ for entry in os.listdir(cache_path()):
+ if entry not in ['chainstate', 'blocks']: # Only keep chainstate and blocks folder
+ os.remove(cache_path(entry))
for i in range(self.num_nodes):
- from_dir = get_datadir_path(self.options.cachedir, i)
+ self.log.debug("Copy cache directory {} to node {}".format(cache_node_dir, i))
to_dir = get_datadir_path(self.options.tmpdir, i)
- shutil.copytree(from_dir, to_dir)
+ shutil.copytree(cache_node_dir, to_dir)
initialize_datadir(self.options.tmpdir, i) # Overwrite port/rpcport in bitcoin.conf
def _initialize_chain_clean(self):
@@ -539,21 +560,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def is_cli_compiled(self):
"""Checks whether bitcoin-cli was compiled."""
- config = configparser.ConfigParser()
- config.read_file(open(self.options.configfile))
-
- return config["components"].getboolean("ENABLE_CLI")
+ return self.config["components"].getboolean("ENABLE_CLI")
def is_wallet_compiled(self):
"""Checks whether the wallet module was compiled."""
- config = configparser.ConfigParser()
- config.read_file(open(self.options.configfile))
-
- return config["components"].getboolean("ENABLE_WALLET")
+ return self.config["components"].getboolean("ENABLE_WALLET")
def is_zmq_compiled(self):
"""Checks whether the zmq module was compiled."""
- config = configparser.ConfigParser()
- config.read_file(open(self.options.configfile))
-
- return config["components"].getboolean("ENABLE_ZMQ")
+ return self.config["components"].getboolean("ENABLE_ZMQ")
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 6745409964..3311377090 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Class for bitcoind node under test"""
@@ -18,9 +18,12 @@ import tempfile
import time
import urllib.parse
import collections
+import shlex
+import sys
from .authproxy import JSONRPCException
from .util import (
+ MAX_NODES,
append_config,
delete_cookie_file,
get_rpc_proxy,
@@ -29,9 +32,6 @@ from .util import (
p2p_port,
)
-# For Python 3.4 compatibility
-JSONDecodeError = getattr(json, "JSONDecodeError", ValueError)
-
BITCOIND_PROC_WAIT_TIMEOUT = 60
@@ -59,7 +59,13 @@ class TestNode():
To make things easier for the test writer, any unrecognised messages will
be dispatched to the RPC connection."""
- def __init__(self, i, datadir, *, rpchost, timewait, bitcoind, bitcoin_cli, mocktime, coverage_dir, extra_conf=None, extra_args=None, use_cli=False):
+ def __init__(self, i, datadir, *, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False):
+ """
+ Kwargs:
+ start_perf (bool): If True, begin profiling the node with `perf` as soon as
+ the node starts.
+ """
+
self.index = i
self.datadir = datadir
self.stdout_dir = os.path.join(self.datadir, "stdout")
@@ -68,25 +74,30 @@ class TestNode():
self.rpc_timeout = timewait
self.binary = bitcoind
self.coverage_dir = coverage_dir
+ self.cwd = cwd
if extra_conf is not None:
append_config(datadir, extra_conf)
# Most callers will just need to add extra args to the standard list below.
# For those callers that need more flexibility, they can just set the args property directly.
# Note that common args are set in the config file (see initialize_datadir)
self.extra_args = extra_args
+ # Configuration for logging is set as command-line args rather than in the bitcoin.conf file.
+ # This means that starting a bitcoind using the temp dir to debug a failed test won't
+ # spam debug.log.
self.args = [
self.binary,
"-datadir=" + self.datadir,
"-logtimemicros",
+ "-logthreadnames",
"-debug",
"-debugexclude=libevent",
"-debugexclude=leveldb",
- "-mocktime=" + str(mocktime),
- "-uacomment=testnode%d" % i
+ "-uacomment=testnode%d" % i,
]
self.cli = TestNodeCLI(bitcoin_cli, self.datadir)
self.use_cli = use_cli
+ self.start_perf = start_perf
self.running = False
self.process = None
@@ -95,13 +106,13 @@ class TestNode():
self.url = None
self.log = logging.getLogger('TestFramework.node%d' % i)
self.cleanup_on_exit = True # Whether to kill the node when this object goes away
+ # Cache perf subprocesses here by their data output filename.
+ self.perf_subprocesses = {}
self.p2ps = []
- def get_deterministic_priv_key(self):
- """Return a deterministic priv key in base58, that only depends on the node's index"""
- AddressKeyPair = collections.namedtuple('AddressKeyPair', ['address', 'key'])
- PRIV_KEYS = [
+ AddressKeyPair = collections.namedtuple('AddressKeyPair', ['address', 'key'])
+ PRIV_KEYS = [
# address , privkey
AddressKeyPair('mjTkW3DjgyZck4KbiRusZsqTgaYTxdSz6z', 'cVpF924EspNh8KjYsfhgY96mmxvT6DgdWiTYMtMjuM74hJaU5psW'),
AddressKeyPair('msX6jQXvxiNhx3Q62PKeLPrhrqZQdSimTg', 'cUxsWyKyZ9MAQTaAhUQWJmBbSvHMwSmuv59KgxQV7oZQU3PXN3KE'),
@@ -112,8 +123,15 @@ class TestNode():
AddressKeyPair('myzuPxRwsf3vvGzEuzPfK9Nf2RfwauwYe6', 'cQMpDLJwA8DBe9NcQbdoSb1BhmFxVjWD5gRyrLZCtpuF9Zi3a9RK'),
AddressKeyPair('mumwTaMtbxEPUswmLBBN3vM9oGRtGBrys8', 'cSXmRKXVcoouhNNVpcNKFfxsTsToY5pvB9DVsFksF1ENunTzRKsy'),
AddressKeyPair('mpV7aGShMkJCZgbW7F6iZgrvuPHjZjH9qg', 'cSoXt6tm3pqy43UMabY6eUTmR3eSUYFtB2iNQDGgb3VUnRsQys2k'),
- ]
- return PRIV_KEYS[self.index]
+ AddressKeyPair('mq4fBNdckGtvY2mijd9am7DRsbRB4KjUkf', 'cN55daf1HotwBAgAKWVgDcoppmUNDtQSfb7XLutTLeAgVc3u8hik'),
+ AddressKeyPair('mpFAHDjX7KregM3rVotdXzQmkbwtbQEnZ6', 'cT7qK7g1wkYEMvKowd2ZrX1E5f6JQ7TM246UfqbCiyF7kZhorpX3'),
+ AddressKeyPair('mzRe8QZMfGi58KyWCse2exxEFry2sfF2Y7', 'cPiRWE8KMjTRxH1MWkPerhfoHFn5iHPWVK5aPqjW8NxmdwenFinJ'),
+ ]
+
+ def get_deterministic_priv_key(self):
+ """Return a deterministic priv key in base58, that only depends on the node's index"""
+ assert len(self.PRIV_KEYS) == MAX_NODES
+ return self.PRIV_KEYS[self.index]
def get_mem_rss_kilobytes(self):
"""Get the memory usage (RSS) per `ps`.
@@ -160,7 +178,7 @@ class TestNode():
assert self.rpc_connected and self.rpc is not None, self._node_msg("Error: no RPC connection")
return getattr(self.rpc, name)
- def start(self, extra_args=None, *, stdout=None, stderr=None, **kwargs):
+ def start(self, extra_args=None, *, cwd=None, stdout=None, stderr=None, **kwargs):
"""Start the node."""
if extra_args is None:
extra_args = self.extra_args
@@ -173,6 +191,9 @@ class TestNode():
self.stderr = stderr
self.stdout = stdout
+ if cwd is None:
+ cwd = self.cwd
+
# Delete any existing cookie file -- if such a file exists (eg due to
# unclean shutdown), it will get overwritten anyway by bitcoind, and
# potentially interfere with our attempt to authenticate
@@ -181,11 +202,14 @@ class TestNode():
# add environment variable LIBC_FATAL_STDERR_=1 so that libc errors are written to stderr and not the terminal
subp_env = dict(os.environ, LIBC_FATAL_STDERR_="1")
- self.process = subprocess.Popen(self.args + extra_args, env=subp_env, stdout=stdout, stderr=stderr, **kwargs)
+ self.process = subprocess.Popen(self.args + extra_args, env=subp_env, stdout=stdout, stderr=stderr, cwd=cwd, **kwargs)
self.running = True
self.log.debug("bitcoind started, waiting for RPC to come up")
+ if self.start_perf:
+ self._start_perf()
+
def wait_for_rpc_connection(self):
"""Sets up an RPC connection to the bitcoind process. Returns False if unable to connect."""
# Poll at a rate of four times per second
@@ -195,12 +219,15 @@ class TestNode():
raise FailedToStartError(self._node_msg(
'bitcoind exited with status {} during initialization'.format(self.process.returncode)))
try:
- self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir)
- self.rpc.getblockcount()
+ rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir)
+ rpc.getblockcount()
# If the call to getblockcount() succeeds then the RPC connection is up
+ self.log.debug("RPC successfully started")
+ if self.use_cli:
+ return
+ self.rpc = rpc
self.rpc_connected = True
self.url = self.rpc.url
- self.log.debug("RPC successfully started")
return
except IOError as e:
if e.errno != errno.ECONNREFUSED: # Port not yet open?
@@ -238,6 +265,10 @@ class TestNode():
except http.client.CannotSendRequest:
self.log.exception("Unable to stop node.")
+ # If there are any running perf processes, stop them.
+ for profile_name in tuple(self.perf_subprocesses.keys()):
+ self._stop_perf(profile_name)
+
# Check that stderr is as expected
self.stderr.seek(0)
stderr = self.stderr.read().decode('utf-8').strip()
@@ -317,6 +348,84 @@ class TestNode():
increase_allowed * 100, before_memory_usage, after_memory_usage,
perc_increase_memory_usage * 100))
+ @contextlib.contextmanager
+ def profile_with_perf(self, profile_name):
+ """
+ Context manager that allows easy profiling of node activity using `perf`.
+
+ See `test/functional/README.md` for details on perf usage.
+
+ Args:
+ profile_name (str): This string will be appended to the
+ profile data filename generated by perf.
+ """
+ subp = self._start_perf(profile_name)
+
+ yield
+
+ if subp:
+ self._stop_perf(profile_name)
+
+ def _start_perf(self, profile_name=None):
+ """Start a perf process to profile this node.
+
+ Returns the subprocess running perf."""
+ subp = None
+
+ def test_success(cmd):
+ return subprocess.call(
+ # shell=True required for pipe use below
+ cmd, shell=True,
+ stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) == 0
+
+ if not sys.platform.startswith('linux'):
+ self.log.warning("Can't profile with perf; only available on Linux platforms")
+ return None
+
+ if not test_success('which perf'):
+ self.log.warning("Can't profile with perf; must install perf-tools")
+ return None
+
+ if not test_success('readelf -S {} | grep .debug_str'.format(shlex.quote(self.binary))):
+ self.log.warning(
+ "perf output won't be very useful without debug symbols compiled into bitcoind")
+
+ output_path = tempfile.NamedTemporaryFile(
+ dir=self.datadir,
+ prefix="{}.perf.data.".format(profile_name or 'test'),
+ delete=False,
+ ).name
+
+ cmd = [
+ 'perf', 'record',
+ '-g', # Record the callgraph.
+ '--call-graph', 'dwarf', # Compatibility for gcc's --fomit-frame-pointer.
+ '-F', '101', # Sampling frequency in Hz.
+ '-p', str(self.process.pid),
+ '-o', output_path,
+ ]
+ subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ self.perf_subprocesses[profile_name] = subp
+
+ return subp
+
+ def _stop_perf(self, profile_name):
+ """Stop (and pop) a perf subprocess."""
+ subp = self.perf_subprocesses.pop(profile_name)
+ output_path = subp.args[subp.args.index('-o') + 1]
+
+ subp.terminate()
+ subp.wait(timeout=10)
+
+ stderr = subp.stderr.read().decode()
+ if 'Consider tweaking /proc/sys/kernel/perf_event_paranoid' in stderr:
+ self.log.warning(
+ "perf couldn't collect data! Try "
+ "'sudo sysctl -w kernel.perf_event_paranoid=-1'")
+ else:
+ report_cmd = "perf report -i {}".format(output_path)
+ self.log.info("See perf output by running '{}'".format(report_cmd))
+
def assert_start_raises_init_error(self, extra_args=None, expected_msg=None, match=ErrorMatch.FULL_TEXT, *args, **kwargs):
"""Attempt to start the node and expect it to raise an error.
@@ -463,5 +572,5 @@ class TestNodeCLI():
raise subprocess.CalledProcessError(returncode, self.binary, output=cli_stderr)
try:
return json.loads(cli_stdout, parse_float=decimal.Decimal)
- except JSONDecodeError:
+ except json.JSONDecodeError:
return cli_stdout.rstrip("\n")
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index d0a78d8dfd..26215083fb 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -1,11 +1,11 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Helpful routines for regression testing."""
from base64 import b64encode
-from binascii import hexlify, unhexlify
+from binascii import unhexlify
from decimal import Decimal, ROUND_DOWN
import hashlib
import inspect
@@ -182,9 +182,6 @@ def check_json_precision():
def count_bytes(hex_string):
return len(bytearray.fromhex(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)
@@ -219,7 +216,7 @@ def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=N
time.sleep(0.05)
# Print the cause of the timeout
- predicate_source = inspect.getsourcelines(predicate)
+ predicate_source = "''''\n" + inspect.getsource(predicate) + "'''"
logger.error("wait_until() failed. Predicate: {}".format(predicate_source))
if attempt >= attempts:
raise AssertionError("Predicate {} not true after {} attempts".format(predicate_source, attempts))
@@ -231,7 +228,7 @@ def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=N
############################################
# The maximum number of nodes a single test can spawn
-MAX_NODES = 8
+MAX_NODES = 12
# Don't assign rpc or p2p ports lower than this
PORT_MIN = 11000
# The number of ports to "reserve" for p2p and rpc, each
@@ -267,7 +264,7 @@ def get_rpc_proxy(url, node_number, timeout=None, coveragedir=None):
return coverage.AuthServiceProxyWrapper(proxy, coverage_logfile)
def p2p_port(n):
- assert(n <= MAX_NODES)
+ assert n <= MAX_NODES
return PORT_MIN + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
def rpc_port(n):
@@ -326,12 +323,14 @@ def get_auth_cookie(datadir):
if line.startswith("rpcpassword="):
assert password is None # Ensure that there is only one rpcpassword line
password = line.split("=")[1].strip("\n")
- if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")) and os.access(os.path.join(datadir, "regtest", ".cookie"), os.R_OK):
+ try:
with open(os.path.join(datadir, "regtest", ".cookie"), 'r', encoding="ascii") as f:
userpass = f.read()
split_userpass = userpass.split(':')
user = split_userpass[0]
password = split_userpass[1]
+ except OSError:
+ pass
if user is None or password is None:
raise ValueError("No RPC credentials")
return user, password
@@ -410,12 +409,12 @@ def sync_mempools(rpc_connections, *, wait=1, timeout=60, flush_scheduler=True):
# Transaction/Block functions
#############################
-def find_output(node, txid, amount):
+def find_output(node, txid, amount, *, blockhash=None):
"""
Return index to output of txid with value amount
Raises exception if there is none.
"""
- txdata = node.getrawtransaction(txid, 1)
+ txdata = node.getrawtransaction(txid, 1, blockhash)
for i in range(len(txdata["vout"])):
if txdata["vout"][i]["value"] == amount:
return i
@@ -425,7 +424,7 @@ def gather_inputs(from_node, amount_needed, confirmations_required=1):
"""
Return a random set of unspent txouts that are enough to pay amount_needed
"""
- assert(confirmations_required >= 0)
+ assert confirmations_required >= 0
utxo = from_node.listunspent(confirmations_required)
random.shuffle(utxo)
inputs = []
@@ -470,7 +469,7 @@ def random_transaction(nodes, amount, min_fee, fee_increment, fee_variants):
rawtx = from_node.createrawtransaction(inputs, outputs)
signresult = from_node.signrawtransactionwithwallet(rawtx)
- txid = from_node.sendrawtransaction(signresult["hex"], True)
+ txid = from_node.sendrawtransaction(signresult["hex"], 0)
return (txid, signresult["hex"], fee)
@@ -503,7 +502,7 @@ def create_confirmed_utxos(fee, node, count):
node.generate(1)
utxos = node.listunspent()
- assert(len(utxos) >= count)
+ assert len(utxos) >= count
return utxos
# Create large OP_RETURN txouts that can be appended to a transaction
@@ -542,7 +541,7 @@ def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
newtx = newtx + txouts
newtx = newtx + rawtx[94:]
signresult = node.signrawtransactionwithwallet(newtx, None, "NONE")
- txid = node.sendrawtransaction(signresult["hex"], True)
+ txid = node.sendrawtransaction(signresult["hex"], 0)
txids.append(txid)
return txids
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 5c92370b85..80dced733e 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Run regression test suite.
@@ -7,8 +7,6 @@
This module calls down into individual test cases via subprocess. It will
forward all unrecognized arguments onto the individual test scripts.
-Functional tests are disabled on Windows by default. Use --force to run them anyway.
-
For a description of arguments recognized by test scripts, see
`test/functional/test_framework/test_framework.py:BitcoinTestFramework.main`.
@@ -68,6 +66,13 @@ if os.name != 'nt' or sys.getwindowsversion() >= (10, 0, 14393):
TEST_EXIT_PASSED = 0
TEST_EXIT_SKIPPED = 77
+EXTENDED_SCRIPTS = [
+ # These tests are not run by the travis build process.
+ # Longest test should go first, to favor running tests in parallel
+ 'feature_pruning.py',
+ 'feature_dbcrash.py',
+]
+
BASE_SCRIPTS = [
# Scripts that are run by the travis build process.
# Longest test should go first, to favor running tests in parallel
@@ -112,14 +117,15 @@ BASE_SCRIPTS = [
'wallet_txn_clone.py',
'wallet_txn_clone.py --segwit',
'rpc_getchaintips.py',
+ 'rpc_misc.py',
'interface_rest.py',
'mempool_spend_coinbase.py',
'mempool_reorg.py',
'mempool_persist.py',
'wallet_multiwallet.py',
'wallet_multiwallet.py --usecli',
- 'wallet_disableprivatekeys.py',
- 'wallet_disableprivatekeys.py --usecli',
+ 'wallet_createwallet.py',
+ 'wallet_createwallet.py --usecli',
'interface_http.py',
'interface_rpc.py',
'rpc_psbt.py',
@@ -135,6 +141,7 @@ BASE_SCRIPTS = [
'rpc_net.py',
'wallet_keypool.py',
'p2p_mempool.py',
+ 'p2p_blocksonly.py',
'mining_prioritisetransaction.py',
'p2p_invalid_locator.py',
'p2p_invalid_block.py',
@@ -145,6 +152,7 @@ BASE_SCRIPTS = [
'wallet_txn_doublespend.py',
'wallet_txn_clone.py --mineblock',
'feature_notifications.py',
+ 'rpc_getblockfilter.py',
'rpc_invalidateblock.py',
'feature_rbf.py',
'mempool_packages.py',
@@ -182,6 +190,8 @@ BASE_SCRIPTS = [
'feature_filelock.py',
'p2p_unrequested_blocks.py',
'feature_includeconf.py',
+ 'rpc_deriveaddresses.py',
+ 'rpc_deriveaddresses.py --usecli',
'rpc_scantxoutset.py',
'feature_logging.py',
'p2p_node_network_limited.py',
@@ -194,13 +204,6 @@ BASE_SCRIPTS = [
# Put them in a random line within the section that fits their approximate run-time
]
-EXTENDED_SCRIPTS = [
- # These tests are not run by the travis build process.
- # Longest test should go first, to favor running tests in parallel
- 'feature_pruning.py',
- 'feature_dbcrash.py',
-]
-
# Place EXTENDED_SCRIPTS first since it has the 3 longest running tests
ALL_SCRIPTS = EXTENDED_SCRIPTS + BASE_SCRIPTS
@@ -224,7 +227,6 @@ def main():
parser.add_argument('--ci', action='store_true', help='Run checks and code that are usually only enabled in a continuous integration environment')
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')
- parser.add_argument('--force', '-f', action='store_true', help='run tests even on platforms where they are disabled by default (e.g. windows).')
parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit')
parser.add_argument('--jobs', '-j', type=int, default=4, help='how many test scripts to run in parallel. Default=4.')
parser.add_argument('--keepcache', '-k', action='store_true', help='the default behavior is to flush the cache directory on startup. --keepcache retains the cache from the previous testrun.')
@@ -251,22 +253,12 @@ def main():
# Create base test directory
tmpdir = "%s/test_runner_₿_🏃_%s" % (args.tmpdirprefix, datetime.datetime.now().strftime("%Y%m%d_%H%M%S"))
- # If we fixed the command-line and filename encoding issue on Windows, these two lines could be removed
- if config["environment"]["EXEEXT"] == ".exe":
- tmpdir = "%s/test_runner_%s" % (args.tmpdirprefix, datetime.datetime.now().strftime("%Y%m%d_%H%M%S"))
-
os.makedirs(tmpdir)
logging.debug("Temporary test directory at %s" % tmpdir)
enable_bitcoind = config["components"].getboolean("ENABLE_BITCOIND")
- if config["environment"]["EXEEXT"] == ".exe" and not args.force:
- # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9
- # https://github.com/bitcoin/bitcoin/pull/5677#issuecomment-136646964
- print("Tests currently disabled on Windows by default. Use --force option to enable")
- sys.exit(0)
-
if not enable_bitcoind:
print("No functional tests to run.")
print("Rerun ./configure with --with-daemon and then make")
@@ -410,16 +402,18 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=
print_results(test_results, max_len_name, (int(time.time() - start_time)))
if coverage:
- coverage.report_rpc_coverage()
+ coverage_passed = coverage.report_rpc_coverage()
logging.debug("Cleaning up coverage data")
coverage.cleanup()
+ else:
+ coverage_passed = True
# Clear up the temp directory if all subdirectories are gone
if not os.listdir(tmpdir):
os.rmdir(tmpdir)
- all_passed = all(map(lambda test_result: test_result.was_successful, test_results))
+ all_passed = all(map(lambda test_result: test_result.was_successful, test_results)) and coverage_passed
# This will be a no-op unless failfast is True in which case there may be dangling
# processes which need to be killed.
@@ -488,6 +482,11 @@ class TestHandler:
log_stderr))
if not self.jobs:
raise IndexError('pop from empty list')
+
+ # Print remaining running jobs when all jobs have been started.
+ if not self.test_list:
+ print("Remaining jobs: [{}]".format(", ".join(j[0] for j in self.jobs)))
+
dot_count = 0
while True:
# Return first proc that finishes
@@ -616,8 +615,10 @@ class RPCCoverage():
if uncovered:
print("Uncovered RPC commands:")
print("".join((" - %s\n" % command) for command in sorted(uncovered)))
+ return False
else:
print("All RPC commands covered.")
+ return True
def cleanup(self):
return shutil.rmtree(self.dir)
diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py
index 0c3c247694..e86679bc31 100755
--- a/test/functional/wallet_abandonconflict.py
+++ b/test/functional/wallet_abandonconflict.py
@@ -18,28 +18,25 @@ from test_framework.util import (
assert_raises_rpc_error,
connect_nodes,
disconnect_nodes,
- sync_blocks,
- sync_mempools,
)
class AbandonConflictTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- # TODO: remove -txindex. Currently required for getrawtransaction call.
- self.extra_args = [["-minrelaytxfee=0.00001", "-txindex"], []]
+ self.extra_args = [["-minrelaytxfee=0.00001"], []]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self):
self.nodes[1].generate(100)
- sync_blocks(self.nodes)
+ self.sync_blocks()
balance = self.nodes[0].getbalance()
txA = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
txB = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
txC = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
- sync_mempools(self.nodes)
+ self.sync_mempools()
self.nodes[1].generate(1)
# Can not abandon non-wallet transaction
@@ -47,7 +44,7 @@ class AbandonConflictTest(BitcoinTestFramework):
# Can not abandon confirmed transaction
assert_raises_rpc_error(-5, 'Transaction not eligible for abandonment', lambda: self.nodes[0].abandontransaction(txid=txA))
- sync_blocks(self.nodes)
+ self.sync_blocks()
newbalance = self.nodes[0].getbalance()
assert balance - newbalance < Decimal("0.001") #no more than fees lost
balance = newbalance
@@ -164,7 +161,7 @@ class AbandonConflictTest(BitcoinTestFramework):
self.nodes[1].generate(1)
connect_nodes(self.nodes[0], 1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
# Verify that B and C's 10 BTC outputs are available for spending again because AB1 is now conflicted
newbalance = self.nodes[0].getbalance()
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index bafa556aad..a40613dfc7 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 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 that the wallet can send and receive using all combinations of address types.
@@ -54,13 +54,15 @@ from decimal import Decimal
import itertools
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.descriptors import (
+ descsum_create,
+ descsum_check,
+)
from test_framework.util import (
assert_equal,
assert_greater_than,
assert_raises_rpc_error,
connect_nodes_bi,
- sync_blocks,
- sync_mempools,
)
@@ -98,62 +100,62 @@ class AddressTypeTest(BitcoinTestFramework):
def test_address(self, node, address, multisig, typ):
"""Run sanity checks on an address."""
info = self.nodes[node].getaddressinfo(address)
- assert(self.nodes[node].validateaddress(address)['isvalid'])
+ assert self.nodes[node].validateaddress(address)['isvalid']
assert_equal(info.get('solvable'), True)
if not multisig and typ == 'legacy':
# P2PKH
- assert(not info['isscript'])
- assert(not info['iswitness'])
- assert('pubkey' in info)
+ assert not info['isscript']
+ assert not info['iswitness']
+ assert 'pubkey' in info
elif not multisig and typ == 'p2sh-segwit':
# P2SH-P2WPKH
- assert(info['isscript'])
- assert(not info['iswitness'])
+ assert info['isscript']
+ assert not info['iswitness']
assert_equal(info['script'], 'witness_v0_keyhash')
- assert('pubkey' in info)
+ assert 'pubkey' in info
elif not multisig and typ == 'bech32':
# P2WPKH
- assert(not info['isscript'])
- assert(info['iswitness'])
+ assert not info['isscript']
+ assert info['iswitness']
assert_equal(info['witness_version'], 0)
assert_equal(len(info['witness_program']), 40)
- assert('pubkey' in info)
+ assert 'pubkey' in info
elif typ == 'legacy':
# P2SH-multisig
- assert(info['isscript'])
+ assert info['isscript']
assert_equal(info['script'], 'multisig')
- assert(not info['iswitness'])
- assert('pubkeys' in info)
+ assert not info['iswitness']
+ assert 'pubkeys' in info
elif typ == 'p2sh-segwit':
# P2SH-P2WSH-multisig
- assert(info['isscript'])
+ assert info['isscript']
assert_equal(info['script'], 'witness_v0_scripthash')
- assert(not info['iswitness'])
- assert(info['embedded']['isscript'])
+ assert not info['iswitness']
+ assert info['embedded']['isscript']
assert_equal(info['embedded']['script'], 'multisig')
- assert(info['embedded']['iswitness'])
+ assert info['embedded']['iswitness']
assert_equal(info['embedded']['witness_version'], 0)
assert_equal(len(info['embedded']['witness_program']), 64)
- assert('pubkeys' in info['embedded'])
+ assert 'pubkeys' in info['embedded']
elif typ == 'bech32':
# P2WSH-multisig
- assert(info['isscript'])
+ assert info['isscript']
assert_equal(info['script'], 'multisig')
- assert(info['iswitness'])
+ assert info['iswitness']
assert_equal(info['witness_version'], 0)
assert_equal(len(info['witness_program']), 64)
- assert('pubkeys' in info)
+ assert 'pubkeys' in info
else:
# Unknown type
- assert(False)
+ assert False
def test_desc(self, node, address, multisig, typ, utxo):
"""Run sanity checks on a descriptor reported by getaddressinfo."""
info = self.nodes[node].getaddressinfo(address)
- assert('desc' in info)
+ assert 'desc' in info
assert_equal(info['desc'], utxo['desc'])
- assert(self.nodes[node].validateaddress(address)['isvalid'])
+ assert self.nodes[node].validateaddress(address)['isvalid']
# Use a ridiculously roundabout way to find the key origin info through
# the PSBT logic. However, this does test consistency between the PSBT reported
@@ -167,27 +169,34 @@ class AddressTypeTest(BitcoinTestFramework):
assert_equal(deriv['path'][0], 'm')
key_descs[deriv['pubkey']] = '[' + deriv['master_fingerprint'] + deriv['path'][1:] + ']' + deriv['pubkey']
+ # Verify the descriptor checksum against the Python implementation
+ assert descsum_check(info['desc'])
+ # Verify that stripping the checksum and recreating it using Python roundtrips
+ assert info['desc'] == descsum_create(info['desc'][:-9])
+ # Verify that stripping the checksum and feeding it to getdescriptorinfo roundtrips
+ assert info['desc'] == self.nodes[0].getdescriptorinfo(info['desc'][:-9])['descriptor']
+
if not multisig and typ == 'legacy':
# P2PKH
- assert_equal(info['desc'], "pkh(%s)" % key_descs[info['pubkey']])
+ assert_equal(info['desc'], descsum_create("pkh(%s)" % key_descs[info['pubkey']]))
elif not multisig and typ == 'p2sh-segwit':
# P2SH-P2WPKH
- assert_equal(info['desc'], "sh(wpkh(%s))" % key_descs[info['pubkey']])
+ assert_equal(info['desc'], descsum_create("sh(wpkh(%s))" % key_descs[info['pubkey']]))
elif not multisig and typ == 'bech32':
# P2WPKH
- assert_equal(info['desc'], "wpkh(%s)" % key_descs[info['pubkey']])
+ assert_equal(info['desc'], descsum_create("wpkh(%s)" % key_descs[info['pubkey']]))
elif typ == 'legacy':
# P2SH-multisig
- assert_equal(info['desc'], "sh(multi(2,%s,%s))" % (key_descs[info['pubkeys'][0]], key_descs[info['pubkeys'][1]]))
+ assert_equal(info['desc'], descsum_create("sh(multi(2,%s,%s))" % (key_descs[info['pubkeys'][0]], key_descs[info['pubkeys'][1]])))
elif typ == 'p2sh-segwit':
# P2SH-P2WSH-multisig
- assert_equal(info['desc'], "sh(wsh(multi(2,%s,%s)))" % (key_descs[info['embedded']['pubkeys'][0]], key_descs[info['embedded']['pubkeys'][1]]))
+ assert_equal(info['desc'], descsum_create("sh(wsh(multi(2,%s,%s)))" % (key_descs[info['embedded']['pubkeys'][0]], key_descs[info['embedded']['pubkeys'][1]])))
elif typ == 'bech32':
# P2WSH-multisig
- assert_equal(info['desc'], "wsh(multi(2,%s,%s))" % (key_descs[info['pubkeys'][0]], key_descs[info['pubkeys'][1]]))
+ assert_equal(info['desc'], descsum_create("wsh(multi(2,%s,%s))" % (key_descs[info['pubkeys'][0]], key_descs[info['pubkeys'][1]])))
else:
# Unknown type
- assert(False)
+ assert False
def test_change_output_type(self, node_sender, destinations, expected_type):
txid = self.nodes[node_sender].sendmany(dummy="", amounts=dict.fromkeys(destinations, 0.001))
@@ -209,7 +218,7 @@ class AddressTypeTest(BitcoinTestFramework):
# Mine 101 blocks on node5 to bring nodes out of IBD and make sure that
# no coinbases are maturing for the nodes-under-test during the test
self.nodes[5].generate(101)
- sync_blocks(self.nodes)
+ self.sync_blocks()
uncompressed_1 = "0496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858ee"
uncompressed_2 = "047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77"
@@ -276,7 +285,7 @@ class AddressTypeTest(BitcoinTestFramework):
self.log.debug("Sending: {}".format(sends))
self.nodes[from_node].sendmany("", sends)
- sync_mempools(self.nodes)
+ self.sync_mempools()
unconf_balances = self.get_balances(False)
self.log.debug("Check unconfirmed balances: {}".format(unconf_balances))
@@ -287,7 +296,7 @@ class AddressTypeTest(BitcoinTestFramework):
# node5 collects fee and block subsidy to keep accounting simple
self.nodes[5].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
# Verify that the receiving wallet contains a UTXO with the expected address, and expected descriptor
for n, to_node in enumerate(range(from_node, from_node + 4)):
@@ -317,7 +326,7 @@ class AddressTypeTest(BitcoinTestFramework):
# Fund node 4:
self.nodes[5].sendtoaddress(self.nodes[4].getnewaddress(), Decimal("1"))
self.nodes[5].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
assert_equal(self.nodes[4].getbalance(), 1)
self.log.info("Nodes with addresstype=legacy never use a P2WPKH change output")
diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py
index dd3750203a..55c517e92f 100755
--- a/test/functional/wallet_backup.py
+++ b/test/functional/wallet_backup.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 wallet backup features.
@@ -36,7 +36,12 @@ from random import randint
import shutil
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, connect_nodes, sync_blocks, sync_mempools
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ connect_nodes,
+)
+
class WalletBackupTest(BitcoinTestFramework):
def set_test_params(self):
@@ -75,9 +80,9 @@ class WalletBackupTest(BitcoinTestFramework):
# Have the miner (node3) mine a block.
# Must sync mempools before mining.
- sync_mempools(self.nodes)
+ self.sync_mempools()
self.nodes[3].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
# As above, this mirrors the original bash test.
def start_three(self):
@@ -102,13 +107,13 @@ class WalletBackupTest(BitcoinTestFramework):
def run_test(self):
self.log.info("Generating initial blockchain")
self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
self.nodes[1].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
self.nodes[2].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
self.nodes[3].generate(100)
- sync_blocks(self.nodes)
+ self.sync_blocks()
assert_equal(self.nodes[0].getbalance(), 50)
assert_equal(self.nodes[1].getbalance(), 50)
@@ -165,7 +170,7 @@ class WalletBackupTest(BitcoinTestFramework):
self.log.info("Re-starting nodes")
self.start_three()
- sync_blocks(self.nodes)
+ self.sync_blocks()
assert_equal(self.nodes[0].getbalance(), balance0)
assert_equal(self.nodes[1].getbalance(), balance1)
@@ -189,7 +194,7 @@ class WalletBackupTest(BitcoinTestFramework):
self.nodes[1].importwallet(os.path.join(self.nodes[1].datadir, 'wallet.dump'))
self.nodes[2].importwallet(os.path.join(self.nodes[2].datadir, 'wallet.dump'))
- sync_blocks(self.nodes)
+ self.sync_blocks()
assert_equal(self.nodes[0].getbalance(), balance0)
assert_equal(self.nodes[1].getbalance(), balance1)
diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py
index 05c97e0340..15f2195e21 100755
--- a/test/functional/wallet_balance.py
+++ b/test/functional/wallet_balance.py
@@ -1,23 +1,26 @@
#!/usr/bin/env python3
-# Copyright (c) 2018 The Bitcoin Core developers
+# Copyright (c) 2018-2019 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 wallet balance RPC methods."""
from decimal import Decimal
+import struct
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE as ADDRESS_WATCHONLY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
+ connect_nodes_bi,
+ sync_blocks,
)
-RANDOM_COINBASE_ADDRESS = 'mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ'
def create_transactions(node, address, amt, fees):
# Create and sign raw transactions from node to address for amt.
# Creates a transaction for each fee and returns an array
# of the raw transactions.
- utxos = node.listunspent(0)
+ utxos = [u for u in node.listunspent(0) if u['spendable']]
# Create transactions
inputs = []
@@ -25,14 +28,20 @@ def create_transactions(node, address, amt, fees):
for utxo in utxos:
inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]})
ins_total += utxo['amount']
- if ins_total > amt:
+ if ins_total >= amt + max(fees):
break
+ # make sure there was enough utxos
+ assert ins_total >= amt + max(fees)
txs = []
for fee in fees:
- outputs = {address: amt, node.getrawchangeaddress(): ins_total - amt - fee}
+ outputs = {address: amt}
+ # prevent 0 change output
+ if ins_total > amt + fee:
+ outputs[node.getrawchangeaddress()] = ins_total - amt - fee
raw_tx = node.createrawtransaction(inputs, outputs, 0, True)
raw_tx = node.signrawtransactionwithwallet(raw_tx)
+ assert_equal(raw_tx['complete'], True)
txs.append(raw_tx)
return txs
@@ -41,31 +50,48 @@ class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
+ self.extra_args = [
+ ['-limitdescendantcount=3'], # Limit mempool descendants as a hack to have wallet txs rejected from the mempool
+ [],
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self):
+ self.nodes[0].importaddress(ADDRESS_WATCHONLY)
# Check that nodes don't own any UTXOs
assert_equal(len(self.nodes[0].listunspent()), 0)
assert_equal(len(self.nodes[1].listunspent()), 0)
- self.log.info("Mining one block for each node")
+ self.log.info("Check that only node 0 is watching an address")
+ assert 'watchonly' in self.nodes[0].getbalances()
+ assert 'watchonly' not in self.nodes[1].getbalances()
+ self.log.info("Mining blocks ...")
self.nodes[0].generate(1)
self.sync_all()
self.nodes[1].generate(1)
- self.nodes[1].generatetoaddress(100, RANDOM_COINBASE_ADDRESS)
+ self.nodes[1].generatetoaddress(101, ADDRESS_WATCHONLY)
self.sync_all()
+ assert_equal(self.nodes[0].getbalances()['mine']['trusted'], 50)
+ assert_equal(self.nodes[0].getwalletinfo()['balance'], 50)
+ assert_equal(self.nodes[1].getbalances()['mine']['trusted'], 50)
+
+ assert_equal(self.nodes[0].getbalances()['watchonly']['immature'], 5000)
+ assert 'watchonly' not in self.nodes[1].getbalances()
+
assert_equal(self.nodes[0].getbalance(), 50)
assert_equal(self.nodes[1].getbalance(), 50)
self.log.info("Test getbalance with different arguments")
assert_equal(self.nodes[0].getbalance("*"), 50)
assert_equal(self.nodes[0].getbalance("*", 1), 50)
- assert_equal(self.nodes[0].getbalance("*", 1, True), 50)
+ assert_equal(self.nodes[0].getbalance("*", 1, True), 100)
assert_equal(self.nodes[0].getbalance(minconf=1), 50)
+ assert_equal(self.nodes[0].getbalance(minconf=0, include_watchonly=True), 100)
+ assert_equal(self.nodes[1].getbalance(minconf=0, include_watchonly=True), 50)
# Send 40 BTC from 0 to 1 and 60 BTC from 1 to 0.
txs = create_transactions(self.nodes[0], self.nodes[1].getnewaddress(), 40, [Decimal('0.01')])
@@ -83,32 +109,37 @@ class WalletTest(BitcoinTestFramework):
self.log.info("Test getbalance and getunconfirmedbalance with unconfirmed inputs")
- # getbalance without any arguments includes unconfirmed transactions, but not untrusted transactions
- assert_equal(self.nodes[0].getbalance(), Decimal('9.99')) # change from node 0's send
- assert_equal(self.nodes[1].getbalance(), Decimal('29.99')) # change from node 1's send
- # Same with minconf=0
- assert_equal(self.nodes[0].getbalance(minconf=0), Decimal('9.99'))
- assert_equal(self.nodes[1].getbalance(minconf=0), Decimal('29.99'))
- # getbalance with a minconf incorrectly excludes coins that have been spent more recently than the minconf blocks ago
- # TODO: fix getbalance tracking of coin spentness depth
- assert_equal(self.nodes[0].getbalance(minconf=1), Decimal('0'))
- assert_equal(self.nodes[1].getbalance(minconf=1), Decimal('0'))
- # getunconfirmedbalance
- assert_equal(self.nodes[0].getunconfirmedbalance(), Decimal('60')) # output of node 1's spend
- assert_equal(self.nodes[1].getunconfirmedbalance(), Decimal('0')) # Doesn't include output of node 0's send since it was spent
+ def test_balances(*, fee_node_1=0):
+ # getbalance without any arguments includes unconfirmed transactions, but not untrusted transactions
+ assert_equal(self.nodes[0].getbalance(), Decimal('9.99')) # change from node 0's send
+ assert_equal(self.nodes[1].getbalance(), Decimal('30') - fee_node_1) # change from node 1's send
+ # Same with minconf=0
+ assert_equal(self.nodes[0].getbalance(minconf=0), Decimal('9.99'))
+ assert_equal(self.nodes[1].getbalance(minconf=0), Decimal('30') - fee_node_1)
+ # getbalance with a minconf incorrectly excludes coins that have been spent more recently than the minconf blocks ago
+ # TODO: fix getbalance tracking of coin spentness depth
+ assert_equal(self.nodes[0].getbalance(minconf=1), Decimal('0'))
+ assert_equal(self.nodes[1].getbalance(minconf=1), Decimal('0'))
+ # getunconfirmedbalance
+ assert_equal(self.nodes[0].getunconfirmedbalance(), Decimal('60')) # output of node 1's spend
+ assert_equal(self.nodes[0].getbalances()['mine']['untrusted_pending'], Decimal('60'))
+ assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], Decimal('60'))
+
+ assert_equal(self.nodes[1].getunconfirmedbalance(), Decimal('0')) # Doesn't include output of node 0's send since it was spent
+ assert_equal(self.nodes[1].getbalances()['mine']['untrusted_pending'], Decimal('0'))
+ assert_equal(self.nodes[1].getwalletinfo()["unconfirmed_balance"], Decimal('0'))
+
+ test_balances(fee_node_1=Decimal('0.01'))
# Node 1 bumps the transaction fee and resends
self.nodes[1].sendrawtransaction(txs[1]['hex'])
+ self.nodes[0].sendrawtransaction(txs[1]['hex']) # sending on both nodes is faster than waiting for propagation
self.sync_all()
self.log.info("Test getbalance and getunconfirmedbalance with conflicted unconfirmed inputs")
+ test_balances(fee_node_1=Decimal('0.02'))
- assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], Decimal('60')) # output of node 1's send
- assert_equal(self.nodes[0].getunconfirmedbalance(), Decimal('60'))
- assert_equal(self.nodes[1].getwalletinfo()["unconfirmed_balance"], Decimal('0')) # Doesn't include output of node 0's send since it was spent
- assert_equal(self.nodes[1].getunconfirmedbalance(), Decimal('0'))
-
- self.nodes[1].generatetoaddress(1, RANDOM_COINBASE_ADDRESS)
+ self.nodes[1].generatetoaddress(1, ADDRESS_WATCHONLY)
self.sync_all()
# balances are correct after the transactions are confirmed
@@ -118,7 +149,7 @@ class WalletTest(BitcoinTestFramework):
# Send total balance away from node 1
txs = create_transactions(self.nodes[1], self.nodes[0].getnewaddress(), Decimal('29.97'), [Decimal('0.01')])
self.nodes[1].sendrawtransaction(txs[0]['hex'])
- self.nodes[1].generatetoaddress(2, RANDOM_COINBASE_ADDRESS)
+ self.nodes[1].generatetoaddress(2, ADDRESS_WATCHONLY)
self.sync_all()
# getbalance with a minconf incorrectly excludes coins that have been spent more recently than the minconf blocks ago
@@ -129,5 +160,62 @@ class WalletTest(BitcoinTestFramework):
# getbalance with minconf=2 will show the new balance.
assert_equal(self.nodes[1].getbalance(minconf=2), Decimal('0'))
+ # check mempool transactions count for wallet unconfirmed balance after
+ # dynamically loading the wallet.
+ before = self.nodes[1].getunconfirmedbalance()
+ dst = self.nodes[1].getnewaddress()
+ self.nodes[1].unloadwallet('')
+ self.nodes[0].sendtoaddress(dst, 0.1)
+ self.sync_all()
+ self.nodes[1].loadwallet('')
+ after = self.nodes[1].getunconfirmedbalance()
+ assert_equal(before + Decimal('0.1'), after)
+
+ # Create 3 more wallet txs, where the last is not accepted to the
+ # mempool because it is the third descendant of the tx above
+ for _ in range(3):
+ # Set amount high enough such that all coins are spent by each tx
+ txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 99)
+
+ self.log.info('Check that wallet txs not in the mempool are untrusted')
+ assert txid not in self.nodes[0].getrawmempool()
+ assert_equal(self.nodes[0].gettransaction(txid)['trusted'], False)
+ assert_equal(self.nodes[0].getbalance(minconf=0), 0)
+
+ self.log.info("Test replacement and reorg of non-mempool tx")
+ tx_orig = self.nodes[0].gettransaction(txid)['hex']
+ # Increase fee by 1 coin
+ tx_replace = tx_orig.replace(
+ struct.pack("<q", 99 * 10**8).hex(),
+ struct.pack("<q", 98 * 10**8).hex(),
+ )
+ tx_replace = self.nodes[0].signrawtransactionwithwallet(tx_replace)['hex']
+ # Total balance is given by the sum of outputs of the tx
+ total_amount = sum([o['value'] for o in self.nodes[0].decoderawtransaction(tx_replace)['vout']])
+ self.sync_all()
+ self.nodes[1].sendrawtransaction(hexstring=tx_replace, maxfeerate=0)
+
+ # Now confirm tx_replace
+ block_reorg = self.nodes[1].generatetoaddress(1, ADDRESS_WATCHONLY)[0]
+ self.sync_all()
+ assert_equal(self.nodes[0].getbalance(minconf=0), total_amount)
+
+ self.log.info('Put txs back into mempool of node 1 (not node 0)')
+ self.nodes[0].invalidateblock(block_reorg)
+ self.nodes[1].invalidateblock(block_reorg)
+ assert_equal(self.nodes[0].getbalance(minconf=0), 0) # wallet txs not in the mempool are untrusted
+ self.nodes[0].generatetoaddress(1, ADDRESS_WATCHONLY)
+ assert_equal(self.nodes[0].getbalance(minconf=0), 0) # wallet txs not in the mempool are untrusted
+
+ # Now confirm tx_orig
+ self.restart_node(1, ['-persistmempool=0'])
+ connect_nodes_bi(self.nodes, 0, 1)
+ sync_blocks(self.nodes)
+ self.nodes[1].sendrawtransaction(tx_orig)
+ self.nodes[1].generatetoaddress(1, ADDRESS_WATCHONLY)
+ self.sync_all()
+ assert_equal(self.nodes[0].getbalance(minconf=0), total_amount + 1) # The reorg recovered our fee of 1 coin
+
+
if __name__ == '__main__':
WalletTest().main()
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 673eb718e6..daa834b5b8 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -11,11 +11,8 @@ from test_framework.util import (
assert_array_result,
assert_equal,
assert_fee_amount,
- assert_greater_than,
assert_raises_rpc_error,
connect_nodes_bi,
- sync_blocks,
- sync_mempools,
wait_until,
)
@@ -24,8 +21,6 @@ class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.setup_clean_chain = True
- # TODO: remove -txindex. Currently required for getrawtransaction call.
- self.extra_args = [[], [], ["-txindex"], []]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -37,7 +32,7 @@ class WalletTest(BitcoinTestFramework):
connect_nodes_bi(self.nodes, 0, 1)
connect_nodes_bi(self.nodes, 1, 2)
connect_nodes_bi(self.nodes, 0, 2)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size):
"""Return curr_balance after asserting the fee was in range"""
@@ -62,9 +57,9 @@ class WalletTest(BitcoinTestFramework):
assert_equal(walletinfo['immature_balance'], 50)
assert_equal(walletinfo['balance'], 0)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
self.nodes[1].generate(101)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
assert_equal(self.nodes[0].getbalance(), 50)
assert_equal(self.nodes[1].getbalance(), 50)
@@ -86,13 +81,8 @@ class WalletTest(BitcoinTestFramework):
assert_equal(txout['value'], 50)
# Send 21 BTC from 0 to 2 using sendtoaddress call.
- # Locked memory should increase to sign transactions
- self.log.info("test getmemoryinfo")
- memory_before = self.nodes[0].getmemoryinfo()
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11)
mempool_txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 10)
- memory_after = self.nodes[0].getmemoryinfo()
- assert_greater_than(memory_after['locked']['used'], memory_before['locked']['used'])
self.log.info("test gettxout (second part)")
# utxo spent in mempool should be visible if you exclude mempool
@@ -116,7 +106,7 @@ class WalletTest(BitcoinTestFramework):
# Have node0 mine a block, thus it will collect its own fee.
self.nodes[0].generate(1)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
# Exercise locking of unspent outputs
unspent_0 = self.nodes[2].listunspent()[0]
@@ -152,7 +142,7 @@ class WalletTest(BitcoinTestFramework):
# Have node1 generate 100 blocks (so node0 can recover the fee)
self.nodes[1].generate(100)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
# node0 should end up with 100 btc in block rewards plus fees, but
# minus the 21 plus fees sent to node2
@@ -176,12 +166,12 @@ class WalletTest(BitcoinTestFramework):
txns_to_send.append(self.nodes[0].signrawtransactionwithwallet(raw_tx))
# Have node 1 (miner) send the transactions
- self.nodes[1].sendrawtransaction(txns_to_send[0]["hex"], True)
- self.nodes[1].sendrawtransaction(txns_to_send[1]["hex"], True)
+ self.nodes[1].sendrawtransaction(txns_to_send[0]["hex"], 0)
+ self.nodes[1].sendrawtransaction(txns_to_send[1]["hex"], 0)
# Have node1 mine a block to confirm transactions:
self.nodes[1].generate(1)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
assert_equal(self.nodes[0].getbalance(), 0)
assert_equal(self.nodes[2].getbalance(), 94)
@@ -196,14 +186,14 @@ class WalletTest(BitcoinTestFramework):
self.nodes[2].settxfee(fee_per_byte * 1000)
txid = self.nodes[2].sendtoaddress(address, 10, "", "", False)
self.nodes[2].generate(1)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('84'), fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))
assert_equal(self.nodes[0].getbalance(), Decimal('10'))
# Send 10 BTC with subtract fee from amount
txid = self.nodes[2].sendtoaddress(address, 10, "", "", True)
self.nodes[2].generate(1)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
node_2_bal -= Decimal('10')
assert_equal(self.nodes[2].getbalance(), node_2_bal)
node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))
@@ -211,7 +201,7 @@ class WalletTest(BitcoinTestFramework):
# Sendmany 10 BTC
txid = self.nodes[2].sendmany('', {address: 10}, 0, "", [])
self.nodes[2].generate(1)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
node_0_bal += Decimal('10')
node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))
assert_equal(self.nodes[0].getbalance(), node_0_bal)
@@ -219,28 +209,14 @@ class WalletTest(BitcoinTestFramework):
# Sendmany 10 BTC with subtract fee from amount
txid = self.nodes[2].sendmany('', {address: 10}, 0, "", [address])
self.nodes[2].generate(1)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
node_2_bal -= Decimal('10')
assert_equal(self.nodes[2].getbalance(), node_2_bal)
node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))
- # Test ResendWalletTransactions:
- # Create a couple of transactions, then start up a fourth
- # node (nodes[3]) and ask nodes[0] to rebroadcast.
- # EXPECT: nodes[3] should have those transactions in its mempool.
- txid1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1)
- txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
- sync_mempools(self.nodes[0:2])
-
self.start_node(3)
connect_nodes_bi(self.nodes, 0, 3)
- sync_blocks(self.nodes)
-
- relayed = self.nodes[0].resendwallettransactions()
- assert_equal(set(relayed), {txid1, txid2})
- sync_mempools(self.nodes)
-
- assert txid1 in self.nodes[3].getrawmempool()
+ self.sync_all()
# check if we can list zero value tx as available coins
# 1. create raw_tx
@@ -277,18 +253,18 @@ class WalletTest(BitcoinTestFramework):
connect_nodes_bi(self.nodes, 0, 1)
connect_nodes_bi(self.nodes, 1, 2)
connect_nodes_bi(self.nodes, 0, 2)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
txid_not_broadcast = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2)
tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast)
self.nodes[1].generate(1) # mine a block, tx should not be in there
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
assert_equal(self.nodes[2].getbalance(), node_2_bal) # should not be changed because tx was not broadcasted
# now broadcast from another node, mine a block, sync, and check the balance
self.nodes[1].sendrawtransaction(tx_obj_not_broadcast['hex'])
self.nodes[1].generate(1)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
node_2_bal += 2
tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast)
assert_equal(self.nodes[2].getbalance(), node_2_bal)
@@ -304,10 +280,10 @@ class WalletTest(BitcoinTestFramework):
connect_nodes_bi(self.nodes, 0, 1)
connect_nodes_bi(self.nodes, 1, 2)
connect_nodes_bi(self.nodes, 0, 2)
- sync_blocks(self.nodes[0:3])
+ self.sync_blocks(self.nodes[0:3])
self.nodes[0].generate(1)
- sync_blocks(self.nodes[0:3])
+ self.sync_blocks(self.nodes[0:3])
node_2_bal += 2
# tx should be added to balance because after restarting the nodes tx should be broadcast
@@ -327,18 +303,44 @@ class WalletTest(BitcoinTestFramework):
tx_obj = self.nodes[0].gettransaction(txid)
assert_equal(tx_obj['amount'], Decimal('-0.0001'))
+ # General checks for errors from incorrect inputs
# This will raise an exception because the amount type is wrong
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_rpc_error(-1, "not an integer", self.nodes[0].generate, "2")
+ # This will raise an exception for the invalid private key format
+ assert_raises_rpc_error(-5, "Invalid private key encoding", self.nodes[0].importprivkey, "invalid")
+
+ # This will raise an exception for importing an address with the PS2H flag
+ temp_address = self.nodes[1].getnewaddress()
+ assert_raises_rpc_error(-5, "Cannot use the p2sh flag with an address - use a script instead", self.nodes[0].importaddress, temp_address, "label", False, True)
+
+ # This will raise an exception for attempting to dump the private key of an address you do not own
+ assert_raises_rpc_error(-3, "Address does not refer to a key", self.nodes[0].dumpprivkey, temp_address)
+
+ # This will raise an exception for attempting to get the private key of an invalid Bitcoin address
+ assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].dumpprivkey, "invalid")
+
+ # This will raise an exception for attempting to set a label for an invalid Bitcoin address
+ assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].setlabel, "invalid address", "label")
+
+ # This will raise an exception for importing an invalid address
+ assert_raises_rpc_error(-5, "Invalid Bitcoin address or script", self.nodes[0].importaddress, "invalid")
+
+ # This will raise an exception for attempting to import a pubkey that isn't in hex
+ assert_raises_rpc_error(-5, "Pubkey must be a hex string", self.nodes[0].importpubkey, "not hex")
+
+ # This will raise an exception for importing an invalid pubkey
+ assert_raises_rpc_error(-5, "Pubkey is not a valid public key", self.nodes[0].importpubkey, "5361746f736869204e616b616d6f746f")
+
# Import address and private key to check correct behavior of spendable unspents
# 1. Send some coins to generate new UTXO
address_to_import = self.nodes[2].getnewaddress()
txid = self.nodes[0].sendtoaddress(address_to_import, 1)
self.nodes[0].generate(1)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
# 2. Import address from node2 to node1
self.nodes[1].importaddress(address_to_import)
@@ -364,15 +366,15 @@ class WalletTest(BitcoinTestFramework):
coinbase_addr = self.nodes[1].getnewaddress()
block_hash = self.nodes[0].generatetoaddress(1, coinbase_addr)[0]
coinbase_txid = self.nodes[0].getblock(block_hash)['tx'][0]
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
# Check that the txid and balance is found by node1
self.nodes[1].gettransaction(coinbase_txid)
# check if wallet or blockchain maintenance changes the balance
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
blocks = self.nodes[0].generate(2)
- self.sync_all([self.nodes[0:3]])
+ self.sync_all(self.nodes[0:3])
balance_nodes = [self.nodes[i].getbalance() for i in range(3)]
block_count = self.nodes[0].getblockcount()
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 7d3d9b61e2..4d9bacf299 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2018 The Bitcoin Core developers
+# Copyright (c) 2016-2019 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 bumpfee RPC.
@@ -19,7 +19,13 @@ import io
from test_framework.blocktools import add_witness_commitment, create_block, create_coinbase, send_to_witness
from test_framework.messages import BIP125_SEQUENCE_NUMBER, CTransaction
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_greater_than, assert_raises_rpc_error, bytes_to_hex_str, connect_nodes_bi, hex_str_to_bytes, sync_mempools
+from test_framework.util import (
+ assert_equal,
+ assert_greater_than,
+ assert_raises_rpc_error,
+ connect_nodes_bi,
+ hex_str_to_bytes,
+)
WALLET_PASSPHRASE = "test"
WALLET_PASSPHRASE_TIMEOUT = 3600
@@ -60,7 +66,7 @@ class BumpFeeTest(BitcoinTestFramework):
self.log.info("Running tests")
dest_address = peer_node.getnewaddress()
- test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address)
+ test_simple_bumpfee_succeeds(self, rbf_node, peer_node, dest_address)
test_segwit_bumpfee_succeeds(rbf_node, dest_address)
test_nonrbf_bumpfee_fails(peer_node, dest_address)
test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address)
@@ -73,19 +79,23 @@ class BumpFeeTest(BitcoinTestFramework):
test_unconfirmed_not_spendable(rbf_node, rbf_node_address)
test_bumpfee_metadata(rbf_node, dest_address)
test_locked_wallet_fails(rbf_node, dest_address)
+ test_change_script_match(rbf_node, dest_address)
+ # These tests wipe out a number of utxos that are expected in other tests
+ test_small_output_with_feerate_succeeds(rbf_node, dest_address)
+ test_no_more_inputs_fails(rbf_node, dest_address)
self.log.info("Success")
-def test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address):
+def test_simple_bumpfee_succeeds(self, rbf_node, peer_node, dest_address):
rbfid = spend_one_input(rbf_node, dest_address)
rbftx = rbf_node.gettransaction(rbfid)
- sync_mempools((rbf_node, peer_node))
+ self.sync_mempools((rbf_node, peer_node))
assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
bumped_tx = rbf_node.bumpfee(rbfid)
assert_equal(bumped_tx["errors"], [])
assert bumped_tx["fee"] - abs(rbftx["fee"]) > 0
# check that bumped_tx propagates, original tx was evicted and has a wallet conflict
- sync_mempools((rbf_node, peer_node))
+ self.sync_mempools((rbf_node, peer_node))
assert bumped_tx["txid"] in rbf_node.getrawmempool()
assert bumped_tx["txid"] in peer_node.getrawmempool()
assert rbfid not in rbf_node.getrawmempool()
@@ -173,6 +183,40 @@ def test_small_output_fails(rbf_node, dest_address):
rbfid = spend_one_input(rbf_node, dest_address)
assert_raises_rpc_error(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001})
+def test_small_output_with_feerate_succeeds(rbf_node, dest_address):
+
+ # Make sure additional inputs exist
+ rbf_node.generatetoaddress(101, rbf_node.getnewaddress())
+ rbfid = spend_one_input(rbf_node, dest_address)
+ original_input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"]
+ assert_equal(len(original_input_list), 1)
+ original_txin = original_input_list[0]
+ # Keep bumping until we out-spend change output
+ tx_fee = 0
+ while tx_fee < Decimal("0.0005"):
+ new_input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"]
+ new_item = list(new_input_list)[0]
+ assert_equal(len(original_input_list), 1)
+ assert_equal(original_txin["txid"], new_item["txid"])
+ assert_equal(original_txin["vout"], new_item["vout"])
+ rbfid_new_details = rbf_node.bumpfee(rbfid)
+ rbfid_new = rbfid_new_details["txid"]
+ raw_pool = rbf_node.getrawmempool()
+ assert rbfid not in raw_pool
+ assert rbfid_new in raw_pool
+ rbfid = rbfid_new
+ tx_fee = rbfid_new_details["origfee"]
+
+ # input(s) have been added
+ final_input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"]
+ assert_greater_than(len(final_input_list), 1)
+ # Original input is in final set
+ assert [txin for txin in final_input_list
+ if txin["txid"] == original_txin["txid"]
+ and txin["vout"] == original_txin["vout"]]
+
+ rbf_node.generatetoaddress(1, rbf_node.getnewaddress())
+ assert_equal(rbf_node.gettransaction(rbfid)["confirmations"], 1)
def test_dust_to_fee(rbf_node, dest_address):
# check that if output is reduced to dust, it will be converted to fee
@@ -272,19 +316,37 @@ def test_locked_wallet_fails(rbf_node, dest_address):
rbf_node.walletlock()
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first.",
rbf_node.bumpfee, rbfid)
+ rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
+
+def test_change_script_match(rbf_node, dest_address):
+ """Test that the same change addresses is used for the replacement transaction when possible."""
+ def get_change_address(tx):
+ tx_details = rbf_node.getrawtransaction(tx, 1)
+ txout_addresses = [txout['scriptPubKey']['addresses'][0] for txout in tx_details["vout"]]
+ return [address for address in txout_addresses if rbf_node.getaddressinfo(address)["ischange"]]
+ # Check that there is only one change output
+ rbfid = spend_one_input(rbf_node, dest_address)
+ change_addresses = get_change_address(rbfid)
+ assert_equal(len(change_addresses), 1)
+
+ # Now find that address in each subsequent tx, and no other change
+ bumped_total_tx = rbf_node.bumpfee(rbfid, {"totalFee": 2000})
+ assert_equal(change_addresses, get_change_address(bumped_total_tx['txid']))
+ bumped_rate_tx = rbf_node.bumpfee(bumped_total_tx["txid"])
+ assert_equal(change_addresses, get_change_address(bumped_rate_tx['txid']))
-def spend_one_input(node, dest_address):
+def spend_one_input(node, dest_address, change_size=Decimal("0.00049000")):
tx_input = dict(
sequence=BIP125_SEQUENCE_NUMBER, **next(u for u in node.listunspent() if u["amount"] == Decimal("0.00100000")))
- rawtx = node.createrawtransaction(
- [tx_input], {dest_address: Decimal("0.00050000"),
- node.getrawchangeaddress(): Decimal("0.00049000")})
+ destinations = {dest_address: Decimal("0.00050000")}
+ if change_size > 0:
+ destinations[node.getrawchangeaddress()] = change_size
+ rawtx = node.createrawtransaction([tx_input], destinations)
signedtx = node.signrawtransactionwithwallet(rawtx)
txid = node.sendrawtransaction(signedtx["hex"])
return txid
-
def submit_block_with_tx(node, tx):
ctx = CTransaction()
ctx.deserialize(io.BytesIO(hex_str_to_bytes(tx)))
@@ -298,9 +360,15 @@ def submit_block_with_tx(node, tx):
block.hashMerkleRoot = block.calc_merkle_root()
add_witness_commitment(block)
block.solve()
- node.submitblock(bytes_to_hex_str(block.serialize(True)))
+ node.submitblock(block.serialize().hex())
return block
+def test_no_more_inputs_fails(rbf_node, dest_address):
+ # feerate rbf requires confirmed outputs when change output doesn't exist or is insufficient
+ rbf_node.generatetoaddress(1, dest_address)
+ # spend all funds, no change output
+ rbfid = rbf_node.sendtoaddress(rbf_node.getnewaddress(), rbf_node.getbalance(), "", "", True)
+ assert_raises_rpc_error(-4, "Unable to create transaction: Insufficient funds", rbf_node.bumpfee, rbfid)
if __name__ == "__main__":
BumpFeeTest().main()
diff --git a/test/functional/wallet_create_tx.py b/test/functional/wallet_create_tx.py
index 27dc0fb279..0b584a0bb2 100755
--- a/test/functional/wallet_create_tx.py
+++ b/test/functional/wallet_create_tx.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2018 The Bitcoin Core developers
+# Copyright (c) 2018-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,17 +7,25 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
)
+from test_framework.blocktools import (
+ TIME_GENESIS_BLOCK,
+)
class CreateTxWalletTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
+ self.setup_clean_chain = True
self.num_nodes = 1
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self):
+ self.log.info('Create some old blocks')
+ self.nodes[0].setmocktime(TIME_GENESIS_BLOCK)
+ self.nodes[0].generate(200)
+ self.nodes[0].setmocktime(0)
+
self.log.info('Check that we have some (old) blocks and that anti-fee-sniping is disabled')
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
new file mode 100755
index 0000000000..c17949a2f6
--- /dev/null
+++ b/test/functional/wallet_createwallet.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018-2019 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 createwallet arguments.
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
+
+class CreateWalletTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 1
+ self.supports_cli = True
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ node = self.nodes[0]
+ node.generate(1) # Leave IBD for sethdseed
+
+ self.nodes[0].createwallet(wallet_name='w0')
+ w0 = node.get_wallet_rpc('w0')
+ address1 = w0.getnewaddress()
+
+ self.log.info("Test disableprivatekeys creation.")
+ self.nodes[0].createwallet(wallet_name='w1', disable_private_keys=True)
+ w1 = node.get_wallet_rpc('w1')
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w1.getnewaddress)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w1.getrawchangeaddress)
+ w1.importpubkey(w0.getaddressinfo(address1)['pubkey'])
+
+ self.log.info('Test that private keys cannot be imported')
+ addr = w0.getnewaddress('', 'legacy')
+ privkey = w0.dumpprivkey(addr)
+ assert_raises_rpc_error(-4, 'Cannot import private keys to a wallet with private keys disabled', w1.importprivkey, privkey)
+ result = w1.importmulti([{'scriptPubKey': {'address': addr}, 'timestamp': 'now', 'keys': [privkey]}])
+ assert not result[0]['success']
+ assert 'warning' not in result[0]
+ assert_equal(result[0]['error']['code'], -4)
+ assert_equal(result[0]['error']['message'], 'Cannot import private keys to a wallet with private keys disabled')
+
+ self.log.info("Test blank creation with private keys disabled.")
+ self.nodes[0].createwallet(wallet_name='w2', disable_private_keys=True, blank=True)
+ w2 = node.get_wallet_rpc('w2')
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w2.getnewaddress)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w2.getrawchangeaddress)
+ w2.importpubkey(w0.getaddressinfo(address1)['pubkey'])
+
+ self.log.info("Test blank creation with private keys enabled.")
+ self.nodes[0].createwallet(wallet_name='w3', disable_private_keys=False, blank=True)
+ w3 = node.get_wallet_rpc('w3')
+ assert_equal(w3.getwalletinfo()['keypoolsize'], 0)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w3.getnewaddress)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w3.getrawchangeaddress)
+ # Import private key
+ w3.importprivkey(w0.dumpprivkey(address1))
+ # Imported private keys are currently ignored by the keypool
+ assert_equal(w3.getwalletinfo()['keypoolsize'], 0)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w3.getnewaddress)
+ # Set the seed
+ w3.sethdseed()
+ assert_equal(w3.getwalletinfo()['keypoolsize'], 1)
+ w3.getnewaddress()
+ w3.getrawchangeaddress()
+
+ self.log.info("Test blank creation with privkeys enabled and then encryption")
+ self.nodes[0].createwallet(wallet_name='w4', disable_private_keys=False, blank=True)
+ w4 = node.get_wallet_rpc('w4')
+ assert_equal(w4.getwalletinfo()['keypoolsize'], 0)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getnewaddress)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getrawchangeaddress)
+ # Encrypt the wallet. Nothing should change about the keypool
+ w4.encryptwallet('pass')
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getnewaddress)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getrawchangeaddress)
+ # Now set a seed and it should work. Wallet should also be encrypted
+ w4.walletpassphrase('pass', 2)
+ w4.sethdseed()
+ w4.getnewaddress()
+ w4.getrawchangeaddress()
+
+ self.log.info("Test blank creation with privkeys disabled and then encryption")
+ self.nodes[0].createwallet(wallet_name='w5', disable_private_keys=True, blank=True)
+ w5 = node.get_wallet_rpc('w5')
+ assert_equal(w5.getwalletinfo()['keypoolsize'], 0)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w5.getnewaddress)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w5.getrawchangeaddress)
+ # Encrypt the wallet
+ assert_raises_rpc_error(-16, "Error: wallet does not contain private keys, nothing to encrypt.", w5.encryptwallet, 'pass')
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w5.getnewaddress)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w5.getrawchangeaddress)
+
+ self.log.info('New blank and encrypted wallets can be created')
+ self.nodes[0].createwallet(wallet_name='wblank', disable_private_keys=False, blank=True, passphrase='thisisapassphrase')
+ wblank = node.get_wallet_rpc('wblank')
+ assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", wblank.signmessage, "needanargument", "test")
+ wblank.walletpassphrase('thisisapassphrase', 10)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getnewaddress)
+ assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getrawchangeaddress)
+
+ self.log.info('Test creating a new encrypted wallet.')
+ # Born encrypted wallet is created (has keys)
+ self.nodes[0].createwallet(wallet_name='w6', disable_private_keys=False, blank=False, passphrase='thisisapassphrase')
+ w6 = node.get_wallet_rpc('w6')
+ assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", w6.signmessage, "needanargument", "test")
+ w6.walletpassphrase('thisisapassphrase', 10)
+ w6.signmessage(w6.getnewaddress('', 'legacy'), "test")
+ w6.keypoolrefill(1)
+ # There should only be 1 key
+ walletinfo = w6.getwalletinfo()
+ assert_equal(walletinfo['keypoolsize'], 1)
+ assert_equal(walletinfo['keypoolsize_hd_internal'], 1)
+ # Empty passphrase, error
+ assert_raises_rpc_error(-16, 'Cannot encrypt a wallet with a blank password', self.nodes[0].createwallet, 'w7', False, False, '')
+
+if __name__ == '__main__':
+ CreateWalletTest().main()
diff --git a/test/functional/wallet_disable.py b/test/functional/wallet_disable.py
index 6530c58c78..7c2ec56b5a 100755
--- a/test/functional/wallet_disable.py
+++ b/test/functional/wallet_disable.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 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 a node with the -disablewallet option.
@@ -21,9 +21,9 @@ class DisableWalletTest (BitcoinTestFramework):
# Make sure wallet is really disabled
assert_raises_rpc_error(-32601, 'Method not found', self.nodes[0].getwalletinfo)
x = self.nodes[0].validateaddress('3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
- assert(x['isvalid'] == False)
+ assert x['isvalid'] == False
x = self.nodes[0].validateaddress('mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ')
- assert(x['isvalid'] == True)
+ assert x['isvalid'] == True
# Checking mining to an address without a wallet. Generating to a valid address should succeed
# but generating to an invalid address will fail.
diff --git a/test/functional/wallet_disableprivatekeys.py b/test/functional/wallet_disableprivatekeys.py
deleted file mode 100755
index e55bb82e76..0000000000
--- a/test/functional/wallet_disableprivatekeys.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2018 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 disable-privatekeys mode.
-"""
-
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (
- assert_equal,
- assert_raises_rpc_error,
-)
-
-
-class DisablePrivateKeysTest(BitcoinTestFramework):
- def set_test_params(self):
- self.setup_clean_chain = False
- self.num_nodes = 1
- self.supports_cli = True
-
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
- def run_test(self):
- node = self.nodes[0]
- self.log.info("Test disableprivatekeys creation.")
- self.nodes[0].createwallet('w1', True)
- self.nodes[0].createwallet('w2')
- w1 = node.get_wallet_rpc('w1')
- w2 = node.get_wallet_rpc('w2')
- assert_raises_rpc_error(-4,"Error: Private keys are disabled for this wallet", w1.getnewaddress)
- assert_raises_rpc_error(-4,"Error: Private keys are disabled for this wallet", w1.getrawchangeaddress)
- w1.importpubkey(w2.getaddressinfo(w2.getnewaddress())['pubkey'])
-
- self.log.info('Test that private keys cannot be imported')
- addr = w2.getnewaddress('', 'legacy')
- privkey = w2.dumpprivkey(addr)
- assert_raises_rpc_error(-4, 'Cannot import private keys to a wallet with private keys disabled', w1.importprivkey, privkey)
- result = w1.importmulti([{'scriptPubKey': {'address': addr}, 'timestamp': 'now', 'keys': [privkey]}])
- assert(not result[0]['success'])
- assert('warning' not in result[0])
- assert_equal(result[0]['error']['code'], -4)
- assert_equal(result[0]['error']['message'], 'Cannot import private keys to a wallet with private keys disabled')
-
-if __name__ == '__main__':
- DisablePrivateKeysTest().main()
diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py
index 3f39654bb8..53edf710b9 100755
--- a/test/functional/wallet_dump.py
+++ b/test/functional/wallet_dump.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2018 The Bitcoin Core developers
+# Copyright (c) 2016-2019 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 dumpwallet RPC."""
@@ -46,10 +46,10 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old):
keypath = None
if keytype == "inactivehdseed=1":
# ensure the old master is still available
- assert (hd_master_addr_old == addr)
+ assert hd_master_addr_old == addr
elif keytype == "hdseed=1":
# ensure we have generated a new hd master key
- assert (hd_master_addr_old != addr)
+ assert hd_master_addr_old != addr
hd_master_addr_ret = addr
elif keytype == "script=1":
# scripts don't have keypaths
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index eb42531693..97172d8b82 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2018 The Bitcoin Core developers
+# Copyright (c) 2016-2019 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 Hierarchical Deterministic wallet function."""
@@ -27,7 +27,6 @@ class WalletHDTest(BitcoinTestFramework):
def run_test(self):
# Make sure we use hd, keep masterkeyid
masterkeyid = self.nodes[1].getwalletinfo()['hdseedid']
- assert_equal(masterkeyid, self.nodes[1].getwalletinfo()['hdmasterkeyid'])
assert_equal(len(masterkeyid), 40)
# create an internal key
@@ -53,7 +52,6 @@ class WalletHDTest(BitcoinTestFramework):
hd_info = self.nodes[1].getaddressinfo(hd_add)
assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i)+"'")
assert_equal(hd_info["hdseedid"], masterkeyid)
- assert_equal(hd_info["hdmasterkeyid"], masterkeyid)
self.nodes[0].sendtoaddress(hd_add, 1)
self.nodes[0].generate(1)
self.nodes[0].sendtoaddress(non_hd_add, 1)
@@ -83,7 +81,6 @@ class WalletHDTest(BitcoinTestFramework):
hd_info_2 = self.nodes[1].getaddressinfo(hd_add_2)
assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(i)+"'")
assert_equal(hd_info_2["hdseedid"], masterkeyid)
- assert_equal(hd_info_2["hdmasterkeyid"], masterkeyid)
assert_equal(hd_add, hd_add_2)
connect_nodes_bi(self.nodes, 0, 1)
self.sync_all()
diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py
index 46462a16f3..47c97f62bf 100755
--- a/test/functional/wallet_import_rescan.py
+++ b/test/functional/wallet_import_rescan.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 wallet import RPCs.
@@ -20,7 +20,11 @@ happened previously.
"""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (assert_raises_rpc_error, connect_nodes, sync_blocks, assert_equal, set_node_times)
+from test_framework.util import (
+ connect_nodes,
+ assert_equal,
+ set_node_times,
+)
import collections
import enum
@@ -34,23 +38,17 @@ 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."""
rescan = self.rescan == Rescan.yes
if self.call == Call.single:
if self.data == Data.address:
- response = self.try_rpc(self.node.importaddress, address=self.address["address"], label=self.label, rescan=rescan)
+ response = self.node.importaddress(address=self.address["address"], label=self.label, rescan=rescan)
elif self.data == Data.pub:
- response = self.try_rpc(self.node.importpubkey, pubkey=self.address["pubkey"], label=self.label, rescan=rescan)
+ response = self.node.importpubkey(pubkey=self.address["pubkey"], label=self.label, rescan=rescan)
elif self.data == Data.priv:
- response = self.try_rpc(self.node.importprivkey, privkey=self.key, label=self.label, rescan=rescan)
+ response = self.node.importprivkey(privkey=self.key, label=self.label, rescan=rescan)
assert_equal(response, None)
elif self.call in (Call.multiaddress, Call.multiscript):
@@ -161,13 +159,13 @@ class ImportRescanTest(BitcoinTestFramework):
timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"]
set_node_times(self.nodes, timestamp + TIMESTAMP_WINDOW + 1)
self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.sync_all()
# For each variation of wallet key import, invoke the import RPC and
# check the results from getbalance and listtransactions.
for variant in IMPORT_VARIANTS:
- variant.expect_disabled = variant.rescan == Rescan.yes and variant.prune and variant.call == Call.single
- expect_rescan = variant.rescan == Rescan.yes and not variant.expect_disabled
+ self.log.info('Run import for variant {}'.format(variant))
+ expect_rescan = variant.rescan == Rescan.yes
variant.node = self.nodes[2 + IMPORT_NODES.index(ImportNode(variant.prune, expect_rescan))]
variant.do_import(timestamp)
if expect_rescan:
@@ -187,16 +185,14 @@ class ImportRescanTest(BitcoinTestFramework):
# Generate a block containing the new transactions.
self.nodes[0].generate(1)
assert_equal(self.nodes[0].getrawmempool(), [])
- sync_blocks(self.nodes)
+ self.sync_all()
# Check the latest results from getbalance and listtransactions.
for variant in IMPORT_VARIANTS:
- if not variant.expect_disabled:
- variant.expected_balance += variant.sent_amount
- variant.expected_txs += 1
- variant.check(variant.sent_txid, variant.sent_amount, 1)
- else:
- variant.check()
+ self.log.info('Run check for variant {}'.format(variant))
+ variant.expected_balance += variant.sent_amount
+ variant.expected_txs += 1
+ variant.check(variant.sent_txid, variant.sent_amount, 1)
if __name__ == "__main__":
ImportRescanTest().main()
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py
index f122f19e3a..e19c7919a9 100755
--- a/test/functional/wallet_importmulti.py
+++ b/test/functional/wallet_importmulti.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 importmulti RPC.
@@ -20,11 +20,11 @@ from test_framework.script import (
OP_NOP,
)
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.descriptors import descsum_create
from test_framework.util import (
assert_equal,
assert_greater_than,
assert_raises_rpc_error,
- bytes_to_hex_str,
)
from test_framework.wallet_util import (
get_key,
@@ -126,7 +126,7 @@ class ImportMultiTest(BitcoinTestFramework):
# Nonstandard scriptPubKey + !internal
self.log.info("Should not import a nonstandard scriptPubKey without internal flag")
- nonstandardScriptPubKey = key.p2pkh_script + bytes_to_hex_str(CScript([OP_NOP]))
+ nonstandardScriptPubKey = key.p2pkh_script + CScript([OP_NOP]).hex()
key = get_key(self.nodes[0])
self.test_importmulti({"scriptPubKey": nonstandardScriptPubKey,
"timestamp": "now"},
@@ -203,7 +203,7 @@ class ImportMultiTest(BitcoinTestFramework):
"keys": [key.privkey]},
success=False,
error_code=-4,
- error_message='The wallet already contains the private key for this address or script')
+ error_message='The wallet already contains the private key for this address or script ("' + key.p2pkh_script + '")')
# Address + Private key + watchonly
self.log.info("Should import an address with private key and with watchonly")
@@ -543,5 +543,309 @@ class ImportMultiTest(BitcoinTestFramework):
solvable=True,
ismine=False)
+ # Test importing of a P2SH-P2WPKH address via descriptor + private key
+ key = get_key(self.nodes[0])
+ self.log.info("Should not import a p2sh-p2wpkh address from descriptor without checksum and private key")
+ self.test_importmulti({"desc": "sh(wpkh(" + key.pubkey + "))",
+ "timestamp": "now",
+ "label": "Descriptor import test",
+ "keys": [key.privkey]},
+ success=False,
+ error_code=-5,
+ error_message="Descriptor is invalid")
+
+ # Test importing of a P2SH-P2WPKH address via descriptor + private key
+ key = get_key(self.nodes[0])
+ self.log.info("Should import a p2sh-p2wpkh address from descriptor and private key")
+ self.test_importmulti({"desc": descsum_create("sh(wpkh(" + key.pubkey + "))"),
+ "timestamp": "now",
+ "label": "Descriptor import test",
+ "keys": [key.privkey]},
+ success=True)
+ test_address(self.nodes[1],
+ key.p2sh_p2wpkh_addr,
+ solvable=True,
+ ismine=True,
+ label="Descriptor import test")
+
+ # Test ranged descriptor fails if range is not specified
+ xpriv = "tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg"
+ addresses = ["2N7yv4p8G8yEaPddJxY41kPihnWvs39qCMf", "2MsHxyb2JS3pAySeNUsJ7mNnurtpeenDzLA"] # hdkeypath=m/0'/0'/0' and 1'
+ addresses += ["bcrt1qrd3n235cj2czsfmsuvqqpr3lu6lg0ju7scl8gn", "bcrt1qfqeppuvj0ww98r6qghmdkj70tv8qpchehegrg8"] # wpkh subscripts corresponding to the above addresses
+ desc = "sh(wpkh(" + xpriv + "/0'/0'/*'" + "))"
+ self.log.info("Ranged descriptor import should fail without a specified range")
+ self.test_importmulti({"desc": descsum_create(desc),
+ "timestamp": "now"},
+ success=False,
+ error_code=-8,
+ error_message='Descriptor is ranged, please specify the range')
+
+ # Test importing of a ranged descriptor with xpriv
+ self.log.info("Should import the ranged descriptor with specified range as solvable")
+ self.test_importmulti({"desc": descsum_create(desc),
+ "timestamp": "now",
+ "range": 1},
+ success=True)
+ for address in addresses:
+ test_address(self.nodes[1],
+ address,
+ solvable=True,
+ ismine=True)
+
+ self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": -1},
+ success=False, error_code=-8, error_message='End of range is too high')
+
+ self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [-1, 10]},
+ success=False, error_code=-8, error_message='Range should be greater or equal than 0')
+
+ self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [(2 << 31 + 1) - 1000000, (2 << 31 + 1)]},
+ success=False, error_code=-8, error_message='End of range is too high')
+
+ self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [2, 1]},
+ success=False, error_code=-8, error_message='Range specified as [begin,end] must not have begin after end')
+
+ self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [0, 1000001]},
+ success=False, error_code=-8, error_message='Range is too large')
+
+ # Test importing a descriptor containing a WIF private key
+ wif_priv = "cTe1f5rdT8A8DFgVWTjyPwACsDPJM9ff4QngFxUixCSvvbg1x6sh"
+ address = "2MuhcG52uHPknxDgmGPsV18jSHFBnnRgjPg"
+ desc = "sh(wpkh(" + wif_priv + "))"
+ self.log.info("Should import a descriptor with a WIF private key as spendable")
+ self.test_importmulti({"desc": descsum_create(desc),
+ "timestamp": "now"},
+ success=True)
+ test_address(self.nodes[1],
+ address,
+ solvable=True,
+ ismine=True)
+
+ # dump the private key to ensure it matches what was imported
+ privkey = self.nodes[1].dumpprivkey(address)
+ assert_equal(privkey, wif_priv)
+
+ # Test importing of a P2PKH address via descriptor
+ key = get_key(self.nodes[0])
+ self.log.info("Should import a p2pkh address from descriptor")
+ self.test_importmulti({"desc": descsum_create("pkh(" + key.pubkey + ")"),
+ "timestamp": "now",
+ "label": "Descriptor import test"},
+ True,
+ warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ test_address(self.nodes[1],
+ key.p2pkh_addr,
+ solvable=True,
+ ismine=False,
+ label="Descriptor import test")
+
+ # Test import fails if both desc and scriptPubKey are provided
+ key = get_key(self.nodes[0])
+ self.log.info("Import should fail if both scriptPubKey and desc are provided")
+ self.test_importmulti({"desc": descsum_create("pkh(" + key.pubkey + ")"),
+ "scriptPubKey": {"address": key.p2pkh_addr},
+ "timestamp": "now"},
+ success=False,
+ error_code=-8,
+ error_message='Both a descriptor and a scriptPubKey should not be provided.')
+
+ # Test import fails if neither desc nor scriptPubKey are present
+ key = get_key(self.nodes[0])
+ self.log.info("Import should fail if neither a descriptor nor a scriptPubKey are provided")
+ self.test_importmulti({"timestamp": "now"},
+ success=False,
+ error_code=-8,
+ error_message='Either a descriptor or scriptPubKey must be provided.')
+
+ # Test importing of a multisig via descriptor
+ key1 = get_key(self.nodes[0])
+ key2 = get_key(self.nodes[0])
+ self.log.info("Should import a 1-of-2 bare multisig from descriptor")
+ self.test_importmulti({"desc": descsum_create("multi(1," + key1.pubkey + "," + key2.pubkey + ")"),
+ "timestamp": "now"},
+ success=True,
+ warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ self.log.info("Should not treat individual keys from the imported bare multisig as watchonly")
+ test_address(self.nodes[1],
+ key1.p2pkh_addr,
+ ismine=False,
+ iswatchonly=False)
+
+ # Import pubkeys with key origin info
+ self.log.info("Addresses should have hd keypath and master key id after import with key origin")
+ pub_addr = self.nodes[1].getnewaddress()
+ pub_addr = self.nodes[1].getnewaddress()
+ info = self.nodes[1].getaddressinfo(pub_addr)
+ pub = info['pubkey']
+ pub_keypath = info['hdkeypath']
+ pub_fpr = info['hdmasterfingerprint']
+ result = self.nodes[0].importmulti(
+ [{
+ 'desc' : descsum_create("wpkh([" + pub_fpr + pub_keypath[1:] +"]" + pub + ")"),
+ "timestamp": "now",
+ }]
+ )
+ assert result[0]['success']
+ pub_import_info = self.nodes[0].getaddressinfo(pub_addr)
+ assert_equal(pub_import_info['hdmasterfingerprint'], pub_fpr)
+ assert_equal(pub_import_info['pubkey'], pub)
+ assert_equal(pub_import_info['hdkeypath'], pub_keypath)
+
+ # Import privkeys with key origin info
+ priv_addr = self.nodes[1].getnewaddress()
+ info = self.nodes[1].getaddressinfo(priv_addr)
+ priv = self.nodes[1].dumpprivkey(priv_addr)
+ priv_keypath = info['hdkeypath']
+ priv_fpr = info['hdmasterfingerprint']
+ result = self.nodes[0].importmulti(
+ [{
+ 'desc' : descsum_create("wpkh([" + priv_fpr + priv_keypath[1:] + "]" + priv + ")"),
+ "timestamp": "now",
+ }]
+ )
+ assert result[0]['success']
+ priv_import_info = self.nodes[0].getaddressinfo(priv_addr)
+ assert_equal(priv_import_info['hdmasterfingerprint'], priv_fpr)
+ assert_equal(priv_import_info['hdkeypath'], priv_keypath)
+
+ # Make sure the key origin info are still there after a restart
+ self.stop_nodes()
+ self.start_nodes()
+ import_info = self.nodes[0].getaddressinfo(pub_addr)
+ assert_equal(import_info['hdmasterfingerprint'], pub_fpr)
+ assert_equal(import_info['hdkeypath'], pub_keypath)
+ import_info = self.nodes[0].getaddressinfo(priv_addr)
+ assert_equal(import_info['hdmasterfingerprint'], priv_fpr)
+ assert_equal(import_info['hdkeypath'], priv_keypath)
+
+ # Check legacy import does not import key origin info
+ self.log.info("Legacy imports don't have key origin info")
+ pub_addr = self.nodes[1].getnewaddress()
+ info = self.nodes[1].getaddressinfo(pub_addr)
+ pub = info['pubkey']
+ result = self.nodes[0].importmulti(
+ [{
+ 'scriptPubKey': {'address': pub_addr},
+ 'pubkeys': [pub],
+ "timestamp": "now",
+ }]
+ )
+ assert result[0]['success']
+ pub_import_info = self.nodes[0].getaddressinfo(pub_addr)
+ assert_equal(pub_import_info['pubkey'], pub)
+ assert 'hdmasterfingerprint' not in pub_import_info
+ assert 'hdkeypath' not in pub_import_info
+
+ # Import some public keys to the keypool of a no privkey wallet
+ self.log.info("Adding pubkey to keypool of disableprivkey wallet")
+ self.nodes[1].createwallet(wallet_name="noprivkeys", disable_private_keys=True)
+ wrpc = self.nodes[1].get_wallet_rpc("noprivkeys")
+
+ addr1 = self.nodes[0].getnewaddress()
+ addr2 = self.nodes[0].getnewaddress()
+ pub1 = self.nodes[0].getaddressinfo(addr1)['pubkey']
+ pub2 = self.nodes[0].getaddressinfo(addr2)['pubkey']
+ result = wrpc.importmulti(
+ [{
+ 'desc': descsum_create('wpkh(' + pub1 + ')'),
+ 'keypool': True,
+ "timestamp": "now",
+ },
+ {
+ 'desc': descsum_create('wpkh(' + pub2 + ')'),
+ 'keypool': True,
+ "timestamp": "now",
+ }]
+ )
+ assert result[0]['success']
+ assert result[1]['success']
+ assert_equal(wrpc.getwalletinfo()["keypoolsize"], 2)
+ newaddr1 = wrpc.getnewaddress()
+ assert_equal(addr1, newaddr1)
+ newaddr2 = wrpc.getnewaddress()
+ assert_equal(addr2, newaddr2)
+
+ # Import some public keys to the internal keypool of a no privkey wallet
+ self.log.info("Adding pubkey to internal keypool of disableprivkey wallet")
+ addr1 = self.nodes[0].getnewaddress()
+ addr2 = self.nodes[0].getnewaddress()
+ pub1 = self.nodes[0].getaddressinfo(addr1)['pubkey']
+ pub2 = self.nodes[0].getaddressinfo(addr2)['pubkey']
+ result = wrpc.importmulti(
+ [{
+ 'desc': descsum_create('wpkh(' + pub1 + ')'),
+ 'keypool': True,
+ 'internal': True,
+ "timestamp": "now",
+ },
+ {
+ 'desc': descsum_create('wpkh(' + pub2 + ')'),
+ 'keypool': True,
+ 'internal': True,
+ "timestamp": "now",
+ }]
+ )
+ assert result[0]['success']
+ assert result[1]['success']
+ assert_equal(wrpc.getwalletinfo()["keypoolsize_hd_internal"], 2)
+ newaddr1 = wrpc.getrawchangeaddress()
+ assert_equal(addr1, newaddr1)
+ newaddr2 = wrpc.getrawchangeaddress()
+ assert_equal(addr2, newaddr2)
+
+ # Import a multisig and make sure the keys don't go into the keypool
+ self.log.info('Imported scripts with pubkeys should not have their pubkeys go into the keypool')
+ addr1 = self.nodes[0].getnewaddress()
+ addr2 = self.nodes[0].getnewaddress()
+ pub1 = self.nodes[0].getaddressinfo(addr1)['pubkey']
+ pub2 = self.nodes[0].getaddressinfo(addr2)['pubkey']
+ result = wrpc.importmulti(
+ [{
+ 'desc': descsum_create('wsh(multi(2,' + pub1 + ',' + pub2 + '))'),
+ 'keypool': True,
+ "timestamp": "now",
+ }]
+ )
+ assert result[0]['success']
+ assert_equal(wrpc.getwalletinfo()["keypoolsize"], 0)
+
+ # Cannot import those pubkeys to keypool of wallet with privkeys
+ self.log.info("Pubkeys cannot be added to the keypool of a wallet with private keys")
+ wrpc = self.nodes[1].get_wallet_rpc("")
+ assert wrpc.getwalletinfo()['private_keys_enabled']
+ result = wrpc.importmulti(
+ [{
+ 'desc': descsum_create('wpkh(' + pub1 + ')'),
+ 'keypool': True,
+ "timestamp": "now",
+ }]
+ )
+ assert_equal(result[0]['error']['code'], -8)
+ assert_equal(result[0]['error']['message'], "Keys can only be imported to the keypool when private keys are disabled")
+
+ # Make sure ranged imports import keys in order
+ self.log.info('Key ranges should be imported in order')
+ wrpc = self.nodes[1].get_wallet_rpc("noprivkeys")
+ assert_equal(wrpc.getwalletinfo()["keypoolsize"], 0)
+ assert_equal(wrpc.getwalletinfo()["private_keys_enabled"], False)
+ xpub = "tpubDAXcJ7s7ZwicqjprRaEWdPoHKrCS215qxGYxpusRLLmJuT69ZSicuGdSfyvyKpvUNYBW1s2U3NSrT6vrCYB9e6nZUEvrqnwXPF8ArTCRXMY"
+ addresses = [
+ 'bcrt1qtmp74ayg7p24uslctssvjm06q5phz4yrxucgnv', # m/0'/0'/0
+ 'bcrt1q8vprchan07gzagd5e6v9wd7azyucksq2xc76k8', # m/0'/0'/1
+ 'bcrt1qtuqdtha7zmqgcrr26n2rqxztv5y8rafjp9lulu', # m/0'/0'/2
+ 'bcrt1qau64272ymawq26t90md6an0ps99qkrse58m640', # m/0'/0'/3
+ 'bcrt1qsg97266hrh6cpmutqen8s4s962aryy77jp0fg0', # m/0'/0'/4
+ ]
+ result = wrpc.importmulti(
+ [{
+ 'desc': descsum_create('wpkh([80002067/0h/0h]' + xpub + '/*)'),
+ 'keypool': True,
+ 'timestamp': 'now',
+ 'range' : [0, 4],
+ }]
+ )
+ for i in range(0, 5):
+ addr = wrpc.getnewaddress('', 'bech32')
+ assert_equal(addr, addresses[i])
+
if __name__ == '__main__':
ImportMultiTest().main()
diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py
index ceb9709712..e3aeb61197 100755
--- a/test/functional/wallet_keypool.py
+++ b/test/functional/wallet_keypool.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 wallet keypool and interaction with wallet encryption/locking."""
@@ -21,8 +21,7 @@ class KeyPoolTest(BitcoinTestFramework):
addr_before_encrypting = nodes[0].getnewaddress()
addr_before_encrypting_data = nodes[0].getaddressinfo(addr_before_encrypting)
wallet_info_old = nodes[0].getwalletinfo()
- assert_equal(wallet_info_old['hdseedid'], wallet_info_old['hdmasterkeyid'])
- assert(addr_before_encrypting_data['hdseedid'] == wallet_info_old['hdseedid'])
+ assert addr_before_encrypting_data['hdseedid'] == wallet_info_old['hdseedid']
# Encrypt wallet and wait to terminate
nodes[0].encryptwallet('test')
@@ -30,9 +29,8 @@ class KeyPoolTest(BitcoinTestFramework):
addr = nodes[0].getnewaddress()
addr_data = nodes[0].getaddressinfo(addr)
wallet_info = nodes[0].getwalletinfo()
- assert_equal(wallet_info['hdseedid'], wallet_info['hdmasterkeyid'])
- assert(addr_before_encrypting_data['hdseedid'] != wallet_info['hdseedid'])
- assert(addr_data['hdseedid'] == wallet_info['hdseedid'])
+ assert addr_before_encrypting_data['hdseedid'] != wallet_info['hdseedid']
+ assert addr_data['hdseedid'] == wallet_info['hdseedid']
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)
@@ -61,7 +59,7 @@ class KeyPoolTest(BitcoinTestFramework):
addr.add(nodes[0].getnewaddress())
addr.add(nodes[0].getnewaddress())
addr.add(nodes[0].getnewaddress())
- assert(len(addr) == 6)
+ assert len(addr) == 6
# the next one should fail
assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py
index b7c8d3098d..0014555ade 100755
--- a/test/functional/wallet_keypool_topup.py
+++ b/test/functional/wallet_keypool_topup.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 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 HD Wallet keypool restore function.
@@ -17,7 +17,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes_bi,
- sync_blocks,
)
@@ -55,11 +54,11 @@ class KeypoolRestoreTest(BitcoinTestFramework):
# Make sure we're creating the outputs we expect
address_details = self.nodes[idx].validateaddress(addr_extpool)
if i == 0:
- assert(not address_details["isscript"] and not address_details["iswitness"])
+ assert not address_details["isscript"] and not address_details["iswitness"]
elif i == 1:
- assert(address_details["isscript"] and not address_details["iswitness"])
+ assert address_details["isscript"] and not address_details["iswitness"]
else:
- assert(not address_details["isscript"] and address_details["iswitness"])
+ assert not address_details["isscript"] and address_details["iswitness"]
self.log.info("Send funds to wallet")
@@ -67,7 +66,7 @@ class KeypoolRestoreTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.nodes[0].sendtoaddress(addr_extpool, 5)
self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
self.log.info("Restart node with wallet backup")
self.stop_node(idx)
diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py
index 011975e371..5e94068930 100755
--- a/test/functional/wallet_listreceivedby.py
+++ b/test/functional/wallet_listreceivedby.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 listreceivedbyaddress RPC."""
@@ -10,7 +10,6 @@ from test_framework.util import (
assert_array_result,
assert_equal,
assert_raises_rpc_error,
- sync_blocks,
)
@@ -24,7 +23,7 @@ class ReceivedByTest(BitcoinTestFramework):
def run_test(self):
# Generate block to get out of IBD
self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.sync_blocks()
# save the number of coinbase reward addresses so far
num_cb_reward_addresses = len(self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True))
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index 25ab222375..021a29d4ac 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 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 listsincelast RPC."""
@@ -98,7 +98,8 @@ class ListSinceBlockTest (BitcoinTestFramework):
self.nodes[2].generate(7)
self.log.info('lastblockhash=%s' % (lastblockhash))
- self.sync_all([self.nodes[:2], self.nodes[2:]])
+ self.sync_all(self.nodes[:2])
+ self.sync_all(self.nodes[2:])
self.join_network()
diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py
index 17f044bf65..997d6e702c 100755
--- a/test/functional/wallet_listtransactions.py
+++ b/test/functional/wallet_listtransactions.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 listtransactions API."""
@@ -11,9 +11,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_array_result,
assert_equal,
- bytes_to_hex_str,
hex_str_to_bytes,
- sync_mempools,
)
def tx_from_hex(hexstring):
@@ -126,9 +124,9 @@ class ListTransactionsTest(BitcoinTestFramework):
# 1. Chain a few transactions that don't opt-in.
txid_1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1)
- assert(not is_opt_in(self.nodes[0], txid_1))
+ assert not is_opt_in(self.nodes[0], txid_1)
assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_1}, {"bip125-replaceable": "no"})
- sync_mempools(self.nodes)
+ self.sync_mempools()
assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_1}, {"bip125-replaceable": "no"})
# Tx2 will build off txid_1, still not opting in to RBF.
@@ -146,9 +144,9 @@ class ListTransactionsTest(BitcoinTestFramework):
txid_2 = self.nodes[1].sendrawtransaction(tx2_signed)
# ...and check the result
- assert(not is_opt_in(self.nodes[1], txid_2))
+ assert not is_opt_in(self.nodes[1], txid_2)
assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_2}, {"bip125-replaceable": "no"})
- sync_mempools(self.nodes)
+ self.sync_mempools()
assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_2}, {"bip125-replaceable": "no"})
# Tx3 will opt-in to RBF
@@ -158,13 +156,13 @@ class ListTransactionsTest(BitcoinTestFramework):
tx3 = self.nodes[0].createrawtransaction(inputs, outputs)
tx3_modified = tx_from_hex(tx3)
tx3_modified.vin[0].nSequence = 0
- tx3 = bytes_to_hex_str(tx3_modified.serialize())
+ tx3 = tx3_modified.serialize().hex()
tx3_signed = self.nodes[0].signrawtransactionwithwallet(tx3)['hex']
txid_3 = self.nodes[0].sendrawtransaction(tx3_signed)
- assert(is_opt_in(self.nodes[0], txid_3))
+ assert is_opt_in(self.nodes[0], txid_3)
assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_3}, {"bip125-replaceable": "yes"})
- sync_mempools(self.nodes)
+ self.sync_mempools()
assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_3}, {"bip125-replaceable": "yes"})
# Tx4 will chain off tx3. Doesn't signal itself, but depends on one
@@ -176,21 +174,21 @@ class ListTransactionsTest(BitcoinTestFramework):
tx4_signed = self.nodes[1].signrawtransactionwithwallet(tx4)["hex"]
txid_4 = self.nodes[1].sendrawtransaction(tx4_signed)
- assert(not is_opt_in(self.nodes[1], txid_4))
+ assert not is_opt_in(self.nodes[1], txid_4)
assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "yes"})
- sync_mempools(self.nodes)
+ self.sync_mempools()
assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "yes"})
# Replace tx3, and check that tx4 becomes unknown
tx3_b = tx3_modified
tx3_b.vout[0].nValue -= int(Decimal("0.004") * COIN) # bump the fee
- tx3_b = bytes_to_hex_str(tx3_b.serialize())
+ tx3_b = tx3_b.serialize().hex()
tx3_b_signed = self.nodes[0].signrawtransactionwithwallet(tx3_b)['hex']
- txid_3b = self.nodes[0].sendrawtransaction(tx3_b_signed, True)
- assert(is_opt_in(self.nodes[0], txid_3b))
+ txid_3b = self.nodes[0].sendrawtransaction(tx3_b_signed, 0)
+ assert is_opt_in(self.nodes[0], txid_3b)
assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "unknown"})
- sync_mempools(self.nodes)
+ self.sync_mempools()
assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "unknown"})
# Check gettransaction as well:
@@ -203,7 +201,7 @@ class ListTransactionsTest(BitcoinTestFramework):
# After mining a transaction, it's no longer BIP125-replaceable
self.nodes[0].generate(1)
- assert(txid_3b not in self.nodes[0].getrawmempool())
+ assert txid_3b not in self.nodes[0].getrawmempool()
assert_equal(self.nodes[0].gettransaction(txid_3b)["bip125-replaceable"], "no")
assert_equal(self.nodes[0].gettransaction(txid_4)["bip125-replaceable"], "unknown")
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 8ab569a3c3..984ffab5a4 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 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 multiwallet.
@@ -315,6 +315,14 @@ class MultiWalletTest(BitcoinTestFramework):
self.nodes[0].loadwallet(wallet_name)
assert_equal(rpc.getaddressinfo(addr)['ismine'], True)
+ # Test .walletlock file is closed
+ self.start_node(1)
+ wallet = os.path.join(self.options.tmpdir, 'my_wallet')
+ self.nodes[0].createwallet(wallet)
+ assert_raises_rpc_error(-4, "Error initializing wallet database environment", self.nodes[1].loadwallet, wallet)
+ self.nodes[0].unloadwallet(wallet)
+ self.nodes[1].loadwallet(wallet)
+
if __name__ == '__main__':
MultiWalletTest().main()
diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py
index 00bf58d709..5810e94938 100755
--- a/test/functional/wallet_resendwallettransactions.py
+++ b/test/functional/wallet_resendwallettransactions.py
@@ -1,32 +1,77 @@
#!/usr/bin/env python3
-# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Copyright (c) 2017-2019 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 resendwallettransactions RPC."""
+"""Test that the wallet resends transactions periodically."""
+from collections import defaultdict
+import time
+from test_framework.blocktools import create_block, create_coinbase
+from test_framework.messages import ToHex
+from test_framework.mininode import P2PInterface, mininode_lock
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error
+from test_framework.util import assert_equal, wait_until
+
+class P2PStoreTxInvs(P2PInterface):
+ def __init__(self):
+ super().__init__()
+ self.tx_invs_received = defaultdict(int)
+
+ def on_inv(self, message):
+ # Store how many times invs have been received for each tx.
+ for i in message.inv:
+ if i.type == 1:
+ # save txid
+ self.tx_invs_received[i.hash] += 1
class ResendWalletTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [['--walletbroadcast=false']]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self):
- # Should raise RPC_WALLET_ERROR (-4) if walletbroadcast is disabled.
- assert_raises_rpc_error(-4, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast", self.nodes[0].resendwallettransactions)
+ node = self.nodes[0] # alias
+
+ node.add_p2p_connection(P2PStoreTxInvs())
+
+ self.log.info("Create a new transaction and wait until it's broadcast")
+ txid = int(node.sendtoaddress(node.getnewaddress(), 1), 16)
+
+ # Wallet rebroadcast is first scheduled 1 sec after startup (see
+ # nNextResend in ResendWalletTransactions()). Sleep for just over a
+ # second to be certain that it has been called before the first
+ # setmocktime call below.
+ time.sleep(1.1)
+
+ # Can take a few seconds due to transaction trickling
+ wait_until(lambda: node.p2p.tx_invs_received[txid] >= 1, lock=mininode_lock)
+
+ # Add a second peer since txs aren't rebroadcast to the same peer (see filterInventoryKnown)
+ node.add_p2p_connection(P2PStoreTxInvs())
+
+ self.log.info("Create a block")
+ # Create and submit a block without the transaction.
+ # Transactions are only rebroadcast if there has been a block at least five minutes
+ # after the last time we tried to broadcast. Use mocktime and give an extra minute to be sure.
+ block_time = int(time.time()) + 6 * 60
+ node.setmocktime(block_time)
+ block = create_block(int(node.getbestblockhash(), 16), create_coinbase(node.getblockchaininfo()['blocks']), block_time)
+ block.nVersion = 3
+ block.rehash()
+ block.solve()
+ node.submitblock(ToHex(block))
- # Should return an empty array if there aren't unconfirmed wallet transactions.
- self.stop_node(0)
- self.start_node(0, extra_args=[])
- assert_equal(self.nodes[0].resendwallettransactions(), [])
+ # Transaction should not be rebroadcast
+ node.p2ps[1].sync_with_ping()
+ assert_equal(node.p2ps[1].tx_invs_received[txid], 0)
- # Should return an array with the unconfirmed wallet transaction.
- txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
- assert_equal(self.nodes[0].resendwallettransactions(), [txid])
+ self.log.info("Transaction should be rebroadcast after 30 minutes")
+ # Use mocktime and give an extra 5 minutes to be sure.
+ rebroadcast_time = int(time.time()) + 41 * 60
+ node.setmocktime(rebroadcast_time)
+ wait_until(lambda: node.p2ps[1].tx_invs_received[txid] >= 1, lock=mininode_lock)
if __name__ == '__main__':
ResendWalletTransactionsTest().main()
diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py
index 1c2e0a9cb7..60d7205887 100755
--- a/test/functional/wallet_txn_clone.py
+++ b/test/functional/wallet_txn_clone.py
@@ -1,16 +1,17 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 wallet accounts properly when there are cloned transactions with malleated scriptsigs."""
+import io
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes,
disconnect_nodes,
- sync_blocks,
)
+from test_framework.messages import CTransaction, COIN
class TxnMallTest(BitcoinTestFramework):
def set_test_params(self):
@@ -72,25 +73,20 @@ class TxnMallTest(BitcoinTestFramework):
clone_raw = self.nodes[0].createrawtransaction(clone_inputs, clone_outputs, clone_locktime)
# createrawtransaction randomizes the order of its outputs, so swap them if necessary.
- # output 0 is at version+#inputs+input+sigstub+sequence+#outputs
- # 40 BTC serialized is 00286bee00000000
- pos0 = 2 * (4 + 1 + 36 + 1 + 4 + 1)
- hex40 = "00286bee00000000"
- output_len = 16 + 2 + 2 * int("0x" + clone_raw[pos0 + 16:pos0 + 16 + 2], 0)
- if (rawtx1["vout"][0]["value"] == 40 and clone_raw[pos0:pos0 + 16] != hex40 or rawtx1["vout"][0]["value"] != 40 and clone_raw[pos0:pos0 + 16] == hex40):
- output0 = clone_raw[pos0:pos0 + output_len]
- output1 = clone_raw[pos0 + output_len:pos0 + 2 * output_len]
- clone_raw = clone_raw[:pos0] + output1 + output0 + clone_raw[pos0 + 2 * output_len:]
+ clone_tx = CTransaction()
+ clone_tx.deserialize(io.BytesIO(bytes.fromhex(clone_raw)))
+ if (rawtx1["vout"][0]["value"] == 40 and clone_tx.vout[0].nValue != 40*COIN or rawtx1["vout"][0]["value"] != 40 and clone_tx.vout[0].nValue == 40*COIN):
+ (clone_tx.vout[0], clone_tx.vout[1]) = (clone_tx.vout[1], clone_tx.vout[0])
# Use a different signature hash type to sign. This creates an equivalent but malleated clone.
# Don't send the clone anywhere yet
- tx1_clone = self.nodes[0].signrawtransactionwithwallet(clone_raw, None, "ALL|ANYONECANPAY")
+ tx1_clone = self.nodes[0].signrawtransactionwithwallet(clone_tx.serialize().hex(), None, "ALL|ANYONECANPAY")
assert_equal(tx1_clone["complete"], True)
# Have node0 mine a block, if requested:
if (self.options.mine_block):
self.nodes[0].generate(1)
- sync_blocks(self.nodes[0:2])
+ self.sync_blocks(self.nodes[0:2])
tx1 = self.nodes[0].gettransaction(txid1)
tx2 = self.nodes[0].gettransaction(txid2)
@@ -126,7 +122,7 @@ class TxnMallTest(BitcoinTestFramework):
self.nodes[2].sendrawtransaction(node0_tx2["hex"])
self.nodes[2].sendrawtransaction(tx2["hex"])
self.nodes[2].generate(1) # Mine another block to make sure we sync
- sync_blocks(self.nodes)
+ self.sync_blocks()
# Re-fetch transaction info:
tx1 = self.nodes[0].gettransaction(txid1)
diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py
index f114d5ab68..40eeb4048c 100755
--- a/test/functional/wallet_txn_doublespend.py
+++ b/test/functional/wallet_txn_doublespend.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Copyright (c) 2014-2019 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 wallet accounts properly when there is a double-spend conflict."""
@@ -11,7 +11,6 @@ from test_framework.util import (
connect_nodes,
disconnect_nodes,
find_output,
- sync_blocks,
)
class TxnMallTest(BitcoinTestFramework):
@@ -34,6 +33,14 @@ class TxnMallTest(BitcoinTestFramework):
def run_test(self):
# All nodes should start with 1,250 BTC:
starting_balance = 1250
+
+ # All nodes should be out of IBD.
+ # If the nodes are not all out of IBD, that can interfere with
+ # blockchain sync later in the test when nodes are connected, due to
+ # timing issues.
+ for n in self.nodes:
+ assert n.getblockchaininfo()["initialblockdownload"] == False
+
for i in range(4):
assert_equal(self.nodes[i].getbalance(), starting_balance)
self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress!
@@ -78,7 +85,7 @@ class TxnMallTest(BitcoinTestFramework):
# Have node0 mine a block:
if (self.options.mine_block):
self.nodes[0].generate(1)
- sync_blocks(self.nodes[0:2])
+ self.sync_blocks(self.nodes[0:2])
tx1 = self.nodes[0].gettransaction(txid1)
tx2 = self.nodes[0].gettransaction(txid2)
@@ -111,7 +118,7 @@ class TxnMallTest(BitcoinTestFramework):
# Reconnect the split network, and sync chain:
connect_nodes(self.nodes[1], 2)
self.nodes[2].generate(1) # Mine another block to make sure we sync
- sync_blocks(self.nodes)
+ self.sync_blocks()
assert_equal(self.nodes[0].gettransaction(doublespend_txid)["confirmations"], 2)
# Re-fetch transaction info:
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
new file mode 100755
index 0000000000..1869f71753
--- /dev/null
+++ b/test/fuzz/test_runner.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Run fuzz test targets.
+"""
+
+import argparse
+import configparser
+import os
+import sys
+import subprocess
+import logging
+
+
+def main():
+ parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ parser.add_argument(
+ "-l",
+ "--loglevel",
+ dest="loglevel",
+ default="INFO",
+ help="log events at this level and higher to the console. Can be set to DEBUG, INFO, WARNING, ERROR or CRITICAL. Passing --loglevel DEBUG will output all logs to console.",
+ )
+ parser.add_argument(
+ '--export_coverage',
+ action='store_true',
+ help='If true, export coverage information to files in the seed corpus',
+ )
+ parser.add_argument(
+ 'seed_dir',
+ help='The seed corpus to run on (must contain subfolders for each fuzz target).',
+ )
+ parser.add_argument(
+ 'target',
+ nargs='*',
+ help='The target(s) to run. Default is to run all targets.',
+ )
+
+ args = parser.parse_args()
+
+ # Set up logging
+ logging.basicConfig(
+ format='%(message)s',
+ level=int(args.loglevel) if args.loglevel.isdigit() else args.loglevel.upper(),
+ )
+
+ # Read config generated by configure.
+ config = configparser.ConfigParser()
+ configfile = os.path.abspath(os.path.dirname(__file__)) + "/../config.ini"
+ config.read_file(open(configfile, encoding="utf8"))
+
+ if not config["components"].getboolean("ENABLE_FUZZ"):
+ logging.error("Must have fuzz targets built")
+ sys.exit(1)
+
+ # Build list of tests
+ test_list_all = parse_test_list(makefile=os.path.join(config["environment"]["SRCDIR"], 'src', 'Makefile.test.include'))
+
+ if not test_list_all:
+ logging.error("No fuzz targets found")
+ sys.exit(1)
+
+ logging.info("Fuzz targets found: {}".format(test_list_all))
+
+ args.target = args.target or test_list_all # By default run all
+ test_list_error = list(set(args.target).difference(set(test_list_all)))
+ if test_list_error:
+ logging.error("Unknown fuzz targets selected: {}".format(test_list_error))
+ test_list_selection = list(set(test_list_all).intersection(set(args.target)))
+ if not test_list_selection:
+ logging.error("No fuzz targets selected")
+ logging.info("Fuzz targets selected: {}".format(test_list_selection))
+
+ try:
+ help_output = subprocess.run(
+ args=[
+ os.path.join(config["environment"]["BUILDDIR"], 'src', 'test', 'fuzz', test_list_selection[0]),
+ '-help=1',
+ ],
+ timeout=1,
+ check=True,
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ ).stderr
+ if "libFuzzer" not in help_output:
+ logging.error("Must be built with libFuzzer")
+ sys.exit(1)
+ except subprocess.TimeoutExpired:
+ logging.error("subprocess timed out: Currently only libFuzzer is supported")
+ sys.exit(1)
+
+ run_once(
+ corpus=args.seed_dir,
+ test_list=test_list_selection,
+ build_dir=config["environment"]["BUILDDIR"],
+ export_coverage=args.export_coverage,
+ )
+
+
+def run_once(*, corpus, test_list, build_dir, export_coverage):
+ for t in test_list:
+ args = [
+ os.path.join(build_dir, 'src', 'test', 'fuzz', t),
+ '-runs=1',
+ os.path.join(corpus, t),
+ ]
+ logging.debug('Run {} with args {}'.format(t, args))
+ output = subprocess.run(args, check=True, stderr=subprocess.PIPE, universal_newlines=True).stderr
+ logging.debug('Output: {}'.format(output))
+ if not export_coverage:
+ continue
+ for l in output.splitlines():
+ if 'INITED' in l:
+ with open(os.path.join(corpus, t + '_coverage'), 'w', encoding='utf-8') as cov_file:
+ cov_file.write(l)
+ break
+
+
+def parse_test_list(makefile):
+ with open(makefile, encoding='utf-8') as makefile_test:
+ test_list_all = []
+ read_targets = False
+ for line in makefile_test.readlines():
+ line = line.strip().replace('test/fuzz/', '').replace(' \\', '')
+ if read_targets:
+ if not line:
+ break
+ test_list_all.append(line)
+ continue
+
+ if line == 'FUZZ_TARGETS =':
+ read_targets = True
+ return test_list_all
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py
index c370ce0c04..1d6122a13d 100755
--- a/test/lint/check-doc.py
+++ b/test/lint/check-doc.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Copyright (c) 2015-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -12,26 +12,23 @@ Author: @MarcoFalke
from subprocess import check_output
import re
-import sys
FOLDER_GREP = 'src'
FOLDER_TEST = 'src/test/'
REGEX_ARG = '(?:ForceSet|SoftSet|Get|Is)(?:Bool)?Args?(?:Set)?\("(-[^"]+)"'
REGEX_DOC = 'AddArg\("(-[^"=]+?)(?:=|")'
-CMD_ROOT_DIR = '`git rev-parse --show-toplevel`/{}'.format(FOLDER_GREP)
+CMD_ROOT_DIR = '$(git rev-parse --show-toplevel)/{}'.format(FOLDER_GREP)
CMD_GREP_ARGS = r"git grep --perl-regexp '{}' -- {} ':(exclude){}'".format(REGEX_ARG, CMD_ROOT_DIR, FOLDER_TEST)
+CMD_GREP_WALLET_ARGS = r"git grep --function-context 'void WalletInit::AddWalletOptions' -- {} | grep AddArg".format(CMD_ROOT_DIR)
+CMD_GREP_WALLET_HIDDEN_ARGS = r"git grep --function-context 'void DummyWalletInit::AddWalletOptions' -- {}".format(CMD_ROOT_DIR)
CMD_GREP_DOCS = r"git grep --perl-regexp '{}' {}".format(REGEX_DOC, CMD_ROOT_DIR)
# list unsupported, deprecated and duplicate args as they need no documentation
SET_DOC_OPTIONAL = set(['-h', '-help', '-dbcrashratio', '-forcecompactdb'])
-def main():
- if sys.version_info >= (3, 6):
- used = check_output(CMD_GREP_ARGS, shell=True, universal_newlines=True, encoding='utf8')
- docd = check_output(CMD_GREP_DOCS, shell=True, universal_newlines=True, encoding='utf8')
- else:
- used = check_output(CMD_GREP_ARGS, shell=True).decode('utf8').strip()
- docd = check_output(CMD_GREP_DOCS, shell=True).decode('utf8').strip()
+def lint_missing_argument_documentation():
+ used = check_output(CMD_GREP_ARGS, shell=True).decode('utf8').strip()
+ docd = check_output(CMD_GREP_DOCS, shell=True).decode('utf8').strip()
args_used = set(re.findall(re.compile(REGEX_ARG), used))
args_docd = set(re.findall(re.compile(REGEX_DOC), docd)).union(SET_DOC_OPTIONAL)
@@ -45,7 +42,24 @@ def main():
print("Args unknown : {}".format(len(args_unknown)))
print(args_unknown)
- sys.exit(len(args_need_doc))
+ assert 0 == len(args_need_doc), "Please document the following arguments: {}".format(args_need_doc)
+
+
+def lint_missing_hidden_wallet_args():
+ wallet_args = check_output(CMD_GREP_WALLET_ARGS, shell=True).decode('utf8').strip()
+ wallet_hidden_args = check_output(CMD_GREP_WALLET_HIDDEN_ARGS, shell=True).decode('utf8').strip()
+
+ wallet_args = set(re.findall(re.compile(REGEX_DOC), wallet_args))
+ wallet_hidden_args = set(re.findall(re.compile(r' "([^"=]+)'), wallet_hidden_args))
+
+ hidden_missing = wallet_args.difference(wallet_hidden_args)
+ if hidden_missing:
+ assert 0, "Please add {} to the hidden args in DummyWalletInit::AddWalletOptions".format(hidden_missing)
+
+
+def main():
+ lint_missing_argument_documentation()
+ lint_missing_hidden_wallet_args()
if __name__ == "__main__":
diff --git a/test/lint/lint-all.sh b/test/lint/lint-all.sh
index 7c4f96cb3b..fabc24c91b 100755
--- a/test/lint/lint-all.sh
+++ b/test/lint/lint-all.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
#
-# Copyright (c) 2017 The Bitcoin Core developers
+# Copyright (c) 2017-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
@@ -16,11 +16,15 @@ set -u
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
LINTALL=$(basename "${BASH_SOURCE[0]}")
+EXIT_CODE=0
+
for f in "${SCRIPTDIR}"/lint-*.sh; do
if [ "$(basename "$f")" != "$LINTALL" ]; then
if ! "$f"; then
echo "^---- failure generated from $f"
- exit 1
+ EXIT_CODE=1
fi
fi
done
+
+exit ${EXIT_CODE}
diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh
index be67cbed31..2701015c79 100755
--- a/test/lint/lint-circular-dependencies.sh
+++ b/test/lint/lint-circular-dependencies.sh
@@ -10,10 +10,8 @@ export LC_ALL=C
EXPECTED_CIRCULAR_DEPENDENCIES=(
"chainparamsbase -> util/system -> chainparamsbase"
- "checkpoints -> validation -> checkpoints"
"index/txindex -> validation -> index/txindex"
"policy/fees -> txmempool -> policy/fees"
- "policy/policy -> validation -> policy/policy"
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
"qt/bantablemodel -> qt/clientmodel -> qt/bantablemodel"
"qt/bitcoingui -> qt/utilitydialog -> qt/bitcoingui"
@@ -26,19 +24,12 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel"
"qt/walletmodel -> qt/walletmodeltransaction -> qt/walletmodel"
"txmempool -> validation -> txmempool"
- "validation -> validationinterface -> validation"
"wallet/coincontrol -> wallet/wallet -> wallet/coincontrol"
"wallet/fees -> wallet/wallet -> wallet/fees"
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
- "policy/fees -> policy/policy -> validation -> policy/fees"
- "policy/rbf -> txmempool -> validation -> policy/rbf"
- "qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/addressbookpage"
+ "policy/fees -> txmempool -> validation -> policy/fees"
"qt/guiutil -> qt/walletmodel -> qt/optionsmodel -> qt/guiutil"
"txmempool -> validation -> validationinterface -> txmempool"
- "qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/receivecoinsdialog -> qt/addressbookpage"
- "qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/signverifymessagedialog -> qt/addressbookpage"
- "qt/guiutil -> qt/walletmodel -> qt/optionsmodel -> qt/intro -> qt/guiutil"
- "qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/sendcoinsdialog -> qt/sendcoinsentry -> qt/addressbookpage"
)
EXIT_CODE=0
diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py
index f5d4780b6d..224e62f04a 100755
--- a/test/lint/lint-format-strings.py
+++ b/test/lint/lint-format-strings.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (c) 2018 The Bitcoin Core developers
+# Copyright (c) 2018-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
@@ -39,7 +39,7 @@ def parse_function_calls(function_name, source_code):
>>> len(parse_function_calls("foo", "#define FOO foo();"))
0
"""
- assert(type(function_name) is str and type(source_code) is str and function_name)
+ assert type(function_name) is str and type(source_code) is str and function_name
lines = [re.sub("// .*", " ", line).strip()
for line in source_code.split("\n")
if not line.strip().startswith("#")]
@@ -53,7 +53,7 @@ def normalize(s):
>>> normalize(" /* nothing */ foo\tfoo /* bar */ foo ")
'foo foo foo'
"""
- assert(type(s) is str)
+ assert type(s) is str
s = s.replace("\n", " ")
s = s.replace("\t", " ")
s = re.sub("/\*.*?\*/", " ", s)
@@ -77,7 +77,7 @@ def escape(s):
>>> escape(r'foo \\t foo \\n foo \\\\ foo \\ foo \\"bar\\"')
'foo [escaped-tab] foo [escaped-newline] foo \\\\\\\\ foo \\\\ foo [escaped-quote]bar[escaped-quote]'
"""
- assert(type(s) is str)
+ assert type(s) is str
for raw_value, escaped_value in ESCAPE_MAP.items():
s = s.replace(raw_value, escaped_value)
return s
@@ -92,7 +92,7 @@ def unescape(s):
>>> unescape("foo [escaped-tab] foo [escaped-newline] foo \\\\\\\\ foo \\\\ foo [escaped-quote]bar[escaped-quote]")
'foo \\\\t foo \\\\n foo \\\\\\\\ foo \\\\ foo \\\\"bar\\\\"'
"""
- assert(type(s) is str)
+ assert type(s) is str
for raw_value, escaped_value in ESCAPE_MAP.items():
s = s.replace(escaped_value, raw_value)
return s
@@ -151,10 +151,10 @@ def parse_function_call_and_arguments(function_name, function_call):
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo>foo<1,2>(1,2),err)');
['strprintf(', '"%s (%d)",', ' foo>foo<1,2>(1,2),', 'err', ')']
"""
- assert(type(function_name) is str and type(function_call) is str and function_name)
+ assert type(function_name) is str and type(function_call) is str and function_name
remaining = normalize(escape(function_call))
expected_function_call = "{}(".format(function_name)
- assert(remaining.startswith(expected_function_call))
+ assert remaining.startswith(expected_function_call)
parts = [expected_function_call]
remaining = remaining[len(expected_function_call):]
open_parentheses = 1
@@ -213,7 +213,7 @@ def parse_string_content(argument):
>>> parse_string_content('1 2 3')
''
"""
- assert(type(argument) is str)
+ assert type(argument) is str
string_content = ""
in_string = False
for char in normalize(escape(argument)):
@@ -240,7 +240,7 @@ def count_format_specifiers(format_string):
>>> count_format_specifiers("foo %d bar %i foo %% foo %*d foo")
4
"""
- assert(type(format_string) is str)
+ assert type(format_string) is str
format_string = format_string.replace('%%', 'X')
n = 0
in_specifier = False
@@ -262,27 +262,27 @@ def main():
parser.add_argument("--skip-arguments", type=int, help="number of arguments before the format string "
"argument (e.g. 1 in the case of fprintf)", default=0)
parser.add_argument("function_name", help="function name (e.g. fprintf)", default=None)
- parser.add_argument("file", type=argparse.FileType("r", encoding="utf-8"), nargs="*", help="C++ source code file (e.g. foo.cpp)")
+ parser.add_argument("file", nargs="*", help="C++ source code file (e.g. foo.cpp)")
args = parser.parse_args()
-
exit_code = 0
- for f in args.file:
- for function_call_str in parse_function_calls(args.function_name, f.read()):
- parts = parse_function_call_and_arguments(args.function_name, function_call_str)
- relevant_function_call_str = unescape("".join(parts))[:512]
- if (f.name, relevant_function_call_str) in FALSE_POSITIVES:
- continue
- if len(parts) < 3 + args.skip_arguments:
- exit_code = 1
- print("{}: Could not parse function call string \"{}(...)\": {}".format(f.name, args.function_name, relevant_function_call_str))
- continue
- argument_count = len(parts) - 3 - args.skip_arguments
- format_str = parse_string_content(parts[1 + args.skip_arguments])
- format_specifier_count = count_format_specifiers(format_str)
- if format_specifier_count != argument_count:
- exit_code = 1
- print("{}: Expected {} argument(s) after format string but found {} argument(s): {}".format(f.name, format_specifier_count, argument_count, relevant_function_call_str))
- continue
+ for filename in args.file:
+ with open(filename, "r", encoding="utf-8") as f:
+ for function_call_str in parse_function_calls(args.function_name, f.read()):
+ parts = parse_function_call_and_arguments(args.function_name, function_call_str)
+ relevant_function_call_str = unescape("".join(parts))[:512]
+ if (f.name, relevant_function_call_str) in FALSE_POSITIVES:
+ continue
+ if len(parts) < 3 + args.skip_arguments:
+ exit_code = 1
+ print("{}: Could not parse function call string \"{}(...)\": {}".format(f.name, args.function_name, relevant_function_call_str))
+ continue
+ argument_count = len(parts) - 3 - args.skip_arguments
+ format_str = parse_string_content(parts[1 + args.skip_arguments])
+ format_specifier_count = count_format_specifiers(format_str)
+ if format_specifier_count != argument_count:
+ exit_code = 1
+ print("{}: Expected {} argument(s) after format string but found {} argument(s): {}".format(f.name, format_specifier_count, argument_count, relevant_function_call_str))
+ continue
sys.exit(exit_code)
diff --git a/test/lint/lint-format-strings.sh b/test/lint/lint-format-strings.sh
index c994ae3f4d..cb630c78ad 100755
--- a/test/lint/lint-format-strings.sh
+++ b/test/lint/lint-format-strings.sh
@@ -13,6 +13,7 @@ export LC_ALL=C
FUNCTION_NAMES_AND_NUMBER_OF_LEADING_ARGUMENTS=(
"FatalError,0"
"fprintf,1"
+ "tfm::format,1" # Assuming tfm::::format(std::ostream&, ...
"LogConnectFailure,1"
"LogPrint,1"
"LogPrintf,0"
diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh
index 1534d5ef68..9a1aa766f7 100755
--- a/test/lint/lint-locale-dependence.sh
+++ b/test/lint/lint-locale-dependence.sh
@@ -18,7 +18,6 @@ KNOWN_VIOLATIONS=(
"src/util/strencodings.cpp:.*strtoul"
"src/util/strencodings.h:.*atoi"
"src/util/system.cpp:.*atoi"
- "src/util/system.cpp:.*fprintf"
)
REGEXP_IGNORE_EXTERNAL_DEPENDENCIES="^src/(crypto/ctaes/|leveldb/|secp256k1/|tinyformat.h|univalue/)"
@@ -85,7 +84,7 @@ LOCALE_DEPENDENT_FUNCTIONS=(
mbtowc # LC_CTYPE
mktime
normalize # boost::locale::normalize
-# printf # LC_NUMERIC
+ printf # LC_NUMERIC
putwc
putwchar
scanf # LC_NUMERIC
@@ -189,8 +188,7 @@ GIT_GREP_OUTPUT=$(git grep -E "[^a-zA-Z0-9_\`'\"<>](${REGEXP_LOCALE_DEPENDENT_FU
EXIT_CODE=0
for LOCALE_DEPENDENT_FUNCTION in "${LOCALE_DEPENDENT_FUNCTIONS[@]}"; do
MATCHES=$(grep -E "[^a-zA-Z0-9_\`'\"<>]${LOCALE_DEPENDENT_FUNCTION}(_r|_s)?[^a-zA-Z0-9_\`'\"<>]" <<< "${GIT_GREP_OUTPUT}" | \
- grep -vE "\.(c|cpp|h):\s*(//|\*|/\*|\").*${LOCALE_DEPENDENT_FUNCTION}" | \
- grep -vE 'fprintf\(.*(stdout|stderr)')
+ grep -vE "\.(c|cpp|h):\s*(//|\*|/\*|\").*${LOCALE_DEPENDENT_FUNCTION}")
if [[ ${REGEXP_IGNORE_EXTERNAL_DEPENDENCIES} != "" ]]; then
MATCHES=$(grep -vE "${REGEXP_IGNORE_EXTERNAL_DEPENDENCIES}" <<< "${MATCHES}")
fi
diff --git a/test/lint/lint-python-dead-code.sh b/test/lint/lint-python-dead-code.sh
index 1b897cd131..588ba428d7 100755
--- a/test/lint/lint-python-dead-code.sh
+++ b/test/lint/lint-python-dead-code.sh
@@ -15,5 +15,5 @@ fi
vulture \
--min-confidence 60 \
- --ignore-names "argtypes,connection_lost,connection_made,converter,data_received,daemon,errcheck,get_ecdh_key,get_privkey,is_compressed,is_fullyvalid,msg_generic,on_*,optionxform,restype,set_privkey" \
+ --ignore-names "argtypes,connection_lost,connection_made,converter,data_received,daemon,errcheck,is_compressed,is_valid,verify_ecdsa,msg_generic,on_*,optionxform,restype,profile_with_perf" \
$(git ls-files -- "*.py" ":(exclude)contrib/" ":(exclude)test/functional/data/invalid_txs.py")
diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh
index f5b851aeab..a76806003f 100755
--- a/test/lint/lint-python.sh
+++ b/test/lint/lint-python.sh
@@ -90,4 +90,10 @@ elif PYTHONWARNINGS="ignore" flake8 --version | grep -q "Python 2"; then
exit 0
fi
-PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=$(IFS=","; echo "${enabled[*]}") "${@:-.}"
+PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=$(IFS=","; echo "${enabled[*]}") $(
+ if [[ $# == 0 ]]; then
+ git ls-files "*.py"
+ else
+ echo "$@"
+ fi
+)
diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt
index f0415443db..a25de2435b 100644
--- a/test/lint/lint-spelling.ignore-words.txt
+++ b/test/lint/lint-spelling.ignore-words.txt
@@ -5,3 +5,10 @@ mut
objext
unselect
useable
+wit
+unparseable
+copyable
+cachable
+errorstring
+keyserver
+homogenous
diff --git a/test/lint/lint-whitespace.sh b/test/lint/lint-whitespace.sh
index f318e19071..d5c1dee42d 100755
--- a/test/lint/lint-whitespace.sh
+++ b/test/lint/lint-whitespace.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
#
-# Copyright (c) 2017 The Bitcoin Core developers
+# Copyright (c) 2017-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
@@ -12,11 +12,11 @@ export LC_ALL=C
while getopts "?" opt; do
case $opt in
?)
- echo "Usage: .lint-whitespace.sh [N]"
- echo " TRAVIS_COMMIT_RANGE='<commit range>' .lint-whitespace.sh"
- echo " .lint-whitespace.sh -?"
+ echo "Usage: $0 [N]"
+ echo " TRAVIS_COMMIT_RANGE='<commit range>' $0"
+ echo " $0 -?"
echo "Checks unstaged changes, the previous N commits, or a commit range."
- echo "TRAVIS_COMMIT_RANGE='47ba2c3...ee50c9e' .lint-whitespace.sh"
+ echo "TRAVIS_COMMIT_RANGE='47ba2c3...ee50c9e' $0"
exit 0
;;
esac
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index f0107f1361..643272de52 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -4,7 +4,6 @@ bool:wallet/wallet.cpp
float-divide-by-zero:policy/fees.cpp
float-divide-by-zero:validation.cpp
float-divide-by-zero:wallet/wallet.cpp
-nonnull-attribute:support/cleanse.cpp
unsigned-integer-overflow:arith_uint256.h
unsigned-integer-overflow:basic_string.h
unsigned-integer-overflow:bench/bench.h
@@ -16,6 +15,7 @@ unsigned-integer-overflow:coded_stream.h
unsigned-integer-overflow:core_write.cpp
unsigned-integer-overflow:crypto/chacha20.cpp
unsigned-integer-overflow:crypto/ctaes/ctaes.c
+unsigned-integer-overflow:crypto/poly1305.cpp
unsigned-integer-overflow:crypto/ripemd160.cpp
unsigned-integer-overflow:crypto/sha1.cpp
unsigned-integer-overflow:crypto/sha256.cpp