aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.clang-format4
-rw-r--r--src/Makefile.am89
-rw-r--r--src/Makefile.bench.include1
-rw-r--r--src/Makefile.qt.include55
-rw-r--r--src/Makefile.test.include135
-rw-r--r--src/addrdb.cpp3
-rw-r--r--src/addrdb.h28
-rw-r--r--src/banman.cpp45
-rw-r--r--src/banman.h63
-rw-r--r--src/bench/bench_bitcoin.cpp2
-rw-r--r--src/bench/coin_selection.cpp4
-rw-r--r--src/bench/hashpadding.cpp47
-rw-r--r--src/bench/prevector.cpp4
-rw-r--r--src/bench/wallet_balance.cpp2
-rw-r--r--src/bitcoin-cli.cpp242
-rw-r--r--src/bitcoin-wallet.cpp1
-rw-r--r--src/bitcoind.cpp6
-rw-r--r--src/blockencodings.cpp7
-rw-r--r--src/blockencodings.h5
-rw-r--r--src/blockfilter.h8
-rw-r--r--src/bloom.cpp4
-rw-r--r--src/bloom.h10
-rw-r--r--src/chainparams.cpp1
-rw-r--r--src/compat/assumptions.h1
-rw-r--r--src/consensus/tx_verify.cpp10
-rw-r--r--src/consensus/tx_verify.h4
-rw-r--r--src/core_read.cpp6
-rw-r--r--src/core_write.cpp34
-rw-r--r--src/crypto/common.h4
-rw-r--r--src/dbwrapper.h12
-rw-r--r--src/flatfile.h8
-rw-r--r--src/fs.cpp42
-rw-r--r--src/httprpc.cpp17
-rw-r--r--src/httprpc.h7
-rw-r--r--src/httpserver.cpp8
-rw-r--r--src/index/base.cpp10
-rw-r--r--src/index/blockfilterindex.cpp43
-rw-r--r--src/index/blockfilterindex.h14
-rw-r--r--src/index/txindex.cpp15
-rw-r--r--src/init.cpp159
-rw-r--r--src/init.h5
-rw-r--r--src/interfaces/chain.cpp6
-rw-r--r--src/interfaces/chain.h7
-rw-r--r--src/interfaces/node.cpp39
-rw-r--r--src/interfaces/node.h23
-rw-r--r--src/interfaces/wallet.cpp45
-rw-r--r--src/interfaces/wallet.h15
-rw-r--r--src/key.cpp78
-rw-r--r--src/logging.cpp20
-rw-r--r--src/logging.h18
-rw-r--r--src/merkleblock.cpp18
-rw-r--r--src/merkleblock.h41
-rw-r--r--src/miner.cpp4
-rw-r--r--src/miner.h2
-rw-r--r--src/net.cpp176
-rw-r--r--src/net.h48
-rw-r--r--src/net_permissions.cpp33
-rw-r--r--src/net_permissions.h25
-rw-r--r--src/net_processing.cpp1323
-rw-r--r--src/net_processing.h11
-rw-r--r--src/netaddress.cpp19
-rw-r--r--src/netaddress.h37
-rw-r--r--src/netbase.cpp23
-rw-r--r--src/netmessagemaker.h8
-rw-r--r--src/node/coinstats.cpp72
-rw-r--r--src/node/coinstats.h8
-rw-r--r--src/node/context.h5
-rw-r--r--src/node/ui_interface.cpp (renamed from src/ui_interface.cpp)16
-rw-r--r--src/node/ui_interface.h (renamed from src/ui_interface.h)23
-rw-r--r--src/node/utxo_snapshot.h11
-rw-r--r--src/noui.cpp30
-rw-r--r--src/outputtype.cpp14
-rw-r--r--src/outputtype.h8
-rw-r--r--src/policy/feerate.cpp9
-rw-r--r--src/policy/feerate.h21
-rw-r--r--src/policy/fees.h9
-rw-r--r--src/policy/policy.cpp20
-rw-r--r--src/policy/policy.h2
-rw-r--r--src/primitives/block.h30
-rw-r--r--src/primitives/transaction.h25
-rw-r--r--src/protocol.cpp67
-rw-r--r--src/protocol.h172
-rw-r--r--src/psbt.cpp47
-rw-r--r--src/psbt.h16
-rw-r--r--src/pubkey.h3
-rw-r--r--src/qt/addressbookpage.cpp2
-rw-r--r--src/qt/addresstablemodel.cpp9
-rw-r--r--src/qt/addresstablemodel.h2
-rw-r--r--src/qt/bitcoin.cpp57
-rw-r--r--src/qt/bitcoin.qrc74
-rw-r--r--src/qt/bitcoinamountfield.cpp8
-rw-r--r--src/qt/bitcoingui.cpp63
-rw-r--r--src/qt/bitcoingui.h13
-rw-r--r--src/qt/bitcoinunits.cpp18
-rw-r--r--src/qt/bitcoinunits.h16
-rw-r--r--src/qt/clientmodel.cpp73
-rw-r--r--src/qt/clientmodel.h14
-rw-r--r--src/qt/coincontroldialog.cpp4
-rw-r--r--src/qt/forms/debugwindow.ui73
-rw-r--r--src/qt/forms/overviewpage.ui28
-rw-r--r--src/qt/forms/psbtoperationsdialog.ui148
-rw-r--r--src/qt/forms/receiverequestdialog.ui288
-rw-r--r--src/qt/guiutil.cpp71
-rw-r--r--src/qt/guiutil.h34
-rw-r--r--src/qt/modaloverlay.cpp28
-rw-r--r--src/qt/modaloverlay.h12
-rw-r--r--src/qt/optionsmodel.h3
-rw-r--r--src/qt/overviewpage.cpp51
-rw-r--r--src/qt/overviewpage.h2
-rw-r--r--src/qt/paymentserver.cpp4
-rw-r--r--src/qt/psbtoperationsdialog.cpp268
-rw-r--r--src/qt/psbtoperationsdialog.h54
-rw-r--r--src/qt/receiverequestdialog.cpp88
-rw-r--r--src/qt/receiverequestdialog.h3
-rw-r--r--src/qt/recentrequeststablemodel.cpp2
-rw-r--r--src/qt/recentrequeststablemodel.h18
-rwxr-xr-xsrc/qt/res/animation/makespinner.sh (renamed from src/qt/res/movies/makespinner.sh)0
-rw-r--r--src/qt/res/animation/spinner-000.png (renamed from src/qt/res/movies/spinner-000.png)bin1794 -> 1794 bytes
-rw-r--r--src/qt/res/animation/spinner-001.png (renamed from src/qt/res/movies/spinner-001.png)bin2376 -> 2376 bytes
-rw-r--r--src/qt/res/animation/spinner-002.png (renamed from src/qt/res/movies/spinner-002.png)bin2376 -> 2376 bytes
-rw-r--r--src/qt/res/animation/spinner-003.png (renamed from src/qt/res/movies/spinner-003.png)bin2355 -> 2355 bytes
-rw-r--r--src/qt/res/animation/spinner-004.png (renamed from src/qt/res/movies/spinner-004.png)bin2349 -> 2349 bytes
-rw-r--r--src/qt/res/animation/spinner-005.png (renamed from src/qt/res/movies/spinner-005.png)bin2305 -> 2305 bytes
-rw-r--r--src/qt/res/animation/spinner-006.png (renamed from src/qt/res/movies/spinner-006.png)bin2304 -> 2304 bytes
-rw-r--r--src/qt/res/animation/spinner-007.png (renamed from src/qt/res/movies/spinner-007.png)bin2283 -> 2283 bytes
-rw-r--r--src/qt/res/animation/spinner-008.png (renamed from src/qt/res/movies/spinner-008.png)bin2312 -> 2312 bytes
-rw-r--r--src/qt/res/animation/spinner-009.png (renamed from src/qt/res/movies/spinner-009.png)bin1810 -> 1810 bytes
-rw-r--r--src/qt/res/animation/spinner-010.png (renamed from src/qt/res/movies/spinner-010.png)bin2305 -> 2305 bytes
-rw-r--r--src/qt/res/animation/spinner-011.png (renamed from src/qt/res/movies/spinner-011.png)bin2338 -> 2338 bytes
-rw-r--r--src/qt/res/animation/spinner-012.png (renamed from src/qt/res/movies/spinner-012.png)bin2352 -> 2352 bytes
-rw-r--r--src/qt/res/animation/spinner-013.png (renamed from src/qt/res/movies/spinner-013.png)bin2377 -> 2377 bytes
-rw-r--r--src/qt/res/animation/spinner-014.png (renamed from src/qt/res/movies/spinner-014.png)bin2358 -> 2358 bytes
-rw-r--r--src/qt/res/animation/spinner-015.png (renamed from src/qt/res/movies/spinner-015.png)bin2405 -> 2405 bytes
-rw-r--r--src/qt/res/animation/spinner-016.png (renamed from src/qt/res/movies/spinner-016.png)bin2429 -> 2429 bytes
-rw-r--r--src/qt/res/animation/spinner-017.png (renamed from src/qt/res/movies/spinner-017.png)bin2408 -> 2408 bytes
-rw-r--r--src/qt/res/animation/spinner-018.png (renamed from src/qt/res/movies/spinner-018.png)bin1831 -> 1831 bytes
-rw-r--r--src/qt/res/animation/spinner-019.png (renamed from src/qt/res/movies/spinner-019.png)bin2380 -> 2380 bytes
-rw-r--r--src/qt/res/animation/spinner-020.png (renamed from src/qt/res/movies/spinner-020.png)bin2366 -> 2366 bytes
-rw-r--r--src/qt/res/animation/spinner-021.png (renamed from src/qt/res/movies/spinner-021.png)bin2368 -> 2368 bytes
-rw-r--r--src/qt/res/animation/spinner-022.png (renamed from src/qt/res/movies/spinner-022.png)bin2356 -> 2356 bytes
-rw-r--r--src/qt/res/animation/spinner-023.png (renamed from src/qt/res/movies/spinner-023.png)bin2311 -> 2311 bytes
-rw-r--r--src/qt/res/animation/spinner-024.png (renamed from src/qt/res/movies/spinner-024.png)bin2315 -> 2315 bytes
-rw-r--r--src/qt/res/animation/spinner-025.png (renamed from src/qt/res/movies/spinner-025.png)bin2298 -> 2298 bytes
-rw-r--r--src/qt/res/animation/spinner-026.png (renamed from src/qt/res/movies/spinner-026.png)bin2291 -> 2291 bytes
-rw-r--r--src/qt/res/animation/spinner-027.png (renamed from src/qt/res/movies/spinner-027.png)bin1816 -> 1816 bytes
-rw-r--r--src/qt/res/animation/spinner-028.png (renamed from src/qt/res/movies/spinner-028.png)bin2308 -> 2308 bytes
-rw-r--r--src/qt/res/animation/spinner-029.png (renamed from src/qt/res/movies/spinner-029.png)bin2356 -> 2356 bytes
-rw-r--r--src/qt/res/animation/spinner-030.png (renamed from src/qt/res/movies/spinner-030.png)bin2346 -> 2346 bytes
-rw-r--r--src/qt/res/animation/spinner-031.png (renamed from src/qt/res/movies/spinner-031.png)bin2380 -> 2380 bytes
-rw-r--r--src/qt/res/animation/spinner-032.png (renamed from src/qt/res/movies/spinner-032.png)bin2345 -> 2345 bytes
-rw-r--r--src/qt/res/animation/spinner-033.png (renamed from src/qt/res/movies/spinner-033.png)bin2401 -> 2401 bytes
-rw-r--r--src/qt/res/animation/spinner-034.png (renamed from src/qt/res/movies/spinner-034.png)bin2422 -> 2422 bytes
-rw-r--r--src/qt/res/animation/spinner-035.png (renamed from src/qt/res/movies/spinner-035.png)bin2406 -> 2406 bytes
-rw-r--r--src/qt/rpcconsole.cpp25
-rw-r--r--src/qt/sendcoinsdialog.cpp4
-rw-r--r--src/qt/sendcoinsrecipient.h33
-rw-r--r--src/qt/signverifymessagedialog.cpp1
-rw-r--r--src/qt/splashscreen.cpp1
-rw-r--r--src/qt/test/addressbooktests.cpp2
-rw-r--r--src/qt/test/apptests.cpp8
-rw-r--r--src/qt/test/test_main.cpp4
-rw-r--r--src/qt/test/wallettests.cpp34
-rw-r--r--src/qt/trafficgraphwidget.cpp1
-rw-r--r--src/qt/transactionrecord.cpp10
-rw-r--r--src/qt/transactionrecord.h13
-rw-r--r--src/qt/transactiontablemodel.cpp27
-rw-r--r--src/qt/transactiontablemodel.h2
-rw-r--r--src/qt/transactionview.cpp17
-rw-r--r--src/qt/transactionview.h14
-rw-r--r--src/qt/walletcontroller.cpp21
-rw-r--r--src/qt/walletcontroller.h1
-rw-r--r--src/qt/walletframe.cpp6
-rw-r--r--src/qt/walletframe.h2
-rw-r--r--src/qt/walletmodel.cpp57
-rw-r--r--src/qt/walletmodel.h15
-rw-r--r--src/qt/walletview.cpp102
-rw-r--r--src/qt/walletview.h3
-rw-r--r--src/rest.cpp62
-rw-r--r--src/rpc/blockchain.cpp144
-rw-r--r--src/rpc/blockchain.h15
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/mining.cpp65
-rw-r--r--src/rpc/mining.h11
-rw-r--r--src/rpc/misc.cpp16
-rw-r--r--src/rpc/net.cpp123
-rw-r--r--src/rpc/rawtransaction.cpp98
-rw-r--r--src/rpc/rawtransaction_util.cpp4
-rw-r--r--src/rpc/request.cpp10
-rw-r--r--src/rpc/request.h19
-rw-r--r--src/rpc/server.cpp96
-rw-r--r--src/rpc/server.h22
-rw-r--r--src/rpc/util.cpp81
-rw-r--r--src/rpc/util.h22
-rw-r--r--src/scheduler.cpp42
-rw-r--r--src/scheduler.h95
-rw-r--r--src/script/descriptor.cpp24
-rw-r--r--src/script/interpreter.cpp2
-rw-r--r--src/script/keyorigin.h8
-rw-r--r--src/script/script.cpp4
-rw-r--r--src/script/script.h9
-rw-r--r--src/script/script_error.cpp4
-rw-r--r--src/script/script_error.h4
-rw-r--r--src/script/sigcache.cpp14
-rw-r--r--src/script/sign.cpp60
-rw-r--r--src/script/signingprovider.cpp6
-rw-r--r--src/script/standard.cpp149
-rw-r--r--src/script/standard.h154
-rw-r--r--src/secp256k1/.gitignore1
-rw-r--r--src/secp256k1/.travis.yml72
-rw-r--r--src/secp256k1/Makefile.am62
-rw-r--r--src/secp256k1/README.md65
-rw-r--r--src/secp256k1/SECURITY.md15
-rw-r--r--src/secp256k1/build-aux/m4/ax_jni_include_dir.m4145
-rw-r--r--src/secp256k1/build-aux/m4/bitcoin_secp.m43
-rw-r--r--src/secp256k1/configure.ac186
-rw-r--r--src/secp256k1/contrib/lax_der_parsing.c6
-rwxr-xr-xsrc/secp256k1/contrib/travis.sh65
-rw-r--r--src/secp256k1/include/secp256k1.h202
-rw-r--r--src/secp256k1/include/secp256k1_ecdh.h37
-rw-r--r--src/secp256k1/include/secp256k1_preallocated.h128
-rw-r--r--src/secp256k1/include/secp256k1_recovery.h2
-rw-r--r--src/secp256k1/src/asm/field_10x26_arm.s6
-rw-r--r--src/secp256k1/src/basic-config.h5
-rw-r--r--src/secp256k1/src/bench.h97
-rw-r--r--src/secp256k1/src/bench_ecdh.c15
-rw-r--r--src/secp256k1/src/bench_ecmult.c49
-rw-r--r--src/secp256k1/src/bench_internal.c214
-rw-r--r--src/secp256k1/src/bench_recover.c8
-rw-r--r--src/secp256k1/src/bench_sign.c8
-rw-r--r--src/secp256k1/src/bench_verify.c15
-rw-r--r--src/secp256k1/src/ecdsa_impl.h94
-rw-r--r--src/secp256k1/src/eckey_impl.h12
-rw-r--r--src/secp256k1/src/ecmult.h8
-rw-r--r--src/secp256k1/src/ecmult_const.h7
-rw-r--r--src/secp256k1/src/ecmult_const_impl.h49
-rw-r--r--src/secp256k1/src/ecmult_gen.h29
-rw-r--r--src/secp256k1/src/ecmult_gen_impl.h98
-rw-r--r--src/secp256k1/src/ecmult_impl.h183
-rw-r--r--src/secp256k1/src/field.h10
-rw-r--r--src/secp256k1/src/field_10x26_impl.h21
-rw-r--r--src/secp256k1/src/field_5x52_impl.h21
-rw-r--r--src/secp256k1/src/field_impl.h2
-rw-r--r--src/secp256k1/src/gen_context.c33
-rw-r--r--src/secp256k1/src/group.h13
-rw-r--r--src/secp256k1/src/group_impl.h55
-rw-r--r--src/secp256k1/src/hash_impl.h3
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java446
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java226
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java45
-rw-r--r--src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java51
-rw-r--r--src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c379
-rw-r--r--src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h119
-rw-r--r--src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c15
-rw-r--r--src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h22
-rw-r--r--src/secp256k1/src/modules/ecdh/main_impl.h46
-rw-r--r--[-rwxr-xr-x]src/secp256k1/src/modules/recovery/main_impl.h39
-rw-r--r--src/secp256k1/src/modules/recovery/tests_impl.h2
-rw-r--r--src/secp256k1/src/scalar.h13
-rw-r--r--src/secp256k1/src/scalar_4x64_impl.h11
-rw-r--r--src/secp256k1/src/scalar_8x32_impl.h15
-rw-r--r--src/secp256k1/src/scalar_impl.h11
-rw-r--r--src/secp256k1/src/scalar_low.h2
-rw-r--r--src/secp256k1/src/scalar_low_impl.h13
-rw-r--r--src/secp256k1/src/scratch.h31
-rw-r--r--src/secp256k1/src/scratch_impl.h92
-rw-r--r--src/secp256k1/src/secp256k1.c312
-rw-r--r--src/secp256k1/src/tests.c679
-rw-r--r--src/secp256k1/src/tests_exhaustive.c6
-rw-r--r--src/secp256k1/src/util.h90
-rw-r--r--src/secp256k1/src/valgrind_ctime_test.c119
-rw-r--r--src/serialize.h159
-rw-r--r--src/span.h168
-rw-r--r--src/support/lockedpool.cpp1
-rw-r--r--src/support/lockedpool.h4
-rw-r--r--src/sync.cpp136
-rw-r--r--src/sync.h12
-rw-r--r--src/test/blockencodings_tests.cpp19
-rw-r--r--src/test/blockfilter_index_tests.cpp8
-rw-r--r--src/test/checkqueue_tests.cpp25
-rw-r--r--src/test/coins_tests.cpp2
-rw-r--r--src/test/data/script_tests.json2
-rw-r--r--src/test/dbwrapper_tests.cpp36
-rw-r--r--src/test/denialofservice_tests.cpp122
-rw-r--r--src/test/descriptor_tests.cpp4
-rw-r--r--src/test/fuzz/addrdb.cpp18
-rw-r--r--src/test/fuzz/asmap.cpp4
-rw-r--r--src/test/fuzz/asmap_direct.cpp6
-rw-r--r--src/test/fuzz/autofile.cpp72
-rw-r--r--src/test/fuzz/banman.cpp88
-rw-r--r--src/test/fuzz/block.cpp5
-rw-r--r--src/test/fuzz/block_header.cpp12
-rw-r--r--src/test/fuzz/blockfilter.cpp4
-rw-r--r--src/test/fuzz/bloom_filter.cpp8
-rw-r--r--src/test/fuzz/buffered_file.cpp78
-rw-r--r--src/test/fuzz/chain.cpp4
-rw-r--r--src/test/fuzz/checkqueue.cpp1
-rw-r--r--src/test/fuzz/coins_view.cpp294
-rw-r--r--src/test/fuzz/crypto.cpp124
-rw-r--r--src/test/fuzz/crypto_aes256.cpp30
-rw-r--r--src/test/fuzz/crypto_aes256cbc.cpp34
-rw-r--r--src/test/fuzz/crypto_chacha20.cpp50
-rw-r--r--src/test/fuzz/crypto_chacha20_poly1305_aead.cpp72
-rw-r--r--src/test/fuzz/crypto_common.cpp70
-rw-r--r--src/test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp25
-rw-r--r--src/test/fuzz/crypto_poly1305.cpp22
-rw-r--r--src/test/fuzz/cuckoocache.cpp1
-rw-r--r--src/test/fuzz/decode_tx.cpp2
-rw-r--r--src/test/fuzz/fees.cpp1
-rw-r--r--src/test/fuzz/flatfile.cpp6
-rw-r--r--src/test/fuzz/fuzz.cpp18
-rw-r--r--src/test/fuzz/golomb_rice.cpp2
-rw-r--r--src/test/fuzz/hex.cpp3
-rw-r--r--src/test/fuzz/http_request.cpp10
-rw-r--r--src/test/fuzz/key.cpp18
-rw-r--r--src/test/fuzz/kitchen_sink.cpp1
-rw-r--r--src/test/fuzz/load_external_block_file.cpp31
-rw-r--r--src/test/fuzz/merkleblock.cpp4
-rw-r--r--src/test/fuzz/message.cpp1
-rw-r--r--src/test/fuzz/net_permissions.cpp6
-rw-r--r--src/test/fuzz/netaddress.cpp30
-rw-r--r--src/test/fuzz/p2p_transport_deserializer.cpp2
-rw-r--r--src/test/fuzz/policy_estimator.cpp17
-rw-r--r--src/test/fuzz/policy_estimator_io.cpp28
-rw-r--r--src/test/fuzz/pow.cpp6
-rw-r--r--src/test/fuzz/prevector.cpp93
-rw-r--r--src/test/fuzz/primitives_transaction.cpp8
-rw-r--r--src/test/fuzz/process_message.cpp24
-rw-r--r--src/test/fuzz/process_messages.cpp5
-rw-r--r--src/test/fuzz/protocol.cpp6
-rw-r--r--src/test/fuzz/psbt.cpp2
-rw-r--r--src/test/fuzz/rbf.cpp6
-rw-r--r--src/test/fuzz/rolling_bloom_filter.cpp4
-rw-r--r--src/test/fuzz/script.cpp52
-rw-r--r--src/test/fuzz/script_bitcoin_consensus.cpp31
-rw-r--r--src/test/fuzz/script_descriptor_cache.cpp42
-rw-r--r--src/test/fuzz/script_interpreter.cpp41
-rw-r--r--src/test/fuzz/script_sigcache.cpp45
-rw-r--r--src/test/fuzz/script_sign.cpp149
-rw-r--r--src/test/fuzz/span.cpp4
-rw-r--r--src/test/fuzz/spanparsing.cpp2
-rw-r--r--src/test/fuzz/string.cpp15
-rw-r--r--src/test/fuzz/strprintf.cpp28
-rw-r--r--src/test/fuzz/util.h331
-rw-r--r--src/test/getarg_tests.cpp133
-rw-r--r--src/test/key_tests.cpp44
-rw-r--r--src/test/miner_tests.cpp6
-rw-r--r--src/test/multisig_tests.cpp2
-rw-r--r--src/test/net_tests.cpp5
-rw-r--r--src/test/netbase_tests.cpp19
-rw-r--r--src/test/policy_fee_tests.cpp34
-rw-r--r--src/test/raii_event_tests.cpp19
-rw-r--r--src/test/ref_tests.cpp33
-rw-r--r--src/test/rpc_tests.cpp14
-rw-r--r--src/test/scheduler_tests.cpp8
-rw-r--r--src/test/script_standard_tests.cpp106
-rw-r--r--src/test/script_tests.cpp4
-rw-r--r--src/test/serialize_tests.cpp23
-rw-r--r--src/test/sighash_tests.cpp2
-rw-r--r--src/test/sync_tests.cpp2
-rw-r--r--src/test/timedata_tests.cpp3
-rw-r--r--src/test/transaction_tests.cpp8
-rw-r--r--src/test/util/mining.cpp6
-rw-r--r--src/test/util/setup_common.cpp19
-rw-r--r--src/test/util/setup_common.h4
-rw-r--r--src/test/util/transaction_utils.h4
-rw-r--r--src/test/util_tests.cpp32
-rw-r--r--src/test/util_threadnames_tests.cpp2
-rw-r--r--src/test/validation_block_tests.cpp14
-rw-r--r--src/threadsafety.h24
-rw-r--r--src/timedata.cpp40
-rw-r--r--src/txdb.cpp20
-rw-r--r--src/txmempool.cpp2
-rw-r--r--src/txmempool.h13
-rw-r--r--src/util/check.h20
-rw-r--r--src/util/error.cpp26
-rw-r--r--src/util/error.h4
-rw-r--r--src/util/fees.cpp40
-rw-r--r--src/util/fees.h1
-rw-r--r--src/util/ref.h38
-rw-r--r--src/util/system.cpp18
-rw-r--r--src/util/time.h9
-rw-r--r--src/util/translation.h5
-rw-r--r--src/util/ui_change_type.h15
-rw-r--r--src/validation.cpp259
-rw-r--r--src/validation.h110
-rw-r--r--src/validationinterface.cpp20
-rw-r--r--src/validationinterface.h9
-rw-r--r--src/version.h9
-rw-r--r--src/wallet/bdb.cpp848
-rw-r--r--src/wallet/bdb.h237
-rw-r--r--src/wallet/coincontrol.cpp2
-rw-r--r--src/wallet/coincontrol.h2
-rw-r--r--src/wallet/context.cpp8
-rw-r--r--src/wallet/context.h34
-rw-r--r--src/wallet/crypter.h12
-rw-r--r--src/wallet/db.cpp896
-rw-r--r--src/wallet/db.h375
-rw-r--r--src/wallet/init.cpp25
-rw-r--r--src/wallet/load.cpp14
-rw-r--r--src/wallet/load.h5
-rw-r--r--src/wallet/rpcdump.cpp143
-rw-r--r--src/wallet/rpcwallet.cpp873
-rw-r--r--src/wallet/rpcwallet.h22
-rw-r--r--src/wallet/salvage.cpp155
-rw-r--r--src/wallet/salvage.h14
-rw-r--r--src/wallet/scriptpubkeyman.cpp215
-rw-r--r--src/wallet/scriptpubkeyman.h121
-rw-r--r--src/wallet/test/coinselector_tests.cpp4
-rw-r--r--src/wallet/test/db_tests.cpp2
-rw-r--r--src/wallet/test/init_test_fixture.cpp5
-rw-r--r--src/wallet/test/init_test_fixture.h1
-rw-r--r--src/wallet/test/ismine_tests.cpp46
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp4
-rw-r--r--src/wallet/test/scriptpubkeyman_tests.cpp2
-rw-r--r--src/wallet/test/wallet_crypto_tests.cpp4
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp2
-rw-r--r--src/wallet/test/wallet_test_fixture.h6
-rw-r--r--src/wallet/test/wallet_tests.cpp87
-rw-r--r--src/wallet/wallet.cpp217
-rw-r--r--src/wallet/wallet.h45
-rw-r--r--src/wallet/walletdb.cpp261
-rw-r--r--src/wallet/walletdb.h76
-rw-r--r--src/wallet/wallettool.cpp44
-rw-r--r--src/wallet/walletutil.h34
-rw-r--r--src/warnings.cpp57
-rw-r--r--src/warnings.h8
426 files changed, 12395 insertions, 8449 deletions
diff --git a/src/.clang-format b/src/.clang-format
index aae039dd77..ef7a0ef5c7 100644
--- a/src/.clang-format
+++ b/src/.clang-format
@@ -1,9 +1,9 @@
Language: Cpp
AccessModifierOffset: -4
-AlignAfterOpenBracket: false
+AlignAfterOpenBracket: true
AlignEscapedNewlinesLeft: true
AlignTrailingComments: true
-AllowAllParametersOfDeclarationOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
diff --git a/src/Makefile.am b/src/Makefile.am
index 13812de0e7..cd3cc95707 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -85,6 +85,10 @@ if BUILD_BITCOIND
bin_PROGRAMS += bitcoind
endif
+if BUILD_BITCOIN_NODE
+ bin_PROGRAMS += bitcoin-node
+endif
+
if BUILD_BITCOIN_CLI
bin_PROGRAMS += bitcoin-cli
endif
@@ -106,9 +110,9 @@ BITCOIN_CORE_H = \
banman.h \
base58.h \
bech32.h \
- bloom.h \
blockencodings.h \
blockfilter.h \
+ bloom.h \
chain.h \
chainparams.h \
chainparamsbase.h \
@@ -129,6 +133,7 @@ BITCOIN_CORE_H = \
core_io.h \
core_memusage.h \
cuckoocache.h \
+ dbwrapper.h \
flatfile.h \
fs.h \
httprpc.h \
@@ -144,7 +149,6 @@ BITCOIN_CORE_H = \
interfaces/wallet.h \
key.h \
key_io.h \
- dbwrapper.h \
limitedmap.h \
logging.h \
logging/timer.h \
@@ -163,6 +167,7 @@ BITCOIN_CORE_H = \
node/context.h \
node/psbt.h \
node/transaction.h \
+ node/ui_interface.h \
node/utxo_snapshot.h \
noui.h \
optional.h \
@@ -180,6 +185,7 @@ BITCOIN_CORE_H = \
reverse_iterator.h \
rpc/blockchain.h \
rpc/client.h \
+ rpc/mining.h \
rpc/protocol.h \
rpc/rawtransaction_util.h \
rpc/register.h \
@@ -201,13 +207,12 @@ BITCOIN_CORE_H = \
support/events.h \
support/lockedpool.h \
sync.h \
- threadsafety.h \
threadinterrupt.h \
+ threadsafety.h \
timedata.h \
torcontrol.h \
txdb.h \
txmempool.h \
- ui_interface.h \
undo.h \
util/asmap.h \
util/bip32.h \
@@ -216,26 +221,30 @@ BITCOIN_CORE_H = \
util/error.h \
util/fees.h \
util/golombrice.h \
- util/spanparsing.h \
- util/system.h \
util/macros.h \
util/memory.h \
util/message.h \
util/moneystr.h \
util/rbf.h \
+ util/ref.h \
util/settings.h \
+ util/spanparsing.h \
util/string.h \
+ util/system.h \
util/threadnames.h \
util/time.h \
util/translation.h \
+ util/ui_change_type.h \
util/url.h \
util/vector.h \
validation.h \
validationinterface.h \
versionbits.h \
versionbitsinfo.h \
- walletinitinterface.h \
+ wallet/bdb.h \
wallet/coincontrol.h \
+ wallet/coinselection.h \
+ wallet/context.h \
wallet/crypter.h \
wallet/db.h \
wallet/feebumper.h \
@@ -243,12 +252,13 @@ BITCOIN_CORE_H = \
wallet/ismine.h \
wallet/load.h \
wallet/rpcwallet.h \
+ wallet/salvage.h \
wallet/scriptpubkeyman.h \
wallet/wallet.h \
wallet/walletdb.h \
wallet/wallettool.h \
wallet/walletutil.h \
- wallet/coinselection.h \
+ walletinitinterface.h \
warnings.h \
zmq/zmqabstractnotifier.h \
zmq/zmqconfig.h\
@@ -277,16 +287,16 @@ libbitcoin_server_a_SOURCES = \
blockfilter.cpp \
chain.cpp \
consensus/tx_verify.cpp \
+ dbwrapper.cpp \
flatfile.cpp \
httprpc.cpp \
httpserver.cpp \
index/base.cpp \
index/blockfilterindex.cpp \
index/txindex.cpp \
+ init.cpp \
interfaces/chain.cpp \
interfaces/node.cpp \
- init.cpp \
- dbwrapper.cpp \
miner.cpp \
net.cpp \
net_processing.cpp \
@@ -295,6 +305,7 @@ libbitcoin_server_a_SOURCES = \
node/context.cpp \
node/psbt.cpp \
node/transaction.cpp \
+ node/ui_interface.cpp \
noui.cpp \
policy/fees.cpp \
policy/rbf.cpp \
@@ -313,7 +324,6 @@ libbitcoin_server_a_SOURCES = \
torcontrol.cpp \
txdb.cpp \
txmempool.cpp \
- ui_interface.cpp \
validation.cpp \
validationinterface.cpp \
versionbits.cpp \
@@ -343,7 +353,9 @@ libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
interfaces/wallet.cpp \
+ wallet/bdb.cpp \
wallet/coincontrol.cpp \
+ wallet/context.cpp \
wallet/crypter.cpp \
wallet/db.cpp \
wallet/feebumper.cpp \
@@ -351,6 +363,7 @@ libbitcoin_wallet_a_SOURCES = \
wallet/load.cpp \
wallet/rpcdump.cpp \
wallet/rpcwallet.cpp \
+ wallet/salvage.cpp \
wallet/scriptpubkeyman.cpp \
wallet/wallet.cpp \
wallet/walletdb.cpp \
@@ -546,22 +559,21 @@ libbitcoin_cli_a_SOURCES = \
nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h
#
-# bitcoind binary #
-bitcoind_SOURCES = bitcoind.cpp
-bitcoind_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
-bitcoind_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-bitcoind_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+# bitcoind & bitcoin-node binaries #
+bitcoin_daemon_sources = bitcoind.cpp
+bitcoin_bin_cppflags = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+bitcoin_bin_cxxflags = $(AM_CXXFLAGS) $(PIE_FLAGS)
+bitcoin_bin_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
if TARGET_WINDOWS
-bitcoind_SOURCES += bitcoind-res.rc
+bitcoin_daemon_sources += bitcoind-res.rc
endif
-bitcoind_LDADD = \
- $(LIBBITCOIN_SERVER) \
+bitcoin_bin_ldadd = \
$(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_COMMON) \
- $(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \
+ $(LIBUNIVALUE) \
$(LIBBITCOIN_ZMQ) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
@@ -570,7 +582,19 @@ bitcoind_LDADD = \
$(LIBMEMENV) \
$(LIBSECP256K1)
-bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
+bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
+
+bitcoind_SOURCES = $(bitcoin_daemon_sources)
+bitcoind_CPPFLAGS = $(bitcoin_bin_cppflags)
+bitcoind_CXXFLAGS = $(bitcoin_bin_cxxflags)
+bitcoind_LDFLAGS = $(bitcoin_bin_ldflags)
+bitcoind_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd)
+
+bitcoin_node_SOURCES = $(bitcoin_daemon_sources)
+bitcoin_node_CPPFLAGS = $(bitcoin_bin_cppflags)
+bitcoin_node_CXXFLAGS = $(bitcoin_bin_cxxflags)
+bitcoin_node_LDFLAGS = $(bitcoin_bin_ldflags)
+bitcoin_node_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd)
# bitcoin-cli binary #
bitcoin_cli_SOURCES = bitcoin-cli.cpp
@@ -614,29 +638,14 @@ bitcoin_tx_LDADD += $(BOOST_LIBS)
# bitcoin-wallet binary #
bitcoin_wallet_SOURCES = bitcoin-wallet.cpp
-bitcoin_wallet_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
-bitcoin_wallet_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-bitcoin_wallet_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bitcoin_wallet_CPPFLAGS = $(bitcoin_bin_cppflags)
+bitcoin_wallet_CXXFLAGS = $(bitcoin_bin_cxxflags)
+bitcoin_wallet_LDFLAGS = $(bitcoin_bin_ldflags)
+bitcoin_wallet_LDADD = $(LIBBITCOIN_WALLET_TOOL) $(bitcoin_bin_ldadd)
if TARGET_WINDOWS
bitcoin_wallet_SOURCES += bitcoin-wallet-res.rc
endif
-
-bitcoin_wallet_LDADD = \
- $(LIBBITCOIN_WALLET_TOOL) \
- $(LIBBITCOIN_WALLET) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_ZMQ) \
- $(LIBLEVELDB) \
- $(LIBLEVELDB_SSE42) \
- $(LIBMEMENV) \
- $(LIBSECP256K1) \
- $(LIBUNIVALUE)
-
-bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(ZMQ_LIBS)
#
# bitcoinconsensus library #
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 766c0fca54..93b5156af3 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -29,6 +29,7 @@ bench_bench_bitcoin_SOURCES = \
bench/crypto_hash.cpp \
bench/ccoins_caching.cpp \
bench/gcs_filter.cpp \
+ bench/hashpadding.cpp \
bench/merkle_root.cpp \
bench/mempool_eviction.cpp \
bench/mempool_stress.cpp \
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index cf09eee2cb..848053e841 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -3,6 +3,11 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
bin_PROGRAMS += qt/bitcoin-qt
+
+if BUILD_BITCOIN_GUI
+ bin_PROGRAMS += bitcoin-gui
+endif
+
EXTRA_LIBRARIES += qt/libbitcoinqt.a
# bitcoin qt core #
@@ -20,6 +25,7 @@ QT_FORMS_UI = \
qt/forms/openuridialog.ui \
qt/forms/optionsdialog.ui \
qt/forms/overviewpage.ui \
+ qt/forms/psbtoperationsdialog.ui \
qt/forms/receivecoinsdialog.ui \
qt/forms/receiverequestdialog.ui \
qt/forms/debugwindow.ui \
@@ -56,6 +62,7 @@ QT_MOC_CPP = \
qt/moc_overviewpage.cpp \
qt/moc_peertablemodel.cpp \
qt/moc_paymentserver.cpp \
+ qt/moc_psbtoperationsdialog.cpp \
qt/moc_qrimagewidget.cpp \
qt/moc_qvalidatedlineedit.cpp \
qt/moc_qvaluecombobox.cpp \
@@ -127,6 +134,7 @@ BITCOIN_QT_H = \
qt/paymentserver.h \
qt/peertablemodel.h \
qt/platformstyle.h \
+ qt/psbtoperationsdialog.h \
qt/qrimagewidget.h \
qt/qvalidatedlineedit.h \
qt/qvaluecombobox.h \
@@ -240,6 +248,7 @@ BITCOIN_QT_WALLET_CPP = \
qt/openuridialog.cpp \
qt/overviewpage.cpp \
qt/paymentserver.cpp \
+ qt/psbtoperationsdialog.cpp \
qt/qrimagewidget.cpp \
qt/receivecoinsdialog.cpp \
qt/receiverequestdialog.cpp \
@@ -267,9 +276,7 @@ if ENABLE_WALLET
BITCOIN_QT_CPP += $(BITCOIN_QT_WALLET_CPP)
endif # ENABLE_WALLET
-RES_IMAGES =
-
-RES_MOVIES = $(wildcard $(srcdir)/qt/res/movies/spinner-*.png)
+RES_ANIMATION = $(wildcard $(srcdir)/qt/res/animation/spinner-*.png)
BITCOIN_RC = qt/res/bitcoin-qt-res.rc
@@ -281,7 +288,7 @@ qt_libbitcoinqt_a_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
qt_libbitcoinqt_a_OBJCXXFLAGS = $(AM_OBJCXXFLAGS) $(QT_PIE_FLAGS)
qt_libbitcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \
- $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES)
+ $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(RES_ICONS) $(RES_ANIMATION)
if TARGET_DARWIN
qt_libbitcoinqt_a_SOURCES += $(BITCOIN_MM)
endif
@@ -294,29 +301,43 @@ QT_FORMS_H=$(join $(dir $(QT_FORMS_UI)),$(addprefix ui_, $(notdir $(QT_FORMS_UI:
# Most files will depend on the forms and moc files as includes. Generate them
# before anything else.
$(QT_MOC): $(QT_FORMS_H)
-$(qt_libbitcoinqt_a_OBJECTS) $(qt_bitcoin_qt_OBJECTS) : | $(QT_MOC)
+$(qt_libbitcoinqt_a_OBJECTS) $(qt_bitcoin_qt_OBJECTS) $(bitcoin_gui_OBJECTS) : | $(QT_MOC)
-# bitcoin-qt binary #
-qt_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \
+# bitcoin-qt and bitcoin-gui binaries #
+bitcoin_qt_cppflags = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \
$(QT_INCLUDES) $(QR_CFLAGS)
-qt_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
+bitcoin_qt_cxxflags = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
-qt_bitcoin_qt_SOURCES = qt/main.cpp
+bitcoin_qt_sources = qt/main.cpp
if TARGET_WINDOWS
- qt_bitcoin_qt_SOURCES += $(BITCOIN_RC)
+ bitcoin_qt_sources += $(BITCOIN_RC)
endif
-qt_bitcoin_qt_LDADD = qt/libbitcoinqt.a $(LIBBITCOIN_SERVER)
+bitcoin_qt_ldadd = qt/libbitcoinqt.a $(LIBBITCOIN_SERVER)
if ENABLE_WALLET
-qt_bitcoin_qt_LDADD += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET)
+bitcoin_qt_ldadd += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET)
endif
if ENABLE_ZMQ
-qt_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
+bitcoin_qt_ldadd += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
-qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \
+bitcoin_qt_ldadd += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \
$(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
-qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-qt_bitcoin_qt_LIBTOOLFLAGS = $(AM_LIBTOOLFLAGS) --tag CXX
+bitcoin_qt_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX
+
+qt_bitcoin_qt_CPPFLAGS = $(bitcoin_qt_cppflags)
+qt_bitcoin_qt_CXXFLAGS = $(bitcoin_qt_cxxflags)
+qt_bitcoin_qt_SOURCES = $(bitcoin_qt_sources)
+qt_bitcoin_qt_LDADD = $(bitcoin_qt_ldadd)
+qt_bitcoin_qt_LDFLAGS = $(bitcoin_qt_ldflags)
+qt_bitcoin_qt_LIBTOOLFLAGS = $(bitcoin_qt_libtoolflags)
+
+bitcoin_gui_CPPFLAGS = $(bitcoin_qt_cppflags)
+bitcoin_gui_CXXFLAGS = $(bitcoin_qt_cxxflags)
+bitcoin_gui_SOURCES = $(bitcoin_qt_sources)
+bitcoin_gui_LDADD = $(bitcoin_qt_ldadd)
+bitcoin_gui_LDFLAGS = $(bitcoin_qt_ldflags)
+bitcoin_gui_LIBTOOLFLAGS = $(bitcoin_qt_libtoolflags)
#locale/foo.ts -> locale/foo.qm
QT_QM=$(QT_TS:.ts=.qm)
@@ -338,7 +359,7 @@ $(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM)
$(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@
@rm $(@D)/temp_$(<F)
-$(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES)
+$(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(RES_ICONS) $(RES_ANIMATION)
@test -f $(RCC)
$(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin $< | \
$(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 3a0d4fdc15..637d1d2f6e 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -10,7 +10,9 @@ FUZZ_TARGETS = \
test/fuzz/addrman_deserialize \
test/fuzz/asmap \
test/fuzz/asmap_direct \
+ test/fuzz/autofile \
test/fuzz/banentry_deserialize \
+ test/fuzz/banman \
test/fuzz/base_encode_decode \
test/fuzz/bech32 \
test/fuzz/block \
@@ -28,9 +30,19 @@ FUZZ_TARGETS = \
test/fuzz/blockundo_deserialize \
test/fuzz/bloom_filter \
test/fuzz/bloomfilter_deserialize \
+ test/fuzz/buffered_file \
test/fuzz/chain \
test/fuzz/checkqueue \
test/fuzz/coins_deserialize \
+ test/fuzz/coins_view \
+ test/fuzz/crypto \
+ test/fuzz/crypto_aes256 \
+ test/fuzz/crypto_aes256cbc \
+ test/fuzz/crypto_chacha20 \
+ test/fuzz/crypto_chacha20_poly1305_aead \
+ test/fuzz/crypto_common \
+ test/fuzz/crypto_hkdf_hmac_sha256_l32 \
+ test/fuzz/crypto_poly1305 \
test/fuzz/cuckoocache \
test/fuzz/decode_tx \
test/fuzz/descriptor_parse \
@@ -51,6 +63,7 @@ FUZZ_TARGETS = \
test/fuzz/key_io \
test/fuzz/key_origin_info_deserialize \
test/fuzz/kitchen_sink \
+ test/fuzz/load_external_block_file \
test/fuzz/locale \
test/fuzz/merkle_block_deserialize \
test/fuzz/merkleblock \
@@ -70,6 +83,7 @@ FUZZ_TARGETS = \
test/fuzz/partial_merkle_tree_deserialize \
test/fuzz/partially_signed_transaction_deserialize \
test/fuzz/policy_estimator \
+ test/fuzz/policy_estimator_io \
test/fuzz/pow \
test/fuzz/prefilled_transaction_deserialize \
test/fuzz/prevector \
@@ -109,9 +123,14 @@ FUZZ_TARGETS = \
test/fuzz/rbf \
test/fuzz/rolling_bloom_filter \
test/fuzz/script \
+ test/fuzz/script_bitcoin_consensus \
+ test/fuzz/script_descriptor_cache \
test/fuzz/script_deserialize \
test/fuzz/script_flags \
+ test/fuzz/script_interpreter \
test/fuzz/script_ops \
+ test/fuzz/script_sigcache \
+ test/fuzz/script_sign \
test/fuzz/scriptnum_ops \
test/fuzz/service_deserialize \
test/fuzz/signature_checker \
@@ -224,11 +243,13 @@ BITCOIN_TESTS =\
test/net_tests.cpp \
test/netbase_tests.cpp \
test/pmt_tests.cpp \
+ test/policy_fee_tests.cpp \
test/policyestimator_tests.cpp \
test/pow_tests.cpp \
test/prevector_tests.cpp \
test/raii_event_tests.cpp \
test/random_tests.cpp \
+ test/ref_tests.cpp \
test/reverselock_tests.cpp \
test/rpc_tests.cpp \
test/sanity_tests.cpp \
@@ -339,12 +360,24 @@ test_fuzz_asmap_direct_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_asmap_direct_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_asmap_direct_SOURCES = test/fuzz/asmap_direct.cpp
+test_fuzz_autofile_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_autofile_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_autofile_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_autofile_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_autofile_SOURCES = test/fuzz/autofile.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_banentry_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_banentry_deserialize_SOURCES = test/fuzz/deserialize.cpp
+test_fuzz_banman_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_banman_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_banman_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_banman_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_banman_SOURCES = test/fuzz/banman.cpp
+
test_fuzz_base_encode_decode_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_base_encode_decode_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_base_encode_decode_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -447,6 +480,12 @@ test_fuzz_bloomfilter_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_bloomfilter_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_bloomfilter_deserialize_SOURCES = test/fuzz/deserialize.cpp
+test_fuzz_buffered_file_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_buffered_file_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_buffered_file_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_buffered_file_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_buffered_file_SOURCES = test/fuzz/buffered_file.cpp
+
test_fuzz_chain_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_chain_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_chain_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -465,6 +504,60 @@ test_fuzz_coins_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_coins_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_coins_deserialize_SOURCES = test/fuzz/deserialize.cpp
+test_fuzz_coins_view_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_coins_view_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_coins_view_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_coins_view_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_coins_view_SOURCES = test/fuzz/coins_view.cpp
+
+test_fuzz_crypto_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_SOURCES = test/fuzz/crypto.cpp
+
+test_fuzz_crypto_aes256_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_aes256_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_aes256_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_aes256_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_aes256_SOURCES = test/fuzz/crypto_aes256.cpp
+
+test_fuzz_crypto_aes256cbc_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_aes256cbc_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_aes256cbc_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_aes256cbc_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_aes256cbc_SOURCES = test/fuzz/crypto_aes256cbc.cpp
+
+test_fuzz_crypto_chacha20_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_chacha20_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_chacha20_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_chacha20_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_chacha20_SOURCES = test/fuzz/crypto_chacha20.cpp
+
+test_fuzz_crypto_chacha20_poly1305_aead_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_chacha20_poly1305_aead_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_chacha20_poly1305_aead_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_chacha20_poly1305_aead_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_chacha20_poly1305_aead_SOURCES = test/fuzz/crypto_chacha20_poly1305_aead.cpp
+
+test_fuzz_crypto_common_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_common_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_common_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_common_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_common_SOURCES = test/fuzz/crypto_common.cpp
+
+test_fuzz_crypto_hkdf_hmac_sha256_l32_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_hkdf_hmac_sha256_l32_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_hkdf_hmac_sha256_l32_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_hkdf_hmac_sha256_l32_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_hkdf_hmac_sha256_l32_SOURCES = test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp
+
+test_fuzz_crypto_poly1305_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_poly1305_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_poly1305_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_poly1305_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_poly1305_SOURCES = test/fuzz/crypto_poly1305.cpp
+
test_fuzz_cuckoocache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_cuckoocache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_cuckoocache_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -585,6 +678,12 @@ test_fuzz_kitchen_sink_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_kitchen_sink_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_kitchen_sink_SOURCES = test/fuzz/kitchen_sink.cpp
+test_fuzz_load_external_block_file_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_load_external_block_file_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_load_external_block_file_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_load_external_block_file_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_load_external_block_file_SOURCES = test/fuzz/load_external_block_file.cpp
+
test_fuzz_locale_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_locale_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_locale_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -705,6 +804,12 @@ test_fuzz_policy_estimator_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_policy_estimator_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_policy_estimator_SOURCES = test/fuzz/policy_estimator.cpp
+test_fuzz_policy_estimator_io_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_policy_estimator_io_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_policy_estimator_io_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_policy_estimator_io_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_policy_estimator_io_SOURCES = test/fuzz/policy_estimator_io.cpp
+
test_fuzz_pow_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_pow_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_pow_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -933,6 +1038,18 @@ test_fuzz_script_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_script_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_script_SOURCES = test/fuzz/script.cpp
+test_fuzz_script_bitcoin_consensus_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_bitcoin_consensus_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_bitcoin_consensus_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_script_bitcoin_consensus_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_bitcoin_consensus_SOURCES = test/fuzz/script_bitcoin_consensus.cpp
+
+test_fuzz_script_descriptor_cache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_descriptor_cache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_descriptor_cache_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_script_descriptor_cache_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_descriptor_cache_SOURCES = test/fuzz/script_descriptor_cache.cpp
+
test_fuzz_script_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSCRIPT_DESERIALIZE=1
test_fuzz_script_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -945,12 +1062,30 @@ test_fuzz_script_flags_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_script_flags_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_script_flags_SOURCES = test/fuzz/script_flags.cpp
+test_fuzz_script_interpreter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_interpreter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_interpreter_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_script_interpreter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_interpreter_SOURCES = test/fuzz/script_interpreter.cpp
+
test_fuzz_script_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_script_ops_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_script_ops_SOURCES = test/fuzz/script_ops.cpp
+test_fuzz_script_sigcache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_sigcache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_sigcache_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_script_sigcache_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_sigcache_SOURCES = test/fuzz/script_sigcache.cpp
+
+test_fuzz_script_sign_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_sign_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_sign_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_script_sign_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_sign_SOURCES = test/fuzz/script_sign.cpp
+
test_fuzz_scriptnum_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_scriptnum_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_scriptnum_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 835c5d6c65..f3e8a19de2 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -8,6 +8,7 @@
#include <addrman.h>
#include <chainparams.h>
#include <clientversion.h>
+#include <cstdint>
#include <hash.h>
#include <random.h>
#include <streams.h>
@@ -36,7 +37,7 @@ template <typename Data>
bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
{
// Generate random temporary filename
- unsigned short randv = 0;
+ uint16_t randv = 0;
GetRandBytes((unsigned char*)&randv, sizeof(randv));
std::string tmpfn = strprintf("%s.%04x", prefix, randv);
diff --git a/src/addrdb.h b/src/addrdb.h
index c6d4307d69..8410c3776c 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -17,13 +17,6 @@ class CSubNet;
class CAddrMan;
class CDataStream;
-typedef enum BanReason
-{
- BanReasonUnknown = 0,
- BanReasonNodeMisbehaving = 1,
- BanReasonManuallyAdded = 2
-} BanReason;
-
class CBanEntry
{
public:
@@ -31,7 +24,6 @@ public:
int nVersion;
int64_t nCreateTime;
int64_t nBanUntil;
- uint8_t banReason;
CBanEntry()
{
@@ -44,31 +36,17 @@ public:
nCreateTime = nCreateTimeIn;
}
- explicit CBanEntry(int64_t n_create_time_in, BanReason ban_reason_in) : CBanEntry(n_create_time_in)
+ SERIALIZE_METHODS(CBanEntry, obj)
{
- banReason = ban_reason_in;
+ uint8_t ban_reason = 2; //! For backward compatibility
+ READWRITE(obj.nVersion, obj.nCreateTime, obj.nBanUntil, ban_reason);
}
- SERIALIZE_METHODS(CBanEntry, obj) { READWRITE(obj.nVersion, obj.nCreateTime, obj.nBanUntil, obj.banReason); }
-
void SetNull()
{
nVersion = CBanEntry::CURRENT_VERSION;
nCreateTime = 0;
nBanUntil = 0;
- banReason = BanReasonUnknown;
- }
-
- std::string banReasonToString() const
- {
- switch (banReason) {
- case BanReasonNodeMisbehaving:
- return "node misbehaving";
- case BanReasonManuallyAdded:
- return "manually added";
- default:
- return "unknown";
- }
}
};
diff --git a/src/banman.cpp b/src/banman.cpp
index 9cc584f0e4..8752185a60 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -6,7 +6,7 @@
#include <banman.h>
#include <netaddress.h>
-#include <ui_interface.h>
+#include <node/ui_interface.h>
#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
@@ -26,7 +26,7 @@ BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t
SweepBanned(); // sweep out unused entries
LogPrint(BCLog::NET, "Loaded %d banned node ips/subnets from banlist.dat %dms\n",
- banmap.size(), GetTimeMillis() - n_start);
+ m_banned.size(), GetTimeMillis() - n_start);
} else {
LogPrintf("Invalid or missing banlist.dat; recreating\n");
SetBannedSetDirty(true); // force write
@@ -68,28 +68,13 @@ void BanMan::ClearBanned()
if (m_client_interface) m_client_interface->BannedListChanged();
}
-int BanMan::IsBannedLevel(CNetAddr net_addr)
+bool BanMan::IsDiscouraged(const CNetAddr& net_addr)
{
- // Returns the most severe level of banning that applies to this address.
- // 0 - Not banned
- // 1 - Automatic misbehavior ban
- // 2 - Any other ban
- int level = 0;
- auto current_time = GetTime();
LOCK(m_cs_banned);
- for (const auto& it : m_banned) {
- CSubNet sub_net = it.first;
- CBanEntry ban_entry = it.second;
-
- if (current_time < ban_entry.nBanUntil && sub_net.Match(net_addr)) {
- if (ban_entry.banReason != BanReasonNodeMisbehaving) return 2;
- level = 1;
- }
- }
- return level;
+ return m_discouraged.contains(net_addr.GetAddrBytes());
}
-bool BanMan::IsBanned(CNetAddr net_addr)
+bool BanMan::IsBanned(const CNetAddr& net_addr)
{
auto current_time = GetTime();
LOCK(m_cs_banned);
@@ -104,7 +89,7 @@ bool BanMan::IsBanned(CNetAddr net_addr)
return false;
}
-bool BanMan::IsBanned(CSubNet sub_net)
+bool BanMan::IsBanned(const CSubNet& sub_net)
{
auto current_time = GetTime();
LOCK(m_cs_banned);
@@ -118,15 +103,21 @@ bool BanMan::IsBanned(CSubNet sub_net)
return false;
}
-void BanMan::Ban(const CNetAddr& net_addr, const BanReason& ban_reason, int64_t ban_time_offset, bool since_unix_epoch)
+void BanMan::Ban(const CNetAddr& net_addr, int64_t ban_time_offset, bool since_unix_epoch)
{
CSubNet sub_net(net_addr);
- Ban(sub_net, ban_reason, ban_time_offset, since_unix_epoch);
+ Ban(sub_net, ban_time_offset, since_unix_epoch);
+}
+
+void BanMan::Discourage(const CNetAddr& net_addr)
+{
+ LOCK(m_cs_banned);
+ m_discouraged.insert(net_addr.GetAddrBytes());
}
-void BanMan::Ban(const CSubNet& sub_net, const BanReason& ban_reason, int64_t ban_time_offset, bool since_unix_epoch)
+void BanMan::Ban(const CSubNet& sub_net, int64_t ban_time_offset, bool since_unix_epoch)
{
- CBanEntry ban_entry(GetTime(), ban_reason);
+ CBanEntry ban_entry(GetTime());
int64_t normalized_ban_time_offset = ban_time_offset;
bool normalized_since_unix_epoch = since_unix_epoch;
@@ -146,8 +137,8 @@ void BanMan::Ban(const CSubNet& sub_net, const BanReason& ban_reason, int64_t ba
}
if (m_client_interface) m_client_interface->BannedListChanged();
- //store banlist to disk immediately if user requested ban
- if (ban_reason == BanReasonManuallyAdded) DumpBanlist();
+ //store banlist to disk immediately
+ DumpBanlist();
}
bool BanMan::Unban(const CNetAddr& net_addr)
diff --git a/src/banman.h b/src/banman.h
index 6bea2e75e9..f6bfbd1e49 100644
--- a/src/banman.h
+++ b/src/banman.h
@@ -6,6 +6,7 @@
#define BITCOIN_BANMAN_H
#include <addrdb.h>
+#include <bloom.h>
#include <fs.h>
#include <net_types.h> // For banmap_t
#include <sync.h>
@@ -23,32 +24,55 @@ class CClientUIInterface;
class CNetAddr;
class CSubNet;
-// Denial-of-service detection/prevention
-// The idea is to detect peers that are behaving
-// badly and disconnect/ban them, but do it in a
-// one-coding-mistake-won't-shatter-the-entire-network
-// way.
-// IMPORTANT: There should be nothing I can give a
-// node that it will forward on that will make that
-// node's peers drop it. If there is, an attacker
-// can isolate a node and/or try to split the network.
-// Dropping a node for sending stuff that is invalid
-// now but might be valid in a later version is also
-// dangerous, because it can cause a network split
-// between nodes running old code and nodes running
-// new code.
+// Banman manages two related but distinct concepts:
+//
+// 1. Banning. This is configured manually by the user, through the setban RPC.
+// If an address or subnet is banned, we never accept incoming connections from
+// it and never create outgoing connections to it. We won't gossip its address
+// to other peers in addr messages. Banned addresses and subnets are stored to
+// banlist.dat on shutdown and reloaded on startup. Banning can be used to
+// prevent connections with spy nodes or other griefers.
+//
+// 2. Discouragement. If a peer misbehaves enough (see Misbehaving() in
+// net_processing.cpp), we'll mark that address as discouraged. We still allow
+// incoming connections from them, but they're preferred for eviction when
+// we receive new incoming connections. We never make outgoing connections to
+// them, and do not gossip their address to other peers. This is implemented as
+// a bloom filter. We can (probabilistically) test for membership, but can't
+// list all discouraged addresses or unmark them as discouraged. Discouragement
+// can prevent our limited connection slots being used up by incompatible
+// or broken peers.
+//
+// Neither banning nor discouragement are protections against denial-of-service
+// attacks, since if an attacker has a way to waste our resources and we
+// disconnect from them and ban that address, it's trivial for them to
+// reconnect from another IP address.
+//
+// Attempting to automatically disconnect or ban any class of peer carries the
+// risk of splitting the network. For example, if we banned/disconnected for a
+// transaction that fails a policy check and a future version changes the
+// policy check so the transaction is accepted, then that transaction could
+// cause the network to split between old nodes and new nodes.
class BanMan
{
public:
~BanMan();
BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time);
- void Ban(const CNetAddr& net_addr, const BanReason& ban_reason, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
- void Ban(const CSubNet& sub_net, const BanReason& ban_reason, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
+ void Ban(const CNetAddr& net_addr, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
+ void Ban(const CSubNet& sub_net, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
+ void Discourage(const CNetAddr& net_addr);
void ClearBanned();
- int IsBannedLevel(CNetAddr net_addr);
- bool IsBanned(CNetAddr net_addr);
- bool IsBanned(CSubNet sub_net);
+
+ //! Return whether net_addr is banned
+ bool IsBanned(const CNetAddr& net_addr);
+
+ //! Return whether sub_net is exactly banned
+ bool IsBanned(const CSubNet& sub_net);
+
+ //! Return whether net_addr is discouraged.
+ bool IsDiscouraged(const CNetAddr& net_addr);
+
bool Unban(const CNetAddr& net_addr);
bool Unban(const CSubNet& sub_net);
void GetBanned(banmap_t& banmap);
@@ -68,6 +92,7 @@ private:
CClientUIInterface* m_client_interface = nullptr;
CBanDB m_ban_db;
const int64_t m_default_ban_time;
+ CRollingBloomFilter m_discouraged GUARDED_BY(m_cs_banned) {50000, 0.000001};
};
#endif
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 1b75854210..1f872ce700 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -4,6 +4,7 @@
#include <bench/bench.h>
+#include <crypto/sha256.h>
#include <util/strencodings.h>
#include <util/system.h>
@@ -35,6 +36,7 @@ int main(int argc, char** argv)
{
ArgsManager argsman;
SetupBenchArgs(argsman);
+ SHA256AutoDetect();
std::string error;
if (!argsman.ParseParameters(argc, argv, error)) {
tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error);
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index d6d5e67c5b..f2d12531d7 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -31,7 +31,7 @@ static void CoinSelection(benchmark::State& state)
{
NodeContext node;
auto chain = interfaces::MakeChain(node);
- CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
wallet.SetupLegacyScriptPubKeyMan();
std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);
@@ -65,7 +65,7 @@ static void CoinSelection(benchmark::State& state)
typedef std::set<CInputCoin> CoinSet;
static NodeContext testNode;
static auto testChain = interfaces::MakeChain(testNode);
-static CWallet testWallet(testChain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+static CWallet testWallet(testChain.get(), WalletLocation(), CreateDummyWalletDatabase());
std::vector<std::unique_ptr<CWalletTx>> wtxn;
// Copied from src/wallet/test/coinselector_tests.cpp
diff --git a/src/bench/hashpadding.cpp b/src/bench/hashpadding.cpp
new file mode 100644
index 0000000000..985be8bdba
--- /dev/null
+++ b/src/bench/hashpadding.cpp
@@ -0,0 +1,47 @@
+// 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 <bench/bench.h>
+#include <hash.h>
+#include <random.h>
+#include <uint256.h>
+
+
+static void PrePadded(benchmark::State& state)
+{
+
+ CSHA256 hasher;
+
+ // Setup the salted hasher
+ uint256 nonce = GetRandHash();
+ hasher.Write(nonce.begin(), 32);
+ hasher.Write(nonce.begin(), 32);
+ uint256 data = GetRandHash();
+ while (state.KeepRunning()) {
+ unsigned char out[32];
+ CSHA256 h = hasher;
+ h.Write(data.begin(), 32);
+ h.Finalize(out);
+ }
+}
+
+BENCHMARK(PrePadded, 10000);
+
+static void RegularPadded(benchmark::State& state)
+{
+ CSHA256 hasher;
+
+ // Setup the salted hasher
+ uint256 nonce = GetRandHash();
+ uint256 data = GetRandHash();
+ while (state.KeepRunning()) {
+ unsigned char out[32];
+ CSHA256 h = hasher;
+ h.Write(nonce.begin(), 32);
+ h.Write(data.begin(), 32);
+ h.Finalize(out);
+ }
+}
+
+BENCHMARK(RegularPadded, 10000);
diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp
index 00e5d7e7a0..42b351a72d 100644
--- a/src/bench/prevector.cpp
+++ b/src/bench/prevector.cpp
@@ -20,9 +20,7 @@
struct nontrivial_t {
int x;
nontrivial_t() :x(-1) {}
- ADD_SERIALIZE_METHODS
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {READWRITE(x);}
+ SERIALIZE_METHODS(nontrivial_t, obj) { READWRITE(obj.x); }
};
static_assert(!IS_TRIVIALLY_CONSTRUCTIBLE<nontrivial_t>::value,
"expected nontrivial_t to not be trivially constructible");
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index 810c344ab5..05cfb3438e 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -26,7 +26,7 @@ static void WalletBalance(benchmark::State& state, const bool set_dirty, const b
NodeContext node;
std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node);
- CWallet wallet{chain.get(), WalletLocation(), WalletDatabase::CreateMock()};
+ CWallet wallet{chain.get(), WalletLocation(), CreateMockWalletDatabase()};
{
wallet.SetupLegacyScriptPubKeyMan();
bool first_run;
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index cdaabd6fab..9afcda4578 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -9,7 +9,9 @@
#include <chainparamsbase.h>
#include <clientversion.h>
+#include <optional.h>
#include <rpc/client.h>
+#include <rpc/mining.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <util/strencodings.h>
@@ -20,6 +22,7 @@
#include <functional>
#include <memory>
#include <stdio.h>
+#include <string>
#include <tuple>
#include <event2/buffer.h>
@@ -37,6 +40,9 @@ static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
static const bool DEFAULT_NAMED=false;
static const int CONTINUE_EXECUTION=-1;
+/** Default number of blocks to generate for RPC generatetoaddress. */
+static const std::string DEFAULT_NBLOCKS = "1";
+
static void SetupCliArgs()
{
SetupHelpOptions(gArgs);
@@ -48,6 +54,7 @@ static void SetupCliArgs()
gArgs.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-generate", strprintf("Generate blocks immediately, equivalent to RPC generatenewaddress followed by RPC generatetoaddress. Optional positional integer arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000", DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions();
gArgs.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -157,7 +164,7 @@ struct HTTPReply
std::string body;
};
-static const char *http_errorstring(int code)
+static std::string http_errorstring(int code)
{
switch(code) {
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
@@ -250,7 +257,7 @@ public:
UniValue ProcessReply(const UniValue &batch_in) override
{
UniValue result(UniValue::VOBJ);
- std::vector<UniValue> batch = JSONRPCProcessBatchReply(batch_in, batch_in.size());
+ const std::vector<UniValue> batch = JSONRPCProcessBatchReply(batch_in);
// Errors in getnetworkinfo() and getblockchaininfo() are fatal, pass them on;
// getwalletinfo() and getbalances() are allowed to fail if there is no wallet.
if (!batch[ID_NETWORKINFO]["error"].isNull()) {
@@ -284,6 +291,28 @@ public:
}
};
+/** Process RPC generatetoaddress request. */
+class GenerateToAddressRequestHandler : public BaseRequestHandler
+{
+public:
+ UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
+ {
+ address_str = args.at(1);
+ UniValue params{RPCConvertValues("generatetoaddress", args)};
+ return JSONRPCRequestObj("generatetoaddress", params, 1);
+ }
+
+ UniValue ProcessReply(const UniValue &reply) override
+ {
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("address", address_str);
+ result.pushKV("blocks", reply.get_obj()["result"]);
+ return JSONRPCReplyObj(result, NullUniValue, 1);
+ }
+protected:
+ std::string address_str;
+};
+
/** Process default single requests */
class DefaultRequestHandler: public BaseRequestHandler {
public:
@@ -304,7 +333,7 @@ public:
}
};
-static UniValue CallRPC(BaseRequestHandler *rh, const std::string& strMethod, const std::vector<std::string>& args)
+static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const Optional<std::string>& rpcwallet = {})
{
std::string host;
// In preference order, we choose the following for the port:
@@ -369,14 +398,12 @@ static UniValue CallRPC(BaseRequestHandler *rh, const std::string& strMethod, co
// check if we should use a special wallet endpoint
std::string endpoint = "/";
- if (!gArgs.GetArgs("-rpcwallet").empty()) {
- std::string walletName = gArgs.GetArg("-rpcwallet", "");
- char *encodedURI = evhttp_uriencode(walletName.data(), walletName.size(), false);
+ if (rpcwallet) {
+ char* encodedURI = evhttp_uriencode(rpcwallet->data(), rpcwallet->size(), false);
if (encodedURI) {
- endpoint = "/wallet/"+ std::string(encodedURI);
+ endpoint = "/wallet/" + std::string(encodedURI);
free(encodedURI);
- }
- else {
+ } else {
throw CConnectionFailed("uri-encode failed");
}
}
@@ -418,6 +445,121 @@ static UniValue CallRPC(BaseRequestHandler *rh, const std::string& strMethod, co
return reply;
}
+/**
+ * ConnectAndCallRPC wraps CallRPC with -rpcwait and an exception handler.
+ *
+ * @param[in] rh Pointer to RequestHandler.
+ * @param[in] strMethod Reference to const string method to forward to CallRPC.
+ * @param[in] rpcwallet Reference to const optional string wallet name to forward to CallRPC.
+ * @returns the RPC response as a UniValue object.
+ * @throws a CConnectionFailed std::runtime_error if connection failed or RPC server still in warmup.
+ */
+static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const Optional<std::string>& rpcwallet = {})
+{
+ UniValue response(UniValue::VOBJ);
+ // Execute and handle connection failures with -rpcwait.
+ const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
+ do {
+ try {
+ response = CallRPC(rh, strMethod, args, rpcwallet);
+ if (fWait) {
+ const UniValue& error = find_value(response, "error");
+ if (!error.isNull() && error["code"].get_int() == RPC_IN_WARMUP) {
+ throw CConnectionFailed("server in warmup");
+ }
+ }
+ break; // Connection succeeded, no need to retry.
+ } catch (const CConnectionFailed&) {
+ if (fWait) {
+ UninterruptibleSleep(std::chrono::milliseconds{1000});
+ } else {
+ throw;
+ }
+ }
+ } while (fWait);
+ return response;
+}
+
+/** Parse UniValue result to update the message to print to std::cout. */
+static void ParseResult(const UniValue& result, std::string& strPrint)
+{
+ if (result.isNull()) return;
+ strPrint = result.isStr() ? result.get_str() : result.write(2);
+}
+
+/** Parse UniValue error to update the message to print to std::cerr and the code to return. */
+static void ParseError(const UniValue& error, std::string& strPrint, int& nRet)
+{
+ if (error.isObject()) {
+ const UniValue& err_code = find_value(error, "code");
+ const UniValue& err_msg = find_value(error, "message");
+ if (!err_code.isNull()) {
+ strPrint = "error code: " + err_code.getValStr() + "\n";
+ }
+ if (err_msg.isStr()) {
+ strPrint += ("error message:\n" + err_msg.get_str());
+ }
+ if (err_code.isNum() && err_code.get_int() == RPC_WALLET_NOT_SPECIFIED) {
+ strPrint += "\nTry adding \"-rpcwallet=<filename>\" option to bitcoin-cli command line.";
+ }
+ } else {
+ strPrint = "error: " + error.write();
+ }
+ nRet = abs(error["code"].get_int());
+}
+
+/**
+ * GetWalletBalances calls listwallets; if more than one wallet is loaded, it then
+ * fetches mine.trusted balances for each loaded wallet and pushes them to `result`.
+ *
+ * @param result Reference to UniValue object the wallet names and balances are pushed to.
+ */
+static void GetWalletBalances(UniValue& result)
+{
+ DefaultRequestHandler rh;
+ const UniValue listwallets = ConnectAndCallRPC(&rh, "listwallets", /* args=*/{});
+ if (!find_value(listwallets, "error").isNull()) return;
+ const UniValue& wallets = find_value(listwallets, "result");
+ if (wallets.size() <= 1) return;
+
+ UniValue balances(UniValue::VOBJ);
+ for (const UniValue& wallet : wallets.getValues()) {
+ const std::string wallet_name = wallet.get_str();
+ const UniValue getbalances = ConnectAndCallRPC(&rh, "getbalances", /* args=*/{}, wallet_name);
+ const UniValue& balance = find_value(getbalances, "result")["mine"]["trusted"];
+ balances.pushKV(wallet_name, balance);
+ }
+ result.pushKV("balances", balances);
+}
+
+/**
+ * Call RPC getnewaddress.
+ * @returns getnewaddress response as a UniValue object.
+ */
+static UniValue GetNewAddress()
+{
+ Optional<std::string> wallet_name{};
+ if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
+ DefaultRequestHandler rh;
+ return ConnectAndCallRPC(&rh, "getnewaddress", /* args=*/{}, wallet_name);
+}
+
+/**
+ * Check bounds and set up args for RPC generatetoaddress params: nblocks, address, maxtries.
+ * @param[in] address Reference to const string address to insert into the args.
+ * @param args Reference to vector of string args to modify.
+ */
+static void SetGenerateToAddressArgs(const std::string& address, std::vector<std::string>& args)
+{
+ if (args.size() > 2) throw std::runtime_error("too many arguments (maximum 2 for nblocks and maxtries)");
+ if (args.size() == 0) {
+ args.emplace_back(DEFAULT_NBLOCKS);
+ } else if (args.at(0) == "0") {
+ throw std::runtime_error("the first argument (number of blocks to generate, default: " + DEFAULT_NBLOCKS + ") must be an integer value greater than zero");
+ }
+ args.emplace(args.begin() + 1, address);
+}
+
static int CommandLineRPC(int argc, char *argv[])
{
std::string strPrint;
@@ -474,9 +616,17 @@ static int CommandLineRPC(int argc, char *argv[])
}
std::unique_ptr<BaseRequestHandler> rh;
std::string method;
- if (gArgs.GetBoolArg("-getinfo", false)) {
+ if (gArgs.IsArgSet("-getinfo")) {
rh.reset(new GetinfoRequestHandler());
- method = "";
+ } else if (gArgs.GetBoolArg("-generate", false)) {
+ const UniValue getnewaddress{GetNewAddress()};
+ const UniValue& error{find_value(getnewaddress, "error")};
+ if (error.isNull()) {
+ SetGenerateToAddressArgs(find_value(getnewaddress, "result").get_str(), args);
+ rh.reset(new GenerateToAddressRequestHandler());
+ } else {
+ ParseError(error, strPrint, nRet);
+ }
} else {
rh.reset(new DefaultRequestHandler());
if (args.size() < 1) {
@@ -485,62 +635,28 @@ static int CommandLineRPC(int argc, char *argv[])
method = args[0];
args.erase(args.begin()); // Remove trailing method name from arguments vector
}
-
- // Execute and handle connection failures with -rpcwait
- const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
- do {
- try {
- const UniValue reply = CallRPC(rh.get(), method, args);
-
- // Parse reply
- const UniValue& result = find_value(reply, "result");
- const UniValue& error = find_value(reply, "error");
-
- if (!error.isNull()) {
- // Error
- int code = error["code"].get_int();
- if (fWait && code == RPC_IN_WARMUP)
- throw CConnectionFailed("server in warmup");
- strPrint = "error: " + error.write();
- nRet = abs(code);
- if (error.isObject())
- {
- UniValue errCode = find_value(error, "code");
- UniValue errMsg = find_value(error, "message");
- strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n";
-
- if (errMsg.isStr())
- strPrint += "error message:\n"+errMsg.get_str();
-
- if (errCode.isNum() && errCode.get_int() == RPC_WALLET_NOT_SPECIFIED) {
- strPrint += "\nTry adding \"-rpcwallet=<filename>\" option to bitcoin-cli command line.";
- }
- }
- } else {
- // Result
- if (result.isNull())
- strPrint = "";
- else if (result.isStr())
- strPrint = result.get_str();
- else
- strPrint = result.write(2);
+ if (nRet == 0) {
+ // Perform RPC call
+ Optional<std::string> wallet_name{};
+ if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
+ const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name);
+
+ // Parse reply
+ UniValue result = find_value(reply, "result");
+ const UniValue& error = find_value(reply, "error");
+ if (error.isNull()) {
+ if (gArgs.IsArgSet("-getinfo") && !gArgs.IsArgSet("-rpcwallet")) {
+ GetWalletBalances(result); // fetch multiwallet balances and append to result
}
- // Connection succeeded, no need to retry.
- break;
+ ParseResult(result, strPrint);
+ } else {
+ ParseError(error, strPrint, nRet);
}
- catch (const CConnectionFailed&) {
- if (fWait)
- UninterruptibleSleep(std::chrono::milliseconds{1000});
- else
- throw;
- }
- } while (fWait);
- }
- catch (const std::exception& e) {
+ }
+ } catch (const std::exception& e) {
strPrint = std::string("error: ") + e.what();
nRet = EXIT_FAILURE;
- }
- catch (...) {
+ } catch (...) {
PrintExceptionContinue(nullptr, "CommandLineRPC()");
throw;
}
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 7f9439788a..b420463c00 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -31,6 +31,7 @@ static void SetupWalletToolArgs()
gArgs.AddArg("info", "Get wallet info", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
gArgs.AddArg("create", "Create new wallet file", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("salvage", "Attempt to recover private keys from a corrupt wallet", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
}
static bool WalletAppInit(int argc, char* argv[])
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 43d3f3c5ac..3dcce92ab5 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -13,9 +13,10 @@
#include <init.h>
#include <interfaces/chain.h>
#include <node/context.h>
+#include <node/ui_interface.h>
#include <noui.h>
#include <shutdown.h>
-#include <ui_interface.h>
+#include <util/ref.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <util/threadnames.h>
@@ -77,6 +78,7 @@ static bool AppInit(int argc, char* argv[])
return true;
}
+ util::Ref context{node};
try
{
if (!CheckDataDirOption()) {
@@ -145,7 +147,7 @@ static bool AppInit(int argc, char* argv[])
// If locking the data directory failed, exit immediately
return false;
}
- fRet = AppInitMain(node);
+ fRet = AppInitMain(context, node);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInit()");
diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
index 263d863cfa..a47709cd82 100644
--- a/src/blockencodings.cpp
+++ b/src/blockencodings.cpp
@@ -105,13 +105,12 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
std::vector<bool> have_txn(txn_available.size());
{
LOCK(pool->cs);
- const std::vector<std::pair<uint256, CTxMemPool::txiter> >& vTxHashes = pool->vTxHashes;
- for (size_t i = 0; i < vTxHashes.size(); i++) {
- uint64_t shortid = cmpctblock.GetShortID(vTxHashes[i].first);
+ for (size_t i = 0; i < pool->vTxHashes.size(); i++) {
+ uint64_t shortid = cmpctblock.GetShortID(pool->vTxHashes[i].first);
std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);
if (idit != shorttxids.end()) {
if (!have_txn[idit->second]) {
- txn_available[idit->second] = vTxHashes[i].second->GetSharedTx();
+ txn_available[idit->second] = pool->vTxHashes[i].second->GetSharedTx();
have_txn[idit->second] = true;
mempool_count++;
} else {
diff --git a/src/blockencodings.h b/src/blockencodings.h
index 377ac3a1a6..326db1b4a7 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -92,12 +92,13 @@ private:
friend class PartiallyDownloadedBlock;
- static const int SHORTTXIDS_LENGTH = 6;
protected:
std::vector<uint64_t> shorttxids;
std::vector<PrefilledTransaction> prefilledtxn;
public:
+ static constexpr int SHORTTXIDS_LENGTH = 6;
+
CBlockHeader header;
// Dummy for deserialization
@@ -125,7 +126,7 @@ class PartiallyDownloadedBlock {
protected:
std::vector<CTransactionRef> txn_available;
size_t prefilled_count = 0, mempool_count = 0, extra_count = 0;
- CTxMemPool* pool;
+ const CTxMemPool* pool;
public:
CBlockHeader header;
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
diff --git a/src/blockfilter.h b/src/blockfilter.h
index ff8744b217..96cefbf3b2 100644
--- a/src/blockfilter.h
+++ b/src/blockfilter.h
@@ -144,8 +144,8 @@ public:
template <typename Stream>
void Serialize(Stream& s) const {
- s << m_block_hash
- << static_cast<uint8_t>(m_filter_type)
+ s << static_cast<uint8_t>(m_filter_type)
+ << m_block_hash
<< m_filter.GetEncoded();
}
@@ -154,8 +154,8 @@ public:
std::vector<unsigned char> encoded_filter;
uint8_t filter_type;
- s >> m_block_hash
- >> filter_type
+ s >> filter_type
+ >> m_block_hash
>> encoded_filter;
m_filter_type = static_cast<BlockFilterType>(filter_type);
diff --git a/src/bloom.cpp b/src/bloom.cpp
index 54fcf487e4..d182f0728e 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -135,8 +135,8 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY)
{
std::vector<std::vector<unsigned char> > vSolutions;
- txnouttype type = Solver(txout.scriptPubKey, vSolutions);
- if (type == TX_PUBKEY || type == TX_MULTISIG) {
+ TxoutType type = Solver(txout.scriptPubKey, vSolutions);
+ if (type == TxoutType::PUBKEY || type == TxoutType::MULTISIG) {
insert(COutPoint(hash, i));
}
}
diff --git a/src/bloom.h b/src/bloom.h
index 9173b80d66..9307257852 100644
--- a/src/bloom.h
+++ b/src/bloom.h
@@ -64,15 +64,7 @@ public:
CBloomFilter(const unsigned int nElements, const double nFPRate, const unsigned int nTweak, unsigned char nFlagsIn);
CBloomFilter() : nHashFuncs(0), nTweak(0), nFlags(0) {}
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(vData);
- READWRITE(nHashFuncs);
- READWRITE(nTweak);
- READWRITE(nFlags);
- }
+ SERIALIZE_METHODS(CBloomFilter, obj) { READWRITE(obj.vData, obj.nHashFuncs, obj.nTweak, obj.nFlags); }
void insert(const std::vector<unsigned char>& vKey);
void insert(const COutPoint& outpoint);
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 799474fae2..092c45e4ce 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -121,6 +121,7 @@ public:
vSeeds.emplace_back("seed.btc.petertodd.org"); // Peter Todd, only supports x1, x5, x9, and xd
vSeeds.emplace_back("seed.bitcoin.sprovoost.nl"); // Sjors Provoost
vSeeds.emplace_back("dnsseed.emzy.de"); // Stephan Oeste
+ vSeeds.emplace_back("seed.bitcoin.wiz.biz"); // Jason Maurice
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5);
diff --git a/src/compat/assumptions.h b/src/compat/assumptions.h
index 6e7b4d3ded..4b0b224c69 100644
--- a/src/compat/assumptions.h
+++ b/src/compat/assumptions.h
@@ -50,6 +50,7 @@ static_assert(sizeof(double) == 8, "64-bit double assumed");
// code.
static_assert(sizeof(short) == 2, "16-bit short assumed");
static_assert(sizeof(int) == 4, "32-bit int assumed");
+static_assert(sizeof(unsigned) == 4, "32-bit unsigned 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(...).
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
index 81245e3e11..9e8e6530f1 100644
--- a/src/consensus/tx_verify.cpp
+++ b/src/consensus/tx_verify.cpp
@@ -27,9 +27,9 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
return true;
}
-std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block)
+std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>& prevHeights, const CBlockIndex& block)
{
- assert(prevHeights->size() == tx.vin.size());
+ assert(prevHeights.size() == tx.vin.size());
// Will be set to the equivalent height- and time-based nLockTime
// values that would be necessary to satisfy all relative lock-
@@ -59,11 +59,11 @@ std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags
// consensus-enforced meaning at this point.
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) {
// The height of this input is not relevant for sequence locks
- (*prevHeights)[txinIndex] = 0;
+ prevHeights[txinIndex] = 0;
continue;
}
- int nCoinHeight = (*prevHeights)[txinIndex];
+ int nCoinHeight = prevHeights[txinIndex];
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) {
int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast();
@@ -99,7 +99,7 @@ bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair<int, int64_t> loc
return true;
}
-bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block)
+bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>& prevHeights, const CBlockIndex& block)
{
return EvaluateSequenceLocks(block, CalculateSequenceLocks(tx, flags, prevHeights, block));
}
diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h
index ffcaf3cab1..e2a9328df8 100644
--- a/src/consensus/tx_verify.h
+++ b/src/consensus/tx_verify.h
@@ -66,13 +66,13 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime);
* Also removes from the vector of input heights any entries which did not
* correspond to sequence locked inputs as they do not affect the calculation.
*/
-std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block);
+std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>& prevHeights, const CBlockIndex& block);
bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair<int, int64_t> lockPair);
/**
* Check if transaction is final per BIP 68 sequence numbers and can be included in a block.
* Consensus critical. Takes as input a list of heights at which tx's inputs (in order) confirmed.
*/
-bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block);
+bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>& prevHeights, const CBlockIndex& block);
#endif // BITCOIN_CONSENSUS_TX_VERIFY_H
diff --git a/src/core_read.cpp b/src/core_read.cpp
index df78c319ee..1c0a8a096d 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -19,6 +19,7 @@
#include <boost/algorithm/string/split.hpp>
#include <algorithm>
+#include <string>
CScript ParseScript(const std::string& s)
{
@@ -34,10 +35,9 @@ CScript ParseScript(const std::string& s)
if (op < OP_NOP && op != OP_RESERVED)
continue;
- const char* name = GetOpName(static_cast<opcodetype>(op));
- if (strcmp(name, "OP_UNKNOWN") == 0)
+ std::string strName = GetOpName(static_cast<opcodetype>(op));
+ if (strName == "OP_UNKNOWN")
continue;
- std::string strName(name);
mapOpNames[strName] = static_cast<opcodetype>(op);
// Convenience: OP_ADD and just ADD are both recognized:
boost::algorithm::replace_first(strName, "OP_", "");
diff --git a/src/core_write.cpp b/src/core_write.cpp
index cb1fc214eb..34cfeecc6f 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -131,20 +131,20 @@ std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags)
{
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | serializeFlags);
ssTx << tx;
- return HexStr(ssTx.begin(), ssTx.end());
+ return HexStr(ssTx);
}
void ScriptToUniv(const CScript& script, UniValue& out, bool include_address)
{
out.pushKV("asm", ScriptToAsmStr(script));
- out.pushKV("hex", HexStr(script.begin(), script.end()));
+ out.pushKV("hex", HexStr(script));
std::vector<std::vector<unsigned char>> solns;
- txnouttype type = Solver(script, solns);
+ TxoutType type = Solver(script, solns);
out.pushKV("type", GetTxnOutputType(type));
CTxDestination address;
- if (include_address && ExtractDestination(script, address) && type != TX_PUBKEY) {
+ if (include_address && ExtractDestination(script, address) && type != TxoutType::PUBKEY) {
out.pushKV("address", EncodeDestination(address));
}
}
@@ -152,15 +152,15 @@ void ScriptToUniv(const CScript& script, UniValue& out, bool include_address)
void ScriptPubKeyToUniv(const CScript& scriptPubKey,
UniValue& out, bool fIncludeHex)
{
- txnouttype type;
+ TxoutType type;
std::vector<CTxDestination> addresses;
int nRequired;
out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
if (fIncludeHex)
- out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
+ out.pushKV("hex", HexStr(scriptPubKey));
- if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired) || type == TX_PUBKEY) {
+ if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired) || type == TxoutType::PUBKEY) {
out.pushKV("type", GetTxnOutputType(type));
return;
}
@@ -179,7 +179,9 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
{
entry.pushKV("txid", tx.GetHash().GetHex());
entry.pushKV("hash", tx.GetWitnessHash().GetHex());
- entry.pushKV("version", tx.nVersion);
+ // Transaction version is actually unsigned in consensus checks, just signed in memory,
+ // so cast to unsigned before giving it to the user.
+ entry.pushKV("version", static_cast<int64_t>(static_cast<uint32_t>(tx.nVersion)));
entry.pushKV("size", (int)::GetSerializeSize(tx, PROTOCOL_VERSION));
entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR);
entry.pushKV("weight", GetTransactionWeight(tx));
@@ -190,21 +192,21 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
const CTxIn& txin = tx.vin[i];
UniValue in(UniValue::VOBJ);
if (tx.IsCoinBase())
- in.pushKV("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
+ in.pushKV("coinbase", HexStr(txin.scriptSig));
else {
in.pushKV("txid", txin.prevout.hash.GetHex());
in.pushKV("vout", (int64_t)txin.prevout.n);
UniValue o(UniValue::VOBJ);
o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
- o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
+ o.pushKV("hex", HexStr(txin.scriptSig));
in.pushKV("scriptSig", o);
- if (!tx.vin[i].scriptWitness.IsNull()) {
- UniValue txinwitness(UniValue::VARR);
- for (const auto& item : tx.vin[i].scriptWitness.stack) {
- txinwitness.push_back(HexStr(item.begin(), item.end()));
- }
- in.pushKV("txinwitness", txinwitness);
+ }
+ if (!tx.vin[i].scriptWitness.IsNull()) {
+ UniValue txinwitness(UniValue::VARR);
+ for (const auto& item : tx.vin[i].scriptWitness.stack) {
+ txinwitness.push_back(HexStr(item));
}
+ in.pushKV("txinwitness", txinwitness);
}
in.pushKV("sequence", (int64_t)txin.nSequence);
vin.push_back(in);
diff --git a/src/crypto/common.h b/src/crypto/common.h
index e7bb020a19..5b4932c992 100644
--- a/src/crypto/common.h
+++ b/src/crypto/common.h
@@ -82,12 +82,12 @@ void static inline WriteBE64(unsigned char* ptr, uint64_t x)
/** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */
uint64_t static inline CountBits(uint64_t x)
{
-#if HAVE_DECL___BUILTIN_CLZL
+#if HAVE_BUILTIN_CLZL
if (sizeof(unsigned long) >= sizeof(uint64_t)) {
return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0;
}
#endif
-#if HAVE_DECL___BUILTIN_CLZLL
+#if HAVE_BUILTIN_CLZLL
if (sizeof(unsigned long long) >= sizeof(uint64_t)) {
return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0;
}
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index 116d7d8679..215b033708 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -292,18 +292,6 @@ public:
// Get an estimate of LevelDB memory usage (in bytes).
size_t DynamicMemoryUsage() const;
- // not available for LevelDB; provide for compatibility with BDB
- bool Flush()
- {
- return true;
- }
-
- bool Sync()
- {
- CDBBatch batch(*this);
- return WriteBatch(batch, true);
- }
-
CDBIterator *NewIterator()
{
return new CDBIterator(*this, pdb->NewIterator(iteroptions));
diff --git a/src/flatfile.h b/src/flatfile.h
index 60b3503cc3..04f6373a24 100644
--- a/src/flatfile.h
+++ b/src/flatfile.h
@@ -16,13 +16,7 @@ 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_MODE(nFile, VarIntMode::NONNEGATIVE_SIGNED));
- READWRITE(VARINT(nPos));
- }
+ SERIALIZE_METHODS(FlatFilePos, obj) { READWRITE(VARINT_MODE(obj.nFile, VarIntMode::NONNEGATIVE_SIGNED), VARINT(obj.nPos)); }
FlatFilePos() : nFile(-1), nPos(0) {}
diff --git a/src/fs.cpp b/src/fs.cpp
index 066c6c10d3..eef9c81de9 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -5,7 +5,12 @@
#include <fs.h>
#ifndef WIN32
+#include <cstring>
#include <fcntl.h>
+#include <string>
+#include <sys/file.h>
+#include <sys/utsname.h>
+#include <unistd.h>
#else
#ifndef NOMINMAX
#define NOMINMAX
@@ -28,7 +33,8 @@ FILE *fopen(const fs::path& p, const char *mode)
#ifndef WIN32
-static std::string GetErrorReason() {
+static std::string GetErrorReason()
+{
return std::strerror(errno);
}
@@ -47,20 +53,38 @@ FileLock::~FileLock()
}
}
+static bool IsWSL()
+{
+ struct utsname uname_data;
+ return uname(&uname_data) == 0 && std::string(uname_data.version).find("Microsoft") != std::string::npos;
+}
+
bool FileLock::TryLock()
{
if (fd == -1) {
return false;
}
- struct flock lock;
- lock.l_type = F_WRLCK;
- lock.l_whence = SEEK_SET;
- lock.l_start = 0;
- lock.l_len = 0;
- if (fcntl(fd, F_SETLK, &lock) == -1) {
- reason = GetErrorReason();
- return false;
+
+ // Exclusive file locking is broken on WSL using fcntl (issue #18622)
+ // This workaround can be removed once the bug on WSL is fixed
+ static const bool is_wsl = IsWSL();
+ if (is_wsl) {
+ if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
+ reason = GetErrorReason();
+ return false;
+ }
+ } else {
+ struct flock lock;
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ if (fcntl(fd, F_SETLK, &lock) == -1) {
+ reason = GetErrorReason();
+ return false;
+ }
}
+
return true;
}
#else
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 3c3e6e5bba..f1b9997371 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -9,7 +9,6 @@
#include <httpserver.h>
#include <rpc/protocol.h>
#include <rpc/server.h>
-#include <ui_interface.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <util/translation.h>
@@ -151,7 +150,7 @@ static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUserna
return multiUserAuthorized(strUserPass);
}
-static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
+static bool HTTPReq_JSONRPC(const util::Ref& context, HTTPRequest* req)
{
// JSONRPC handles only POST
if (req->GetRequestMethod() != HTTPRequest::POST) {
@@ -166,7 +165,7 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
return false;
}
- JSONRPCRequest jreq;
+ JSONRPCRequest jreq(context);
jreq.peerAddr = req->GetPeer().ToString();
if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", jreq.peerAddr);
@@ -249,11 +248,8 @@ static bool InitRPCAuthentication()
{
if (gArgs.GetArg("-rpcpassword", "") == "")
{
- LogPrintf("No rpcpassword set - using random cookie authentication.\n");
+ LogPrintf("Using random cookie authentication.\n");
if (!GenerateAuthCookie(&strRPCUserColonPass)) {
- uiInterface.ThreadSafeMessageBox(
- _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
- "", CClientUIInterface::MSG_ERROR);
return false;
}
} else {
@@ -288,15 +284,16 @@ static bool InitRPCAuthentication()
return true;
}
-bool StartHTTPRPC()
+bool StartHTTPRPC(const util::Ref& context)
{
LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
if (!InitRPCAuthentication())
return false;
- RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
+ auto handle_rpc = [&context](HTTPRequest* req, const std::string&) { return HTTPReq_JSONRPC(context, req); };
+ RegisterHTTPHandler("/", true, handle_rpc);
if (g_wallet_init_interface.HasWalletSupport()) {
- RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
+ RegisterHTTPHandler("/wallet/", false, handle_rpc);
}
struct event_base* eventBase = EventBase();
assert(eventBase);
diff --git a/src/httprpc.h b/src/httprpc.h
index 99e4d59b8a..a6a38fc95a 100644
--- a/src/httprpc.h
+++ b/src/httprpc.h
@@ -5,11 +5,14 @@
#ifndef BITCOIN_HTTPRPC_H
#define BITCOIN_HTTPRPC_H
+namespace util {
+class Ref;
+} // namespace util
/** Start HTTP RPC subsystem.
* Precondition; HTTP and RPC has been started.
*/
-bool StartHTTPRPC();
+bool StartHTTPRPC(const util::Ref& context);
/** Interrupt HTTP RPC subsystem.
*/
void InterruptHTTPRPC();
@@ -21,7 +24,7 @@ void StopHTTPRPC();
/** Start HTTP REST subsystem.
* Precondition; HTTP and RPC has been started.
*/
-void StartREST();
+void StartREST(const util::Ref& context);
/** Interrupt RPC REST subsystem.
*/
void InterruptREST();
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index ffe246b241..1e5ea2de83 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -7,10 +7,10 @@
#include <chainparamsbase.h>
#include <compat.h>
#include <netbase.h>
+#include <node/ui_interface.h>
#include <rpc/protocol.h> // For HTTP status codes
#include <shutdown.h>
#include <sync.h>
-#include <ui_interface.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <util/threadnames.h>
@@ -421,7 +421,7 @@ bool UpdateHTTPServerLogging(bool enable) {
#endif
}
-static std::thread threadHTTP;
+static std::thread g_thread_http;
static std::vector<std::thread> g_thread_http_workers;
void StartHTTPServer()
@@ -429,7 +429,7 @@ void StartHTTPServer()
LogPrint(BCLog::HTTP, "Starting HTTP server\n");
int rpcThreads = std::max((long)gArgs.GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
LogPrintf("HTTP: starting %d worker threads\n", rpcThreads);
- threadHTTP = std::thread(ThreadHTTP, eventBase);
+ g_thread_http = std::thread(ThreadHTTP, eventBase);
for (int i = 0; i < rpcThreads; i++) {
g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue, i);
@@ -467,7 +467,7 @@ void StopHTTPServer()
boundSockets.clear();
if (eventBase) {
LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n");
- threadHTTP.join();
+ if (g_thread_http.joinable()) g_thread_http.join();
}
if (eventHTTP) {
evhttp_free(eventHTTP);
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 74ea421e13..f587205a28 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -4,9 +4,9 @@
#include <chainparams.h>
#include <index/base.h>
+#include <node/ui_interface.h>
#include <shutdown.h>
#include <tinyformat.h>
-#include <ui_interface.h>
#include <util/system.h>
#include <util/translation.h>
#include <validation.h>
@@ -17,15 +17,13 @@ constexpr char DB_BEST_BLOCK = 'B';
constexpr int64_t SYNC_LOG_INTERVAL = 30; // seconds
constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL = 30; // seconds
-template<typename... Args>
+template <typename... Args>
static void FatalError(const char* fmt, const Args&... args)
{
std::string strMessage = tfm::format(fmt, args...);
- SetMiscWarning(strMessage);
+ SetMiscWarning(Untranslated(strMessage));
LogPrintf("*** %s\n", strMessage);
- uiInterface.ThreadSafeMessageBox(
- Untranslated("Error: A fatal internal error occurred, see debug.log for details"),
- "", CClientUIInterface::MSG_ERROR);
+ AbortError(_("A fatal internal error occurred, see debug.log for details"));
StartShutdown();
}
diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp
index c3ce8d7af0..65a5f03a8e 100644
--- a/src/index/blockfilterindex.cpp
+++ b/src/index/blockfilterindex.cpp
@@ -31,6 +31,12 @@ 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
+/** Maximum size of the cfheaders cache
+ * We have a limit to prevent a bug in filling this cache
+ * potentially turning into an OOM. At 2000 entries, this cache
+ * is big enough for a 2,000,000 length block chain, which
+ * we should be enough until ~2047. */
+constexpr size_t CF_HEADERS_CACHE_MAX_SZ{2000};
namespace {
@@ -39,14 +45,7 @@ struct DBVal {
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);
- }
+ SERIALIZE_METHODS(DBVal, obj) { READWRITE(obj.hash, obj.header, obj.pos); }
};
struct DBHeightKey {
@@ -78,17 +77,14 @@ struct DBHashKey {
explicit DBHashKey(const uint256& hash_in) : hash(hash_in) {}
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
+ SERIALIZE_METHODS(DBHashKey, obj) {
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);
+ READWRITE(obj.hash);
}
};
@@ -387,13 +383,32 @@ bool BlockFilterIndex::LookupFilter(const CBlockIndex* block_index, BlockFilter&
return ReadFilterFromDisk(entry.pos, filter_out);
}
-bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out) const
+bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out)
{
+ LOCK(m_cs_headers_cache);
+
+ bool is_checkpoint{block_index->nHeight % CFCHECKPT_INTERVAL == 0};
+
+ if (is_checkpoint) {
+ // Try to find the block in the headers cache if this is a checkpoint height.
+ auto header = m_headers_cache.find(block_index->GetBlockHash());
+ if (header != m_headers_cache.end()) {
+ header_out = header->second;
+ return true;
+ }
+ }
+
DBVal entry;
if (!LookupOne(*m_db, block_index, entry)) {
return false;
}
+ if (is_checkpoint &&
+ m_headers_cache.size() < CF_HEADERS_CACHE_MAX_SZ) {
+ // Add to the headers cache if this is a checkpoint height.
+ m_headers_cache.emplace(block_index->GetBlockHash(), entry.header);
+ }
+
header_out = entry.header;
return true;
}
diff --git a/src/index/blockfilterindex.h b/src/index/blockfilterindex.h
index 436d52515f..317f8c0e40 100644
--- a/src/index/blockfilterindex.h
+++ b/src/index/blockfilterindex.h
@@ -10,6 +10,14 @@
#include <flatfile.h>
#include <index/base.h>
+/** Interval between compact filter checkpoints. See BIP 157. */
+static constexpr int CFCHECKPT_INTERVAL = 1000;
+
+struct FilterHeaderHasher
+{
+ size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
+};
+
/**
* 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
@@ -30,6 +38,10 @@ private:
bool ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& filter) const;
size_t WriteFilterToDisk(FlatFilePos& pos, const BlockFilter& filter);
+ Mutex m_cs_headers_cache;
+ /** cache of block hash to filter header, to avoid disk access when responding to getcfcheckpt. */
+ std::unordered_map<uint256, uint256, FilterHeaderHasher> m_headers_cache GUARDED_BY(m_cs_headers_cache);
+
protected:
bool Init() override;
@@ -54,7 +66,7 @@ public:
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;
+ bool LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out);
/** Get a range of filters between two heights on a chain. */
bool LookupFilterRange(int start_height, const CBlockIndex* stop_index,
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index 5bbe6ad1df..64472714cc 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -3,14 +3,12 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <index/txindex.h>
+#include <node/ui_interface.h>
#include <shutdown.h>
-#include <ui_interface.h>
#include <util/system.h>
#include <util/translation.h>
#include <validation.h>
-#include <boost/thread.hpp>
-
constexpr char DB_BEST_BLOCK = 'B';
constexpr char DB_TXINDEX = 't';
constexpr char DB_TXINDEX_BLOCK = 'T';
@@ -21,12 +19,10 @@ struct CDiskTxPos : public FlatFilePos
{
unsigned int nTxOffset; // after header
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITEAS(FlatFilePos, *this);
- READWRITE(VARINT(nTxOffset));
+ SERIALIZE_METHODS(CDiskTxPos, obj)
+ {
+ READWRITEAS(FlatFilePos, obj);
+ READWRITE(VARINT(obj.nTxOffset));
}
CDiskTxPos(const FlatFilePos &blockIn, unsigned int nTxOffsetIn) : FlatFilePos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) {
@@ -152,7 +148,6 @@ bool TxIndex::DB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator&
bool interrupted = false;
std::unique_ptr<CDBIterator> cursor(block_tree_db.NewIterator());
for (cursor->Seek(begin_key); cursor->Valid(); cursor->Next()) {
- boost::this_thread::interruption_point();
if (ShutdownRequested()) {
interrupted = true;
break;
diff --git a/src/init.cpp b/src/init.cpp
index 3b97ba08d9..9864bad291 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -18,6 +18,7 @@
#include <compat/sanity.h>
#include <consensus/validation.h>
#include <fs.h>
+#include <hash.h>
#include <httprpc.h>
#include <httpserver.h>
#include <index/blockfilterindex.h>
@@ -30,6 +31,7 @@
#include <net_processing.h>
#include <netbase.h>
#include <node/context.h>
+#include <node/ui_interface.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
@@ -42,26 +44,27 @@
#include <script/sigcache.h>
#include <script/standard.h>
#include <shutdown.h>
+#include <sync.h>
#include <timedata.h>
#include <torcontrol.h>
#include <txdb.h>
#include <txmempool.h>
-#include <ui_interface.h>
#include <util/asmap.h>
+#include <util/check.h>
#include <util/moneystr.h>
+#include <util/string.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <util/translation.h>
#include <validation.h>
-#include <hash.h>
-
#include <validationinterface.h>
#include <walletinitinterface.h>
+#include <functional>
+#include <set>
#include <stdint.h>
#include <stdio.h>
-#include <set>
#ifndef WIN32
#include <attributes.h>
@@ -70,11 +73,9 @@
#include <sys/stat.h>
#endif
-#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/replace.hpp>
-#include <boost/algorithm/string/split.hpp>
#include <boost/signals2/signal.hpp>
-#include <boost/thread.hpp>
+#include <boost/thread/thread.hpp>
#if ENABLE_ZMQ
#include <zmq/zmqabstractnotifier.h>
@@ -152,6 +153,8 @@ NODISCARD static bool CreatePidFile()
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
+static std::thread g_load_block;
+
static boost::thread_group threadGroup;
void Interrupt(NodeContext& node)
@@ -172,11 +175,10 @@ void Interrupt(NodeContext& node)
void Shutdown(NodeContext& node)
{
+ static Mutex g_shutdown_mutex;
+ TRY_LOCK(g_shutdown_mutex, lock_shutdown);
+ if (!lock_shutdown) return;
LogPrintf("%s: In progress...\n", __func__);
- static RecursiveMutex cs_Shutdown;
- TRY_LOCK(cs_Shutdown, lockShutdown);
- if (!lockShutdown)
- return;
/// Note: Shutdown() must be able to handle cases in which initialization failed part of the way,
/// for example if the data directory was found to be locked.
@@ -215,8 +217,9 @@ void Shutdown(NodeContext& node)
StopTorControl();
// After everything has been shut down, but before things get flushed, stop the
- // CScheduler/checkqueue threadGroup
+ // CScheduler/checkqueue, threadGroup and load block thread.
if (node.scheduler) node.scheduler->stop();
+ if (g_load_block.joinable()) g_load_block.join();
threadGroup.interrupt_all();
threadGroup.join_all();
@@ -243,9 +246,9 @@ void Shutdown(NodeContext& node)
}
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
- {
+ if (node.chainman) {
LOCK(cs_main);
- for (CChainState* chainstate : g_chainman.GetAll()) {
+ for (CChainState* chainstate : node.chainman->GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
}
@@ -270,9 +273,9 @@ void Shutdown(NodeContext& node)
// up with our current chain to avoid any strange pruning edge cases and make
// next startup faster by avoiding rescan.
- {
+ if (node.chainman) {
LOCK(cs_main);
- for (CChainState* chainstate : g_chainman.GetAll()) {
+ for (CChainState* chainstate : node.chainman->GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
chainstate->ResetCoinsViews();
@@ -298,7 +301,8 @@ void Shutdown(NodeContext& node)
globalVerifyHandle.reset();
ECC_Stop();
node.args = nullptr;
- if (node.mempool) node.mempool = nullptr;
+ node.mempool = nullptr;
+ node.chainman = nullptr;
node.scheduler.reset();
try {
@@ -350,13 +354,13 @@ static void registerSignalHandler(int signal, void(*handler)(int))
static boost::signals2::connection rpc_notify_block_change_connection;
static void OnRPCStarted()
{
- rpc_notify_block_change_connection = uiInterface.NotifyBlockTip_connect(&RPCNotifyBlockChange);
+ rpc_notify_block_change_connection = uiInterface.NotifyBlockTip_connect(std::bind(RPCNotifyBlockChange, std::placeholders::_2));
}
static void OnRPCStopped()
{
rpc_notify_block_change_connection.disconnect();
- RPCNotifyBlockChange(false, nullptr);
+ RPCNotifyBlockChange(nullptr);
g_best_block_cv.notify_all();
LogPrint(BCLog::RPC, "RPC stopped.\n");
}
@@ -392,7 +396,7 @@ void SetupServerArgs(NodeContext& node)
gArgs.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
gArgs.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- gArgs.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Automatic broadcast and rebroadcast of any transactions from inbound peers is disabled, unless '-whitelistforcerelay' is '1', in which case whitelisted peers' transactions will be relayed. RPC transactions are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Automatic broadcast and rebroadcast of any transactions from inbound peers is disabled, unless the peer has the 'forcerelay' permission. RPC transactions are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
@@ -427,8 +431,7 @@ void SetupServerArgs(NodeContext& node)
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.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
gArgs.AddArg("-asmap=<file>", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Relative paths will be prefixed by the net-specific datadir location.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- gArgs.AddArg("-bantime=<n>", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-bantime=<n>", strprintf("Default duration (in seconds) of manually configured bans (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
gArgs.AddArg("-connect=<ip>", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
gArgs.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -442,7 +445,7 @@ void SetupServerArgs(NodeContext& node)
gArgs.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- gArgs.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h). Limit does not apply to peers with 'download' permission. 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -465,16 +468,12 @@ void SetupServerArgs(NodeContext& node)
#else
hidden_args.emplace_back("-upnp");
#endif
- gArgs.AddArg("-whitebind=<[permissions@]addr>", "Bind to given address and whitelist peers connecting to it. "
- "Use [host]:port notation for IPv6. Allowed permissions are bloomfilter (allow requesting BIP37 filtered blocks and transactions), "
- "noban (do not ban for misbehavior), "
- "forcerelay (relay transactions that are already in the mempool; implies relay), "
- "relay (relay even in -blocksonly mode), "
- "and mempool (allow requesting BIP35 mempool contents). "
- "Specify multiple permissions separated by commas (default: noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
-
- gArgs.AddArg("-whitelist=<[permissions@]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). Uses same permissions as "
+ gArgs.AddArg("-whitebind=<[permissions@]addr>", "Bind to the given address and add permission flags to the peers connecting to it. "
+ "Use [host]:port notation for IPv6. Allowed permissions: " + Join(NET_PERMISSIONS_DOC, ", ") + ". "
+ "Specify multiple permissions separated by commas (default: download,noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+
+ gArgs.AddArg("-whitelist=<[permissions@]IP address or network>", "Add permission flags to the peers connecting from the given IP address (e.g. 1.2.3.4) or "
+ "CIDR-notated network (e.g. 1.2.3.0/24). Uses the same permissions as "
"-whitebind. Can be specified multiple times." , ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
g_wallet_init_interface.AddWalletOptions();
@@ -500,14 +499,7 @@ void SetupServerArgs(NodeContext& node)
#endif
gArgs.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: "
- "level 0 reads the blocks from disk, "
- "level 1 verifies block validity, "
- "level 2 verifies undo data, "
- "level 3 checks disconnection of tip blocks, "
- "and level 4 tries to reconnect the blocks, "
- "each level includes the checks of the previous levels "
- "(0-4, default: %u)", DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: %s (0-4, default: %u)", Join(CHECKLEVEL_DOC, ", "), DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkblockindex", strprintf("Do a consistency check for the block tree, chainstate, and other validation data structures occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkpoints", strprintf("Enable rejection of any forks from the known historical chain until block 295000 (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
@@ -604,9 +596,9 @@ std::string LicenseInfo()
}
#if HAVE_SYSTEM
-static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex)
+static void BlockNotifyCallback(SynchronizationState sync_state, const CBlockIndex* pBlockIndex)
{
- if (initialSync || !pBlockIndex)
+ if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex)
return;
std::string strCmd = gArgs.GetArg("-blocknotify", "");
@@ -622,7 +614,7 @@ static bool fHaveGenesis = false;
static Mutex g_genesis_wait_mutex;
static std::condition_variable g_genesis_wait_cv;
-static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex)
+static void BlockNotifyGenesisWait(const CBlockIndex* pBlockIndex)
{
if (pBlockIndex != nullptr) {
{
@@ -688,10 +680,9 @@ static void CleanupBlockRevFiles()
}
}
-static void ThreadImport(std::vector<fs::path> vImportFiles)
+static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles)
{
const CChainParams& chainparams = Params();
- util::ThreadRename("loadblk");
ScheduleBatchPriority();
{
@@ -740,9 +731,9 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
// scan for better chains in the block chain database, that are not yet connected in the active best chain
// We can't hold cs_main during ActivateBestChain even though we're accessing
- // the g_chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
+ // the chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
// the relevant pointers before the ABC call.
- for (CChainState* chainstate : WITH_LOCK(::cs_main, return g_chainman.GetAll())) {
+ for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
BlockValidationState state;
if (!chainstate->ActivateBestChain(state, chainparams, nullptr)) {
LogPrintf("Failed to connect best block (%s)\n", state.ToString());
@@ -783,16 +774,17 @@ static bool InitSanityCheck()
return true;
}
-static bool AppInitServers()
+static bool AppInitServers(const util::Ref& context, NodeContext& node)
{
RPCServer::OnStarted(&OnRPCStarted);
RPCServer::OnStopped(&OnRPCStopped);
if (!InitHTTPServer())
return false;
StartRPC();
- if (!StartHTTPRPC())
+ node.rpc_interruption_point = RpcInterruptionPoint;
+ if (!StartHTTPRPC(context))
return false;
- if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST();
+ if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST(context);
StartHTTPServer();
return true;
}
@@ -962,17 +954,27 @@ bool AppInitParameterInteraction()
// also see: InitParameterInteraction()
- // Warn if network-specific options (-addnode, -connect, etc) are
+ // Error 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();
+ bilingual_str errors;
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));
+ errors += strprintf(_("Config setting for %s only applied on %s network when in [%s] section.") + Untranslated("\n"), arg, network, network);
+ }
+
+ if (!errors.empty()) {
+ return InitError(errors);
}
// Warn if unrecognized section name are present in the config file.
+ bilingual_str warnings;
for (const auto& section : gArgs.GetUnrecognizedSections()) {
- InitWarning(strprintf(Untranslated("%s:%i ") + _("Section [%s] is not recognized."), section.m_file, section.m_line, section.m_name));
+ warnings += strprintf(Untranslated("%s:%i ") + _("Section [%s] is not recognized.") + Untranslated("\n"), section.m_file, section.m_line, section.m_name);
+ }
+
+ if (!warnings.empty()) {
+ InitWarning(warnings);
}
if (!fs::is_directory(GetBlocksDir())) {
@@ -1237,7 +1239,7 @@ bool AppInitLockDataDirectory()
return true;
}
-bool AppInitMain(NodeContext& node)
+bool AppInitMain(const util::Ref& context, NodeContext& node)
{
const CChainParams& chainparams = Params();
// ********************************************************* Step 4a: application initialization
@@ -1316,8 +1318,7 @@ bool AppInitMain(NodeContext& node)
node.scheduler = MakeUnique<CScheduler>();
// Start the lightweight task scheduler thread
- CScheduler::Function serviceLoop = [&node]{ node.scheduler->serviceQueue(); };
- threadGroup.create_thread(std::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));
+ threadGroup.create_thread([&] { TraceThread("scheduler", [&] { node.scheduler->serviceQueue(); }); });
// Gather some entropy once per minute.
node.scheduler->scheduleEvery([]{
@@ -1339,7 +1340,6 @@ bool AppInitMain(NodeContext& node)
for (const auto& client : node.chain_clients) {
client->registerRpcs();
}
- g_rpc_node = &node;
#if ENABLE_ZMQ
RegisterZMQRPCCommands(tableRPC);
#endif
@@ -1352,7 +1352,7 @@ bool AppInitMain(NodeContext& node)
if (gArgs.GetBoolArg("-server", false))
{
uiInterface.InitMessage_connect(SetRPCWarmupStatus);
- if (!AppInitServers())
+ if (!AppInitServers(context, node))
return InitError(_("Unable to start HTTP server. See debug log for details."));
}
@@ -1377,8 +1377,11 @@ bool AppInitMain(NodeContext& node)
// which are all started after this, may use it from the node context.
assert(!node.mempool);
node.mempool = &::mempool;
+ assert(!node.chainman);
+ node.chainman = &g_chainman;
+ ChainstateManager& chainman = *Assert(node.chainman);
- node.peer_logic.reset(new PeerLogicValidation(node.connman.get(), node.banman.get(), *node.scheduler, *node.mempool));
+ node.peer_logic.reset(new PeerLogicValidation(node.connman.get(), node.banman.get(), *node.scheduler, chainman, *node.mempool));
RegisterValidationInterface(node.peer_logic.get());
// sanitize comments per BIP-0014, format user agent and check total size
@@ -1464,7 +1467,7 @@ bool AppInitMain(NodeContext& node)
if (Lookup(strAddr, addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid())
AddLocal(addrLocal, LOCAL_MANUAL);
else
- return InitError(Untranslated(ResolveErrMsg("externalip", strAddr)));
+ return InitError(ResolveErrMsg("externalip", strAddr));
}
// Read asmap file if configured
@@ -1557,7 +1560,7 @@ bool AppInitMain(NodeContext& node)
const int64_t load_block_index_start_time = GetTimeMillis();
try {
LOCK(cs_main);
- g_chainman.InitializeChainstate();
+ chainman.InitializeChainstate();
UnloadBlockIndex();
// new CBlockTreeDB tries to delete the existing file, which
@@ -1578,7 +1581,7 @@ bool AppInitMain(NodeContext& node)
// block file from disk.
// 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 (!chainman.LoadBlockIndex(chainparams)) {
if (ShutdownRequested()) break;
strLoadError = _("Error loading block database");
break;
@@ -1586,7 +1589,7 @@ bool AppInitMain(NodeContext& node)
// If the loaded chain has a wrong genesis, bail out immediately
// (we're likely using a testnet datadir, or the other way around).
- if (!::BlockIndex().empty() &&
+ if (!chainman.BlockIndex().empty() &&
!LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
}
@@ -1612,7 +1615,7 @@ bool AppInitMain(NodeContext& node)
bool failed_chainstate_init = false;
- for (CChainState* chainstate : g_chainman.GetAll()) {
+ for (CChainState* chainstate : chainman.GetAll()) {
LogPrintf("Initializing chainstate %s\n", chainstate->ToString());
chainstate->InitCoinsDB(
/* cache_size_bytes */ nCoinDBCache,
@@ -1667,7 +1670,7 @@ bool AppInitMain(NodeContext& node)
bool failed_rewind{false};
// Can't hold cs_main while calling RewindBlockIndex, so retrieve the relevant
// chainstates beforehand.
- for (CChainState* chainstate : WITH_LOCK(::cs_main, return g_chainman.GetAll())) {
+ for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
if (!fReset) {
// Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
// It both disconnects blocks based on the chainstate, and drops block data in
@@ -1692,7 +1695,7 @@ bool AppInitMain(NodeContext& node)
try {
LOCK(cs_main);
- for (CChainState* chainstate : g_chainman.GetAll()) {
+ for (CChainState* chainstate : chainman.GetAll()) {
if (!is_coinsview_empty(chainstate)) {
uiInterface.InitMessage(_("Verifying blocks...").translated);
if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
@@ -1701,7 +1704,7 @@ bool AppInitMain(NodeContext& node)
}
const CBlockIndex* tip = chainstate->m_chain.Tip();
- RPCNotifyBlockChange(true, tip);
+ RPCNotifyBlockChange(tip);
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
strLoadError = _("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. "
@@ -1798,7 +1801,7 @@ bool AppInitMain(NodeContext& node)
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
if (!fReindex) {
LOCK(cs_main);
- for (CChainState* chainstate : g_chainman.GetAll()) {
+ for (CChainState* chainstate : chainman.GetAll()) {
uiInterface.InitMessage(_("Pruning blockstore...").translated);
chainstate->PruneAndFlush();
}
@@ -1826,7 +1829,7 @@ bool AppInitMain(NodeContext& node)
// No locking, as this happens before any background thread is started.
boost::signals2::connection block_notify_genesis_wait_connection;
if (::ChainActive().Tip() == nullptr) {
- block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(BlockNotifyGenesisWait);
+ block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(std::bind(BlockNotifyGenesisWait, std::placeholders::_2));
} else {
fHaveGenesis = true;
}
@@ -1841,7 +1844,7 @@ bool AppInitMain(NodeContext& node)
vImportFiles.push_back(strFile);
}
- threadGroup.create_thread(std::bind(&ThreadImport, vImportFiles));
+ g_load_block = std::thread(&TraceThread<std::function<void()>>, "loadblk", [=, &chainman]{ ThreadImport(chainman, vImportFiles); });
// Wait for genesis block to be processed
{
@@ -1866,8 +1869,8 @@ bool AppInitMain(NodeContext& node)
//// debug print
{
LOCK(cs_main);
- LogPrintf("block tree size = %u\n", ::BlockIndex().size());
- chain_active_height = ::ChainActive().Height();
+ LogPrintf("block tree size = %u\n", chainman.BlockIndex().size());
+ chain_active_height = chainman.ActiveChain().Height();
}
LogPrintf("nBestHeight = %d\n", chain_active_height);
@@ -1903,21 +1906,21 @@ bool AppInitMain(NodeContext& node)
for (const std::string& strBind : gArgs.GetArgs("-bind")) {
CService addrBind;
if (!Lookup(strBind, addrBind, GetListenPort(), false)) {
- return InitError(Untranslated(ResolveErrMsg("bind", strBind)));
+ return InitError(ResolveErrMsg("bind", strBind));
}
connOptions.vBinds.push_back(addrBind);
}
for (const std::string& strBind : gArgs.GetArgs("-whitebind")) {
NetWhitebindPermissions whitebind;
- std::string error;
- if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(Untranslated(error));
+ bilingual_str error;
+ if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(error);
connOptions.vWhiteBinds.push_back(whitebind);
}
for (const auto& net : gArgs.GetArgs("-whitelist")) {
NetWhitelistPermissions subnet;
- std::string error;
- if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(Untranslated(error));
+ bilingual_str error;
+ if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error);
connOptions.vWhitelistedRange.push_back(subnet);
}
diff --git a/src/init.h b/src/init.h
index ef568b6f38..33fe96e8ea 100644
--- a/src/init.h
+++ b/src/init.h
@@ -14,6 +14,9 @@ struct NodeContext;
namespace boost {
class thread_group;
} // namespace boost
+namespace util {
+class Ref;
+} // namespace util
/** Interrupt threads */
void Interrupt(NodeContext& node);
@@ -51,7 +54,7 @@ bool AppInitLockDataDirectory();
* @note This should only be done after daemonization. Call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called.
*/
-bool AppInitMain(NodeContext& node);
+bool AppInitMain(const util::Ref& context, NodeContext& node);
/**
* Register all arguments with the ArgsManager
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index d8e459a8e8..d49e4454af 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -13,6 +13,7 @@
#include <node/coin.h>
#include <node/context.h>
#include <node/transaction.h>
+#include <node/ui_interface.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
@@ -25,7 +26,6 @@
#include <sync.h>
#include <timedata.h>
#include <txmempool.h>
-#include <ui_interface.h>
#include <uint256.h>
#include <univalue.h>
#include <util/system.h>
@@ -63,9 +63,9 @@ public:
{
m_notifications->transactionAddedToMempool(tx);
}
- void TransactionRemovedFromMempool(const CTransactionRef& tx) override
+ void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) override
{
- m_notifications->transactionRemovedFromMempool(tx);
+ m_notifications->transactionRemovedFromMempool(tx, reason);
}
void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* index) override
{
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 7dfc77db7b..bbeb0fa801 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -8,18 +8,21 @@
#include <optional.h> // For Optional and nullopt
#include <primitives/transaction.h> // For CTransactionRef
+#include <functional>
#include <memory>
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
+class ArgsManager;
class CBlock;
class CFeeRate;
class CRPCCommand;
class CScheduler;
class Coin;
class uint256;
+enum class MemPoolRemovalReason;
enum class RBFTransactionState;
struct bilingual_str;
struct CBlockLocator;
@@ -239,7 +242,7 @@ public:
public:
virtual ~Notifications() {}
virtual void transactionAddedToMempool(const CTransactionRef& tx) {}
- virtual void transactionRemovedFromMempool(const CTransactionRef& ptx) {}
+ virtual void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {}
virtual void blockConnected(const CBlock& block, int height) {}
virtual void blockDisconnected(const CBlock& block, int height) {}
virtual void updatedBlockTip() {}
@@ -320,7 +323,7 @@ std::unique_ptr<Chain> MakeChain(NodeContext& node);
//! analysis, or fee estimation. These clients need to expose their own
//! MakeXXXClient functions returning their implementations of the ChainClient
//! interface.
-std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, std::vector<std::string> wallet_filenames);
+std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames);
} // namespace interfaces
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 9e603a12cd..834a16ecf5 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -17,6 +17,7 @@
#include <netaddress.h>
#include <netbase.h>
#include <node/context.h>
+#include <node/ui_interface.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/settings.h>
@@ -26,7 +27,7 @@
#include <support/allocators/secure.h>
#include <sync.h>
#include <txmempool.h>
-#include <ui_interface.h>
+#include <util/ref.h>
#include <util/system.h>
#include <util/translation.h>
#include <validation.h>
@@ -55,7 +56,7 @@ namespace {
class NodeImpl : public Node
{
public:
- void initError(const std::string& message) override { InitError(Untranslated(message)); }
+ void initError(const bilingual_str& message) override { InitError(message); }
bool parseParameters(int argc, const char* const argv[], std::string& error) override
{
return gArgs.ParseParameters(argc, argv, error);
@@ -70,7 +71,7 @@ public:
std::string getNetwork() override { return Params().NetworkIDString(); }
void initLogging() override { InitLogging(); }
void initParameterInteraction() override { InitParameterInteraction(); }
- std::string getWarnings() override { return GetWarnings(true); }
+ bilingual_str getWarnings() override { return GetWarnings(true); }
uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
bool baseInitialize() override
{
@@ -80,14 +81,22 @@ public:
bool appInitMain() override
{
m_context.chain = MakeChain(m_context);
- return AppInitMain(m_context);
+ return AppInitMain(m_context_ref, m_context);
}
void appShutdown() override
{
Interrupt(m_context);
Shutdown(m_context);
}
- void startShutdown() override { StartShutdown(); }
+ void startShutdown() override
+ {
+ StartShutdown();
+ // Stop RPC for clean shutdown if any of waitfor* commands is executed.
+ if (gArgs.GetBoolArg("-server", false)) {
+ InterruptRPC();
+ StopRPC();
+ }
+ }
bool shutdownRequested() override { return ShutdownRequested(); }
void mapPort(bool use_upnp) override
{
@@ -137,10 +146,10 @@ public:
}
return false;
}
- bool ban(const CNetAddr& net_addr, BanReason reason, int64_t ban_time_offset) override
+ bool ban(const CNetAddr& net_addr, int64_t ban_time_offset) override
{
if (m_context.banman) {
- m_context.banman->Ban(net_addr, reason, ban_time_offset);
+ m_context.banman->Ban(net_addr, ban_time_offset);
return true;
}
return false;
@@ -186,6 +195,11 @@ public:
LOCK(::cs_main);
return ::ChainActive().Height();
}
+ uint256 getBestBlockHash() override
+ {
+ const CBlockIndex* tip = WITH_LOCK(::cs_main, return ::ChainActive().Tip());
+ return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash();
+ }
int64_t getLastBlockTime() override
{
LOCK(::cs_main);
@@ -225,7 +239,7 @@ public:
CFeeRate getDustRelayFee() override { return ::dustRelayFee; }
UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override
{
- JSONRPCRequest req;
+ JSONRPCRequest req(m_context_ref);
req.params = params;
req.strMethod = command;
req.URI = uri;
@@ -308,21 +322,22 @@ public:
}
std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) override
{
- return MakeHandler(::uiInterface.NotifyBlockTip_connect([fn](bool initial_download, const CBlockIndex* block) {
- fn(initial_download, block->nHeight, block->GetBlockTime(),
+ return MakeHandler(::uiInterface.NotifyBlockTip_connect([fn](SynchronizationState sync_state, const CBlockIndex* block) {
+ fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()},
GuessVerificationProgress(Params().TxData(), block));
}));
}
std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) override
{
return MakeHandler(
- ::uiInterface.NotifyHeaderTip_connect([fn](bool initial_download, const CBlockIndex* block) {
- fn(initial_download, block->nHeight, block->GetBlockTime(),
+ ::uiInterface.NotifyHeaderTip_connect([fn](SynchronizationState sync_state, const CBlockIndex* block) {
+ fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()},
/* verification progress is unused when a header was received */ 0);
}));
}
NodeContext* context() override { return &m_context; }
NodeContext m_context;
+ util::Ref m_context_ref{m_context};
};
} // namespace
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index aef6b19458..b88b5bc14e 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -10,6 +10,7 @@
#include <net_types.h> // For banmap_t
#include <netaddress.h> // For Network
#include <support/allocators/secure.h> // For SecureString
+#include <util/translation.h>
#include <functional>
#include <memory>
@@ -27,6 +28,7 @@ class Coin;
class RPCTimerInterface;
class UniValue;
class proxyType;
+enum class SynchronizationState;
enum class WalletCreationStatus;
struct CNodeStateStats;
struct NodeContext;
@@ -35,6 +37,7 @@ struct bilingual_str;
namespace interfaces {
class Handler;
class Wallet;
+struct BlockTip;
//! Top-level interface for a bitcoin node (bitcoind process).
class Node
@@ -43,7 +46,7 @@ public:
virtual ~Node() {}
//! Send init error.
- virtual void initError(const std::string& message) = 0;
+ virtual void initError(const bilingual_str& message) = 0;
//! Set command line arguments.
virtual bool parseParameters(int argc, const char* const argv[], std::string& error) = 0;
@@ -79,7 +82,7 @@ public:
virtual void initParameterInteraction() = 0;
//! Get warnings.
- virtual std::string getWarnings() = 0;
+ virtual bilingual_str getWarnings() = 0;
// Get log flags.
virtual uint32_t getLogCategories() = 0;
@@ -119,7 +122,7 @@ public:
virtual bool getBanned(banmap_t& banmap) = 0;
//! Ban node.
- virtual bool ban(const CNetAddr& net_addr, BanReason reason, int64_t ban_time_offset) = 0;
+ virtual bool ban(const CNetAddr& net_addr, int64_t ban_time_offset) = 0;
//! Unban node.
virtual bool unban(const CSubNet& ip) = 0;
@@ -148,6 +151,9 @@ public:
//! Get num blocks.
virtual int getNumBlocks() = 0;
+ //! Get best block hash.
+ virtual uint256 getBestBlockHash() = 0;
+
//! Get last block time.
virtual int64_t getLastBlockTime() = 0;
@@ -249,12 +255,12 @@ public:
//! Register handler for block tip messages.
using NotifyBlockTipFn =
- std::function<void(bool initial_download, int height, int64_t block_time, double verification_progress)>;
+ std::function<void(SynchronizationState, interfaces::BlockTip tip, double verification_progress)>;
virtual std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) = 0;
//! Register handler for header tip messages.
using NotifyHeaderTipFn =
- std::function<void(bool initial_download, int height, int64_t block_time, double verification_progress)>;
+ std::function<void(SynchronizationState, interfaces::BlockTip tip, double verification_progress)>;
virtual std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) = 0;
//! Return pointer to internal chain interface, useful for testing.
@@ -264,6 +270,13 @@ public:
//! Return implementation of Node interface.
std::unique_ptr<Node> MakeNode();
+//! Block tip (could be a header or not, depends on the subscribed signal).
+struct BlockTip {
+ int block_height;
+ int64_t block_time;
+ uint256 block_hash;
+};
+
} // namespace interfaces
#endif // BITCOIN_INTERFACES_NODE_H
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 13b034936b..7fd24425cf 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -9,13 +9,16 @@
#include <interfaces/handler.h>
#include <policy/fees.h>
#include <primitives/transaction.h>
+#include <rpc/server.h>
#include <script/standard.h>
#include <support/allocators/secure.h>
#include <sync.h>
-#include <ui_interface.h>
#include <uint256.h>
#include <util/check.h>
+#include <util/ref.h>
#include <util/system.h>
+#include <util/ui_change_type.h>
+#include <wallet/context.h>
#include <wallet/feebumper.h>
#include <wallet/fees.h>
#include <wallet/ismine.h>
@@ -332,9 +335,10 @@ public:
bool sign,
bool bip32derivs,
PartiallySignedTransaction& psbtx,
- bool& complete) override
+ bool& complete,
+ size_t* n_signed) override
{
- return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs);
+ return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs, n_signed);
}
WalletBalances getBalances() override
{
@@ -351,14 +355,13 @@ public:
}
return result;
}
- bool tryGetBalances(WalletBalances& balances, int& num_blocks, bool force, int cached_num_blocks) override
+ bool tryGetBalances(WalletBalances& balances, uint256& block_hash) override
{
TRY_LOCK(m_wallet->cs_wallet, locked_wallet);
if (!locked_wallet) {
return false;
}
- num_blocks = m_wallet->GetLastBlockHeight();
- if (!force && num_blocks == cached_num_blocks) return false;
+ block_hash = m_wallet->GetLastBlockHash();
balances = getBalances();
return true;
}
@@ -435,7 +438,6 @@ public:
bool canGetAddresses() override { return m_wallet->CanGetAddresses(); }
bool privateKeysDisabled() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); }
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
{
@@ -481,18 +483,24 @@ public:
class WalletClientImpl : public ChainClient
{
public:
- WalletClientImpl(Chain& chain, std::vector<std::string> wallet_filenames)
- : m_chain(chain), m_wallet_filenames(std::move(wallet_filenames))
+ WalletClientImpl(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames)
+ : m_wallet_filenames(std::move(wallet_filenames))
{
+ m_context.chain = &chain;
+ m_context.args = &args;
}
void registerRpcs() override
{
- g_rpc_chain = &m_chain;
- return RegisterWalletRPCCommands(m_chain, m_rpc_handlers);
+ for (const CRPCCommand& command : GetWalletRPCCommands()) {
+ m_rpc_commands.emplace_back(command.category, command.name, [this, &command](const JSONRPCRequest& request, UniValue& result, bool last_handler) {
+ return command.actor({request, m_context}, result, last_handler);
+ }, command.argNames, command.unique_id);
+ m_rpc_handlers.emplace_back(m_context.chain->handleRpc(m_rpc_commands.back()));
+ }
}
- 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); }
+ bool verify() override { return VerifyWallets(*m_context.chain, m_wallet_filenames); }
+ bool load() override { return LoadWallets(*m_context.chain, m_wallet_filenames); }
+ void start(CScheduler& scheduler) override { return StartWallets(scheduler, *Assert(m_context.args)); }
void flush() override { return FlushWallets(); }
void stop() override { return StopWallets(); }
void setMockTime(int64_t time) override { return SetMockTime(time); }
@@ -506,18 +514,19 @@ public:
}
~WalletClientImpl() override { UnloadWallets(); }
- Chain& m_chain;
- std::vector<std::string> m_wallet_filenames;
+ WalletContext m_context;
+ const std::vector<std::string> m_wallet_filenames;
std::vector<std::unique_ptr<Handler>> m_rpc_handlers;
+ std::list<CRPCCommand> m_rpc_commands;
};
} // namespace
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)
+std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames)
{
- return MakeUnique<WalletClientImpl>(chain, std::move(wallet_filenames));
+ return MakeUnique<WalletClientImpl>(chain, args, std::move(wallet_filenames));
}
} // namespace interfaces
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index f35335c69f..3cdadbc72e 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -9,8 +9,8 @@
#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation)
#include <script/standard.h> // For CTxDestination
#include <support/allocators/secure.h> // For SecureString
-#include <ui_interface.h> // For ChangeType
#include <util/message.h>
+#include <util/ui_change_type.h>
#include <functional>
#include <map>
@@ -197,16 +197,14 @@ public:
bool sign,
bool bip32derivs,
PartiallySignedTransaction& psbtx,
- bool& complete) = 0;
+ bool& complete,
+ size_t* n_signed) = 0;
//! Get balances.
virtual WalletBalances getBalances() = 0;
- //! Get balances if possible without waiting for chain and wallet locks.
- virtual bool tryGetBalances(WalletBalances& balances,
- int& num_blocks,
- bool force,
- int cached_num_blocks) = 0;
+ //! Get balances if possible without blocking.
+ virtual bool tryGetBalances(WalletBalances& balances, uint256& block_hash) = 0;
//! Get balance.
virtual CAmount getBalance() = 0;
@@ -258,9 +256,6 @@ public:
// Get default address type.
virtual OutputType getDefaultAddressType() = 0;
- // Get default change type.
- virtual OutputType getDefaultChangeType() = 0;
-
//! Get max tx fee.
virtual CAmount getDefaultMaxTxFee() = 0;
diff --git a/src/key.cpp b/src/key.cpp
index b6ed29e8e3..7eecc6e083 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -31,46 +31,46 @@ static secp256k1_context* secp256k1_context_sign = nullptr;
*
* out32 must point to an output buffer of length at least 32 bytes.
*/
-static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *privkey, size_t privkeylen) {
- const unsigned char *end = privkey + privkeylen;
+static int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *seckey, size_t seckeylen) {
+ const unsigned char *end = seckey + seckeylen;
memset(out32, 0, 32);
/* sequence header */
- if (end - privkey < 1 || *privkey != 0x30u) {
+ if (end - seckey < 1 || *seckey != 0x30u) {
return 0;
}
- privkey++;
+ seckey++;
/* sequence length constructor */
- if (end - privkey < 1 || !(*privkey & 0x80u)) {
+ if (end - seckey < 1 || !(*seckey & 0x80u)) {
return 0;
}
- ptrdiff_t lenb = *privkey & ~0x80u; privkey++;
+ ptrdiff_t lenb = *seckey & ~0x80u; seckey++;
if (lenb < 1 || lenb > 2) {
return 0;
}
- if (end - privkey < lenb) {
+ if (end - seckey < lenb) {
return 0;
}
/* sequence length */
- ptrdiff_t len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0u);
- privkey += lenb;
- if (end - privkey < len) {
+ ptrdiff_t len = seckey[lenb-1] | (lenb > 1 ? seckey[lenb-2] << 8 : 0u);
+ seckey += lenb;
+ if (end - seckey < len) {
return 0;
}
/* sequence element 0: version number (=1) */
- if (end - privkey < 3 || privkey[0] != 0x02u || privkey[1] != 0x01u || privkey[2] != 0x01u) {
+ if (end - seckey < 3 || seckey[0] != 0x02u || seckey[1] != 0x01u || seckey[2] != 0x01u) {
return 0;
}
- privkey += 3;
+ seckey += 3;
/* sequence element 1: octet string, up to 32 bytes */
- if (end - privkey < 2 || privkey[0] != 0x04u) {
+ if (end - seckey < 2 || seckey[0] != 0x04u) {
return 0;
}
- ptrdiff_t oslen = privkey[1];
- privkey += 2;
- if (oslen > 32 || end - privkey < oslen) {
+ ptrdiff_t oslen = seckey[1];
+ seckey += 2;
+ if (oslen > 32 || end - seckey < oslen) {
return 0;
}
- memcpy(out32 + (32 - oslen), privkey, oslen);
+ memcpy(out32 + (32 - oslen), seckey, oslen);
if (!secp256k1_ec_seckey_verify(ctx, out32)) {
memset(out32, 0, 32);
return 0;
@@ -83,17 +83,17 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou
* <http://www.secg.org/sec1-v2.pdf>. The optional parameters and publicKey fields are
* included.
*
- * privkey must point to an output buffer of length at least CKey::SIZE bytes.
- * privkeylen must initially be set to the size of the privkey buffer. Upon return it
+ * seckey must point to an output buffer of length at least CKey::SIZE bytes.
+ * seckeylen must initially be set to the size of the seckey buffer. Upon return it
* will be set to the number of bytes used in the buffer.
* key32 must point to a 32-byte raw private key.
*/
-static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, bool compressed) {
- assert(*privkeylen >= CKey::SIZE);
+static int ec_seckey_export_der(const secp256k1_context *ctx, unsigned char *seckey, size_t *seckeylen, const unsigned char *key32, bool compressed) {
+ assert(*seckeylen >= CKey::SIZE);
secp256k1_pubkey pubkey;
size_t pubkeylen = 0;
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) {
- *privkeylen = 0;
+ *seckeylen = 0;
return 0;
}
if (compressed) {
@@ -111,15 +111,15 @@ static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *pr
0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,0x00
};
- unsigned char *ptr = privkey;
+ unsigned char *ptr = seckey;
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
memcpy(ptr, key32, 32); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
pubkeylen = CPubKey::COMPRESSED_SIZE;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED);
ptr += pubkeylen;
- *privkeylen = ptr - privkey;
- assert(*privkeylen == CKey::COMPRESSED_SIZE);
+ *seckeylen = ptr - seckey;
+ assert(*seckeylen == CKey::COMPRESSED_SIZE);
} else {
static const unsigned char begin[] = {
0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20
@@ -137,15 +137,15 @@ static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *pr
0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,0x00
};
- unsigned char *ptr = privkey;
+ unsigned char *ptr = seckey;
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
memcpy(ptr, key32, 32); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
pubkeylen = CPubKey::SIZE;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
ptr += pubkeylen;
- *privkeylen = ptr - privkey;
- assert(*privkeylen == CKey::SIZE);
+ *seckeylen = ptr - seckey;
+ assert(*seckeylen == CKey::SIZE);
}
return 1;
}
@@ -165,20 +165,20 @@ void CKey::MakeNewKey(bool fCompressedIn) {
bool CKey::Negate()
{
assert(fValid);
- return secp256k1_ec_privkey_negate(secp256k1_context_sign, keydata.data());
+ return secp256k1_ec_seckey_negate(secp256k1_context_sign, keydata.data());
}
CPrivKey CKey::GetPrivKey() const {
assert(fValid);
- CPrivKey privkey;
+ CPrivKey seckey;
int ret;
- size_t privkeylen;
- privkey.resize(SIZE);
- privkeylen = SIZE;
- ret = ec_privkey_export_der(secp256k1_context_sign, privkey.data(), &privkeylen, begin(), fCompressed);
+ size_t seckeylen;
+ seckey.resize(SIZE);
+ seckeylen = SIZE;
+ ret = ec_seckey_export_der(secp256k1_context_sign, seckey.data(), &seckeylen, begin(), fCompressed);
assert(ret);
- privkey.resize(privkeylen);
- return privkey;
+ seckey.resize(seckeylen);
+ return seckey;
}
CPubKey CKey::GetPubKey() const {
@@ -258,8 +258,8 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
return true;
}
-bool CKey::Load(const CPrivKey &privkey, const CPubKey &vchPubKey, bool fSkipCheck=false) {
- if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)begin(), privkey.data(), privkey.size()))
+bool CKey::Load(const CPrivKey &seckey, const CPubKey &vchPubKey, bool fSkipCheck=false) {
+ if (!ec_seckey_import_der(secp256k1_context_sign, (unsigned char*)begin(), seckey.data(), seckey.size()))
return false;
fCompressed = vchPubKey.IsCompressed();
fValid = true;
@@ -284,7 +284,7 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const
}
memcpy(ccChild.begin(), vout.data()+32, 32);
memcpy((unsigned char*)keyChild.begin(), begin(), 32);
- bool ret = secp256k1_ec_privkey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), vout.data());
+ bool ret = secp256k1_ec_seckey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), vout.data());
keyChild.fCompressed = true;
keyChild.fValid = ret;
return ret;
diff --git a/src/logging.cpp b/src/logging.cpp
index eb9da06d9b..35e0754f20 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -22,8 +22,8 @@ BCLog::Logger& LogInstance()
* 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.
+ * Since the ~Logger() destructor is never called, the Logger class and all
+ * its subclasses must have implicitly-defined destructors.
*
* This method of initialization was originally introduced in
* ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c.
@@ -41,7 +41,7 @@ static int FileWriteStr(const std::string &str, FILE *fp)
bool BCLog::Logger::StartLogging()
{
- std::lock_guard<std::mutex> scoped_lock(m_cs);
+ StdLockGuard scoped_lock(m_cs);
assert(m_buffering);
assert(m_fileout == nullptr);
@@ -80,7 +80,7 @@ bool BCLog::Logger::StartLogging()
void BCLog::Logger::DisconnectTestLogger()
{
- std::lock_guard<std::mutex> scoped_lock(m_cs);
+ StdLockGuard scoped_lock(m_cs);
m_buffering = true;
if (m_fileout != nullptr) fclose(m_fileout);
m_fileout = nullptr;
@@ -95,15 +95,7 @@ void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
bool BCLog::Logger::EnableCategory(const std::string& str)
{
BCLog::LogFlags flag;
- if (!GetLogCategory(flag, str)) {
- if (str == "db") {
- // DEPRECATION: Added in 0.20, should start returning an error in 0.21
- LogPrintf("Warning: logging category 'db' is deprecated, use 'walletdb' instead\n");
- EnableCategory(BCLog::WALLETDB);
- return true;
- }
- return false;
- }
+ if (!GetLogCategory(flag, str)) return false;
EnableCategory(flag);
return true;
}
@@ -246,7 +238,7 @@ namespace BCLog {
void BCLog::Logger::LogPrintStr(const std::string& str)
{
- std::lock_guard<std::mutex> scoped_lock(m_cs);
+ StdLockGuard scoped_lock(m_cs);
std::string str_prefixed = LogEscapeMessage(str);
if (m_log_threadnames && m_started_new_line) {
diff --git a/src/logging.h b/src/logging.h
index ab07010316..7e646ef67a 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -8,6 +8,7 @@
#include <fs.h>
#include <tinyformat.h>
+#include <threadsafety.h>
#include <util/string.h>
#include <atomic>
@@ -61,10 +62,11 @@ namespace BCLog {
class Logger
{
private:
- mutable std::mutex m_cs; // Can not use Mutex from sync.h because in debug mode it would cause a deadlock when a potential deadlock was detected
- FILE* m_fileout = nullptr; // GUARDED_BY(m_cs)
- std::list<std::string> m_msgs_before_open; // GUARDED_BY(m_cs)
- bool m_buffering{true}; //!< Buffer messages before logging can be started. GUARDED_BY(m_cs)
+ mutable StdMutex m_cs; // Can not use Mutex from sync.h because in debug mode it would cause a deadlock when a potential deadlock was detected
+
+ FILE* m_fileout GUARDED_BY(m_cs) = nullptr;
+ std::list<std::string> m_msgs_before_open GUARDED_BY(m_cs);
+ bool m_buffering GUARDED_BY(m_cs) = true; //!< Buffer messages before logging can be started.
/**
* m_started_new_line is a state variable that will suppress printing of
@@ -79,7 +81,7 @@ namespace BCLog {
std::string LogTimestampStr(const std::string& str);
/** Slots that connect to the print signal */
- std::list<std::function<void(const std::string&)>> m_print_callbacks /* GUARDED_BY(m_cs) */ {};
+ std::list<std::function<void(const std::string&)>> m_print_callbacks GUARDED_BY(m_cs) {};
public:
bool m_print_to_console = false;
@@ -98,14 +100,14 @@ namespace BCLog {
/** Returns whether logs will be written to any output */
bool Enabled() const
{
- std::lock_guard<std::mutex> scoped_lock(m_cs);
+ StdLockGuard scoped_lock(m_cs);
return m_buffering || m_print_to_console || m_print_to_file || !m_print_callbacks.empty();
}
/** Connect a slot to the print signal and return the connection */
std::list<std::function<void(const std::string&)>>::iterator PushBackCallback(std::function<void(const std::string&)> fun)
{
- std::lock_guard<std::mutex> scoped_lock(m_cs);
+ StdLockGuard scoped_lock(m_cs);
m_print_callbacks.push_back(std::move(fun));
return --m_print_callbacks.end();
}
@@ -113,7 +115,7 @@ namespace BCLog {
/** Delete a connection */
void DeleteCallback(std::list<std::function<void(const std::string&)>>::iterator it)
{
- std::lock_guard<std::mutex> scoped_lock(m_cs);
+ StdLockGuard scoped_lock(m_cs);
m_print_callbacks.erase(it);
}
diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp
index 4ac6219886..8072b12119 100644
--- a/src/merkleblock.cpp
+++ b/src/merkleblock.cpp
@@ -9,6 +9,24 @@
#include <consensus/consensus.h>
+std::vector<unsigned char> BitsToBytes(const std::vector<bool>& bits)
+{
+ std::vector<unsigned char> ret((bits.size() + 7) / 8);
+ for (unsigned int p = 0; p < bits.size(); p++) {
+ ret[p / 8] |= bits[p] << (p % 8);
+ }
+ return ret;
+}
+
+std::vector<bool> BytesToBits(const std::vector<unsigned char>& bytes)
+{
+ std::vector<bool> ret(bytes.size() * 8);
+ for (unsigned int p = 0; p < ret.size(); p++) {
+ ret[p] = (bytes[p / 8] & (1 << (p % 8))) != 0;
+ }
+ return ret;
+}
+
CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids)
{
header = block.GetBlockHeader();
diff --git a/src/merkleblock.h b/src/merkleblock.h
index e641c8aa94..b2d2828784 100644
--- a/src/merkleblock.h
+++ b/src/merkleblock.h
@@ -13,6 +13,10 @@
#include <vector>
+// Helper functions for serialization.
+std::vector<unsigned char> BitsToBytes(const std::vector<bool>& bits);
+std::vector<bool> BytesToBits(const std::vector<unsigned char>& bytes);
+
/** Data structure that represents a partial merkle tree.
*
* It represents a subset of the txid's of a known block, in a way that
@@ -81,27 +85,14 @@ protected:
public:
- /** serialization implementation */
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(nTransactions);
- READWRITE(vHash);
- std::vector<unsigned char> vBytes;
- if (ser_action.ForRead()) {
- READWRITE(vBytes);
- CPartialMerkleTree &us = *(const_cast<CPartialMerkleTree*>(this));
- us.vBits.resize(vBytes.size() * 8);
- for (unsigned int p = 0; p < us.vBits.size(); p++)
- us.vBits[p] = (vBytes[p / 8] & (1 << (p % 8))) != 0;
- us.fBad = false;
- } else {
- vBytes.resize((vBits.size()+7)/8);
- for (unsigned int p = 0; p < vBits.size(); p++)
- vBytes[p / 8] |= vBits[p] << (p % 8);
- READWRITE(vBytes);
- }
+ SERIALIZE_METHODS(CPartialMerkleTree, obj)
+ {
+ READWRITE(obj.nTransactions, obj.vHash);
+ std::vector<unsigned char> bytes;
+ SER_WRITE(obj, bytes = BitsToBytes(obj.vBits));
+ READWRITE(bytes);
+ SER_READ(obj, obj.vBits = BytesToBits(bytes));
+ SER_READ(obj, obj.fBad = false);
}
/** Construct a partial merkle tree from a list of transaction ids, and a mask that selects a subset of them */
@@ -157,13 +148,7 @@ public:
CMerkleBlock() {}
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(header);
- READWRITE(txn);
- }
+ SERIALIZE_METHODS(CMerkleBlock, obj) { READWRITE(obj.header, obj.txn); }
private:
// Combined constructor to consolidate code
diff --git a/src/miner.cpp b/src/miner.cpp
index d9dcbe8a70..41a835f70a 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -109,7 +109,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
if(!pblocktemplate.get())
return nullptr;
- pblock = &pblocktemplate->block; // pointer for convenience
+ CBlock* const pblock = &pblocktemplate->block; // pointer for convenience
// Add dummy coinbase tx as first transaction
pblock->vtx.emplace_back();
@@ -226,7 +226,7 @@ bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& packa
void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
{
- pblock->vtx.emplace_back(iter->GetSharedTx());
+ pblocktemplate->block.vtx.emplace_back(iter->GetSharedTx());
pblocktemplate->vTxFees.push_back(iter->GetFee());
pblocktemplate->vTxSigOpsCost.push_back(iter->GetSigOpCost());
nBlockWeight += iter->GetTxWeight();
diff --git a/src/miner.h b/src/miner.h
index 69296f9078..096585dfe4 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -128,8 +128,6 @@ class BlockAssembler
private:
// The constructed block template
std::unique_ptr<CBlockTemplate> pblocktemplate;
- // A convenience pointer that always refers to the CBlock in pblocktemplate
- CBlock* pblock;
// Configuration parameters for the block size
bool fIncludeWitness;
diff --git a/src/net.cpp b/src/net.cpp
index 9950b9aea4..cf5757d6c0 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -14,12 +14,12 @@
#include <clientversion.h>
#include <consensus/consensus.h>
#include <crypto/sha256.h>
-#include <netbase.h>
#include <net_permissions.h>
+#include <netbase.h>
+#include <node/ui_interface.h>
#include <protocol.h>
#include <random.h>
#include <scheduler.h>
-#include <ui_interface.h>
#include <util/strencodings.h>
#include <util/translation.h>
@@ -42,6 +42,7 @@
static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed");
#endif
+#include <cstdint>
#include <unordered_map>
#include <math.h>
@@ -52,6 +53,19 @@ static constexpr std::chrono::minutes DUMP_PEERS_INTERVAL{15};
/** Number of DNS seeds to query when the number of connections is low. */
static constexpr int DNSSEEDS_TO_QUERY_AT_ONCE = 3;
+/** How long to delay before querying DNS seeds
+ *
+ * If we have more than THRESHOLD entries in addrman, then it's likely
+ * that we got those addresses from having previously connected to the P2P
+ * network, and that we'll be able to successfully reconnect to the P2P
+ * network via contacting one of them. So if that's the case, spend a
+ * little longer trying to connect to known peers before querying the
+ * DNS seeds.
+ */
+static constexpr std::chrono::seconds DNSSEEDS_DELAY_FEW_PEERS{11};
+static constexpr std::chrono::minutes DNSSEEDS_DELAY_MANY_PEERS{5};
+static constexpr int DNSSEEDS_DELAY_PEER_THRESHOLD = 1000; // "many" vs "few" peers
+
// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.
#define FEELER_SLEEP_WINDOW 1
@@ -97,9 +111,9 @@ void CConnman::AddOneShot(const std::string& strDest)
vOneShots.push_back(strDest);
}
-unsigned short GetListenPort()
+uint16_t GetListenPort()
{
- return (unsigned short)(gArgs.GetArg("-port", Params().GetDefaultPort()));
+ return (uint16_t)(gArgs.GetArg("-port", Params().GetDefaultPort()));
}
// find 'best' local address for a particular peer
@@ -550,15 +564,15 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
// since pingtime does not update until the ping is complete, which might take a while.
// So, if a ping is taking an unusually long time in flight,
// the caller can immediately detect that this is happening.
- int64_t nPingUsecWait = 0;
- if ((0 != nPingNonceSent) && (0 != nPingUsecStart)) {
- nPingUsecWait = GetTimeMicros() - nPingUsecStart;
+ std::chrono::microseconds ping_wait{0};
+ if ((0 != nPingNonceSent) && (0 != m_ping_start.load().count())) {
+ ping_wait = GetTime<std::chrono::microseconds>() - m_ping_start.load();
}
// Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :)
stats.m_ping_usec = nPingUsecTime;
stats.m_min_ping_usec = nMinPingUsecTime;
- stats.m_ping_wait_usec = nPingUsecWait;
+ stats.m_ping_wait_usec = count_microseconds(ping_wait);
// Leave string empty if addrLocal invalid (not filled in yet)
CService addrLocalUnlocked = GetAddrLocal();
@@ -569,9 +583,9 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete)
{
complete = false;
- int64_t nTimeMicros = GetTimeMicros();
+ const auto time = GetTime<std::chrono::microseconds>();
LOCK(cs_vRecv);
- nLastRecv = nTimeMicros / 1000000;
+ nLastRecv = std::chrono::duration_cast<std::chrono::seconds>(time).count();
nRecvBytes += nBytes;
while (nBytes > 0) {
// absorb network data
@@ -583,7 +597,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete
if (m_deserializer->Complete()) {
// decompose a transport agnostic CNetMessage from the deserializer
- CNetMessage msg = m_deserializer->GetMessage(Params().MessageStart(), nTimeMicros);
+ CNetMessage msg = m_deserializer->GetMessage(Params().MessageStart(), time);
//store received bytes per message command
//to prevent a memory DOS, only allow valid commands
@@ -686,7 +700,8 @@ const uint256& V1TransportDeserializer::GetMessageHash() const
return data_hash;
}
-CNetMessage V1TransportDeserializer::GetMessage(const CMessageHeader::MessageStartChars& message_start, int64_t time) {
+CNetMessage V1TransportDeserializer::GetMessage(const CMessageHeader::MessageStartChars& message_start, const std::chrono::microseconds time)
+{
// decompose a single CNetMessage from the TransportDeserializer
CNetMessage msg(std::move(vRecv));
@@ -724,7 +739,7 @@ void V1TransportSerializer::prepareForTransport(CSerializedNetMsg& msg, std::vec
uint256 hash = Hash(msg.data.begin(), msg.data.end());
// create header
- CMessageHeader hdr(Params().MessageStart(), msg.command.c_str(), msg.data.size());
+ CMessageHeader hdr(Params().MessageStart(), msg.m_type.c_str(), msg.data.size());
memcpy(hdr.pchChecksum, hash.begin(), CMessageHeader::CHECKSUM_SIZE);
// serialize header
@@ -997,17 +1012,24 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
// on all platforms. Set it again here just to be sure.
SetSocketNoDelay(hSocket);
- int bannedlevel = m_banman ? m_banman->IsBannedLevel(addr) : 0;
-
- // Don't accept connections from banned peers, but if our inbound slots aren't almost full, accept
- // if the only banning reason was an automatic misbehavior ban.
- if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
+ // Don't accept connections from banned peers.
+ bool banned = m_banman && m_banman->IsBanned(addr);
+ if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && banned)
{
LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
CloseSocket(hSocket);
return;
}
+ // Only accept connections from discouraged peers if our inbound slots aren't (almost) full.
+ bool discouraged = m_banman && m_banman->IsDiscouraged(addr);
+ if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && nInbound + 1 >= nMaxInbound && discouraged)
+ {
+ LogPrint(BCLog::NET, "connection from %s dropped (discouraged)\n", addr.ToString());
+ CloseSocket(hSocket);
+ return;
+ }
+
if (nInbound >= nMaxInbound)
{
if (!AttemptToEvictConnection()) {
@@ -1031,7 +1053,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
pnode->m_permissionFlags = permissionFlags;
// If this flag is present, the user probably expect that RPC and QT report it as whitelisted (backward compatibility)
pnode->m_legacyWhitelisted = legacyWhitelisted;
- pnode->m_prefer_evict = bannedlevel > 0;
+ pnode->m_prefer_evict = discouraged;
m_msgproc->InitializeNode(pnode);
LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString());
@@ -1090,12 +1112,9 @@ void CConnman::DisconnectNodes()
if (pnode->GetRefCount() <= 0) {
bool fDelete = false;
{
- TRY_LOCK(pnode->cs_inventory, lockInv);
- if (lockInv) {
- TRY_LOCK(pnode->cs_vSend, lockSend);
- if (lockSend) {
- fDelete = true;
- }
+ TRY_LOCK(pnode->cs_vSend, lockSend);
+ if (lockSend) {
+ fDelete = true;
}
}
if (fDelete) {
@@ -1141,9 +1160,9 @@ void CConnman::InactivityCheck(CNode *pnode)
LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv);
pnode->fDisconnect = true;
}
- else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros())
+ else if (pnode->nPingNonceSent && pnode->m_ping_start.load() + std::chrono::seconds{TIMEOUT_INTERVAL} < GetTime<std::chrono::microseconds>())
{
- LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart));
+ LogPrintf("ping timeout: %fs\n", 0.000001 * count_microseconds(GetTime<std::chrono::microseconds>() - pnode->m_ping_start.load()));
pnode->fDisconnect = true;
}
else if (!pnode->fSuccessfullyConnected)
@@ -1455,7 +1474,7 @@ void CConnman::ThreadSocketHandler()
void CConnman::WakeMessageHandler()
{
{
- std::lock_guard<std::mutex> lock(mutexMsgProc);
+ LOCK(mutexMsgProc);
fMsgProcWake = true;
}
condMsgProc.notify_one();
@@ -1587,31 +1606,72 @@ void CConnman::ThreadDNSAddressSeed()
if (gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED)) {
// When -forcednsseed is provided, query all.
seeds_right_now = seeds.size();
+ } else if (addrman.size() == 0) {
+ // If we have no known peers, query all.
+ // This will occur on the first run, or if peers.dat has been
+ // deleted.
+ seeds_right_now = seeds.size();
}
+ // goal: only query DNS seed if address need is acute
+ // * If we have a reasonable number of peers in addrman, spend
+ // some time trying them first. This improves user privacy by
+ // creating fewer identifying DNS requests, reduces trust by
+ // giving seeds less influence on the network topology, and
+ // reduces traffic to the seeds.
+ // * When querying DNS seeds query a few at once, this ensures
+ // that we don't give DNS seeds the ability to eclipse nodes
+ // that query them.
+ // * If we continue having problems, eventually query all the
+ // DNS seeds, and if that fails too, also try the fixed seeds.
+ // (done in ThreadOpenConnections)
+ const std::chrono::seconds seeds_wait_time = (addrman.size() >= DNSSEEDS_DELAY_PEER_THRESHOLD ? DNSSEEDS_DELAY_MANY_PEERS : DNSSEEDS_DELAY_FEW_PEERS);
+
for (const std::string& seed : seeds) {
- // goal: only query DNS seed if address need is acute
- // Avoiding DNS seeds when we don't need them improves user privacy by
- // creating fewer identifying DNS requests, reduces trust by giving seeds
- // less influence on the network topology, and reduces traffic to the seeds.
- if (addrman.size() > 0 && seeds_right_now == 0) {
- if (!interruptNet.sleep_for(std::chrono::seconds(11))) return;
+ if (seeds_right_now == 0) {
+ seeds_right_now += DNSSEEDS_TO_QUERY_AT_ONCE;
- LOCK(cs_vNodes);
- int nRelevant = 0;
- for (const CNode* pnode : vNodes) {
- nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound;
- }
- if (nRelevant >= 2) {
- LogPrintf("P2P peers available. Skipped DNS seeding.\n");
- return;
+ if (addrman.size() > 0) {
+ LogPrintf("Waiting %d seconds before querying DNS seeds.\n", seeds_wait_time.count());
+ std::chrono::seconds to_wait = seeds_wait_time;
+ while (to_wait.count() > 0) {
+ // if sleeping for the MANY_PEERS interval, wake up
+ // early to see if we have enough peers and can stop
+ // this thread entirely freeing up its resources
+ std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait);
+ if (!interruptNet.sleep_for(w)) return;
+ to_wait -= w;
+
+ int nRelevant = 0;
+ {
+ LOCK(cs_vNodes);
+ for (const CNode* pnode : vNodes) {
+ nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound;
+ }
+ }
+ if (nRelevant >= 2) {
+ if (found > 0) {
+ LogPrintf("%d addresses found from DNS seeds\n", found);
+ LogPrintf("P2P peers available. Finished DNS seeding.\n");
+ } else {
+ LogPrintf("P2P peers available. Skipped DNS seeding.\n");
+ }
+ return;
+ }
+ }
}
- seeds_right_now += DNSSEEDS_TO_QUERY_AT_ONCE;
}
- if (interruptNet) {
- return;
+ if (interruptNet) return;
+
+ // hold off on querying seeds if P2P network deactivated
+ if (!fNetworkActive) {
+ LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n");
+ do {
+ if (!interruptNet.sleep_for(std::chrono::seconds{1})) return;
+ } while (!fNetworkActive);
}
+
LogPrintf("Loading addresses from DNS seed %s\n", seed);
if (HaveNameProxy()) {
AddOneShot(seed);
@@ -1755,6 +1815,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
return;
// Add seed nodes if DNS seeds are all down (an infrastructure attack?).
+ // Note that we only do this if we started with an empty peers.dat,
+ // (in which case we will query DNS seeds immediately) *and* the DNS
+ // seeds have not returned any results.
if (addrman.size() == 0 && (GetTime() - nStart > 60)) {
static bool done = false;
if (!done) {
@@ -1988,10 +2051,10 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
return;
}
if (!pszDest) {
- if (IsLocal(addrConnect) ||
- FindNode(static_cast<CNetAddr>(addrConnect)) || (m_banman && m_banman->IsBanned(addrConnect)) ||
- FindNode(addrConnect.ToStringIPPort()))
+ bool banned_or_discouraged = m_banman && (m_banman->IsDiscouraged(addrConnect) || m_banman->IsBanned(addrConnect));
+ if (IsLocal(addrConnect) || FindNode(static_cast<CNetAddr>(addrConnect)) || banned_or_discouraged || FindNode(addrConnect.ToStringIPPort())) {
return;
+ }
} else if (FindNode(std::string(pszDest)))
return;
@@ -2058,7 +2121,7 @@ void CConnman::ThreadMessageHandler()
WAIT_LOCK(mutexMsgProc, lock);
if (!fMoreWork) {
- condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return fMsgProcWake; });
+ condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this]() EXCLUSIVE_LOCKS_REQUIRED(mutexMsgProc) { return fMsgProcWake; });
}
fMsgProcWake = false;
}
@@ -2366,7 +2429,7 @@ static CNetCleanup instance_of_cnetcleanup;
void CConnman::Interrupt()
{
{
- std::lock_guard<std::mutex> lock(mutexMsgProc);
+ LOCK(mutexMsgProc);
flagInterruptMsgProc = true;
}
condMsgProc.notify_all();
@@ -2448,11 +2511,6 @@ CConnman::~CConnman()
Stop();
}
-size_t CConnman::GetAddressCount() const
-{
- return addrman.size();
-}
-
void CConnman::SetServices(const CService &addr, ServiceFlags nServices)
{
addrman.SetServices(addr, nServices);
@@ -2582,7 +2640,7 @@ void CConnman::RecordBytesSent(uint64_t bytes)
nMaxOutboundTotalBytesSentInCycle = 0;
}
- // TODO, exclude whitebind peers
+ // TODO, exclude peers with download permission
nMaxOutboundTotalBytesSentInCycle += bytes;
}
@@ -2737,7 +2795,7 @@ bool CConnman::NodeFullyConnected(const CNode* pnode)
void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
{
size_t nMessageSize = msg.data.size();
- LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", SanitizeString(msg.command), nMessageSize, pnode->GetId());
+ LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", SanitizeString(msg.m_type), nMessageSize, pnode->GetId());
// make sure we use the appropriate network transport format
std::vector<unsigned char> serializedHeader;
@@ -2749,8 +2807,8 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
LOCK(pnode->cs_vSend);
bool optimisticSend(pnode->vSendMsg.empty());
- //log total amount of bytes per command
- pnode->mapSendBytesPerMsgCmd[msg.command] += nTotalSize;
+ //log total amount of bytes per message type
+ pnode->mapSendBytesPerMsgCmd[msg.m_type] += nTotalSize;
pnode->nSendSize += nTotalSize;
if (pnode->nSendSize > nSendBufferMaxSize)
diff --git a/src/net.h b/src/net.h
index 66aac6f084..b240e52a18 100644
--- a/src/net.h
+++ b/src/net.h
@@ -25,8 +25,8 @@
#include <uint256.h>
#include <atomic>
+#include <cstdint>
#include <deque>
-#include <stdint.h>
#include <thread>
#include <memory>
#include <condition_variable>
@@ -110,7 +110,7 @@ struct CSerializedNetMsg
CSerializedNetMsg& operator=(const CSerializedNetMsg&) = delete;
std::vector<unsigned char> data;
- std::string command;
+ std::string m_type;
};
@@ -247,7 +247,6 @@ public:
};
// Addrman functions
- size_t GetAddressCount() const;
void SetServices(const CService &addr, ServiceFlags nServices);
void MarkAddressGood(const CAddress& addr);
void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
@@ -448,13 +447,14 @@ private:
std::atomic<int> nBestHeight;
CClientUIInterface* clientInterface;
NetEventsInterface* m_msgproc;
+ /** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */
BanMan* m_banman;
/** SipHasher seeds for deterministic randomness */
const uint64_t nSeed0, nSeed1;
/** flag for waking the message processor. */
- bool fMsgProcWake;
+ bool fMsgProcWake GUARDED_BY(mutexMsgProc);
std::condition_variable condMsgProc;
Mutex mutexMsgProc;
@@ -482,7 +482,7 @@ void Discover();
void StartMapPort();
void InterruptMapPort();
void StopMapPort();
-unsigned short GetListenPort();
+uint16_t GetListenPort();
struct CombinerAll
{
@@ -612,13 +612,13 @@ public:
*/
class CNetMessage {
public:
- CDataStream m_recv; // received message data
- int64_t m_time = 0; // time (in microseconds) of message receipt.
+ CDataStream m_recv; //!< received message data
+ std::chrono::microseconds m_time{0}; //!< time of message receipt
bool m_valid_netmagic = false;
bool m_valid_header = false;
bool m_valid_checksum = false;
- uint32_t m_message_size = 0; // size of the payload
- uint32_t m_raw_message_size = 0; // used wire size of the message (including header/checksum)
+ uint32_t m_message_size{0}; //!< size of the payload
+ uint32_t m_raw_message_size{0}; //!< used wire size of the message (including header/checksum)
std::string m_command;
CNetMessage(CDataStream&& recv_in) : m_recv(std::move(recv_in)) {}
@@ -642,7 +642,7 @@ public:
// read and deserialize data
virtual int Read(const char *data, unsigned int bytes) = 0;
// decomposes a message from the context
- virtual CNetMessage GetMessage(const CMessageHeader::MessageStartChars& message_start, int64_t time) = 0;
+ virtual CNetMessage GetMessage(const CMessageHeader::MessageStartChars& message_start, std::chrono::microseconds time) = 0;
virtual ~TransportDeserializer() {}
};
@@ -695,7 +695,7 @@ public:
if (ret < 0) Reset();
return ret;
}
- CNetMessage GetMessage(const CMessageHeader::MessageStartChars& message_start, int64_t time) override;
+ CNetMessage GetMessage(const CMessageHeader::MessageStartChars& message_start, std::chrono::microseconds time) override;
};
/** The TransportSerializer prepares messages for the network transport
@@ -803,7 +803,7 @@ public:
// There is no final sorting before sending, as they are always sent immediately
// and in the order requested.
std::vector<uint256> vInventoryBlockToSend GUARDED_BY(cs_inventory);
- RecursiveMutex cs_inventory;
+ Mutex cs_inventory;
struct TxRelay {
mutable RecursiveMutex cs_filter;
@@ -845,8 +845,8 @@ public:
// Ping time measurement:
// The pong reply we're expecting, or 0 if no pong expected.
std::atomic<uint64_t> nPingNonceSent{0};
- // Time (in usec) the last ping was sent, or 0 if no ping was ever sent.
- std::atomic<int64_t> nPingUsecStart{0};
+ /** When the last ping was sent, or 0 if no ping was ever sent */
+ std::atomic<std::chrono::microseconds> m_ping_start{std::chrono::microseconds{0}};
// Last measured round-trip time.
std::atomic<int64_t> nPingUsecTime{0};
// Best measured round-trip time.
@@ -973,25 +973,15 @@ public:
}
}
- void PushInventory(const CInv& inv)
+ void PushTxInventory(const uint256& hash)
{
- if (inv.type == MSG_TX && m_tx_relay != nullptr) {
- LOCK(m_tx_relay->cs_tx_inventory);
- if (!m_tx_relay->filterInventoryKnown.contains(inv.hash)) {
- m_tx_relay->setInventoryTxToSend.insert(inv.hash);
- }
- } else if (inv.type == MSG_BLOCK) {
- LOCK(cs_inventory);
- vInventoryBlockToSend.push_back(inv.hash);
+ if (m_tx_relay == nullptr) return;
+ LOCK(m_tx_relay->cs_tx_inventory);
+ if (!m_tx_relay->filterInventoryKnown.contains(hash)) {
+ m_tx_relay->setInventoryTxToSend.insert(hash);
}
}
- void PushBlockHash(const uint256 &hash)
- {
- LOCK(cs_inventory);
- vBlockHashesToAnnounce.push_back(hash);
- }
-
void CloseSocketDisconnect();
void copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap);
diff --git a/src/net_permissions.cpp b/src/net_permissions.cpp
index 22fa5ee73b..a75838307c 100644
--- a/src/net_permissions.cpp
+++ b/src/net_permissions.cpp
@@ -8,8 +8,19 @@
#include <util/system.h>
#include <util/translation.h>
+const std::vector<std::string> NET_PERMISSIONS_DOC{
+ "bloomfilter (allow requesting BIP37 filtered blocks and transactions)",
+ "noban (do not ban for misbehavior; implies download)",
+ "forcerelay (relay transactions that are already in the mempool; implies relay)",
+ "relay (relay even in -blocksonly mode)",
+ "mempool (allow requesting BIP35 mempool contents)",
+ "download (allow getheaders during IBD, no disconnect after maxuploadtarget limit)",
+};
+
+namespace {
+
// The parse the following format "perm1,perm2@xxxxxx"
-bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output, size_t& readen, std::string& error)
+bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output, size_t& readen, bilingual_str& error)
{
NetPermissionFlags flags = PF_NONE;
const auto atSeparator = str.find('@');
@@ -36,11 +47,12 @@ bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output,
else if (permission == "noban") NetPermissions::AddFlag(flags, PF_NOBAN);
else if (permission == "forcerelay") NetPermissions::AddFlag(flags, PF_FORCERELAY);
else if (permission == "mempool") NetPermissions::AddFlag(flags, PF_MEMPOOL);
+ else if (permission == "download") NetPermissions::AddFlag(flags, PF_DOWNLOAD);
else if (permission == "all") NetPermissions::AddFlag(flags, PF_ALL);
else if (permission == "relay") NetPermissions::AddFlag(flags, PF_RELAY);
else if (permission.length() == 0); // Allow empty entries
else {
- error = strprintf(_("Invalid P2P permission: '%s'").translated, permission);
+ error = strprintf(_("Invalid P2P permission: '%s'"), permission);
return false;
}
}
@@ -48,10 +60,12 @@ bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output,
}
output = flags;
- error = "";
+ error = Untranslated("");
return true;
}
+}
+
std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags)
{
std::vector<std::string> strings;
@@ -60,10 +74,11 @@ std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags)
if (NetPermissions::HasFlag(flags, PF_FORCERELAY)) strings.push_back("forcerelay");
if (NetPermissions::HasFlag(flags, PF_RELAY)) strings.push_back("relay");
if (NetPermissions::HasFlag(flags, PF_MEMPOOL)) strings.push_back("mempool");
+ if (NetPermissions::HasFlag(flags, PF_DOWNLOAD)) strings.push_back("download");
return strings;
}
-bool NetWhitebindPermissions::TryParse(const std::string str, NetWhitebindPermissions& output, std::string& error)
+bool NetWhitebindPermissions::TryParse(const std::string str, NetWhitebindPermissions& output, bilingual_str& error)
{
NetPermissionFlags flags;
size_t offset;
@@ -76,17 +91,17 @@ bool NetWhitebindPermissions::TryParse(const std::string str, NetWhitebindPermis
return false;
}
if (addrBind.GetPort() == 0) {
- error = strprintf(_("Need to specify a port with -whitebind: '%s'").translated, strBind);
+ error = strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind);
return false;
}
output.m_flags = flags;
output.m_service = addrBind;
- error = "";
+ error = Untranslated("");
return true;
}
-bool NetWhitelistPermissions::TryParse(const std::string str, NetWhitelistPermissions& output, std::string& error)
+bool NetWhitelistPermissions::TryParse(const std::string str, NetWhitelistPermissions& output, bilingual_str& error)
{
NetPermissionFlags flags;
size_t offset;
@@ -96,12 +111,12 @@ bool NetWhitelistPermissions::TryParse(const std::string str, NetWhitelistPermis
CSubNet subnet;
LookupSubNet(net, subnet);
if (!subnet.IsValid()) {
- error = strprintf(_("Invalid netmask specified in -whitelist: '%s'").translated, net);
+ error = strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net);
return false;
}
output.m_flags = flags;
output.m_subnet = subnet;
- error = "";
+ error = Untranslated("");
return true;
}
diff --git a/src/net_permissions.h b/src/net_permissions.h
index 962a2159fc..a9633ee2ae 100644
--- a/src/net_permissions.h
+++ b/src/net_permissions.h
@@ -2,14 +2,19 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <netaddress.h>
+
#include <string>
#include <vector>
-#include <netaddress.h>
#ifndef BITCOIN_NET_PERMISSIONS_H
#define BITCOIN_NET_PERMISSIONS_H
-enum NetPermissionFlags
-{
+
+struct bilingual_str;
+
+extern const std::vector<std::string> NET_PERMISSIONS_DOC;
+
+enum NetPermissionFlags {
PF_NONE = 0,
// Can query bloomfilter even if -peerbloomfilters is false
PF_BLOOMFILTER = (1U << 1),
@@ -18,15 +23,18 @@ enum NetPermissionFlags
// Always relay transactions from this peer, even if already in mempool
// Keep parameter interaction: forcerelay implies relay
PF_FORCERELAY = (1U << 2) | PF_RELAY,
- // Can't be banned for misbehavior
- PF_NOBAN = (1U << 4),
+ // Allow getheaders during IBD and block-download after maxuploadtarget limit
+ PF_DOWNLOAD = (1U << 6),
+ // Can't be banned/disconnected/discouraged for misbehavior
+ PF_NOBAN = (1U << 4) | PF_DOWNLOAD,
// Can query the mempool
PF_MEMPOOL = (1U << 5),
// True if the user did not specifically set fine grained permissions
PF_ISIMPLICIT = (1U << 31),
- PF_ALL = PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY | PF_NOBAN | PF_MEMPOOL,
+ PF_ALL = PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY | PF_NOBAN | PF_MEMPOOL | PF_DOWNLOAD,
};
+
class NetPermissions
{
public:
@@ -45,17 +53,18 @@ public:
flags = static_cast<NetPermissionFlags>(flags & ~f);
}
};
+
class NetWhitebindPermissions : public NetPermissions
{
public:
- static bool TryParse(const std::string str, NetWhitebindPermissions& output, std::string& error);
+ static bool TryParse(const std::string str, NetWhitebindPermissions& output, bilingual_str& error);
CService m_service;
};
class NetWhitelistPermissions : public NetPermissions
{
public:
- static bool TryParse(const std::string str, NetWhitelistPermissions& output, std::string& error);
+ static bool TryParse(const std::string str, NetWhitelistPermissions& output, bilingual_str& error);
CSubNet m_subnet;
};
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 7e9bb2f27c..7a58de35d7 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -13,10 +13,9 @@
#include <consensus/validation.h>
#include <hash.h>
#include <index/blockfilterindex.h>
-#include <validation.h>
#include <merkleblock.h>
-#include <netmessagemaker.h>
#include <netbase.h>
+#include <netmessagemaker.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <primitives/block.h>
@@ -26,22 +25,22 @@
#include <scheduler.h>
#include <tinyformat.h>
#include <txmempool.h>
-#include <util/system.h>
+#include <util/check.h> // For NDEBUG compile time check
#include <util/strencodings.h>
+#include <util/system.h>
+#include <validation.h>
#include <memory>
#include <typeinfo>
-#if defined(NDEBUG)
-# error "Bitcoin cannot be compiled without assertions."
-#endif
-
/** Expiration time for orphan transactions in seconds */
static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
/** Minimum time between orphan transactions expire time checks in seconds */
static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
/** How long to cache transactions in mapRelay for normal relay */
-static constexpr std::chrono::seconds RELAY_TX_CACHE_TIME{15 * 60};
+static constexpr std::chrono::seconds RELAY_TX_CACHE_TIME = std::chrono::minutes{15};
+/** How long a transaction has to be in the mempool before it can unconditionally be relayed (even when not in mapRelay). */
+static constexpr std::chrono::seconds UNCONDITIONAL_RELAY_DELAY = std::chrono::minutes{2};
/** Headers download timeout expressed in microseconds
* Timeout = base + per_header * (expected number of headers) */
static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
@@ -66,8 +65,8 @@ 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;
-/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */
-static const int PING_INTERVAL = 2 * 60;
+/** Time between pings automatically sent out for latency probing and keepalive */
+static constexpr std::chrono::minutes PING_INTERVAL{2};
/** The maximum number of entries in a locator */
static const unsigned int MAX_LOCATOR_SZ = 101;
/** The maximum number of entries in an 'inv' protocol message */
@@ -120,17 +119,28 @@ static constexpr std::chrono::hours AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL{24};
/** Average delay between peer address broadcasts */
static constexpr std::chrono::seconds AVG_ADDRESS_BROADCAST_INTERVAL{30};
/** Average delay between trickled inventory transmissions in seconds.
- * Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */
+ * Blocks and peers with noban permission bypass this, outbound peers get half this delay. */
static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
-/** Maximum number of inventory items to send per transmission.
+/** Maximum rate of inventory items to send per second.
* Limits the impact of low-fee transaction floods. */
-static constexpr unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL;
+static constexpr unsigned int INVENTORY_BROADCAST_PER_SECOND = 7;
+/** Maximum number of inventory items to send per transmission. */
+static constexpr unsigned int INVENTORY_BROADCAST_MAX = INVENTORY_BROADCAST_PER_SECOND * INVENTORY_BROADCAST_INTERVAL;
+/** The number of most recently announced transactions a peer can request. */
+static constexpr unsigned int INVENTORY_MAX_RECENT_RELAY = 3500;
+/** Verify that INVENTORY_MAX_RECENT_RELAY is enough to cache everything typically
+ * relayed before unconditional relay from the mempool kicks in. This is only a
+ * lower bound, and it should be larger to account for higher inv rate to outbound
+ * peers, and random variations in the broadcast mechanism. */
+static_assert(INVENTORY_MAX_RECENT_RELAY >= INVENTORY_BROADCAST_PER_SECOND * UNCONDITIONAL_RELAY_DELAY / std::chrono::seconds{1}, "INVENTORY_RELAY_MAX too low");
/** Average delay between feefilter broadcasts in seconds. */
static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
/** Maximum feefilter broadcast delay after significant change. */
static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
-/** Interval between compact filter checkpoints. See BIP 157. */
-static constexpr int CFCHECKPT_INTERVAL = 1000;
+/** Maximum number of compact filters that may be requested with one getcfilters. See BIP 157. */
+static constexpr uint32_t MAX_GETCFILTERS_SIZE = 1000;
+/** Maximum number of cf hashes that may be requested with one getcfheaders. See BIP 157. */
+static constexpr uint32_t MAX_GETCFHEADERS_SIZE = 2000;
struct COrphanTx {
// When modifying, adapt the copy of this definition in tests/DoS_tests.
@@ -187,7 +197,7 @@ namespace {
* We use this to avoid requesting transactions that have already been
* confirnmed.
*/
- RecursiveMutex g_cs_recent_confirmed_transactions;
+ Mutex g_cs_recent_confirmed_transactions;
std::unique_ptr<CRollingBloomFilter> g_recent_confirmed_transactions GUARDED_BY(g_cs_recent_confirmed_transactions);
/** Blocks that are in flight, and that are in the queue to be downloaded. */
@@ -250,8 +260,8 @@ struct CNodeState {
bool fCurrentlyConnected;
//! Accumulated misbehaviour score for this peer.
int nMisbehavior;
- //! Whether this peer should be disconnected and banned (unless whitelisted).
- bool fShouldBan;
+ //! Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission).
+ bool m_should_discourage;
//! String name of this peer (debugging/logging purposes).
const std::string name;
//! The best known block we know this peer has announced.
@@ -396,13 +406,16 @@ struct CNodeState {
//! Whether this peer is a manual connection
bool m_is_manual_connection;
+ //! A rolling bloom filter of all announced tx CInvs to this peer.
+ CRollingBloomFilter m_recently_announced_invs = CRollingBloomFilter{INVENTORY_MAX_RECENT_RELAY, 0.000001};
+
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;
+ m_should_discourage = false;
pindexBestKnownBlock = nullptr;
hashLastUnknownBlock.SetNull();
pindexLastCommonBlock = nullptr;
@@ -423,6 +436,7 @@ struct CNodeState {
fSupportsDesiredCmpctVersion = false;
m_chain_sync = { 0, nullptr, false, false };
m_last_block_announcement = 0;
+ m_recently_announced_invs.reset();
}
};
@@ -439,32 +453,32 @@ static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return &it->second;
}
-static void UpdatePreferredDownload(CNode* node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
nPreferredDownload -= state->fPreferredDownload;
// Whether this node should be marked as a preferred download node.
- state->fPreferredDownload = (!node->fInbound || node->HasPermission(PF_NOBAN)) && !node->fOneShot && !node->fClient;
+ state->fPreferredDownload = (!node.fInbound || node.HasPermission(PF_NOBAN)) && !node.fOneShot && !node.fClient;
nPreferredDownload += state->fPreferredDownload;
}
-static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
+static void PushNodeVersion(CNode& pnode, CConnman& connman, int64_t nTime)
{
// Note that pnode->GetLocalServices() is a reflection of the local
// services we were offering when the CNode object was created for this
// peer.
- ServiceFlags nLocalNodeServices = pnode->GetLocalServices();
- uint64_t nonce = pnode->GetLocalNonce();
- int nNodeStartingHeight = pnode->GetMyStartingHeight();
- NodeId nodeid = pnode->GetId();
- CAddress addr = pnode->addr;
+ ServiceFlags nLocalNodeServices = pnode.GetLocalServices();
+ uint64_t nonce = pnode.GetLocalNonce();
+ int nNodeStartingHeight = pnode.GetMyStartingHeight();
+ NodeId nodeid = pnode.GetId();
+ CAddress addr = pnode.addr;
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices));
CAddress addrMe = CAddress(CService(), nLocalNodeServices);
- connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
- nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes && pnode->m_tx_relay != nullptr));
+ connman.PushMessage(&pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
+ nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes && pnode.m_tx_relay != nullptr));
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);
@@ -574,7 +588,7 @@ static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIV
* lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by
* removing the first element if necessary.
*/
-static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
CNodeState* nodestate = State(nodeid);
@@ -590,20 +604,20 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connma
return;
}
}
- connman->ForNode(nodeid, [connman](CNode* pfrom){
+ connman.ForNode(nodeid, [&connman](CNode* pfrom){
AssertLockHeld(cs_main);
uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings.
- connman->ForNode(lNodesAnnouncingHeaderAndIDs.front(), [connman, nCMPCTBLOCKVersion](CNode* pnodeStop){
+ connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, nCMPCTBLOCKVersion](CNode* pnodeStop){
AssertLockHeld(cs_main);
- connman->PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
+ connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
return true;
});
lNodesAnnouncingHeaderAndIDs.pop_front();
}
- connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion));
+ connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion));
lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
return true;
});
@@ -797,9 +811,9 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
// Returns true for outbound peers, excluding manual connections, feelers, and
// one-shots.
-static bool IsOutboundDisconnectionCandidate(const CNode *node)
+static bool IsOutboundDisconnectionCandidate(const CNode& node)
{
- return !(node->fInbound || node->m_manual_connection || node->fFeeler || node->fOneShot);
+ return !(node.fInbound || node.m_manual_connection || node.fFeeler || node.fOneShot);
}
void PeerLogicValidation::InitializeNode(CNode *pnode) {
@@ -811,7 +825,7 @@ void PeerLogicValidation::InitializeNode(CNode *pnode) {
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());
+ PushNodeVersion(*pnode, *connman, GetTime());
}
void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
@@ -819,10 +833,16 @@ void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
std::set<uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs();
for (const uint256& txid : unbroadcast_txids) {
- RelayTransaction(txid, *connman);
+ // Sanity check: all unbroadcast txns should exist in the mempool
+ if (m_mempool.exists(txid)) {
+ RelayTransaction(txid, *connman);
+ } else {
+ m_mempool.RemoveUnbroadcastTx(txid, true);
+ }
}
- // schedule next run for 10-15 minutes in the future
+ // Schedule next run for 10-15 minutes in the future.
+ // We add randomness on every cycle to avoid the possibility of P2P fingerprinting.
const std::chrono::milliseconds delta = std::chrono::minutes{10} + GetRandMillis(std::chrono::minutes{5});
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
}
@@ -1011,7 +1031,8 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
}
/**
- * Mark a misbehaving peer to be banned depending upon the value of `-banscore`.
+ * Increment peer's misbehavior score. If the new value >= DISCOURAGEMENT_THRESHOLD, mark the node
+ * to be discouraged, meaning the peer might be disconnected and added to the discouragement filter.
*/
void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
@@ -1023,18 +1044,17 @@ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIV
return;
state->nMisbehavior += howmuch;
- int banscore = gArgs.GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD);
std::string message_prefixed = message.empty() ? "" : (": " + message);
- if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
+ if (state->nMisbehavior >= DISCOURAGEMENT_THRESHOLD && state->nMisbehavior - howmuch < DISCOURAGEMENT_THRESHOLD)
{
- LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d) BAN THRESHOLD EXCEEDED%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed);
- state->fShouldBan = true;
+ LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed);
+ state->m_should_discourage = true;
} else
LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d)%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed);
}
/**
- * Potentially ban a node based on the contents of a BlockValidationState object
+ * Potentially mark a node discouraged based on the contents of a BlockValidationState 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
@@ -1064,7 +1084,7 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
break;
}
- // Ban outbound (but not inbound) peers if on an invalid chain.
+ // Discourage 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);
@@ -1099,7 +1119,7 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
}
/**
- * Potentially ban a node based on the contents of a TxValidationState object
+ * Potentially disconnect and discourage a node based on the contents of a TxValidationState object
*
* @return Returns true if the peer was punished (probably disconnected)
*/
@@ -1150,9 +1170,10 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
}
-PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CScheduler& scheduler, CTxMemPool& pool)
+PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool)
: connman(connmanIn),
m_banman(banman),
+ m_chainman(chainman),
m_mempool(pool),
m_stale_tip_check_time(0)
{
@@ -1319,9 +1340,10 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
}
// Relay inventory, but don't relay old inventory during initial block download.
connman->ForEachNode([nNewHeight, &vHashes](CNode* pnode) {
+ LOCK(pnode->cs_inventory);
if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) {
for (const uint256& hash : reverse_iterate(vHashes)) {
- pnode->PushBlockHash(hash);
+ pnode->vBlockHashesToAnnounce.push_back(hash);
}
}
});
@@ -1330,7 +1352,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
}
/**
- * Handle invalid block rejection and consequent peer banning, maintain which
+ * Handle invalid block rejection and consequent peer discouragement, maintain which
* peers announce compact blocks.
*/
void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidationState& state) {
@@ -1356,7 +1378,7 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidatio
!::ChainstateActive().IsInitialBlockDownload() &&
mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) {
if (it != mapBlockSource.end()) {
- MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, connman);
+ MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, *connman);
}
}
if (it != mapBlockSource.end())
@@ -1410,10 +1432,9 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LO
void RelayTransaction(const uint256& txid, const CConnman& connman)
{
- CInv inv(MSG_TX, txid);
- connman.ForEachNode([&inv](CNode* pnode)
+ connman.ForEachNode([&txid](CNode* pnode)
{
- pnode->PushInventory(inv);
+ pnode->PushTxInventory(txid);
});
}
@@ -1432,7 +1453,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman&
assert(nRelayNodes <= best.size());
auto sortfunc = [&best, &hasher, nRelayNodes](CNode* pnode) {
- if (pnode->nVersion >= CADDR_TIME_VERSION && pnode->IsAddrRelayPeer()) {
+ if (pnode->IsAddrRelayPeer()) {
uint64_t hashKey = CSipHasher(hasher).Write(pnode->GetId()).Finalize();
for (unsigned int i = 0; i < nRelayNodes; i++) {
if (hashKey > best[i].first) {
@@ -1453,7 +1474,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman&
connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
}
-void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, const CInv& inv, CConnman* connman)
+void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, const CInv& inv, CConnman& connman)
{
bool send = false;
std::shared_ptr<const CBlock> a_recent_block;
@@ -1495,28 +1516,30 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
if (pindex) {
send = BlockRequestAllowed(pindex, consensusParams);
if (!send) {
- LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
+ LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId());
}
}
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
// disconnect node in case we have reached the outbound limit for serving historical blocks
- // never disconnect whitelisted nodes
- if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->HasPermission(PF_NOBAN))
- {
- LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
+ if (send &&
+ connman.OutboundTargetReached(true) &&
+ (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) &&
+ !pfrom.HasPermission(PF_DOWNLOAD) // nodes with the download permission may exceed target
+ ) {
+ LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
//disconnect node
- pfrom->fDisconnect = true;
+ pfrom.fDisconnect = true;
send = false;
}
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
- if (send && !pfrom->HasPermission(PF_NOBAN) && (
- (((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 */) )
+ if (send && !pfrom.HasPermission(PF_NOBAN) && (
+ (((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());
+ LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom.GetId());
//disconnect node and prevent it from stalling (would otherwise wait for the missing block)
- pfrom->fDisconnect = true;
+ pfrom.fDisconnect = true;
send = false;
}
// Pruned nodes may have deleted the block, so check whether
@@ -1533,7 +1556,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
if (!ReadRawBlockFromDisk(block_data, pindex, chainparams.MessageStart())) {
assert(!"cannot load block from disk");
}
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data)));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data)));
// Don't set pblock as we've sent the block
} else {
// Send block from disk
@@ -1544,22 +1567,22 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
}
if (pblock) {
if (inv.type == MSG_BLOCK)
- connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
+ connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
else if (inv.type == MSG_WITNESS_BLOCK)
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
else if (inv.type == MSG_FILTERED_BLOCK)
{
bool sendMerkleBlock = false;
CMerkleBlock merkleBlock;
- if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_filter);
- if (pfrom->m_tx_relay->pfilter) {
+ if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ if (pfrom.m_tx_relay->pfilter) {
sendMerkleBlock = true;
- merkleBlock = CMerkleBlock(*pblock, *pfrom->m_tx_relay->pfilter);
+ merkleBlock = CMerkleBlock(*pblock, *pfrom.m_tx_relay->pfilter);
}
}
if (sendMerkleBlock) {
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
// This avoids hurting performance by pointlessly requiring a round-trip
// Note that there is currently no way for a node to request any single transactions we didn't send here -
@@ -1568,7 +1591,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
// however we MUST always provide at least what the remote peer needs
typedef std::pair<unsigned int, uint256> PairType;
for (PairType& pair : merkleBlock.vMatchedTxn)
- connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
+ connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
}
// else
// no response
@@ -1579,105 +1602,117 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
// they won't have a useful mempool to match against a compact block,
// and we don't feel like constructing the object for them, so
// instead we respond with the full, non-compact block.
- bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
+ 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 ((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));
+ connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
} else {
CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness);
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
}
} else {
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
+ connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
}
}
}
// Trigger the peer node to send a getblocks request for the next batch of inventory
- if (inv.hash == pfrom->hashContinue)
+ if (inv.hash == pfrom.hashContinue)
{
- // Bypass PushInventory, this must send even if redundant,
+ // Send immediately. This must send even if redundant,
// 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()));
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv));
- pfrom->hashContinue.SetNull();
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv));
+ pfrom.hashContinue.SetNull();
+ }
+ }
+}
+
+//! Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed).
+CTransactionRef static FindTxForGetData(const CNode& peer, const uint256& txid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main)
+{
+ auto txinfo = mempool.info(txid);
+ if (txinfo.tx) {
+ // If a TX could have been INVed in reply to a MEMPOOL request,
+ // or is older than UNCONDITIONAL_RELAY_DELAY, permit the request
+ // unconditionally.
+ if ((mempool_req.count() && txinfo.m_time <= mempool_req) || txinfo.m_time <= now - UNCONDITIONAL_RELAY_DELAY) {
+ return std::move(txinfo.tx);
+ }
+ }
+
+ {
+ LOCK(cs_main);
+
+ // Otherwise, the transaction must have been announced recently.
+ if (State(peer.GetId())->m_recently_announced_invs.contains(txid)) {
+ // If it was, it can be relayed from either the mempool...
+ if (txinfo.tx) return std::move(txinfo.tx);
+ // ... or the relay pool.
+ auto mi = mapRelay.find(txid);
+ if (mi != mapRelay.end()) return mi->second;
}
}
+
+ return {};
}
-void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnman* connman, CTxMemPool& mempool, const std::atomic<bool>& interruptMsgProc) LOCKS_EXCLUDED(cs_main)
+void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnman& connman, CTxMemPool& mempool, const std::atomic<bool>& interruptMsgProc) LOCKS_EXCLUDED(cs_main)
{
AssertLockNotHeld(cs_main);
- std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
+ std::deque<CInv>::iterator it = pfrom.vRecvGetData.begin();
std::vector<CInv> vNotFound;
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
- // mempool entries added before this time have likely expired from mapRelay
- const std::chrono::seconds longlived_mempool_time = GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME;
+ const std::chrono::seconds now = GetTime<std::chrono::seconds>();
// Get last mempool request time
- const std::chrono::seconds mempool_req = pfrom->m_tx_relay != nullptr ? pfrom->m_tx_relay->m_last_mempool_req.load()
+ const std::chrono::seconds mempool_req = pfrom.m_tx_relay != nullptr ? pfrom.m_tx_relay->m_last_mempool_req.load()
: std::chrono::seconds::min();
- {
- LOCK(cs_main);
+ // Process as many TX items from the front of the getdata queue as
+ // possible, since they're common and it's efficient to batch process
+ // them.
+ while (it != pfrom.vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) {
+ if (interruptMsgProc) return;
+ // The send buffer provides backpressure. If there's no space in
+ // the buffer, pause processing until the next call.
+ if (pfrom.fPauseSend) break;
- // Process as many TX items from the front of the getdata queue as
- // possible, since they're common and it's efficient to batch process
- // them.
- while (it != pfrom->vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) {
- if (interruptMsgProc)
- return;
- // The send buffer provides backpressure. If there's no space in
- // the buffer, pause processing until the next call.
- if (pfrom->fPauseSend)
- break;
-
- const CInv &inv = *it++;
+ const CInv &inv = *it++;
- if (pfrom->m_tx_relay == nullptr) {
- // Ignore GETDATA requests for transactions from blocks-only peers.
- continue;
- }
+ if (pfrom.m_tx_relay == nullptr) {
+ // Ignore GETDATA requests for transactions from blocks-only peers.
+ continue;
+ }
- // Send stream from relay memory
- bool push = false;
- auto mi = mapRelay.find(inv.hash);
+ CTransactionRef tx = FindTxForGetData(pfrom, inv.hash, mempool_req, now);
+ if (tx) {
int nSendFlags = (inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
- if (mi != mapRelay.end()) {
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second));
- push = true;
- } else {
- auto txinfo = mempool.info(inv.hash);
- // To protect privacy, do not answer getdata using the mempool when
- // that TX couldn't have been INVed in reply to a MEMPOOL request,
- // or when it's too recent to have expired from mapRelay.
- if (txinfo.tx && (
- (mempool_req.count() && txinfo.m_time <= mempool_req)
- || (txinfo.m_time <= longlived_mempool_time)))
- {
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx));
- push = true;
+ connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *tx));
+ mempool.RemoveUnbroadcastTx(inv.hash);
+ // As we're going to send tx, make sure its unconfirmed parents are made requestable.
+ for (const auto& txin : tx->vin) {
+ auto txinfo = mempool.info(txin.prevout.hash);
+ if (txinfo.tx && txinfo.m_time > now - UNCONDITIONAL_RELAY_DELAY) {
+ // Relaying a transaction with a recent but unconfirmed parent.
+ if (WITH_LOCK(pfrom.m_tx_relay->cs_tx_inventory, return !pfrom.m_tx_relay->filterInventoryKnown.contains(txin.prevout.hash))) {
+ LOCK(cs_main);
+ State(pfrom.GetId())->m_recently_announced_invs.insert(txin.prevout.hash);
+ }
}
}
-
- if (push) {
- // We interpret fulfilling a GETDATA for a transaction as a
- // successful initial broadcast and remove it from our
- // unbroadcast set.
- mempool.RemoveUnbroadcastTx(inv.hash);
- } else {
- vNotFound.push_back(inv);
- }
+ } else {
+ vNotFound.push_back(inv);
}
- } // release cs_main
+ }
// Only process one BLOCK item per call, since they're uncommon and can be
// expensive to process.
- if (it != pfrom->vRecvGetData.end() && !pfrom->fPauseSend) {
+ if (it != pfrom.vRecvGetData.end() && !pfrom.fPauseSend) {
const CInv &inv = *it++;
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) {
ProcessGetBlockData(pfrom, chainparams, inv, connman);
@@ -1686,7 +1721,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
// and continue processing the queue on the next call.
}
- pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it);
+ pfrom.vRecvGetData.erase(pfrom.vRecvGetData.begin(), it);
if (!vNotFound.empty()) {
// Let the peer know that we didn't find what it asked for, so it doesn't
@@ -1703,49 +1738,49 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
// 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));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
}
}
-static uint32_t GetFetchFlags(CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
+static uint32_t GetFetchFlags(const CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
uint32_t nFetchFlags = 0;
- if ((pfrom->GetLocalServices() & NODE_WITNESS) && State(pfrom->GetId())->fHaveWitness) {
+ if ((pfrom.GetLocalServices() & NODE_WITNESS) && State(pfrom.GetId())->fHaveWitness) {
nFetchFlags |= MSG_WITNESS_FLAG;
}
return nFetchFlags;
}
-inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode* pfrom, CConnman* connman) {
+inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode& pfrom, CConnman& connman) {
BlockTransactions resp(req);
for (size_t i = 0; i < req.indexes.size(); i++) {
if (req.indexes[i] >= block.vtx.size()) {
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100, strprintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->GetId()));
+ Misbehaving(pfrom.GetId(), 100, strprintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom.GetId()));
return;
}
resp.txn[i] = block.vtx[req.indexes[i]];
}
LOCK(cs_main);
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
- int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
+ int nSendFlags = State(pfrom.GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
+ connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
}
-bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, CTxMemPool& mempool, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool via_compact_block)
+static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateManager& chainman, CTxMemPool& mempool, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool via_compact_block)
{
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
size_t nCount = headers.size();
if (nCount == 0) {
// Nothing interesting. Stop asking this peers for more headers.
- return true;
+ return;
}
bool received_new_header = false;
const CBlockIndex *pindexLast = nullptr;
{
LOCK(cs_main);
- CNodeState *nodestate = State(pfrom->GetId());
+ CNodeState *nodestate = State(pfrom.GetId());
// If this looks like it could be a block announcement (nCount <
// MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that
@@ -1757,28 +1792,28 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, CTxMemPool& m
// 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(),
pindexBestHeader->nHeight,
- pfrom->GetId(), nodestate->nUnconnectingHeaders);
+ pfrom.GetId(), nodestate->nUnconnectingHeaders);
// Set hashLastUnknownBlock for this peer, so that if we
// eventually get the headers - even from a different peer -
// we can use this peer to download.
- UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash());
+ UpdateBlockAvailability(pfrom.GetId(), headers.back().GetHash());
if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) {
- Misbehaving(pfrom->GetId(), 20);
+ Misbehaving(pfrom.GetId(), 20);
}
- return true;
+ return;
}
uint256 hashLastBlock;
for (const CBlockHeader& header : headers) {
if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
- Misbehaving(pfrom->GetId(), 20, "non-continuous headers sequence");
- return false;
+ Misbehaving(pfrom.GetId(), 20, "non-continuous headers sequence");
+ return;
}
hashLastBlock = header.GetHash();
}
@@ -1791,23 +1826,23 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, CTxMemPool& m
}
BlockValidationState state;
- if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
+ if (!chainman.ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
if (state.IsInvalid()) {
- MaybePunishNodeForBlock(pfrom->GetId(), state, via_compact_block, "invalid header received");
- return false;
+ MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received");
+ return;
}
}
{
LOCK(cs_main);
- CNodeState *nodestate = State(pfrom->GetId());
+ CNodeState *nodestate = State(pfrom.GetId());
if (nodestate->nUnconnectingHeaders > 0) {
- LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders);
+ LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom.GetId(), nodestate->nUnconnectingHeaders);
}
nodestate->nUnconnectingHeaders = 0;
assert(pindexLast);
- UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
+ UpdateBlockAvailability(pfrom.GetId(), pindexLast->GetBlockHash());
// From here, pindexBestKnownBlock should be guaranteed to be non-null,
// because it is set in UpdateBlockAvailability. Some nullptr checks
@@ -1821,8 +1856,8 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, CTxMemPool& m
// Headers message had its maximum size; the peer may have more headers.
// TODO: optimize: if pindexLast is an ancestor of ::ChainActive().Tip or pindexBestHeader, continue
// from there instead.
- LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight);
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
+ LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom.GetId(), pfrom.nStartingHeight);
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
}
bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
@@ -1835,7 +1870,7 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, CTxMemPool& m
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)) {
+ (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
// We don't have this block, and it's not yet in flight.
vToFetch.push_back(pindexWalk);
}
@@ -1859,9 +1894,9 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, CTxMemPool& m
}
uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(mempool, pfrom->GetId(), pindex->GetBlockHash(), pindex);
+ MarkBlockAsInFlight(mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
- pindex->GetBlockHash().ToString(), pfrom->GetId());
+ pindex->GetBlockHash().ToString(), pfrom.GetId());
}
if (vGetData.size() > 1) {
LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n",
@@ -1872,7 +1907,7 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, CTxMemPool& m
// In any case, we want to download using a compact block, not a regular one
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
}
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
}
}
}
@@ -1883,37 +1918,37 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, CTxMemPool& m
// headers to fetch from this peer.
if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
// This peer has too little work on their headers chain to help
- // us sync -- disconnect if using an outbound slot (unless
- // whitelisted or addnode).
+ // us sync -- disconnect if it is an outbound disconnection
+ // candidate.
// Note: We compare their tip to nMinimumChainWork (rather than
// ::ChainActive().Tip()) because we won't start block download
// until we have a headers chain that has at least
// nMinimumChainWork, even if a peer has a chain past our tip,
// as an anti-DoS measure.
if (IsOutboundDisconnectionCandidate(pfrom)) {
- LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
}
}
}
- if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr && pfrom->m_tx_relay != nullptr) {
+ if (!pfrom.fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr && pfrom.m_tx_relay != nullptr) {
// If this is an outbound full-relay peer, check to see if we should protect
// it from the bad/lagging chain logic.
// Note that block-relay-only peers are already implicitly protected, so we
// only consider setting m_protect for the full-relay peers.
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());
+ LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId());
nodestate->m_chain_sync.m_protect = true;
++g_outbound_peers_with_protect_from_disconnect;
}
}
}
- return true;
+ return;
}
-void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uint256>& orphan_work_set, std::list<CTransactionRef>& removed_txn) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans)
+void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, 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);
@@ -1937,7 +1972,7 @@ void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uin
if (setMisbehaving.count(fromPeer)) continue;
if (AcceptToMemoryPool(mempool, orphan_state, porphanTx, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
- RelayTransaction(orphanHash, *connman);
+ RelayTransaction(orphanHash, 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()) {
@@ -1954,7 +1989,10 @@ void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uin
if (MaybePunishNodeForTx(fromPeer, orphan_state)) {
setMisbehaving.insert(fromPeer);
}
- LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
+ LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s from peer=%d. %s\n",
+ orphanHash.ToString(),
+ fromPeer,
+ orphan_state.ToString());
}
// Has inputs but not accepted to mempool
// Probably non-standard or insufficient fee
@@ -1981,24 +2019,26 @@ void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uin
* @param[in] pfrom The peer that we received the request from
* @param[in] chain_params Chain parameters
* @param[in] filter_type The filter type the request is for. Must be basic filters.
+ * @param[in] start_height The start height for the request
* @param[in] stop_hash The stop_hash for the request
+ * @param[in] max_height_diff The maximum number of items permitted to request, as specified in BIP 157
* @param[out] stop_index The CBlockIndex for the stop_hash block, if the request can be serviced.
* @param[out] filter_index The filter index, if the request can be serviced.
* @return True if the request can be serviced.
*/
-static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_params,
- BlockFilterType filter_type,
- const uint256& stop_hash,
+static bool PrepareBlockFilterRequest(CNode& pfrom, const CChainParams& chain_params,
+ BlockFilterType filter_type, uint32_t start_height,
+ const uint256& stop_hash, uint32_t max_height_diff,
const CBlockIndex*& stop_index,
- const BlockFilterIndex*& filter_index)
+ BlockFilterIndex*& filter_index)
{
const bool supported_filter_type =
(filter_type == BlockFilterType::BASIC &&
gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS));
if (!supported_filter_type) {
LogPrint(BCLog::NET, "peer %d requested unsupported block filter type: %d\n",
- pfrom->GetId(), static_cast<uint8_t>(filter_type));
- pfrom->fDisconnect = true;
+ pfrom.GetId(), static_cast<uint8_t>(filter_type));
+ pfrom.fDisconnect = true;
return false;
}
@@ -2009,12 +2049,27 @@ static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_pa
// Check that the stop block exists and the peer would be allowed to fetch it.
if (!stop_index || !BlockRequestAllowed(stop_index, chain_params.GetConsensus())) {
LogPrint(BCLog::NET, "peer %d requested invalid block hash: %s\n",
- pfrom->GetId(), stop_hash.ToString());
- pfrom->fDisconnect = true;
+ pfrom.GetId(), stop_hash.ToString());
+ pfrom.fDisconnect = true;
return false;
}
}
+ uint32_t stop_height = stop_index->nHeight;
+ if (start_height > stop_height) {
+ LogPrint(BCLog::NET, "peer %d sent invalid getcfilters/getcfheaders with " /* Continued */
+ "start height %d and stop height %d\n",
+ pfrom.GetId(), start_height, stop_height);
+ pfrom.fDisconnect = true;
+ return false;
+ }
+ if (stop_height - start_height >= max_height_diff) {
+ LogPrint(BCLog::NET, "peer %d requested too many cfilters/cfheaders: %d / %d\n",
+ pfrom.GetId(), stop_height - start_height + 1, max_height_diff);
+ pfrom.fDisconnect = true;
+ return false;
+ }
+
filter_index = GetBlockFilterIndex(filter_type);
if (!filter_index) {
LogPrint(BCLog::NET, "Filter index for supported type %s not found\n", BlockFilterTypeName(filter_type));
@@ -2025,6 +2080,104 @@ static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_pa
}
/**
+ * Handle a cfilters request.
+ *
+ * May disconnect from the peer in the case of a bad request.
+ *
+ * @param[in] pfrom The peer that we received the request from
+ * @param[in] vRecv The raw message received
+ * @param[in] chain_params Chain parameters
+ * @param[in] connman Pointer to the connection manager
+ */
+static void ProcessGetCFilters(CNode& pfrom, CDataStream& vRecv, const CChainParams& chain_params,
+ CConnman& connman)
+{
+ uint8_t filter_type_ser;
+ uint32_t start_height;
+ uint256 stop_hash;
+
+ vRecv >> filter_type_ser >> start_height >> stop_hash;
+
+ const BlockFilterType filter_type = static_cast<BlockFilterType>(filter_type_ser);
+
+ const CBlockIndex* stop_index;
+ BlockFilterIndex* filter_index;
+ if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, start_height, stop_hash,
+ MAX_GETCFILTERS_SIZE, stop_index, filter_index)) {
+ return;
+ }
+
+ std::vector<BlockFilter> filters;
+
+ if (!filter_index->LookupFilterRange(start_height, stop_index, filters)) {
+ LogPrint(BCLog::NET, "Failed to find block filter in index: filter_type=%s, start_height=%d, stop_hash=%s\n",
+ BlockFilterTypeName(filter_type), start_height, stop_hash.ToString());
+ return;
+ }
+
+ for (const auto& filter : filters) {
+ CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion())
+ .Make(NetMsgType::CFILTER, filter);
+ connman.PushMessage(&pfrom, std::move(msg));
+ }
+}
+
+/**
+ * Handle a cfheaders request.
+ *
+ * May disconnect from the peer in the case of a bad request.
+ *
+ * @param[in] pfrom The peer that we received the request from
+ * @param[in] vRecv The raw message received
+ * @param[in] chain_params Chain parameters
+ * @param[in] connman Pointer to the connection manager
+ */
+static void ProcessGetCFHeaders(CNode& pfrom, CDataStream& vRecv, const CChainParams& chain_params,
+ CConnman& connman)
+{
+ uint8_t filter_type_ser;
+ uint32_t start_height;
+ uint256 stop_hash;
+
+ vRecv >> filter_type_ser >> start_height >> stop_hash;
+
+ const BlockFilterType filter_type = static_cast<BlockFilterType>(filter_type_ser);
+
+ const CBlockIndex* stop_index;
+ BlockFilterIndex* filter_index;
+ if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, start_height, stop_hash,
+ MAX_GETCFHEADERS_SIZE, stop_index, filter_index)) {
+ return;
+ }
+
+ uint256 prev_header;
+ if (start_height > 0) {
+ const CBlockIndex* const prev_block =
+ stop_index->GetAncestor(static_cast<int>(start_height - 1));
+ if (!filter_index->LookupFilterHeader(prev_block, prev_header)) {
+ LogPrint(BCLog::NET, "Failed to find block filter header in index: filter_type=%s, block_hash=%s\n",
+ BlockFilterTypeName(filter_type), prev_block->GetBlockHash().ToString());
+ return;
+ }
+ }
+
+ std::vector<uint256> filter_hashes;
+ if (!filter_index->LookupFilterHashRange(start_height, stop_index, filter_hashes)) {
+ LogPrint(BCLog::NET, "Failed to find block filter hashes in index: filter_type=%s, start_height=%d, stop_hash=%s\n",
+ BlockFilterTypeName(filter_type), start_height, stop_hash.ToString());
+ return;
+ }
+
+ CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion())
+ .Make(NetMsgType::CFHEADERS,
+ filter_type_ser,
+ stop_index->GetBlockHash(),
+ prev_header,
+ filter_hashes);
+ connman.PushMessage(&pfrom, std::move(msg));
+}
+
+/**
* Handle a getcfcheckpt request.
*
* May disconnect from the peer in the case of a bad request.
@@ -2034,8 +2187,8 @@ static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_pa
* @param[in] chain_params Chain parameters
* @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFCheckPt(CNode* pfrom, CDataStream& vRecv, const CChainParams& chain_params,
- CConnman* connman)
+static void ProcessGetCFCheckPt(CNode& pfrom, CDataStream& vRecv, const CChainParams& chain_params,
+ CConnman& connman)
{
uint8_t filter_type_ser;
uint256 stop_hash;
@@ -2045,8 +2198,9 @@ static void ProcessGetCFCheckPt(CNode* pfrom, CDataStream& vRecv, const CChainPa
const BlockFilterType filter_type = static_cast<BlockFilterType>(filter_type_ser);
const CBlockIndex* stop_index;
- const BlockFilterIndex* filter_index;
- if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, stop_hash,
+ BlockFilterIndex* filter_index;
+ if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, /*start_height=*/0, stop_hash,
+ /*max_height_diff=*/std::numeric_limits<uint32_t>::max(),
stop_index, filter_index)) {
return;
}
@@ -2066,45 +2220,41 @@ static void ProcessGetCFCheckPt(CNode* pfrom, CDataStream& vRecv, const CChainPa
}
}
- CSerializedNetMsg msg = CNetMsgMaker(pfrom->GetSendVersion())
+ CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion())
.Make(NetMsgType::CFCHECKPT,
filter_type_ser,
stop_index->GetBlockHash(),
headers);
- connman->PushMessage(pfrom, std::move(msg));
+ connman.PushMessage(&pfrom, std::move(msg));
}
-bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc)
+void ProcessMessage(
+ CNode& pfrom,
+ const std::string& msg_type,
+ CDataStream& vRecv,
+ const std::chrono::microseconds time_received,
+ const CChainParams& chainparams,
+ ChainstateManager& chainman,
+ CTxMemPool& mempool,
+ CConnman& connman,
+ BanMan* banman,
+ const std::atomic<bool>& interruptMsgProc)
{
- LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom.GetId());
if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0)
{
LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
- return true;
+ return;
}
- if (!(pfrom->GetLocalServices() & NODE_BLOOM) &&
- (msg_type == NetMsgType::FILTERLOAD ||
- msg_type == NetMsgType::FILTERADD))
- {
- if (pfrom->nVersion >= NO_BLOOM_VERSION) {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
- return false;
- } else {
- pfrom->fDisconnect = true;
- return false;
- }
- }
-
if (msg_type == NetMsgType::VERSION) {
// Each connection can only send one version message
- if (pfrom->nVersion != 0)
+ if (pfrom.nVersion != 0)
{
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
- return false;
+ Misbehaving(pfrom.GetId(), 1);
+ return;
}
int64_t nTime;
@@ -2122,22 +2272,22 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
vRecv >> nVersion >> nServiceInt >> nTime >> addrMe;
nSendVersion = std::min(nVersion, PROTOCOL_VERSION);
nServices = ServiceFlags(nServiceInt);
- if (!pfrom->fInbound)
+ if (!pfrom.fInbound)
{
- connman->SetServices(pfrom->addr, nServices);
+ connman.SetServices(pfrom.addr, nServices);
}
- if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices))
+ if (!pfrom.fInbound && !pfrom.fFeeler && !pfrom.m_manual_connection && !HasAllDesirableServiceFlags(nServices))
{
- LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices));
- pfrom->fDisconnect = true;
- return false;
+ LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom.GetId(), nServices, GetDesirableServiceFlags(nServices));
+ pfrom.fDisconnect = true;
+ return;
}
if (nVersion < MIN_PEER_PROTO_VERSION) {
// disconnect from peers older than this proto version
- LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion);
- pfrom->fDisconnect = true;
- return false;
+ LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom.GetId(), nVersion);
+ pfrom.fDisconnect = true;
+ return;
}
if (!vRecv.empty())
@@ -2153,145 +2303,142 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (!vRecv.empty())
vRecv >> fRelay;
// Disconnect if we connected to ourself
- if (pfrom->fInbound && !connman->CheckIncomingNonce(nNonce))
+ if (pfrom.fInbound && !connman.CheckIncomingNonce(nNonce))
{
- LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString());
- pfrom->fDisconnect = true;
- return true;
+ LogPrintf("connected to self at %s, disconnecting\n", pfrom.addr.ToString());
+ pfrom.fDisconnect = true;
+ return;
}
- if (pfrom->fInbound && addrMe.IsRoutable())
+ if (pfrom.fInbound && addrMe.IsRoutable())
{
SeenLocal(addrMe);
}
// Be shy and don't send version until we hear
- if (pfrom->fInbound)
+ if (pfrom.fInbound)
PushNodeVersion(pfrom, connman, GetAdjustedTime());
- connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
+ connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
- pfrom->nServices = nServices;
- pfrom->SetAddrLocal(addrMe);
+ pfrom.nServices = nServices;
+ pfrom.SetAddrLocal(addrMe);
{
- LOCK(pfrom->cs_SubVer);
- pfrom->cleanSubVer = cleanSubVer;
+ LOCK(pfrom.cs_SubVer);
+ pfrom.cleanSubVer = cleanSubVer;
}
- pfrom->nStartingHeight = nStartingHeight;
+ pfrom.nStartingHeight = nStartingHeight;
// set nodes not relaying blocks and tx and not serving (parts) of the historical blockchain as "clients"
- pfrom->fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED));
+ pfrom.fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED));
// set nodes not capable of serving the complete blockchain history as "limited nodes"
- pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
+ pfrom.m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
- if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_filter);
- pfrom->m_tx_relay->fRelayTxes = fRelay; // set to true after we get the first filter* message
+ if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ pfrom.m_tx_relay->fRelayTxes = fRelay; // set to true after we get the first filter* message
}
// Change version
- pfrom->SetSendVersion(nSendVersion);
- pfrom->nVersion = nVersion;
+ pfrom.SetSendVersion(nSendVersion);
+ pfrom.nVersion = nVersion;
if((nServices & NODE_WITNESS))
{
LOCK(cs_main);
- State(pfrom->GetId())->fHaveWitness = true;
+ State(pfrom.GetId())->fHaveWitness = true;
}
// Potentially mark this peer as a preferred download peer.
{
LOCK(cs_main);
- UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
+ UpdatePreferredDownload(pfrom, State(pfrom.GetId()));
}
- if (!pfrom->fInbound && pfrom->IsAddrRelayPeer())
+ if (!pfrom.fInbound && pfrom.IsAddrRelayPeer())
{
// Advertise our address
if (fListen && !::ChainstateActive().IsInitialBlockDownload())
{
- CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices());
+ CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices());
FastRandomContext insecure_rand;
if (addr.IsRoutable())
{
LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToString());
- pfrom->PushAddress(addr, insecure_rand);
- } else if (IsPeerAddrLocalGood(pfrom)) {
+ pfrom.PushAddress(addr, insecure_rand);
+ } else if (IsPeerAddrLocalGood(&pfrom)) {
addr.SetIP(addrMe);
LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToString());
- pfrom->PushAddress(addr, insecure_rand);
+ pfrom.PushAddress(addr, insecure_rand);
}
}
// Get recent addresses
- if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman->GetAddressCount() < 1000)
- {
- connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
- pfrom->fGetAddr = true;
- }
- connman->MarkAddressGood(pfrom->addr);
+ connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
+ pfrom.fGetAddr = true;
+ connman.MarkAddressGood(pfrom.addr);
}
std::string remoteAddr;
if (fLogIPs)
- remoteAddr = ", peeraddr=" + pfrom->addr.ToString();
+ remoteAddr = ", peeraddr=" + pfrom.addr.ToString();
LogPrint(BCLog::NET, "receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
- cleanSubVer, pfrom->nVersion,
- pfrom->nStartingHeight, addrMe.ToString(), pfrom->GetId(),
+ cleanSubVer, pfrom.nVersion,
+ pfrom.nStartingHeight, addrMe.ToString(), pfrom.GetId(),
remoteAddr);
int64_t nTimeOffset = nTime - GetTime();
- pfrom->nTimeOffset = nTimeOffset;
- AddTimeData(pfrom->addr, nTimeOffset);
+ pfrom.nTimeOffset = nTimeOffset;
+ AddTimeData(pfrom.addr, nTimeOffset);
// If the peer is old enough to have the old alert system, send it the final alert.
- if (pfrom->nVersion <= 70012) {
+ if (pfrom.nVersion <= 70012) {
CDataStream finalAlert(ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffeffff7f01ffffff7f00000000ffffff7f00ffffff7f002f555247454e543a20416c657274206b657920636f6d70726f6d697365642c2075706772616465207265717569726564004630440220653febd6410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3abd5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fecaae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION);
- connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert));
+ connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert));
}
// Feeler connections exist only to verify if address is online.
- if (pfrom->fFeeler) {
- assert(pfrom->fInbound == false);
- pfrom->fDisconnect = true;
+ if (pfrom.fFeeler) {
+ assert(pfrom.fInbound == false);
+ pfrom.fDisconnect = true;
}
- return true;
+ return;
}
- if (pfrom->nVersion == 0) {
+ if (pfrom.nVersion == 0) {
// Must have a version message before anything else
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
- return false;
+ Misbehaving(pfrom.GetId(), 1);
+ return;
}
// At this point, the outgoing message serialization version can't change.
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
if (msg_type == NetMsgType::VERACK)
{
- pfrom->SetRecvVersion(std::min(pfrom->nVersion.load(), PROTOCOL_VERSION));
+ pfrom.SetRecvVersion(std::min(pfrom.nVersion.load(), PROTOCOL_VERSION));
- if (!pfrom->fInbound) {
+ if (!pfrom.fInbound) {
// Mark this node as currently connected, so we update its timestamp later.
LOCK(cs_main);
- State(pfrom->GetId())->fCurrentlyConnected = true;
+ State(pfrom.GetId())->fCurrentlyConnected = true;
LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s (%s)\n",
- pfrom->nVersion.load(), pfrom->nStartingHeight,
- pfrom->GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom->addr.ToString()) : ""),
- pfrom->m_tx_relay == nullptr ? "block-relay" : "full-relay");
+ pfrom.nVersion.load(), pfrom.nStartingHeight,
+ pfrom.GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToString()) : ""),
+ pfrom.m_tx_relay == nullptr ? "block-relay" : "full-relay");
}
- if (pfrom->nVersion >= SENDHEADERS_VERSION) {
+ if (pfrom.nVersion >= SENDHEADERS_VERSION) {
// Tell our peer we prefer to receive headers rather than inv's
// We send this to non-NODE NETWORK peers as well, because even
// non-NODE NETWORK peers can announce blocks (such as pruning
// nodes)
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
}
- if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) {
+ if (pfrom.nVersion >= SHORT_IDS_BLOCKS_VERSION) {
// Tell our peer we are willing to provide version 1 or 2 cmpctblocks
// However, we do not request new block announcements using
// cmpctblock messages.
@@ -2299,37 +2446,34 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// they may wish to request compact blocks from us
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 2;
- if (pfrom->GetLocalServices() & NODE_WITNESS)
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ if (pfrom.GetLocalServices() & NODE_WITNESS)
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
nCMPCTBLOCKVersion = 1;
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
}
- pfrom->fSuccessfullyConnected = true;
- return true;
+ pfrom.fSuccessfullyConnected = true;
+ return;
}
- if (!pfrom->fSuccessfullyConnected) {
+ if (!pfrom.fSuccessfullyConnected) {
// Must have a verack message before anything else
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
- return false;
+ Misbehaving(pfrom.GetId(), 1);
+ return;
}
if (msg_type == NetMsgType::ADDR) {
std::vector<CAddress> vAddr;
vRecv >> vAddr;
- // Don't want addr from older versions unless seeding
- if (pfrom->nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000)
- return true;
- if (!pfrom->IsAddrRelayPeer()) {
- return true;
+ if (!pfrom.IsAddrRelayPeer()) {
+ return;
}
if (vAddr.size() > 1000)
{
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20, strprintf("message addr size() = %u", vAddr.size()));
- return false;
+ Misbehaving(pfrom.GetId(), 20, strprintf("addr message size = %u", vAddr.size()));
+ return;
}
// Store the new addresses
@@ -2339,7 +2483,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
for (CAddress& addr : vAddr)
{
if (interruptMsgProc)
- return true;
+ return;
// We only bother storing full nodes, though this may include
// things which we would not make an outbound connection to, in
@@ -2349,53 +2493,56 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60;
- pfrom->AddAddressKnown(addr);
- if (banman->IsBanned(addr)) continue; // Do not process banned addresses beyond remembering we received them
+ pfrom.AddAddressKnown(addr);
+ if (banman && (banman->IsDiscouraged(addr) || banman->IsBanned(addr))) {
+ // Do not process banned/discouraged addresses beyond remembering we received them
+ continue;
+ }
bool fReachable = IsReachable(addr);
- if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
+ if (addr.nTime > nSince && !pfrom.fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
{
// Relay to a limited number of other nodes
- RelayAddress(addr, fReachable, *connman);
+ RelayAddress(addr, fReachable, connman);
}
// Do not store addresses outside our network
if (fReachable)
vAddrOk.push_back(addr);
}
- connman->AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60);
+ connman.AddNewAddresses(vAddrOk, pfrom.addr, 2 * 60 * 60);
if (vAddr.size() < 1000)
- pfrom->fGetAddr = false;
- if (pfrom->fOneShot)
- pfrom->fDisconnect = true;
- return true;
+ pfrom.fGetAddr = false;
+ if (pfrom.fOneShot)
+ pfrom.fDisconnect = true;
+ return;
}
if (msg_type == NetMsgType::SENDHEADERS) {
LOCK(cs_main);
- State(pfrom->GetId())->fPreferHeaders = true;
- return true;
+ State(pfrom.GetId())->fPreferHeaders = true;
+ return;
}
if (msg_type == NetMsgType::SENDCMPCT) {
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 0;
vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
- if (nCMPCTBLOCKVersion == 1 || ((pfrom->GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
+ if (nCMPCTBLOCKVersion == 1 || ((pfrom.GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
LOCK(cs_main);
// fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness)
- if (!State(pfrom->GetId())->fProvidesHeaderAndIDs) {
- State(pfrom->GetId())->fProvidesHeaderAndIDs = true;
- State(pfrom->GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2;
+ if (!State(pfrom.GetId())->fProvidesHeaderAndIDs) {
+ State(pfrom.GetId())->fProvidesHeaderAndIDs = true;
+ State(pfrom.GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2;
}
- if (State(pfrom->GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) // ignore later version announces
- State(pfrom->GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
- if (!State(pfrom->GetId())->fSupportsDesiredCmpctVersion) {
- if (pfrom->GetLocalServices() & NODE_WITNESS)
- State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
+ if (State(pfrom.GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) // ignore later version announces
+ State(pfrom.GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
+ if (!State(pfrom.GetId())->fSupportsDesiredCmpctVersion) {
+ if (pfrom.GetLocalServices() & NODE_WITNESS)
+ State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
else
- State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
+ State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
}
}
- return true;
+ return;
}
if (msg_type == NetMsgType::INV) {
@@ -2404,17 +2551,18 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (vInv.size() > MAX_INV_SZ)
{
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20, strprintf("message inv size() = %u", vInv.size()));
- return false;
+ Misbehaving(pfrom.GetId(), 20, strprintf("inv message size = %u", vInv.size()));
+ return;
}
// We won't accept tx inv's if we're in blocks-only mode, or this is a
// block-relay-only peer
- bool fBlocksOnly = !g_relay_txes || (pfrom->m_tx_relay == nullptr);
+ bool fBlocksOnly = !g_relay_txes || (pfrom.m_tx_relay == nullptr);
- // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
- if (pfrom->HasPermission(PF_RELAY))
+ // Allow peers with relay permission to send data other than blocks in blocks only mode
+ if (pfrom.HasPermission(PF_RELAY)) {
fBlocksOnly = false;
+ }
LOCK(cs_main);
@@ -2425,17 +2573,17 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
for (CInv &inv : vInv)
{
if (interruptMsgProc)
- return true;
+ return;
bool fAlreadyHave = AlreadyHave(inv, mempool);
- LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->GetId());
+ LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
if (inv.type == MSG_TX) {
inv.type |= nFetchFlags;
}
if (inv.type == MSG_BLOCK) {
- UpdateBlockAvailability(pfrom->GetId(), inv.hash);
+ UpdateBlockAvailability(pfrom.GetId(), inv.hash);
if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
// Headers-first is the primary method of announcement on
// the network. If a node fell back to sending blocks by inv,
@@ -2445,23 +2593,23 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
best_block = &inv.hash;
}
} else {
- pfrom->AddInventoryKnown(inv);
+ pfrom.AddInventoryKnown(inv);
if (fBlocksOnly) {
- LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom->GetId());
- pfrom->fDisconnect = true;
- return true;
- } else if (!fAlreadyHave && !fImporting && !fReindex && !::ChainstateActive().IsInitialBlockDownload()) {
- RequestTx(State(pfrom->GetId()), inv.hash, current_time);
+ LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
+ } else if (!fAlreadyHave && !chainman.ActiveChainstate().IsInitialBlockDownload()) {
+ RequestTx(State(pfrom.GetId()), inv.hash, current_time);
}
}
}
if (best_block != nullptr) {
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block));
- LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom->GetId());
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block));
+ LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom.GetId());
}
- return true;
+ return;
}
if (msg_type == NetMsgType::GETDATA) {
@@ -2470,19 +2618,19 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (vInv.size() > MAX_INV_SZ)
{
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20, strprintf("message getdata size() = %u", vInv.size()));
- return false;
+ Misbehaving(pfrom.GetId(), 20, strprintf("getdata message size = %u", vInv.size()));
+ return;
}
- LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom.GetId());
if (vInv.size() > 0) {
- LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom.GetId());
}
- pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
+ pfrom.vRecvGetData.insert(pfrom.vRecvGetData.end(), vInv.begin(), vInv.end());
ProcessGetData(pfrom, chainparams, connman, mempool, interruptMsgProc);
- return true;
+ return;
}
if (msg_type == NetMsgType::GETBLOCKS) {
@@ -2491,9 +2639,9 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
vRecv >> locator >> hashStop;
if (locator.vHave.size() > MAX_LOCATOR_SZ) {
- LogPrint(BCLog::NET, "getblocks locator size %lld > %d, disconnect peer=%d\n", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom->GetId());
- pfrom->fDisconnect = true;
- return true;
+ LogPrint(BCLog::NET, "getblocks locator size %lld > %d, disconnect peer=%d\n", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
}
// We might have announced the currently-being-connected tip using a
@@ -2524,7 +2672,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (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());
+ 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))
{
if (pindex->GetBlockHash() == hashStop)
@@ -2540,17 +2688,17 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
break;
}
- pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
+ WITH_LOCK(pfrom.cs_inventory, pfrom.vInventoryBlockToSend.push_back(pindex->GetBlockHash()));
if (--nLimit <= 0)
{
// When this block is requested, we'll send an inv that'll
// trigger the peer to getblocks the next batch of inventory.
LogPrint(BCLog::NET, " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
- pfrom->hashContinue = pindex->GetBlockHash();
+ pfrom.hashContinue = pindex->GetBlockHash();
break;
}
}
- return true;
+ return;
}
if (msg_type == NetMsgType::GETBLOCKTXN) {
@@ -2566,15 +2714,15 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
}
if (recent_block) {
SendBlockTransactions(*recent_block, req, pfrom, connman);
- return true;
+ return;
}
LOCK(cs_main);
const CBlockIndex* pindex = LookupBlockIndex(req.blockhash);
if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) {
- LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom->GetId());
- return true;
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId());
+ return;
}
if (pindex->nHeight < ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) {
@@ -2585,13 +2733,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// might maliciously send lots of getblocktxn requests to trigger
// expensive disk reads, because it will require the peer to
// actually receive all the data read from disk over the network.
- LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom->GetId(), MAX_BLOCKTXN_DEPTH);
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom.GetId(), MAX_BLOCKTXN_DEPTH);
CInv inv;
- inv.type = State(pfrom->GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
+ inv.type = State(pfrom.GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
inv.hash = req.blockhash;
- pfrom->vRecvGetData.push_back(inv);
+ pfrom.vRecvGetData.push_back(inv);
// The message processing loop will go around again (without pausing) and we'll respond then (without cs_main)
- return true;
+ return;
}
CBlock block;
@@ -2599,7 +2747,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
assert(ret);
SendBlockTransactions(block, req, pfrom, connman);
- return true;
+ return;
}
if (msg_type == NetMsgType::GETHEADERS) {
@@ -2608,30 +2756,30 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
vRecv >> locator >> hashStop;
if (locator.vHave.size() > MAX_LOCATOR_SZ) {
- LogPrint(BCLog::NET, "getheaders locator size %lld > %d, disconnect peer=%d\n", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom->GetId());
- pfrom->fDisconnect = true;
- return true;
+ LogPrint(BCLog::NET, "getheaders locator size %lld > %d, disconnect peer=%d\n", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
}
LOCK(cs_main);
- if (::ChainstateActive().IsInitialBlockDownload() && !pfrom->HasPermission(PF_NOBAN)) {
- LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->GetId());
- return true;
+ if (::ChainstateActive().IsInitialBlockDownload() && !pfrom.HasPermission(PF_DOWNLOAD)) {
+ LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom.GetId());
+ return;
}
- CNodeState *nodestate = State(pfrom->GetId());
+ CNodeState *nodestate = State(pfrom.GetId());
const CBlockIndex* pindex = nullptr;
if (locator.IsNull())
{
// If locator is null, return the hashStop block
pindex = LookupBlockIndex(hashStop);
if (!pindex) {
- return true;
+ return;
}
if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) {
- LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom->GetId());
- return true;
+ LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom.GetId());
+ return;
}
}
else
@@ -2645,7 +2793,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// 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());
+ 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))
{
vHeaders.push_back(pindex->GetBlockHeader());
@@ -2665,19 +2813,19 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// will re-announce the new block via headers (or compact blocks again)
// in the SendMessages logic.
nodestate->pindexBestHeaderSent = pindex ? pindex : ::ChainActive().Tip();
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
- return true;
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ return;
}
if (msg_type == NetMsgType::TX) {
// Stop processing the transaction early if
- // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
- // or if this peer is supposed to be a block-relay-only peer
- if ((!g_relay_txes && !pfrom->HasPermission(PF_RELAY)) || (pfrom->m_tx_relay == nullptr))
+ // 1) We are in blocks only mode and peer has no relay permission
+ // 2) This peer is a block-relay-only peer
+ if ((!g_relay_txes && !pfrom.HasPermission(PF_RELAY)) || (pfrom.m_tx_relay == nullptr))
{
- LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
- return true;
+ LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
}
CTransactionRef ptx;
@@ -2685,13 +2833,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
const CTransaction& tx = *ptx;
CInv inv(MSG_TX, tx.GetHash());
- pfrom->AddInventoryKnown(inv);
+ pfrom.AddInventoryKnown(inv);
LOCK2(cs_main, g_cs_orphans);
TxValidationState state;
- CNodeState* nodestate = State(pfrom->GetId());
+ 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);
@@ -2701,25 +2849,25 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (!AlreadyHave(inv, mempool) &&
AcceptToMemoryPool(mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
mempool.check(&::ChainstateActive().CoinsTip());
- RelayTransaction(tx.GetHash(), *connman);
+ RelayTransaction(tx.GetHash(), connman);
for (unsigned int i = 0; i < tx.vout.size(); 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.orphan_work_set.insert(elem->first);
}
}
}
- pfrom->nLastTXTime = GetTime();
+ pfrom.nLastTXTime = GetTime();
LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
- pfrom->GetId(),
+ pfrom.GetId(),
tx.GetHash().ToString(),
mempool.size(), mempool.DynamicMemoryUsage() / 1000);
// Recursively process any orphan transactions that depended on this one
- ProcessOrphanTx(connman, mempool, pfrom->orphan_work_set, lRemovedTxn);
+ ProcessOrphanTx(connman, mempool, pfrom.orphan_work_set, lRemovedTxn);
}
else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS)
{
@@ -2736,10 +2884,10 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
for (const CTxIn& txin : tx.vin) {
CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash);
- pfrom->AddInventoryKnown(_inv);
- if (!AlreadyHave(_inv, mempool)) RequestTx(State(pfrom->GetId()), _inv.hash, current_time);
+ pfrom.AddInventoryKnown(_inv);
+ if (!AlreadyHave(_inv, mempool)) RequestTx(State(pfrom.GetId()), _inv.hash, current_time);
}
- AddOrphanTx(ptx, pfrom->GetId());
+ AddOrphanTx(ptx, pfrom.GetId());
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded (see CVE-2012-3789)
unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
@@ -2767,16 +2915,16 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
AddToCompactExtraTransactions(ptx);
}
- if (pfrom->HasPermission(PF_FORCERELAY)) {
- // Always relay transactions received from whitelisted peers, even
+ if (pfrom.HasPermission(PF_FORCERELAY)) {
+ // Always relay transactions received from peers with forcerelay permission, even
// if they were already in the mempool,
// allowing the node to function as a gateway for
// nodes hidden behind it.
if (!mempool.exists(tx.GetHash())) {
- LogPrintf("Not relaying non-mempool transaction %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->GetId());
+ LogPrintf("Not relaying non-mempool transaction %s from forcerelay peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
} else {
- LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->GetId());
- RelayTransaction(tx.GetHash(), *connman);
+ LogPrintf("Force relaying tx %s from peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
+ RelayTransaction(tx.GetHash(), connman);
}
}
}
@@ -2801,22 +2949,21 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// peer simply for relaying a tx that our recentRejects has caught,
// regardless of false positives.
- if (state.IsInvalid())
- {
+ if (state.IsInvalid()) {
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
- pfrom->GetId(),
+ pfrom.GetId(),
state.ToString());
- MaybePunishNodeForTx(pfrom->GetId(), state);
+ MaybePunishNodeForTx(pfrom.GetId(), state);
}
- return true;
+ return;
}
if (msg_type == 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;
+ LogPrint(BCLog::NET, "Unexpected cmpctblock message received from peer %d\n", pfrom.GetId());
+ return;
}
CBlockHeaderAndShortTxIDs cmpctblock;
@@ -2830,8 +2977,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
// Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
if (!::ChainstateActive().IsInitialBlockDownload())
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
- return true;
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ return;
}
if (!LookupBlockIndex(cmpctblock.header.GetHash())) {
@@ -2841,10 +2988,10 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
const CBlockIndex *pindex = nullptr;
BlockValidationState state;
- if (!ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
+ if (!chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
if (state.IsInvalid()) {
- MaybePunishNodeForBlock(pfrom->GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
- return true;
+ MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
+ return;
}
}
@@ -2868,9 +3015,9 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
LOCK2(cs_main, g_cs_orphans);
// If AcceptBlockHeader returned true, it set pindex
assert(pindex);
- UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash());
+ UpdateBlockAvailability(pfrom.GetId(), pindex->GetBlockHash());
- CNodeState *nodestate = State(pfrom->GetId());
+ CNodeState *nodestate = State(pfrom.GetId());
// If this was a new header with more work than our tip, update the
// peer's last block announcement time
@@ -2882,7 +3029,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end();
if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
- return true;
+ return;
if (pindex->nChainWork <= ::ChainActive().Tip()->nChainWork || // We know something better
pindex->nTx != 0) { // We had this block at some point, but pruned it
@@ -2891,49 +3038,49 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// so we just grab the block via normal getdata
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
}
- return true;
+ return;
}
// If we're not close to tip yet, give up and let parallel block fetch work its magic
if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus()))
- return true;
+ return;
if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
// Don't bother trying to process compact blocks from v1 peers
// after segwit activates.
- return true;
+ return;
}
// We want to be a bit conservative just to be extra careful about DoS
// possibilities in compact block processing...
if (pindex->nHeight <= ::ChainActive().Height() + 2) {
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
- (fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) {
+ (fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
- if (!MarkBlockAsInFlight(mempool, pfrom->GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
+ if (!MarkBlockAsInFlight(mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock)
(*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&mempool));
else {
// The block was already in flight using compact blocks from the same peer
LogPrint(BCLog::NET, "Peer sent us compact block we were already syncing!\n");
- return true;
+ return;
}
}
PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock;
ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case of whitelist
- Misbehaving(pfrom->GetId(), 100, strprintf("Peer %d sent us invalid compact block\n", pfrom->GetId()));
- return true;
+ MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
+ Misbehaving(pfrom.GetId(), 100, strprintf("Peer %d sent us invalid compact block\n", pfrom.GetId()));
+ return;
} else if (status == READ_STATUS_FAILED) {
// Duplicate txindexes, the block is now in-flight, so just request it
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
- return true;
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ return;
}
BlockTransactionsRequest req;
@@ -2949,7 +3096,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
fProcessBLOCKTXN = true;
} else {
req.blockhash = pindex->GetBlockHash();
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
}
} else {
// This block is either already in flight from a different
@@ -2961,7 +3108,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
ReadStatus status = tempBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status != READ_STATUS_OK) {
// TODO: don't ignore failures
- return true;
+ return;
}
std::vector<CTransactionRef> dummy;
status = tempBlock.FillBlock(*pblock, dummy);
@@ -2975,8 +3122,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// mempool will probably be useless - request the block normally
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
- return true;
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ return;
} else {
// If this was an announce-cmpctblock, we want the same treatment as a header message
fRevertToHeaderProcessing = true;
@@ -2985,15 +3132,15 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
} // cs_main
if (fProcessBLOCKTXN)
- return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, mempool, connman, banman, interruptMsgProc);
+ return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, chainparams, chainman, mempool, connman, banman, interruptMsgProc);
if (fRevertToHeaderProcessing) {
// Headers received from HB compact block peers are permitted to be
// relayed before full validation (see BIP 152), so we don't want to disconnect
// the peer if the header turns out to be for an invalid block.
// Note that if a peer tries to build on an invalid chain, that
- // will be detected and the peer will be banned.
- return ProcessHeadersMessage(pfrom, connman, mempool, {cmpctblock.header}, chainparams, /*via_compact_block=*/true);
+ // will be detected and the peer will be disconnected/discouraged.
+ return ProcessHeadersMessage(pfrom, connman, chainman, mempool, {cmpctblock.header}, chainparams, /*via_compact_block=*/true);
}
if (fBlockReconstructed) {
@@ -3001,7 +3148,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// block that is in flight from some other peer.
{
LOCK(cs_main);
- mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom->GetId(), false));
+ mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom.GetId(), false));
}
bool fNewBlock = false;
// Setting fForceProcessing to true means that we bypass some of
@@ -3013,9 +3160,9 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// we have a chain with at least nMinimumChainWork), and we ignore
// compact blocks with less work than our tip, it is safe to treat
// reconstructed compact blocks as having been requested.
- ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
+ chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
- pfrom->nLastBlockTime = GetTime();
+ pfrom.nLastBlockTime = GetTime();
} else {
LOCK(cs_main);
mapBlockSource.erase(pblock->GetHash());
@@ -3029,15 +3176,15 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
MarkBlockAsReceived(pblock->GetHash());
}
}
- return true;
+ return;
}
if (msg_type == 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;
+ LogPrint(BCLog::NET, "Unexpected blocktxn message received from peer %d\n", pfrom.GetId());
+ return;
}
BlockTransactions resp;
@@ -3050,22 +3197,22 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash);
if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock ||
- it->second.first != pfrom->GetId()) {
- LogPrint(BCLog::NET, "Peer %d sent us block transactions for block we weren't expecting\n", pfrom->GetId());
- return true;
+ it->second.first != pfrom.GetId()) {
+ LogPrint(BCLog::NET, "Peer %d sent us block transactions for block we weren't expecting\n", pfrom.GetId());
+ return;
}
PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist
- Misbehaving(pfrom->GetId(), 100, strprintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->GetId()));
- return true;
+ MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect
+ Misbehaving(pfrom.GetId(), 100, strprintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom.GetId()));
+ return;
} else if (status == READ_STATUS_FAILED) {
// Might have collided, fall back to getdata now :(
std::vector<CInv> invs;
invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom), resp.blockhash));
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
} else {
// Block is either okay, or possibly we received
// READ_STATUS_CHECKBLOCK_FAILED.
@@ -3078,7 +3225,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// 3. the block is otherwise invalid (eg invalid coinbase,
// block is too big, too many legacy sigops, etc).
// So if CheckBlock failed, #3 is the only possibility.
- // Under BIP 152, we don't DoS-ban unless proof of work is
+ // Under BIP 152, we don't discourage the peer unless proof of work is
// invalid (we don't require all the stateless checks to have
// been run). This is handled below, so just treat this as
// though the block was successfully read, and rely on the
@@ -3092,7 +3239,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// BIP 152 permits peers to relay compact blocks after validating
// the header only; we should not punish peers if the block turns
// out to be invalid.
- mapBlockSource.emplace(resp.blockhash, std::make_pair(pfrom->GetId(), false));
+ mapBlockSource.emplace(resp.blockhash, std::make_pair(pfrom.GetId(), false));
}
} // Don't hold cs_main when we call into ProcessNewBlock
if (fBlockRead) {
@@ -3103,23 +3250,23 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// disk-space attacks), but this should be safe due to the
// protections in the compact block handler -- see related comment
// in compact block optimistic reconstruction handling.
- ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
+ chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
- pfrom->nLastBlockTime = GetTime();
+ pfrom.nLastBlockTime = GetTime();
} else {
LOCK(cs_main);
mapBlockSource.erase(pblock->GetHash());
}
}
- return true;
+ return;
}
if (msg_type == 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;
+ LogPrint(BCLog::NET, "Unexpected headers message received from peer %d\n", pfrom.GetId());
+ return;
}
std::vector<CBlockHeader> headers;
@@ -3128,8 +3275,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
unsigned int nCount = ReadCompactSize(vRecv);
if (nCount > MAX_HEADERS_RESULTS) {
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20, strprintf("headers message size = %u", nCount));
- return false;
+ Misbehaving(pfrom.GetId(), 20, strprintf("headers message size = %u", nCount));
+ return;
}
headers.resize(nCount);
for (unsigned int n = 0; n < nCount; n++) {
@@ -3137,21 +3284,21 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
}
- return ProcessHeadersMessage(pfrom, connman, mempool, headers, chainparams, /*via_compact_block=*/false);
+ return ProcessHeadersMessage(pfrom, connman, chainman, mempool, headers, chainparams, /*via_compact_block=*/false);
}
if (msg_type == 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;
+ LogPrint(BCLog::NET, "Unexpected block message received from peer %d\n", pfrom.GetId());
+ return;
}
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
vRecv >> *pblock;
- LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom.GetId());
bool forceProcessing = false;
const uint256 hash(pblock->GetHash());
@@ -3163,17 +3310,17 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// mapBlockSource is only used for punishing peers and setting
// which peers send us compact blocks, so the race between here and
// cs_main in ProcessNewBlock is fine.
- mapBlockSource.emplace(hash, std::make_pair(pfrom->GetId(), true));
+ mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true));
}
bool fNewBlock = false;
- ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock);
+ chainman.ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock);
if (fNewBlock) {
- pfrom->nLastBlockTime = GetTime();
+ pfrom.nLastBlockTime = GetTime();
} else {
LOCK(cs_main);
mapBlockSource.erase(pblock->GetHash());
}
- return true;
+ return;
}
if (msg_type == NetMsgType::GETADDR) {
@@ -3182,64 +3329,65 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// to users' AddrMan and later request them by sending getaddr messages.
// Making nodes which are behind NAT and can only make outgoing connections ignore
// the getaddr message mitigates the attack.
- if (!pfrom->fInbound) {
- LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->GetId());
- return true;
+ if (!pfrom.fInbound) {
+ LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom.GetId());
+ return;
}
- if (!pfrom->IsAddrRelayPeer()) {
- LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom->GetId());
- return true;
+ if (!pfrom.IsAddrRelayPeer()) {
+ LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom.GetId());
+ return;
}
// Only send one GetAddr response per connection to reduce resource waste
// and discourage addr stamping of INV announcements.
- if (pfrom->fSentAddr) {
- LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->GetId());
- return true;
+ if (pfrom.fSentAddr) {
+ LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", pfrom.GetId());
+ return;
}
- pfrom->fSentAddr = true;
+ pfrom.fSentAddr = true;
- pfrom->vAddrToSend.clear();
- std::vector<CAddress> vAddr = connman->GetAddresses();
+ pfrom.vAddrToSend.clear();
+ std::vector<CAddress> vAddr = connman.GetAddresses();
FastRandomContext insecure_rand;
for (const CAddress &addr : vAddr) {
- if (!banman->IsBanned(addr)) {
- pfrom->PushAddress(addr, insecure_rand);
+ bool banned_or_discouraged = banman && (banman->IsDiscouraged(addr) || banman->IsBanned(addr));
+ if (!banned_or_discouraged) {
+ pfrom.PushAddress(addr, insecure_rand);
}
}
- return true;
+ return;
}
if (msg_type == NetMsgType::MEMPOOL) {
- if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->HasPermission(PF_MEMPOOL))
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM) && !pfrom.HasPermission(PF_MEMPOOL))
{
- if (!pfrom->HasPermission(PF_NOBAN))
+ if (!pfrom.HasPermission(PF_NOBAN))
{
- LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
}
- return true;
+ return;
}
- if (connman->OutboundTargetReached(false) && !pfrom->HasPermission(PF_MEMPOOL))
+ if (connman.OutboundTargetReached(false) && !pfrom.HasPermission(PF_MEMPOOL))
{
- if (!pfrom->HasPermission(PF_NOBAN))
+ if (!pfrom.HasPermission(PF_NOBAN))
{
- LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
}
- return true;
+ return;
}
- if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_tx_inventory);
- pfrom->m_tx_relay->fSendMempool = true;
+ if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_tx_inventory);
+ pfrom.m_tx_relay->fSendMempool = true;
}
- return true;
+ return;
}
if (msg_type == NetMsgType::PING) {
- if (pfrom->nVersion > BIP0031_VERSION)
+ if (pfrom.nVersion > BIP0031_VERSION)
{
uint64_t nonce = 0;
vRecv >> nonce;
@@ -3254,13 +3402,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// it, if the remote node sends a ping once per second and this node takes 5
// seconds to respond to each, the 5th ping the remote sends would appear to
// return very quickly.
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
+ connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
}
- return true;
+ return;
}
if (msg_type == NetMsgType::PONG) {
- int64_t pingUsecEnd = nTimeReceived;
+ const auto ping_end = time_received;
uint64_t nonce = 0;
size_t nAvail = vRecv.in_avail();
bool bPingFinished = false;
@@ -3270,15 +3418,15 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
vRecv >> nonce;
// Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
- if (pfrom->nPingNonceSent != 0) {
- if (nonce == pfrom->nPingNonceSent) {
+ if (pfrom.nPingNonceSent != 0) {
+ if (nonce == pfrom.nPingNonceSent) {
// Matching pong received, this ping is no longer outstanding
bPingFinished = true;
- int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart;
- if (pingUsecTime > 0) {
+ const auto ping_time = ping_end - pfrom.m_ping_start.load();
+ if (ping_time.count() > 0) {
// Successful ping time measurement, replace previous
- pfrom->nPingUsecTime = pingUsecTime;
- pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime.load(), pingUsecTime);
+ pfrom.nPingUsecTime = count_microseconds(ping_time);
+ pfrom.nMinPingUsecTime = std::min(pfrom.nMinPingUsecTime.load(), count_microseconds(ping_time));
} else {
// This should never happen
sProblem = "Timing mishap";
@@ -3303,19 +3451,23 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (!(sProblem.empty())) {
LogPrint(BCLog::NET, "pong peer=%d: %s, %x expected, %x received, %u bytes\n",
- pfrom->GetId(),
+ pfrom.GetId(),
sProblem,
- pfrom->nPingNonceSent,
+ pfrom.nPingNonceSent,
nonce,
nAvail);
}
if (bPingFinished) {
- pfrom->nPingNonceSent = 0;
+ pfrom.nPingNonceSent = 0;
}
- return true;
+ return;
}
if (msg_type == NetMsgType::FILTERLOAD) {
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM)) {
+ pfrom.fDisconnect = true;
+ return;
+ }
CBloomFilter filter;
vRecv >> filter;
@@ -3323,18 +3475,22 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
{
// There is no excuse for sending a too-large filter
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
+ Misbehaving(pfrom.GetId(), 100);
}
- else if (pfrom->m_tx_relay != nullptr)
+ else if (pfrom.m_tx_relay != nullptr)
{
- LOCK(pfrom->m_tx_relay->cs_filter);
- pfrom->m_tx_relay->pfilter.reset(new CBloomFilter(filter));
- pfrom->m_tx_relay->fRelayTxes = true;
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ pfrom.m_tx_relay->pfilter.reset(new CBloomFilter(filter));
+ pfrom.m_tx_relay->fRelayTxes = true;
}
- return true;
+ return;
}
if (msg_type == NetMsgType::FILTERADD) {
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM)) {
+ pfrom.fDisconnect = true;
+ return;
+ }
std::vector<unsigned char> vData;
vRecv >> vData;
@@ -3343,55 +3499,67 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
bool bad = false;
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
bad = true;
- } else if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_filter);
- if (pfrom->m_tx_relay->pfilter) {
- pfrom->m_tx_relay->pfilter->insert(vData);
+ } else if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ if (pfrom.m_tx_relay->pfilter) {
+ pfrom.m_tx_relay->pfilter->insert(vData);
} else {
bad = true;
}
}
if (bad) {
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
+ Misbehaving(pfrom.GetId(), 100);
}
- return true;
+ return;
}
if (msg_type == NetMsgType::FILTERCLEAR) {
- if (pfrom->m_tx_relay == nullptr) {
- return true;
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM)) {
+ pfrom.fDisconnect = true;
+ return;
}
- LOCK(pfrom->m_tx_relay->cs_filter);
- if (pfrom->GetLocalServices() & NODE_BLOOM) {
- pfrom->m_tx_relay->pfilter = nullptr;
+ if (pfrom.m_tx_relay == nullptr) {
+ return;
}
- pfrom->m_tx_relay->fRelayTxes = true;
- return true;
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ pfrom.m_tx_relay->pfilter = nullptr;
+ pfrom.m_tx_relay->fRelayTxes = true;
+ return;
}
if (msg_type == NetMsgType::FEEFILTER) {
CAmount newFeeFilter = 0;
vRecv >> newFeeFilter;
if (MoneyRange(newFeeFilter)) {
- if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_feeFilter);
- pfrom->m_tx_relay->minFeeFilter = newFeeFilter;
+ if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_feeFilter);
+ pfrom.m_tx_relay->minFeeFilter = newFeeFilter;
}
- LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom.GetId());
}
- return true;
+ return;
+ }
+
+ if (msg_type == NetMsgType::GETCFILTERS) {
+ ProcessGetCFilters(pfrom, vRecv, chainparams, connman);
+ return;
+ }
+
+ if (msg_type == NetMsgType::GETCFHEADERS) {
+ ProcessGetCFHeaders(pfrom, vRecv, chainparams, connman);
+ return;
}
if (msg_type == NetMsgType::GETCFCHECKPT) {
ProcessGetCFCheckPt(pfrom, vRecv, chainparams, connman);
- return true;
+ return;
}
if (msg_type == NetMsgType::NOTFOUND) {
// Remove the NOTFOUND transactions from the peer
LOCK(cs_main);
- CNodeState *state = State(pfrom->GetId());
+ 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) {
@@ -3410,35 +3578,36 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
}
}
}
- return true;
+ return;
}
// Ignore unknown commands for extensibility
- LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(msg_type), pfrom->GetId());
- return true;
+ LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(msg_type), pfrom.GetId());
+ return;
}
-bool PeerLogicValidation::CheckIfBanned(CNode* pnode)
+bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode)
{
AssertLockHeld(cs_main);
- CNodeState &state = *State(pnode->GetId());
-
- if (state.fShouldBan) {
- state.fShouldBan = false;
- if (pnode->HasPermission(PF_NOBAN))
- LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode->addr.ToString());
- else if (pnode->m_manual_connection)
- LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode->addr.ToString());
- else if (pnode->addr.IsLocal()) {
- // Disconnect but don't ban _this_ local node
- LogPrintf("Warning: disconnecting but not banning local peer %s!\n", pnode->addr.ToString());
- pnode->fDisconnect = true;
+ CNodeState &state = *State(pnode.GetId());
+
+ if (state.m_should_discourage) {
+ state.m_should_discourage = false;
+ if (pnode.HasPermission(PF_NOBAN)) {
+ LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode.addr.ToString());
+ } else if (pnode.m_manual_connection) {
+ LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode.addr.ToString());
+ } else if (pnode.addr.IsLocal()) {
+ // Disconnect but don't discourage this local node
+ LogPrintf("Warning: disconnecting but not discouraging local peer %s!\n", pnode.addr.ToString());
+ pnode.fDisconnect = true;
} else {
- // Disconnect and ban all nodes sharing the address
+ // Disconnect and discourage all nodes sharing the address
+ LogPrintf("Disconnecting and discouraging peer %s!\n", pnode.addr.ToString());
if (m_banman) {
- m_banman->Ban(pnode->addr, BanReasonNodeMisbehaving);
+ m_banman->Discourage(pnode.addr);
}
- connman->DisconnectNode(pnode->addr);
+ connman->DisconnectNode(pnode.addr);
}
return true;
}
@@ -3459,12 +3628,12 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
bool fMoreWork = false;
if (!pfrom->vRecvGetData.empty())
- ProcessGetData(pfrom, chainparams, connman, m_mempool, interruptMsgProc);
+ ProcessGetData(*pfrom, chainparams, *connman, m_mempool, interruptMsgProc);
if (!pfrom->orphan_work_set.empty()) {
std::list<CTransactionRef> removed_txn;
LOCK2(cs_main, g_cs_orphans);
- ProcessOrphanTx(connman, m_mempool, pfrom->orphan_work_set, removed_txn);
+ ProcessOrphanTx(*connman, m_mempool, pfrom->orphan_work_set, removed_txn);
for (const CTransactionRef& removedTx : removed_txn) {
AddToCompactExtraTransactions(removedTx);
}
@@ -3523,11 +3692,8 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
return fMoreWork;
}
- // Process message
- bool fRet = false;
- try
- {
- fRet = ProcessMessage(pfrom, msg_type, vRecv, msg.m_time, chainparams, m_mempool, connman, m_banman, interruptMsgProc);
+ try {
+ ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, chainparams, m_chainman, m_mempool, *connman, m_banman, interruptMsgProc);
if (interruptMsgProc)
return false;
if (!pfrom->vRecvGetData.empty())
@@ -3538,22 +3704,18 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
LogPrint(BCLog::NET, "%s(%s, %u bytes): Unknown exception caught\n", __func__, SanitizeString(msg_type), nMessageSize);
}
- if (!fRet) {
- LogPrint(BCLog::NET, "%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(msg_type), nMessageSize, pfrom->GetId());
- }
-
LOCK(cs_main);
- CheckIfBanned(pfrom);
+ MaybeDiscourageAndDisconnect(*pfrom);
return fMoreWork;
}
-void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
+void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
{
AssertLockHeld(cs_main);
- CNodeState &state = *State(pto->GetId());
- const CNetMsgMaker msgMaker(pto->GetSendVersion());
+ CNodeState &state = *State(pto.GetId());
+ const CNetMsgMaker msgMaker(pto.GetSendVersion());
if (!state.m_chain_sync.m_protect && IsOutboundDisconnectionCandidate(pto) && state.fSyncStarted) {
// This is an outbound peer subject to disconnection if they don't
@@ -3582,12 +3744,12 @@ void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
// message to give the peer a chance to update us.
if (state.m_chain_sync.m_sent_getheaders) {
// They've run out of time to catch up!
- LogPrintf("Disconnecting outbound peer %d for old chain, best known block = %s\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>");
- pto->fDisconnect = true;
+ LogPrintf("Disconnecting outbound peer %d for old chain, best known block = %s\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>");
+ pto.fDisconnect = true;
} else {
assert(state.m_chain_sync.m_work_header);
- LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
+ LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
+ connman->PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
state.m_chain_sync.m_sent_getheaders = true;
constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes
// Bump the timeout to allow a response, which could clear the timeout
@@ -3617,7 +3779,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
AssertLockHeld(cs_main);
// Ignore non-outbound peers, or nodes marked for disconnect already
- if (!IsOutboundDisconnectionCandidate(pnode) || pnode->fDisconnect) return;
+ if (!IsOutboundDisconnectionCandidate(*pnode) || pnode->fDisconnect) return;
CNodeState *state = State(pnode->GetId());
if (state == nullptr) return; // shouldn't be possible, but just in case
// Don't evict our protected peers
@@ -3721,7 +3883,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// RPC ping request by user
pingSend = true;
}
- if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) {
+ if (pto->nPingNonceSent == 0 && pto->m_ping_start.load() + PING_INTERVAL < GetTime<std::chrono::microseconds>()) {
// Ping automatically sent as a latency probe & keepalive.
pingSend = true;
}
@@ -3731,7 +3893,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
}
pto->fPingQueued = false;
- pto->nPingUsecStart = GetTimeMicros();
+ pto->m_ping_start = GetTime<std::chrono::microseconds>();
if (pto->nVersion > BIP0031_VERSION) {
pto->nPingNonceSent = nonce;
connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce));
@@ -3746,7 +3908,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (!lockMain)
return true;
- if (CheckIfBanned(pto)) return true;
+ if (MaybeDiscourageAndDisconnect(*pto)) return true;
CNodeState &state = *State(pto->GetId());
@@ -3944,7 +4106,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// If the peer's chain has this block, don't inv it back.
if (!PeerHasHeader(&state, pindex)) {
- pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce));
+ pto->vInventoryBlockToSend.push_back(hashToAnnounce);
LogPrint(BCLog::NET, "%s: sending inv peer=%d hash=%s\n", __func__,
pto->GetId(), hashToAnnounce.ToString());
}
@@ -4015,6 +4177,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (!pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
}
pto->m_tx_relay->filterInventoryKnown.insert(hash);
+ // Responses to MEMPOOL requests bypass the m_recently_announced_invs filter.
vInv.push_back(inv);
if (vInv.size() == MAX_INV_SZ) {
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
@@ -4068,6 +4231,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
// Send
+ State(pto->GetId())->m_recently_announced_invs.insert(hash);
vInv.push_back(CInv(MSG_TX, hash));
nRelayedTransactions++;
{
@@ -4125,9 +4289,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Check for headers sync timeouts
if (state.fSyncStarted && state.nHeadersSyncTimeout < std::numeric_limits<int64_t>::max()) {
// Detect whether this is a stalling initial-headers-sync peer
- if (pindexBestHeader->GetBlockTime() <= GetAdjustedTime() - 24*60*60) {
+ if (pindexBestHeader->GetBlockTime() <= GetAdjustedTime() - 24 * 60 * 60) {
if (nNow > state.nHeadersSyncTimeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) {
- // Disconnect a (non-whitelisted) peer if it is our only sync peer,
+ // Disconnect a peer (without the noban permission) if it is our only sync peer,
// and we have others we could be using instead.
// Note: If all our peers are inbound, then we won't
// disconnect our sync peer for stalling; we have bigger
@@ -4137,7 +4301,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
pto->fDisconnect = true;
return true;
} else {
- LogPrintf("Timeout downloading headers from whitelisted peer=%d, not disconnecting\n", pto->GetId());
+ LogPrintf("Timeout downloading headers from noban peer=%d, not disconnecting\n", pto->GetId());
// Reset the headers sync state so that we have a
// chance to try downloading from a different peer.
// Note: this will also result in at least one more
@@ -4157,7 +4321,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Check that outbound peers have reasonable chains
// GetTime() is used by this anti-DoS logic so we can test this using mocktime
- ConsiderEviction(pto, GetTime());
+ ConsiderEviction(*pto, GetTime());
//
// Message: getdata (blocks)
@@ -4168,7 +4332,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
for (const CBlockIndex *pindex : vToDownload) {
- uint32_t nFetchFlags = GetFetchFlags(pto);
+ uint32_t nFetchFlags = GetFetchFlags(*pto);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
MarkBlockAsInFlight(m_mempool, pto->GetId(), pindex->GetBlockHash(), pindex);
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
@@ -4212,7 +4376,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// 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);
+ CInv inv(MSG_TX | GetFetchFlags(*pto), txid);
if (!AlreadyHave(inv, m_mempool)) {
// If this transaction was last requested more than 1 minute ago,
// then request.
@@ -4248,15 +4412,26 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
//
// Message: feefilter
//
- // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
if (pto->m_tx_relay != nullptr && pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
- !pto->HasPermission(PF_FORCERELAY)) {
+ !pto->HasPermission(PF_FORCERELAY) // peers with the forcerelay permission should not filter txs to us
+ ) {
CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
int64_t timeNow = GetTimeMicros();
+ static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}};
+ if (m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
+ // Received tx-inv messages are discarded when the active
+ // chainstate is in IBD, so tell the peer to not send them.
+ currentFilter = MAX_MONEY;
+ } else {
+ static const CAmount MAX_FILTER{g_filter_rounder.round(MAX_MONEY)};
+ if (pto->m_tx_relay->lastSentFeeFilter == MAX_FILTER) {
+ // Send the current filter if we sent MAX_FILTER previously
+ // and made it out of IBD.
+ pto->m_tx_relay->nextSendTimeFeeFilter = timeNow - 1;
+ }
+ }
if (timeNow > pto->m_tx_relay->nextSendTimeFeeFilter) {
- static CFeeRate default_feerate(DEFAULT_MIN_RELAY_TX_FEE);
- static FeeFilterRounder filterRounder(default_feerate);
- CAmount filterToSend = filterRounder.round(currentFilter);
+ CAmount filterToSend = g_filter_rounder.round(currentFilter);
// We always have a fee filter of at least minRelayTxFee
filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
if (filterToSend != pto->m_tx_relay->lastSentFeeFilter) {
diff --git a/src/net_processing.h b/src/net_processing.h
index 4033c85d07..fa1555fbe6 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -12,6 +12,7 @@
#include <validationinterface.h>
class CTxMemPool;
+class ChainstateManager;
extern RecursiveMutex cs_main;
extern RecursiveMutex g_cs_orphans;
@@ -22,17 +23,21 @@ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100;
static const bool DEFAULT_PEERBLOOMFILTERS = false;
static const bool DEFAULT_PEERBLOCKFILTERS = false;
+/** Threshold for marking a node to be discouraged, e.g. disconnected and added to the discouragement filter. */
+static const int DISCOURAGEMENT_THRESHOLD{100};
class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface {
private:
CConnman* const connman;
+ /** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */
BanMan* const m_banman;
+ ChainstateManager& m_chainman;
CTxMemPool& m_mempool;
- bool CheckIfBanned(CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool MaybeDiscourageAndDisconnect(CNode& pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
public:
- PeerLogicValidation(CConnman* connman, BanMan* banman, CScheduler& scheduler, CTxMemPool& pool);
+ PeerLogicValidation(CConnman* connman, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool);
/**
* Overridden from CValidationInterface.
@@ -72,7 +77,7 @@ public:
bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing);
/** Consider evicting an outbound peer based on the amount of time they've been behind our tip */
- void ConsiderEviction(CNode *pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ConsiderEviction(CNode& pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound */
void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams);
/** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index f79425a52e..0aaba440b8 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -3,6 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <cstdint>
#include <netaddress.h>
#include <hash.h>
#include <util/strencodings.h>
@@ -627,15 +628,15 @@ CService::CService() : port(0)
{
}
-CService::CService(const CNetAddr& cip, unsigned short portIn) : CNetAddr(cip), port(portIn)
+CService::CService(const CNetAddr& cip, uint16_t portIn) : CNetAddr(cip), port(portIn)
{
}
-CService::CService(const struct in_addr& ipv4Addr, unsigned short portIn) : CNetAddr(ipv4Addr), port(portIn)
+CService::CService(const struct in_addr& ipv4Addr, uint16_t portIn) : CNetAddr(ipv4Addr), port(portIn)
{
}
-CService::CService(const struct in6_addr& ipv6Addr, unsigned short portIn) : CNetAddr(ipv6Addr), port(portIn)
+CService::CService(const struct in6_addr& ipv6Addr, uint16_t portIn) : CNetAddr(ipv6Addr), port(portIn)
{
}
@@ -663,7 +664,7 @@ bool CService::SetSockAddr(const struct sockaddr *paddr)
}
}
-unsigned short CService::GetPort() const
+uint16_t CService::GetPort() const
{
return port;
}
@@ -725,12 +726,10 @@ bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
*/
std::vector<unsigned char> CService::GetKey() const
{
- std::vector<unsigned char> vKey;
- vKey.resize(18);
- memcpy(vKey.data(), ip, 16);
- vKey[16] = port / 0x100; // most significant byte of our port
- vKey[17] = port & 0x0FF; // least significant byte of our port
- return vKey;
+ auto key = GetAddrBytes();
+ key.push_back(port / 0x100); // most significant byte of our port
+ key.push_back(port & 0x0FF); // least significant byte of our port
+ return key;
}
std::string CService::ToStringPort() const
diff --git a/src/netaddress.h b/src/netaddress.h
index d8f19deffe..f2daad7fb6 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -12,7 +12,7 @@
#include <compat.h>
#include <serialize.h>
-#include <stdint.h>
+#include <cstdint>
#include <string>
#include <vector>
@@ -90,6 +90,7 @@ class CNetAddr
uint32_t GetMappedAS(const std::vector<bool> &asmap) const;
std::vector<unsigned char> GetGroup(const std::vector<bool> &asmap) const;
+ std::vector<unsigned char> GetAddrBytes() const { return {std::begin(ip), std::end(ip)}; }
int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const;
explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
@@ -99,12 +100,7 @@ class CNetAddr
friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); }
friend bool operator<(const CNetAddr& a, const CNetAddr& b);
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(ip);
- }
+ SERIALIZE_METHODS(CNetAddr, obj) { READWRITE(obj.ip); }
friend class CSubNet;
};
@@ -136,14 +132,7 @@ class CSubNet
friend bool operator!=(const CSubNet& a, const CSubNet& b) { return !(a == b); }
friend bool operator<(const CSubNet& a, const CSubNet& b);
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(network);
- READWRITE(netmask);
- READWRITE(valid);
- }
+ SERIALIZE_METHODS(CSubNet, obj) { READWRITE(obj.network, obj.netmask, obj.valid); }
};
/** A combination of a network address (CNetAddr) and a (TCP) port */
@@ -154,10 +143,10 @@ class CService : public CNetAddr
public:
CService();
- CService(const CNetAddr& ip, unsigned short port);
- CService(const struct in_addr& ipv4Addr, unsigned short port);
+ CService(const CNetAddr& ip, uint16_t port);
+ CService(const struct in_addr& ipv4Addr, uint16_t port);
explicit CService(const struct sockaddr_in& addr);
- unsigned short GetPort() const;
+ uint16_t GetPort() const;
bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const;
bool SetSockAddr(const struct sockaddr* paddr);
friend bool operator==(const CService& a, const CService& b);
@@ -168,15 +157,13 @@ class CService : public CNetAddr
std::string ToStringPort() const;
std::string ToStringIPPort() const;
- CService(const struct in6_addr& ipv6Addr, unsigned short port);
+ CService(const struct in6_addr& ipv6Addr, uint16_t port);
explicit CService(const struct sockaddr_in6& addr);
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(ip);
- READWRITE(WrapBigEndian(port));
+ SERIALIZE_METHODS(CService, obj)
+ {
+ READWRITEAS(CNetAddr, obj);
+ READWRITE(Using<BigEndianFormatter<2>>(obj.port));
}
};
diff --git a/src/netbase.cpp b/src/netbase.cpp
index a70179cb16..3a3b5f3e66 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -12,6 +12,7 @@
#include <util/system.h>
#include <atomic>
+#include <cstdint>
#ifndef WIN32
#include <fcntl.h>
@@ -28,9 +29,9 @@
#endif
// Settings
-static RecursiveMutex cs_proxyInfos;
-static proxyType proxyInfo[NET_MAX] GUARDED_BY(cs_proxyInfos);
-static proxyType nameProxy GUARDED_BY(cs_proxyInfos);
+static Mutex g_proxyinfo_mutex;
+static proxyType proxyInfo[NET_MAX] GUARDED_BY(g_proxyinfo_mutex);
+static proxyType nameProxy GUARDED_BY(g_proxyinfo_mutex);
int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
bool fNameLookup = DEFAULT_NAME_LOOKUP;
@@ -711,14 +712,14 @@ bool SetProxy(enum Network net, const proxyType &addrProxy) {
assert(net >= 0 && net < NET_MAX);
if (!addrProxy.IsValid())
return false;
- LOCK(cs_proxyInfos);
+ LOCK(g_proxyinfo_mutex);
proxyInfo[net] = addrProxy;
return true;
}
bool GetProxy(enum Network net, proxyType &proxyInfoOut) {
assert(net >= 0 && net < NET_MAX);
- LOCK(cs_proxyInfos);
+ LOCK(g_proxyinfo_mutex);
if (!proxyInfo[net].IsValid())
return false;
proxyInfoOut = proxyInfo[net];
@@ -744,13 +745,13 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut) {
bool SetNameProxy(const proxyType &addrProxy) {
if (!addrProxy.IsValid())
return false;
- LOCK(cs_proxyInfos);
+ LOCK(g_proxyinfo_mutex);
nameProxy = addrProxy;
return true;
}
bool GetNameProxy(proxyType &nameProxyOut) {
- LOCK(cs_proxyInfos);
+ LOCK(g_proxyinfo_mutex);
if(!nameProxy.IsValid())
return false;
nameProxyOut = nameProxy;
@@ -758,12 +759,12 @@ bool GetNameProxy(proxyType &nameProxyOut) {
}
bool HaveNameProxy() {
- LOCK(cs_proxyInfos);
+ LOCK(g_proxyinfo_mutex);
return nameProxy.IsValid();
}
bool IsProxy(const CNetAddr &addr) {
- LOCK(cs_proxyInfos);
+ LOCK(g_proxyinfo_mutex);
for (int i = 0; i < NET_MAX; i++) {
if (addr == static_cast<CNetAddr>(proxyInfo[i].proxy))
return true;
@@ -798,11 +799,11 @@ bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int
ProxyCredentials random_auth;
static std::atomic_int counter(0);
random_auth.username = random_auth.password = strprintf("%i", counter++);
- if (!Socks5(strDest, (unsigned short)port, &random_auth, hSocket)) {
+ if (!Socks5(strDest, (uint16_t)port, &random_auth, hSocket)) {
return false;
}
} else {
- if (!Socks5(strDest, (unsigned short)port, 0, hSocket)) {
+ if (!Socks5(strDest, (uint16_t)port, 0, hSocket)) {
return false;
}
}
diff --git a/src/netmessagemaker.h b/src/netmessagemaker.h
index 2efb384a7b..ffb3fe2f29 100644
--- a/src/netmessagemaker.h
+++ b/src/netmessagemaker.h
@@ -15,18 +15,18 @@ public:
explicit CNetMsgMaker(int nVersionIn) : nVersion(nVersionIn){}
template <typename... Args>
- CSerializedNetMsg Make(int nFlags, std::string sCommand, Args&&... args) const
+ CSerializedNetMsg Make(int nFlags, std::string msg_type, Args&&... args) const
{
CSerializedNetMsg msg;
- msg.command = std::move(sCommand);
+ msg.m_type = std::move(msg_type);
CVectorWriter{ SER_NETWORK, nFlags | nVersion, msg.data, 0, std::forward<Args>(args)... };
return msg;
}
template <typename... Args>
- CSerializedNetMsg Make(std::string sCommand, Args&&... args) const
+ CSerializedNetMsg Make(std::string msg_type, Args&&... args) const
{
- return Make(0, std::move(sCommand), std::forward<Args>(args)...);
+ return Make(0, std::move(msg_type), std::forward<Args>(args)...);
}
private:
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index ec52a08ace..fb46ea1731 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -8,13 +8,23 @@
#include <coins.h>
#include <hash.h>
#include <serialize.h>
-#include <validation.h>
#include <uint256.h>
#include <util/system.h>
+#include <validation.h>
#include <map>
-static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
+static uint64_t GetBogoSize(const CScript& scriptPubKey)
+{
+ return 32 /* txid */ +
+ 4 /* vout index */ +
+ 4 /* height + coinbase */ +
+ 8 /* amount */ +
+ 2 /* scriptPubKey len */ +
+ scriptPubKey.size() /* scriptPubKey */;
+}
+
+static void ApplyStats(CCoinsStats& stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
{
assert(!outputs.empty());
ss << hash;
@@ -26,34 +36,47 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash,
ss << VARINT_MODE(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
stats.nTransactionOutputs++;
stats.nTotalAmount += output.second.out.nValue;
- stats.nBogoSize += 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + 8 /* amount */ +
- 2 /* scriptPubKey len */ + output.second.out.scriptPubKey.size() /* scriptPubKey */;
+ stats.nBogoSize += GetBogoSize(output.second.out.scriptPubKey);
}
ss << VARINT(0u);
}
+static void ApplyStats(CCoinsStats& stats, std::nullptr_t, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
+{
+ assert(!outputs.empty());
+ stats.nTransactions++;
+ for (const auto& output : outputs) {
+ stats.nTransactionOutputs++;
+ stats.nTotalAmount += output.second.out.nValue;
+ stats.nBogoSize += GetBogoSize(output.second.out.scriptPubKey);
+ }
+}
+
//! Calculate statistics about the unspent transaction output set
-bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
+template <typename T>
+static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
{
stats = CCoinsStats();
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
assert(pcursor);
- CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
stats.hashBlock = pcursor->GetBestBlock();
{
LOCK(cs_main);
stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight;
}
- ss << stats.hashBlock;
+
+ PrepareHash(hash_obj, stats);
+
uint256 prevkey;
std::map<uint32_t, Coin> outputs;
while (pcursor->Valid()) {
+ interruption_point();
COutPoint key;
Coin coin;
if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
if (!outputs.empty() && key.hash != prevkey) {
- ApplyStats(stats, ss, prevkey, outputs);
+ ApplyStats(stats, hash_obj, prevkey, outputs);
outputs.clear();
}
prevkey = key.hash;
@@ -65,9 +88,38 @@ bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
pcursor->Next();
}
if (!outputs.empty()) {
- ApplyStats(stats, ss, prevkey, outputs);
+ ApplyStats(stats, hash_obj, prevkey, outputs);
}
- stats.hashSerialized = ss.GetHash();
+
+ FinalizeHash(hash_obj, stats);
+
stats.nDiskSize = view->EstimateSize();
return true;
}
+
+bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, CoinStatsHashType hash_type, const std::function<void()>& interruption_point)
+{
+ switch (hash_type) {
+ case(CoinStatsHashType::HASH_SERIALIZED): {
+ CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
+ return GetUTXOStats(view, stats, ss, interruption_point);
+ }
+ case(CoinStatsHashType::NONE): {
+ return GetUTXOStats(view, stats, nullptr, interruption_point);
+ }
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
+}
+
+// The legacy hash serializes the hashBlock
+static void PrepareHash(CHashWriter& ss, CCoinsStats& stats)
+{
+ ss << stats.hashBlock;
+}
+static void PrepareHash(std::nullptr_t, CCoinsStats& stats) {}
+
+static void FinalizeHash(CHashWriter& ss, CCoinsStats& stats)
+{
+ stats.hashSerialized = ss.GetHash();
+}
+static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
diff --git a/src/node/coinstats.h b/src/node/coinstats.h
index a19af0fd1b..2a7441c10e 100644
--- a/src/node/coinstats.h
+++ b/src/node/coinstats.h
@@ -10,9 +10,15 @@
#include <uint256.h>
#include <cstdint>
+#include <functional>
class CCoinsView;
+enum class CoinStatsHashType {
+ HASH_SERIALIZED,
+ NONE,
+};
+
struct CCoinsStats
{
int nHeight{0};
@@ -29,6 +35,6 @@ struct CCoinsStats
};
//! Calculate statistics about the unspent transaction output set
-bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats);
+bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, const CoinStatsHashType hash_type, const std::function<void()>& interruption_point = {});
#endif // BITCOIN_NODE_COINSTATS_H
diff --git a/src/node/context.h b/src/node/context.h
index 566ff170be..be568cba36 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_NODE_CONTEXT_H
#define BITCOIN_NODE_CONTEXT_H
+#include <cassert>
+#include <functional>
#include <memory>
#include <vector>
@@ -13,6 +15,7 @@ class BanMan;
class CConnman;
class CScheduler;
class CTxMemPool;
+class ChainstateManager;
class PeerLogicValidation;
namespace interfaces {
class Chain;
@@ -33,11 +36,13 @@ struct NodeContext {
std::unique_ptr<CConnman> connman;
CTxMemPool* mempool{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<PeerLogicValidation> peer_logic;
+ ChainstateManager* chainman{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<BanMan> banman;
ArgsManager* args{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<interfaces::Chain> chain;
std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients;
std::unique_ptr<CScheduler> scheduler;
+ std::function<void()> rpc_interruption_point = [] {};
//! Declare default constructor and destructor that are not inline, so code
//! instantiating the NodeContext struct doesn't need to #include class
diff --git a/src/ui_interface.cpp b/src/node/ui_interface.cpp
index bb41154afc..8d3665975d 100644
--- a/src/ui_interface.cpp
+++ b/src/node/ui_interface.cpp
@@ -2,18 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <ui_interface.h>
+#include <node/ui_interface.h>
#include <util/translation.h>
-#include <boost/signals2/last_value.hpp>
+#include <boost/signals2/optional_last_value.hpp>
#include <boost/signals2/signal.hpp>
CClientUIInterface uiInterface;
struct UISignals {
- boost::signals2::signal<CClientUIInterface::ThreadSafeMessageBoxSig, boost::signals2::last_value<bool>> ThreadSafeMessageBox;
- boost::signals2::signal<CClientUIInterface::ThreadSafeQuestionSig, boost::signals2::last_value<bool>> ThreadSafeQuestion;
+ boost::signals2::signal<CClientUIInterface::ThreadSafeMessageBoxSig, boost::signals2::optional_last_value<bool>> ThreadSafeMessageBox;
+ boost::signals2::signal<CClientUIInterface::ThreadSafeQuestionSig, boost::signals2::optional_last_value<bool>> ThreadSafeQuestion;
boost::signals2::signal<CClientUIInterface::InitMessageSig> InitMessage;
boost::signals2::signal<CClientUIInterface::NotifyNumConnectionsChangedSig> NotifyNumConnectionsChanged;
boost::signals2::signal<CClientUIInterface::NotifyNetworkActiveChangedSig> NotifyNetworkActiveChanged;
@@ -42,15 +42,15 @@ ADD_SIGNALS_IMPL_WRAPPER(NotifyBlockTip);
ADD_SIGNALS_IMPL_WRAPPER(NotifyHeaderTip);
ADD_SIGNALS_IMPL_WRAPPER(BannedListChanged);
-bool CClientUIInterface::ThreadSafeMessageBox(const bilingual_str& message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeMessageBox(message, caption, style); }
-bool CClientUIInterface::ThreadSafeQuestion(const bilingual_str& message, const std::string& non_interactive_message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeQuestion(message, non_interactive_message, caption, style); }
+bool CClientUIInterface::ThreadSafeMessageBox(const bilingual_str& message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeMessageBox(message, caption, style).value_or(false);}
+bool CClientUIInterface::ThreadSafeQuestion(const bilingual_str& message, const std::string& non_interactive_message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeQuestion(message, non_interactive_message, caption, style).value_or(false);}
void CClientUIInterface::InitMessage(const std::string& message) { return g_ui_signals.InitMessage(message); }
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::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); }
+void CClientUIInterface::NotifyBlockTip(SynchronizationState s, const CBlockIndex* i) { return g_ui_signals.NotifyBlockTip(s, i); }
+void CClientUIInterface::NotifyHeaderTip(SynchronizationState s, const CBlockIndex* i) { return g_ui_signals.NotifyHeaderTip(s, i); }
void CClientUIInterface::BannedListChanged() { return g_ui_signals.BannedListChanged(); }
bool InitError(const bilingual_str& str)
diff --git a/src/ui_interface.h b/src/node/ui_interface.h
index 9c49451e84..d574ab879f 100644
--- a/src/ui_interface.h
+++ b/src/node/ui_interface.h
@@ -3,14 +3,15 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_UI_INTERFACE_H
-#define BITCOIN_UI_INTERFACE_H
+#ifndef BITCOIN_NODE_UI_INTERFACE_H
+#define BITCOIN_NODE_UI_INTERFACE_H
#include <functional>
#include <memory>
#include <string>
class CBlockIndex;
+enum class SynchronizationState;
struct bilingual_str;
namespace boost {
@@ -19,14 +20,6 @@ class connection;
}
} // namespace boost
-/** General change type (added, updated, removed). */
-enum ChangeType
-{
- CT_NEW,
- CT_UPDATED,
- CT_DELETED
-};
-
/** Signals for UI communication. */
class CClientUIInterface
{
@@ -66,9 +59,6 @@ public:
/** Force blocking, modal message box dialog (not just OS notification) */
MODAL = 0x10000000U,
- /** Do not prepend error/warning prefix */
- MSG_NOPREFIX = 0x20000000U,
-
/** Do not print contents of message to debug log */
SECURE = 0x40000000U,
@@ -110,10 +100,10 @@ public:
ADD_SIGNALS_DECL_WRAPPER(ShowProgress, void, const std::string& title, int nProgress, bool resume_possible);
/** New block has been accepted */
- ADD_SIGNALS_DECL_WRAPPER(NotifyBlockTip, void, bool, const CBlockIndex*);
+ ADD_SIGNALS_DECL_WRAPPER(NotifyBlockTip, void, SynchronizationState, const CBlockIndex*);
/** Best header has changed */
- ADD_SIGNALS_DECL_WRAPPER(NotifyHeaderTip, void, bool, const CBlockIndex*);
+ ADD_SIGNALS_DECL_WRAPPER(NotifyHeaderTip, void, SynchronizationState, const CBlockIndex*);
/** Banlist did change. */
ADD_SIGNALS_DECL_WRAPPER(BannedListChanged, void, void);
@@ -124,7 +114,8 @@ void InitWarning(const bilingual_str& str);
/** Show error message **/
bool InitError(const bilingual_str& str);
+constexpr auto AbortError = InitError;
extern CClientUIInterface uiInterface;
-#endif // BITCOIN_UI_INTERFACE_H
+#endif // BITCOIN_NODE_UI_INTERFACE_H
diff --git a/src/node/utxo_snapshot.h b/src/node/utxo_snapshot.h
index 702a0cbe53..c8b4d60fd0 100644
--- a/src/node/utxo_snapshot.h
+++ b/src/node/utxo_snapshot.h
@@ -35,16 +35,7 @@ public:
m_coins_count(coins_count),
m_nchaintx(nchaintx) { }
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action)
- {
- READWRITE(m_base_blockhash);
- READWRITE(m_coins_count);
- READWRITE(m_nchaintx);
- }
-
+ SERIALIZE_METHODS(SnapshotMetadata, obj) { READWRITE(obj.m_base_blockhash, obj.m_coins_count, obj.m_nchaintx); }
};
#endif // BITCOIN_NODE_UTXO_SNAPSHOT_H
diff --git a/src/noui.cpp b/src/noui.cpp
index ddb3a50ff7..3c82512fac 100644
--- a/src/noui.cpp
+++ b/src/noui.cpp
@@ -6,7 +6,7 @@
#include <noui.h>
#include <logging.h>
-#include <ui_interface.h>
+#include <node/ui_interface.h>
#include <util/translation.h>
#include <string>
@@ -23,24 +23,20 @@ bool noui_ThreadSafeMessageBox(const bilingual_str& message, const std::string&
{
bool fSecure = style & CClientUIInterface::SECURE;
style &= ~CClientUIInterface::SECURE;
- bool prefix = !(style & CClientUIInterface::MSG_NOPREFIX);
- style &= ~CClientUIInterface::MSG_NOPREFIX;
std::string strCaption;
- if (prefix) {
- switch (style) {
- case CClientUIInterface::MSG_ERROR:
- strCaption = "Error: ";
- break;
- case CClientUIInterface::MSG_WARNING:
- strCaption = "Warning: ";
- break;
- case CClientUIInterface::MSG_INFORMATION:
- strCaption = "Information: ";
- break;
- default:
- strCaption = caption + ": "; // Use supplied caption (can be empty)
- }
+ switch (style) {
+ case CClientUIInterface::MSG_ERROR:
+ strCaption = "Error: ";
+ break;
+ case CClientUIInterface::MSG_WARNING:
+ strCaption = "Warning: ";
+ break;
+ case CClientUIInterface::MSG_INFORMATION:
+ strCaption = "Information: ";
+ break;
+ default:
+ strCaption = caption + ": "; // Use supplied caption (can be empty)
}
if (!fSecure) {
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
index ea7a86d6d6..e978852826 100644
--- a/src/outputtype.cpp
+++ b/src/outputtype.cpp
@@ -42,8 +42,8 @@ const std::string& FormatOutputType(OutputType type)
case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
case OutputType::P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
case OutputType::BECH32: return OUTPUT_TYPE_STRING_BECH32;
- default: assert(false);
- }
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
@@ -53,7 +53,7 @@ CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
case OutputType::P2SH_SEGWIT:
case OutputType::BECH32: {
if (!key.IsCompressed()) return PKHash(key);
- CTxDestination witdest = WitnessV0KeyHash(PKHash(key));
+ CTxDestination witdest = WitnessV0KeyHash(key);
CScript witprog = GetScriptForDestination(witdest);
if (type == OutputType::P2SH_SEGWIT) {
return ScriptHash(witprog);
@@ -61,8 +61,8 @@ CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
return witdest;
}
}
- default: assert(false);
- }
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
@@ -100,6 +100,6 @@ CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore,
return ScriptHash(witprog);
}
}
- default: assert(false);
- }
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
diff --git a/src/outputtype.h b/src/outputtype.h
index 1438f65844..77a16b1d05 100644
--- a/src/outputtype.h
+++ b/src/outputtype.h
@@ -18,14 +18,6 @@ enum class OutputType {
LEGACY,
P2SH_SEGWIT,
BECH32,
-
- /**
- * Special output type for change outputs only. Automatically choose type
- * based on address type setting and the types other of non-change outputs
- * (see -changetype option documentation and implementation in
- * CWallet::TransactionChangeType for details).
- */
- CHANGE_AUTO,
};
extern const std::array<OutputType, 3> OUTPUT_TYPES;
diff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp
index 14be6192fe..a01e259731 100644
--- a/src/policy/feerate.cpp
+++ b/src/policy/feerate.cpp
@@ -7,8 +7,6 @@
#include <tinyformat.h>
-const std::string CURRENCY_UNIT = "BTC";
-
CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nBytes_)
{
assert(nBytes_ <= uint64_t(std::numeric_limits<int64_t>::max()));
@@ -37,7 +35,10 @@ CAmount CFeeRate::GetFee(size_t nBytes_) const
return nFee;
}
-std::string CFeeRate::ToString() const
+std::string CFeeRate::ToString(const FeeEstimateMode& fee_estimate_mode) const
{
- return strprintf("%d.%08d %s/kB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT);
+ switch (fee_estimate_mode) {
+ case FeeEstimateMode::SAT_B: return strprintf("%d.%03d %s/B", nSatoshisPerK / 1000, nSatoshisPerK % 1000, CURRENCY_ATOM);
+ default: return strprintf("%d.%08d %s/kB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT);
+ }
}
diff --git a/src/policy/feerate.h b/src/policy/feerate.h
index c040867965..883940f73c 100644
--- a/src/policy/feerate.h
+++ b/src/policy/feerate.h
@@ -11,7 +11,17 @@
#include <string>
-extern const std::string CURRENCY_UNIT;
+const std::string CURRENCY_UNIT = "BTC"; // One formatted unit
+const std::string CURRENCY_ATOM = "sat"; // One indivisible minimum value unit
+
+/* Used to determine type of fee estimation requested */
+enum class FeeEstimateMode {
+ UNSET, //!< Use default settings based on other criteria
+ ECONOMICAL, //!< Force estimateSmartFee to use non-conservative estimates
+ CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
+ BTC_KB, //!< Use explicit BTC/kB fee given in coin control
+ SAT_B, //!< Use explicit sat/B fee given in coin control
+};
/**
* Fee rate in satoshis per kilobyte: CAmount / kB
@@ -46,14 +56,9 @@ public:
friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; }
friend bool operator!=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK != b.nSatoshisPerK; }
CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; }
- std::string ToString() const;
+ std::string ToString(const FeeEstimateMode& fee_estimate_mode = FeeEstimateMode::BTC_KB) const;
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(nSatoshisPerK);
- }
+ SERIALIZE_METHODS(CFeeRate, obj) { READWRITE(obj.nSatoshisPerK); }
};
#endif // BITCOIN_POLICY_FEERATE_H
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 6ee6e0d547..e79dbc9868 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -45,13 +45,6 @@ enum class FeeReason {
REQUIRED,
};
-/* Used to determine type of fee estimation requested */
-enum class FeeEstimateMode {
- UNSET, //!< Use default settings based on other criteria
- ECONOMICAL, //!< Force estimateSmartFee to use non-conservative estimates
- CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
-};
-
/* Used to return detailed information about a feerate bucket */
struct EstimatorBucket
{
@@ -280,7 +273,7 @@ public:
/** Create new FeeFilterRounder */
explicit FeeFilterRounder(const CFeeRate& minIncrementalFee);
- /** Quantize a minimum fee for privacy purpose before broadcast **/
+ /** Quantize a minimum fee for privacy purpose before broadcast. Not thread-safe due to use of FastRandomContext */
CAmount round(CAmount currentMinFee);
private:
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 07d51c0088..c56abaf6c9 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -50,14 +50,14 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn));
}
-bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
+bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType)
{
std::vector<std::vector<unsigned char> > vSolutions;
whichType = Solver(scriptPubKey, vSolutions);
- if (whichType == TX_NONSTANDARD) {
+ if (whichType == TxoutType::NONSTANDARD) {
return false;
- } else if (whichType == TX_MULTISIG) {
+ } else if (whichType == TxoutType::MULTISIG) {
unsigned char m = vSolutions.front()[0];
unsigned char n = vSolutions.back()[0];
// Support up to x-of-3 multisig txns as standard
@@ -65,7 +65,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
return false;
if (m < 1 || m > n)
return false;
- } else if (whichType == TX_NULL_DATA &&
+ } else if (whichType == TxoutType::NULL_DATA &&
(!fAcceptDatacarrier || scriptPubKey.size() > nMaxDatacarrierBytes)) {
return false;
}
@@ -110,16 +110,16 @@ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeR
}
unsigned int nDataOut = 0;
- txnouttype whichType;
+ TxoutType whichType;
for (const CTxOut& txout : tx.vout) {
if (!::IsStandard(txout.scriptPubKey, whichType)) {
reason = "scriptpubkey";
return false;
}
- if (whichType == TX_NULL_DATA)
+ if (whichType == TxoutType::NULL_DATA)
nDataOut++;
- else if ((whichType == TX_MULTISIG) && (!permit_bare_multisig)) {
+ else if ((whichType == TxoutType::MULTISIG) && (!permit_bare_multisig)) {
reason = "bare-multisig";
return false;
} else if (IsDust(txout, dust_relay_fee)) {
@@ -163,10 +163,10 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
const CTxOut& prev = mapInputs.AccessCoin(tx.vin[i].prevout).out;
std::vector<std::vector<unsigned char> > vSolutions;
- txnouttype whichType = Solver(prev.scriptPubKey, vSolutions);
- if (whichType == TX_NONSTANDARD) {
+ TxoutType whichType = Solver(prev.scriptPubKey, vSolutions);
+ if (whichType == TxoutType::NONSTANDARD) {
return false;
- } else if (whichType == TX_SCRIPTHASH) {
+ } else if (whichType == TxoutType::SCRIPTHASH) {
std::vector<std::vector<unsigned char> > stack;
// convert the scriptSig into a stack, so we can inspect the redeemScript
if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE))
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 1561a41c5e..7f168ee20f 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -81,7 +81,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee);
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee);
-bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
+bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType);
/**
* Check for standard transaction types
* @return True if all outputs (scriptPubKeys) use only standard transaction forms
diff --git a/src/primitives/block.h b/src/primitives/block.h
index 750d42efbc..fd8fc8b868 100644
--- a/src/primitives/block.h
+++ b/src/primitives/block.h
@@ -33,17 +33,7 @@ public:
SetNull();
}
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(this->nVersion);
- READWRITE(hashPrevBlock);
- READWRITE(hashMerkleRoot);
- READWRITE(nTime);
- READWRITE(nBits);
- READWRITE(nNonce);
- }
+ SERIALIZE_METHODS(CBlockHeader, obj) { READWRITE(obj.nVersion, obj.hashPrevBlock, obj.hashMerkleRoot, obj.nTime, obj.nBits, obj.nNonce); }
void SetNull()
{
@@ -89,12 +79,10 @@ public:
*(static_cast<CBlockHeader*>(this)) = header;
}
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITEAS(CBlockHeader, *this);
- READWRITE(vtx);
+ SERIALIZE_METHODS(CBlock, obj)
+ {
+ READWRITEAS(CBlockHeader, obj);
+ READWRITE(obj.vtx);
}
void SetNull()
@@ -131,14 +119,12 @@ struct CBlockLocator
explicit CBlockLocator(const std::vector<uint256>& vHaveIn) : vHave(vHaveIn) {}
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
+ SERIALIZE_METHODS(CBlockLocator, obj)
+ {
int nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH))
READWRITE(nVersion);
- READWRITE(vHave);
+ READWRITE(obj.vHave);
}
void SetNull()
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 58b3e8aedc..4514db578a 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -26,13 +26,7 @@ public:
COutPoint(): n(NULL_INDEX) { }
COutPoint(const uint256& hashIn, uint32_t nIn): hash(hashIn), n(nIn) { }
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(hash);
- READWRITE(n);
- }
+ SERIALIZE_METHODS(COutPoint, obj) { READWRITE(obj.hash, obj.n); }
void SetNull() { hash.SetNull(); n = NULL_INDEX; }
bool IsNull() const { return (hash.IsNull() && n == NULL_INDEX); }
@@ -103,14 +97,7 @@ public:
explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);
CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(prevout);
- READWRITE(scriptSig);
- READWRITE(nSequence);
- }
+ SERIALIZE_METHODS(CTxIn, obj) { READWRITE(obj.prevout, obj.scriptSig, obj.nSequence); }
friend bool operator==(const CTxIn& a, const CTxIn& b)
{
@@ -143,13 +130,7 @@ public:
CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn);
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(nValue);
- READWRITE(scriptPubKey);
- }
+ SERIALIZE_METHODS(CTxOut, obj) { READWRITE(obj.nValue, obj.scriptPubKey); }
void SetNull()
{
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 25851e786c..2dfe4bee74 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -5,8 +5,8 @@
#include <protocol.h>
-#include <util/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
#ifndef WIN32
# include <arpa/inet.h>
@@ -40,6 +40,10 @@ const char *SENDCMPCT="sendcmpct";
const char *CMPCTBLOCK="cmpctblock";
const char *GETBLOCKTXN="getblocktxn";
const char *BLOCKTXN="blocktxn";
+const char *GETCFILTERS="getcfilters";
+const char *CFILTER="cfilter";
+const char *GETCFHEADERS="getcfheaders";
+const char *CFHEADERS="cfheaders";
const char *GETCFCHECKPT="getcfcheckpt";
const char *CFCHECKPT="cfcheckpt";
} // namespace NetMsgType
@@ -73,6 +77,10 @@ const static std::string allNetMessageTypes[] = {
NetMsgType::CMPCTBLOCK,
NetMsgType::GETBLOCKTXN,
NetMsgType::BLOCKTXN,
+ NetMsgType::GETCFILTERS,
+ NetMsgType::CFILTER,
+ NetMsgType::GETCFHEADERS,
+ NetMsgType::CFHEADERS,
NetMsgType::GETCFCHECKPT,
NetMsgType::CFCHECKPT,
};
@@ -147,24 +155,6 @@ void SetServiceFlagsIBDCache(bool state) {
g_initial_block_download_completed = state;
}
-
-CAddress::CAddress() : CService()
-{
- Init();
-}
-
-CAddress::CAddress(CService ipIn, ServiceFlags nServicesIn) : CService(ipIn)
-{
- Init();
- nServices = nServicesIn;
-}
-
-void CAddress::Init()
-{
- nServices = NODE_NONE;
- nTime = 100000000;
-}
-
CInv::CInv()
{
type = 0;
@@ -208,3 +198,42 @@ const std::vector<std::string> &getAllNetMessageTypes()
{
return allNetMessageTypesVec;
}
+
+/**
+ * Convert a service flag (NODE_*) to a human readable string.
+ * It supports unknown service flags which will be returned as "UNKNOWN[...]".
+ * @param[in] bit the service flag is calculated as (1 << bit)
+ */
+static std::string serviceFlagToStr(size_t bit)
+{
+ const uint64_t service_flag = 1ULL << bit;
+ switch ((ServiceFlags)service_flag) {
+ case NODE_NONE: abort(); // impossible
+ case NODE_NETWORK: return "NETWORK";
+ case NODE_GETUTXO: return "GETUTXO";
+ case NODE_BLOOM: return "BLOOM";
+ case NODE_WITNESS: return "WITNESS";
+ case NODE_NETWORK_LIMITED: return "NETWORK_LIMITED";
+ // Not using default, so we get warned when a case is missing
+ }
+
+ std::ostringstream stream;
+ stream.imbue(std::locale::classic());
+ stream << "UNKNOWN[";
+ stream << "2^" << bit;
+ stream << "]";
+ return stream.str();
+}
+
+std::vector<std::string> serviceFlagsToStr(uint64_t flags)
+{
+ std::vector<std::string> str_flags;
+
+ for (size_t i = 0; i < sizeof(flags) * 8; ++i) {
+ if (flags & (1ULL << i)) {
+ str_flags.emplace_back(serviceFlagToStr(i));
+ }
+ }
+
+ return str_flags;
+}
diff --git a/src/protocol.h b/src/protocol.h
index dfcb0e0660..9ab63a30fb 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -46,16 +46,7 @@ public:
std::string GetCommand() const;
bool IsValid(const MessageStartChars& messageStart) const;
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action)
- {
- READWRITE(pchMessageStart);
- READWRITE(pchCommand);
- READWRITE(nMessageSize);
- READWRITE(pchChecksum);
- }
+ SERIALIZE_METHODS(CMessageHeader, obj) { READWRITE(obj.pchMessageStart, obj.pchCommand, obj.nMessageSize, obj.pchChecksum); }
char pchMessageStart[MESSAGE_START_SIZE];
char pchCommand[COMMAND_SIZE];
@@ -74,100 +65,100 @@ namespace NetMsgType {
* receiving node at the beginning of a connection.
* @see https://bitcoin.org/en/developer-reference#version
*/
-extern const char *VERSION;
+extern const char* VERSION;
/**
* The verack message acknowledges a previously-received version message,
* informing the connecting node that it can begin to send other messages.
* @see https://bitcoin.org/en/developer-reference#verack
*/
-extern const char *VERACK;
+extern const char* VERACK;
/**
* The addr (IP address) message relays connection information for peers on the
* network.
* @see https://bitcoin.org/en/developer-reference#addr
*/
-extern const char *ADDR;
+extern const char* ADDR;
/**
* The inv message (inventory message) transmits one or more inventories of
* objects known to the transmitting peer.
* @see https://bitcoin.org/en/developer-reference#inv
*/
-extern const char *INV;
+extern const char* INV;
/**
* The getdata message requests one or more data objects from another node.
* @see https://bitcoin.org/en/developer-reference#getdata
*/
-extern const char *GETDATA;
+extern const char* GETDATA;
/**
* The merkleblock message is a reply to a getdata message which requested a
* block using the inventory type MSG_MERKLEBLOCK.
* @since protocol version 70001 as described by BIP37.
* @see https://bitcoin.org/en/developer-reference#merkleblock
*/
-extern const char *MERKLEBLOCK;
+extern const char* MERKLEBLOCK;
/**
* The getblocks message requests an inv message that provides block header
* hashes starting from a particular point in the block chain.
* @see https://bitcoin.org/en/developer-reference#getblocks
*/
-extern const char *GETBLOCKS;
+extern const char* GETBLOCKS;
/**
* The getheaders message requests a headers message that provides block
* headers starting from a particular point in the block chain.
* @since protocol version 31800.
* @see https://bitcoin.org/en/developer-reference#getheaders
*/
-extern const char *GETHEADERS;
+extern const char* GETHEADERS;
/**
* The tx message transmits a single transaction.
* @see https://bitcoin.org/en/developer-reference#tx
*/
-extern const char *TX;
+extern const char* TX;
/**
* The headers message sends one or more block headers to a node which
* previously requested certain headers with a getheaders message.
* @since protocol version 31800.
* @see https://bitcoin.org/en/developer-reference#headers
*/
-extern const char *HEADERS;
+extern const char* HEADERS;
/**
* The block message transmits a single serialized block.
* @see https://bitcoin.org/en/developer-reference#block
*/
-extern const char *BLOCK;
+extern const char* BLOCK;
/**
* The getaddr message requests an addr message from the receiving node,
* preferably one with lots of IP addresses of other receiving nodes.
* @see https://bitcoin.org/en/developer-reference#getaddr
*/
-extern const char *GETADDR;
+extern const char* GETADDR;
/**
* The mempool message requests the TXIDs of transactions that the receiving
* node has verified as valid but which have not yet appeared in a block.
* @since protocol version 60002.
* @see https://bitcoin.org/en/developer-reference#mempool
*/
-extern const char *MEMPOOL;
+extern const char* MEMPOOL;
/**
* The ping message is sent periodically to help confirm that the receiving
* peer is still connected.
* @see https://bitcoin.org/en/developer-reference#ping
*/
-extern const char *PING;
+extern const char* PING;
/**
* The pong message replies to a ping message, proving to the pinging node that
* the ponging node is still alive.
* @since protocol version 60001 as described by BIP31.
* @see https://bitcoin.org/en/developer-reference#pong
*/
-extern const char *PONG;
+extern const char* PONG;
/**
* The notfound message is a reply to a getdata message which requested an
* object the receiving node does not have available for relay.
* @since protocol version 70001.
* @see https://bitcoin.org/en/developer-reference#notfound
*/
-extern const char *NOTFOUND;
+extern const char* NOTFOUND;
/**
* The filterload message tells the receiving peer to filter all relayed
* transactions and requested merkle blocks through the provided filter.
@@ -176,7 +167,7 @@ extern const char *NOTFOUND;
* 70011 as described by BIP111.
* @see https://bitcoin.org/en/developer-reference#filterload
*/
-extern const char *FILTERLOAD;
+extern const char* FILTERLOAD;
/**
* The filteradd message tells the receiving peer to add a single element to a
* previously-set bloom filter, such as a new public key.
@@ -185,7 +176,7 @@ extern const char *FILTERLOAD;
* 70011 as described by BIP111.
* @see https://bitcoin.org/en/developer-reference#filteradd
*/
-extern const char *FILTERADD;
+extern const char* FILTERADD;
/**
* The filterclear message tells the receiving peer to remove a previously-set
* bloom filter.
@@ -194,20 +185,20 @@ extern const char *FILTERADD;
* 70011 as described by BIP111.
* @see https://bitcoin.org/en/developer-reference#filterclear
*/
-extern const char *FILTERCLEAR;
+extern const char* FILTERCLEAR;
/**
* Indicates that a node prefers to receive new block announcements via a
* "headers" message rather than an "inv".
* @since protocol version 70012 as described by BIP130.
* @see https://bitcoin.org/en/developer-reference#sendheaders
*/
-extern const char *SENDHEADERS;
+extern const char* SENDHEADERS;
/**
* The feefilter message tells the receiving peer not to inv us any txs
* which do not meet the specified min fee rate.
* @since protocol version 70013 as described by BIP133
*/
-extern const char *FEEFILTER;
+extern const char* FEEFILTER;
/**
* Contains a 1-byte bool and 8-byte LE version number.
* Indicates that a node is willing to provide blocks via "cmpctblock" messages.
@@ -215,47 +206,69 @@ extern const char *FEEFILTER;
* "cmpctblock" message rather than an "inv", depending on message contents.
* @since protocol version 70014 as described by BIP 152
*/
-extern const char *SENDCMPCT;
+extern const char* SENDCMPCT;
/**
* Contains a CBlockHeaderAndShortTxIDs object - providing a header and
* list of "short txids".
* @since protocol version 70014 as described by BIP 152
*/
-extern const char *CMPCTBLOCK;
+extern const char* CMPCTBLOCK;
/**
* Contains a BlockTransactionsRequest
* Peer should respond with "blocktxn" message.
* @since protocol version 70014 as described by BIP 152
*/
-extern const char *GETBLOCKTXN;
+extern const char* GETBLOCKTXN;
/**
* Contains a BlockTransactions.
* Sent in response to a "getblocktxn" message.
* @since protocol version 70014 as described by BIP 152
*/
-extern const char *BLOCKTXN;
+extern const char* BLOCKTXN;
+/**
+ * getcfilters requests compact filters for a range of blocks.
+ * Only available with service bit NODE_COMPACT_FILTERS as described by
+ * BIP 157 & 158.
+ */
+extern const char* GETCFILTERS;
+/**
+ * cfilter is a response to a getcfilters request containing a single compact
+ * filter.
+ */
+extern const char* CFILTER;
+/**
+ * getcfheaders requests a compact filter header and the filter hashes for a
+ * range of blocks, which can then be used to reconstruct the filter headers
+ * for those blocks.
+ * Only available with service bit NODE_COMPACT_FILTERS as described by
+ * BIP 157 & 158.
+ */
+extern const char* GETCFHEADERS;
+/**
+ * cfheaders is a response to a getcfheaders request containing a filter header
+ * and a vector of filter hashes for each subsequent block in the requested range.
+ */
+extern const char* CFHEADERS;
/**
* getcfcheckpt requests evenly spaced compact filter headers, enabling
* parallelized download and validation of the headers between them.
* Only available with service bit NODE_COMPACT_FILTERS as described by
* BIP 157 & 158.
*/
-extern const char *GETCFCHECKPT;
+extern const char* GETCFCHECKPT;
/**
* cfcheckpt is a response to a getcfcheckpt request containing a vector of
* evenly spaced filter headers for blocks on the requested chain.
- * Only available with service bit NODE_COMPACT_FILTERS as described by
- * BIP 157 & 158.
*/
-extern const char *CFCHECKPT;
-};
+extern const char* CFCHECKPT;
+}; // namespace NetMsgType
/* Get a vector of all valid message types (see above) */
-const std::vector<std::string> &getAllNetMessageTypes();
+const std::vector<std::string>& getAllNetMessageTypes();
/** nServices flags */
enum ServiceFlags : uint64_t {
- // NOTE: When adding here, be sure to update qt/guiutil.cpp's formatServicesStr too
+ // NOTE: When adding here, be sure to update serviceFlagToStr too
// Nothing
NODE_NONE = 0,
// NODE_NETWORK means that the node is capable of serving the complete block chain. It is currently
@@ -287,6 +300,13 @@ enum ServiceFlags : uint64_t {
};
/**
+ * Convert service flags (a bitmask of NODE_*) to human readable strings.
+ * It supports unknown service flags which will be returned as "UNKNOWN[...]".
+ * @param[in] flags multiple NODE_* bitwise-OR-ed together
+ */
+std::vector<std::string> serviceFlagsToStr(uint64_t flags);
+
+/**
* Gets the set of service flags which are "desirable" for a given peer.
*
* These are the flags which are required for a peer to support for them
@@ -320,7 +340,8 @@ void SetServiceFlagsIBDCache(bool status);
* == GetDesirableServiceFlags(services), ie determines whether the given
* set of service flags are sufficient for a peer to be "relevant".
*/
-static inline bool HasAllDesirableServiceFlags(ServiceFlags services) {
+static inline bool HasAllDesirableServiceFlags(ServiceFlags services)
+{
return !(GetDesirableServiceFlags(services) & (~services));
}
@@ -328,62 +349,61 @@ static inline bool HasAllDesirableServiceFlags(ServiceFlags services) {
* Checks if a peer with the given service flags may be capable of having a
* robust address-storage DB.
*/
-static inline bool MayHaveUsefulAddressDB(ServiceFlags services) {
+static inline bool MayHaveUsefulAddressDB(ServiceFlags services)
+{
return (services & NODE_NETWORK) || (services & NODE_NETWORK_LIMITED);
}
/** A CService with information about it as peer */
class CAddress : public CService
{
-public:
- CAddress();
- explicit CAddress(CService ipIn, ServiceFlags nServicesIn);
-
- void Init();
+ static constexpr uint32_t TIME_INIT{100000000};
- ADD_SERIALIZE_METHODS;
+public:
+ CAddress() : CService{} {};
+ explicit CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action)
+ SERIALIZE_METHODS(CAddress, obj)
{
- if (ser_action.ForRead())
- Init();
+ SER_READ(obj, obj.nTime = TIME_INIT);
int nVersion = s.GetVersion();
- if (s.GetType() & SER_DISK)
+ if (s.GetType() & SER_DISK) {
READWRITE(nVersion);
+ }
if ((s.GetType() & SER_DISK) ||
- (nVersion >= CADDR_TIME_VERSION && !(s.GetType() & SER_GETHASH)))
- READWRITE(nTime);
- uint64_t nServicesInt = nServices;
- READWRITE(nServicesInt);
- nServices = static_cast<ServiceFlags>(nServicesInt);
- READWRITEAS(CService, *this);
+ (nVersion != INIT_PROTO_VERSION && !(s.GetType() & SER_GETHASH))) {
+ // The only time we serialize a CAddress object without nTime is in
+ // the initial VERSION messages which contain two CAddress records.
+ // At that point, the serialization version is INIT_PROTO_VERSION.
+ // After the version handshake, serialization version is >=
+ // MIN_PEER_PROTO_VERSION and all ADDR messages are serialized with
+ // nTime.
+ READWRITE(obj.nTime);
+ }
+ READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
+ READWRITEAS(CService, obj);
}
- // TODO: make private (improves encapsulation)
-public:
- ServiceFlags nServices;
-
+ ServiceFlags nServices{NODE_NONE};
// disk and network only
- unsigned int nTime;
+ uint32_t nTime{TIME_INIT};
};
/** getdata message type flags */
const uint32_t MSG_WITNESS_FLAG = 1 << 30;
-const uint32_t MSG_TYPE_MASK = 0xffffffff >> 2;
+const uint32_t MSG_TYPE_MASK = 0xffffffff >> 2;
/** getdata / inv message types.
* These numbers are defined by the protocol. When adding a new value, be sure
* to mention it in the respective BIP.
*/
-enum GetDataMsg
-{
+enum GetDataMsg {
UNDEFINED = 0,
MSG_TX = 1,
MSG_BLOCK = 2,
// The following can only occur in getdata. Invs always use TX or BLOCK.
- MSG_FILTERED_BLOCK = 3, //!< Defined in BIP37
- MSG_CMPCT_BLOCK = 4, //!< Defined in BIP152
+ MSG_FILTERED_BLOCK = 3, //!< Defined in BIP37
+ MSG_CMPCT_BLOCK = 4, //!< Defined in BIP152
MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG, //!< Defined in BIP144
MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG, //!< Defined in BIP144
MSG_FILTERED_WITNESS_BLOCK = MSG_FILTERED_BLOCK | MSG_WITNESS_FLAG,
@@ -396,21 +416,13 @@ public:
CInv();
CInv(int typeIn, const uint256& hashIn);
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action)
- {
- READWRITE(type);
- READWRITE(hash);
- }
+ SERIALIZE_METHODS(CInv, obj) { READWRITE(obj.type, obj.hash); }
friend bool operator<(const CInv& a, const CInv& b);
std::string GetCommand() const;
std::string ToString() const;
-public:
int type;
uint256 hash;
};
diff --git a/src/psbt.cpp b/src/psbt.cpp
index ef9781817a..3fb743e5db 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -35,14 +35,6 @@ bool PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
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()) {
@@ -144,8 +136,8 @@ 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()) {
+ // TODO: For segwit v1, we will want to clear out the non-witness utxo when setting a witness one. For v0 and non-segwit, this is not safe
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());
@@ -158,18 +150,6 @@ void PSBTInput::Merge(const PSBTInput& input)
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()) {
@@ -214,6 +194,17 @@ bool PSBTInputSigned(const PSBTInput& input)
return !input.final_script_sig.empty() || !input.final_script_witness.IsNull();
}
+size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt) {
+ size_t count = 0;
+ for (const auto& input : psbt.inputs) {
+ if (!PSBTInputSigned(input)) {
+ count++;
+ }
+ }
+
+ return count;
+}
+
void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index)
{
const CTxOut& out = psbt.tx->vout.at(index);
@@ -250,11 +241,6 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
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;
@@ -288,10 +274,11 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
if (require_witness_sig && !sigdata.witness) return false;
input.FromSignatureData(sigdata);
- // If we have a witness signature, use the smaller witness UTXO.
+ // If we have a witness signature, put a witness UTXO.
+ // TODO: For segwit v1, we should remove the non_witness_utxo
if (sigdata.witness) {
input.witness_utxo = utxo;
- input.non_witness_utxo = nullptr;
+ // input.non_witness_utxo = nullptr;
}
// Fill in the missing info
@@ -345,10 +332,6 @@ TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector
return TransactionError::PSBT_MISMATCH;
}
}
- if (!out.IsSane()) {
- return TransactionError::INVALID_PSBT;
- }
-
return TransactionError::OK;
}
diff --git a/src/psbt.h b/src/psbt.h
index af57994f3a..0951b76f83 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -41,7 +41,7 @@ static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;
static constexpr uint8_t PSBT_SEPARATOR = 0x00;
// BIP 174 does not specify a maximum file size, but we set a limit anyway
-// to prevent reading a stream indefinately and running out of memory.
+// to prevent reading a stream indefinitely and running out of memory.
const std::streamsize MAX_FILE_SIZE_PSBT = 100000000; // 100 MiB
/** A structure for PSBTs which contain per-input information */
@@ -62,18 +62,17 @@ struct PSBTInput
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()) {
+ }
+ if (!witness_utxo.IsNull()) {
SerializeToVector(s, PSBT_IN_WITNESS_UTXO);
SerializeToVector(s, witness_utxo);
}
@@ -284,7 +283,6 @@ struct PSBTOutput
void FillSignatureData(SignatureData& sigdata) const;
void FromSignatureData(const SignatureData& sigdata);
void Merge(const PSBTOutput& output);
- bool IsSane() const;
PSBTOutput() {}
template <typename Stream>
@@ -401,7 +399,6 @@ struct PartiallySignedTransaction
/** 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() {}
@@ -551,10 +548,6 @@ struct PartiallySignedTransaction
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>
@@ -579,6 +572,9 @@ 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);
+/** Counts the unsigned inputs of a PSBT. */
+size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);
+
/** Updates a PSBTOutput with information from provider.
*
* This fills in the redeem_script, witness_script, and hd_keypaths where possible.
diff --git a/src/pubkey.h b/src/pubkey.h
index 261842b7f7..4c28af4a4d 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -142,6 +142,9 @@ public:
unsigned int len = ::ReadCompactSize(s);
if (len <= SIZE) {
s.read((char*)vch, len);
+ if (len != size()) {
+ Invalidate();
+ }
} else {
// invalid pubkey, skip available data
char dummy;
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 0d3b08fe7d..aa4ec04497 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -106,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. Use the 'Create new receiving address' button in the receive tab to create new addresses."));
+ 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.\nSigning is only possible with addresses of the type 'legacy'."));
ui->deleteAddress->setVisible(false);
ui->newAddress->setVisible(false);
break;
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index 8110f4e895..665c8e6053 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -11,6 +11,7 @@
#include <wallet/wallet.h>
#include <algorithm>
+#include <typeinfo>
#include <QFont>
#include <QDebug>
@@ -75,12 +76,14 @@ public:
explicit AddressTablePriv(AddressTableModel *_parent):
parent(_parent) {}
- void refreshAddressTable(interfaces::Wallet& wallet)
+ void refreshAddressTable(interfaces::Wallet& wallet, bool pk_hash_only = false)
{
cachedAddressTable.clear();
{
for (const auto& address : wallet.getAddresses())
{
+ if (pk_hash_only && address.dest.type() != typeid(PKHash))
+ continue;
AddressTableEntry::Type addressType = translateTransactionType(
QString::fromStdString(address.purpose), address.is_mine);
cachedAddressTable.append(AddressTableEntry(addressType,
@@ -159,12 +162,12 @@ public:
}
};
-AddressTableModel::AddressTableModel(WalletModel *parent) :
+AddressTableModel::AddressTableModel(WalletModel *parent, bool pk_hash_only) :
QAbstractTableModel(parent), walletModel(parent)
{
columns << tr("Label") << tr("Address");
priv = new AddressTablePriv(this);
- priv->refreshAddressTable(parent->wallet());
+ priv->refreshAddressTable(parent->wallet(), pk_hash_only);
}
AddressTableModel::~AddressTableModel()
diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h
index 97f673caf1..73316cadc4 100644
--- a/src/qt/addresstablemodel.h
+++ b/src/qt/addresstablemodel.h
@@ -25,7 +25,7 @@ class AddressTableModel : public QAbstractTableModel
Q_OBJECT
public:
- explicit AddressTableModel(WalletModel *parent = nullptr);
+ explicit AddressTableModel(WalletModel *parent = nullptr, bool pk_hash_only = false);
~AddressTableModel();
enum ColumnIndex {
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 8939b566f7..a304c23a2c 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -30,10 +30,11 @@
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <noui.h>
-#include <ui_interface.h>
#include <uint256.h>
#include <util/system.h>
#include <util/threadnames.h>
+#include <util/translation.h>
+#include <validation.h>
#include <memory>
@@ -61,8 +62,26 @@ Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
// Declare meta types used for QMetaObject::invokeMethod
Q_DECLARE_METATYPE(bool*)
Q_DECLARE_METATYPE(CAmount)
+Q_DECLARE_METATYPE(SynchronizationState)
Q_DECLARE_METATYPE(uint256)
+static void RegisterMetaTypes()
+{
+ // Register meta types used for QMetaObject::invokeMethod and Qt::QueuedConnection
+ qRegisterMetaType<bool*>();
+ qRegisterMetaType<SynchronizationState>();
+ #ifdef ENABLE_WALLET
+ qRegisterMetaType<WalletModel*>();
+ #endif
+ // Register typedefs (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
+ // IMPORTANT: if CAmount is no longer a typedef use the normal variant above (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1)
+ qRegisterMetaType<CAmount>("CAmount");
+ qRegisterMetaType<size_t>("size_t");
+
+ qRegisterMetaType<std::function<void()>>("std::function<void()>");
+ qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
+}
+
static QString GetLangTerritory()
{
QSettings settings;
@@ -135,7 +154,7 @@ BitcoinCore::BitcoinCore(interfaces::Node& node) :
void BitcoinCore::handleRunawayException(const std::exception *e)
{
PrintExceptionContinue(e, "Runaway exception");
- Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings()));
+ Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings().translated));
}
void BitcoinCore::initialize()
@@ -182,6 +201,7 @@ BitcoinApplication::BitcoinApplication(interfaces::Node& node):
returnValue(0),
platformStyle(nullptr)
{
+ RegisterMetaTypes();
setQuitOnLastWindowClosed(false);
}
@@ -210,8 +230,6 @@ BitcoinApplication::~BitcoinApplication()
delete window;
window = nullptr;
- delete optionsModel;
- optionsModel = nullptr;
delete platformStyle;
platformStyle = nullptr;
}
@@ -225,7 +243,7 @@ void BitcoinApplication::createPaymentServer()
void BitcoinApplication::createOptionsModel(bool resetSettings)
{
- optionsModel = new OptionsModel(m_node, nullptr, resetSettings);
+ optionsModel = new OptionsModel(m_node, this, resetSettings);
}
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
@@ -381,7 +399,7 @@ void BitcoinApplication::shutdownResult()
void BitcoinApplication::handleRunawayException(const QString &message)
{
- QMessageBox::critical(nullptr, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) + QString("\n\n") + message);
+ QMessageBox::critical(nullptr, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) + QString("<br><br>") + message);
::exit(EXIT_FAILURE);
}
@@ -433,26 +451,13 @@ int GuiMain(int argc, char* argv[])
BitcoinApplication app(*node);
- // Register meta types used for QMetaObject::invokeMethod and Qt::QueuedConnection
- qRegisterMetaType<bool*>();
-#ifdef ENABLE_WALLET
- qRegisterMetaType<WalletModel*>();
-#endif
- // Register typedefs (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
- // IMPORTANT: if CAmount is no longer a typedef use the normal variant above (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1)
- qRegisterMetaType<CAmount>("CAmount");
- qRegisterMetaType<size_t>("size_t");
-
- 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();
SetupUIArgs();
std::string error;
if (!node->parseParameters(argc, argv, error)) {
- node->initError(strprintf("Error parsing command line arguments: %s\n", error));
+ node->initError(strprintf(Untranslated("Error parsing command line arguments: %s\n"), error));
// Create a message box, because the gui has neither been created nor has subscribed to core signals
QMessageBox::critical(nullptr, PACKAGE_NAME,
// message can not be translated because translations have not been initialized
@@ -493,13 +498,13 @@ int GuiMain(int argc, char* argv[])
/// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
if (!CheckDataDirOption()) {
- node->initError(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")));
+ node->initError(strprintf(Untranslated("Specified data directory \"%s\" does not exist.\n"), gArgs.GetArg("-datadir", "")));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", ""))));
return EXIT_FAILURE;
}
if (!node->readConfigFiles(error)) {
- node->initError(strprintf("Error reading configuration file: %s\n", error));
+ node->initError(strprintf(Untranslated("Error reading configuration file: %s\n"), error));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error)));
return EXIT_FAILURE;
@@ -515,7 +520,7 @@ int GuiMain(int argc, char* argv[])
try {
node->selectParams(gArgs.GetChainName());
} catch(std::exception &e) {
- node->initError(strprintf("%s\n", e.what()));
+ node->initError(Untranslated(strprintf("%s\n", e.what())));
QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(e.what()));
return EXIT_FAILURE;
}
@@ -551,6 +556,8 @@ int GuiMain(int argc, char* argv[])
/// 9. Main GUI initialization
// Install global event filter that makes sure that long tooltips can be word-wrapped
app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
+ // Install global event filter that makes sure that out-of-focus labels do not contain text cursor.
+ app.installEventFilter(new GUIUtil::LabelOutOfFocusEventFilter(&app));
#if defined(Q_OS_WIN)
// Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
qApp->installNativeEventFilter(new WinShutdownMonitor());
@@ -593,10 +600,10 @@ int GuiMain(int argc, char* argv[])
}
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(node->getWarnings()));
+ app.handleRunawayException(QString::fromStdString(node->getWarnings().translated));
} catch (...) {
PrintExceptionContinue(nullptr, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(node->getWarnings()));
+ app.handleRunawayException(QString::fromStdString(node->getWarnings().translated));
}
return rv;
}
diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc
index 037b23e4b2..7115459808 100644
--- a/src/qt/bitcoin.qrc
+++ b/src/qt/bitcoin.qrc
@@ -45,42 +45,42 @@
<file alias="network_disabled">res/icons/network_disabled.png</file>
<file alias="proxy">res/icons/proxy.png</file>
</qresource>
- <qresource prefix="/movies">
- <file alias="spinner-000">res/movies/spinner-000.png</file>
- <file alias="spinner-001">res/movies/spinner-001.png</file>
- <file alias="spinner-002">res/movies/spinner-002.png</file>
- <file alias="spinner-003">res/movies/spinner-003.png</file>
- <file alias="spinner-004">res/movies/spinner-004.png</file>
- <file alias="spinner-005">res/movies/spinner-005.png</file>
- <file alias="spinner-006">res/movies/spinner-006.png</file>
- <file alias="spinner-007">res/movies/spinner-007.png</file>
- <file alias="spinner-008">res/movies/spinner-008.png</file>
- <file alias="spinner-009">res/movies/spinner-009.png</file>
- <file alias="spinner-010">res/movies/spinner-010.png</file>
- <file alias="spinner-011">res/movies/spinner-011.png</file>
- <file alias="spinner-012">res/movies/spinner-012.png</file>
- <file alias="spinner-013">res/movies/spinner-013.png</file>
- <file alias="spinner-014">res/movies/spinner-014.png</file>
- <file alias="spinner-015">res/movies/spinner-015.png</file>
- <file alias="spinner-016">res/movies/spinner-016.png</file>
- <file alias="spinner-017">res/movies/spinner-017.png</file>
- <file alias="spinner-018">res/movies/spinner-018.png</file>
- <file alias="spinner-019">res/movies/spinner-019.png</file>
- <file alias="spinner-020">res/movies/spinner-020.png</file>
- <file alias="spinner-021">res/movies/spinner-021.png</file>
- <file alias="spinner-022">res/movies/spinner-022.png</file>
- <file alias="spinner-023">res/movies/spinner-023.png</file>
- <file alias="spinner-024">res/movies/spinner-024.png</file>
- <file alias="spinner-025">res/movies/spinner-025.png</file>
- <file alias="spinner-026">res/movies/spinner-026.png</file>
- <file alias="spinner-027">res/movies/spinner-027.png</file>
- <file alias="spinner-028">res/movies/spinner-028.png</file>
- <file alias="spinner-029">res/movies/spinner-029.png</file>
- <file alias="spinner-030">res/movies/spinner-030.png</file>
- <file alias="spinner-031">res/movies/spinner-031.png</file>
- <file alias="spinner-032">res/movies/spinner-032.png</file>
- <file alias="spinner-033">res/movies/spinner-033.png</file>
- <file alias="spinner-034">res/movies/spinner-034.png</file>
- <file alias="spinner-035">res/movies/spinner-035.png</file>
+ <qresource prefix="/animation">
+ <file alias="spinner-000">res/animation/spinner-000.png</file>
+ <file alias="spinner-001">res/animation/spinner-001.png</file>
+ <file alias="spinner-002">res/animation/spinner-002.png</file>
+ <file alias="spinner-003">res/animation/spinner-003.png</file>
+ <file alias="spinner-004">res/animation/spinner-004.png</file>
+ <file alias="spinner-005">res/animation/spinner-005.png</file>
+ <file alias="spinner-006">res/animation/spinner-006.png</file>
+ <file alias="spinner-007">res/animation/spinner-007.png</file>
+ <file alias="spinner-008">res/animation/spinner-008.png</file>
+ <file alias="spinner-009">res/animation/spinner-009.png</file>
+ <file alias="spinner-010">res/animation/spinner-010.png</file>
+ <file alias="spinner-011">res/animation/spinner-011.png</file>
+ <file alias="spinner-012">res/animation/spinner-012.png</file>
+ <file alias="spinner-013">res/animation/spinner-013.png</file>
+ <file alias="spinner-014">res/animation/spinner-014.png</file>
+ <file alias="spinner-015">res/animation/spinner-015.png</file>
+ <file alias="spinner-016">res/animation/spinner-016.png</file>
+ <file alias="spinner-017">res/animation/spinner-017.png</file>
+ <file alias="spinner-018">res/animation/spinner-018.png</file>
+ <file alias="spinner-019">res/animation/spinner-019.png</file>
+ <file alias="spinner-020">res/animation/spinner-020.png</file>
+ <file alias="spinner-021">res/animation/spinner-021.png</file>
+ <file alias="spinner-022">res/animation/spinner-022.png</file>
+ <file alias="spinner-023">res/animation/spinner-023.png</file>
+ <file alias="spinner-024">res/animation/spinner-024.png</file>
+ <file alias="spinner-025">res/animation/spinner-025.png</file>
+ <file alias="spinner-026">res/animation/spinner-026.png</file>
+ <file alias="spinner-027">res/animation/spinner-027.png</file>
+ <file alias="spinner-028">res/animation/spinner-028.png</file>
+ <file alias="spinner-029">res/animation/spinner-029.png</file>
+ <file alias="spinner-030">res/animation/spinner-030.png</file>
+ <file alias="spinner-031">res/animation/spinner-031.png</file>
+ <file alias="spinner-032">res/animation/spinner-032.png</file>
+ <file alias="spinner-033">res/animation/spinner-033.png</file>
+ <file alias="spinner-034">res/animation/spinner-034.png</file>
+ <file alias="spinner-035">res/animation/spinner-035.png</file>
</qresource>
</RCC>
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index 4c57f1e352..a953c991bc 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -56,7 +56,7 @@ public:
if (valid) {
val = qBound(m_min_amount, val, m_max_amount);
- input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways);
+ input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::SeparatorStyle::ALWAYS);
lineEdit()->setText(input);
}
}
@@ -68,7 +68,7 @@ public:
void setValue(const CAmount& value)
{
- lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::separatorAlways));
+ lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::SeparatorStyle::ALWAYS));
Q_EMIT valueChanged();
}
@@ -102,7 +102,7 @@ public:
CAmount val = value(&valid);
currentUnit = unit;
- lineEdit()->setPlaceholderText(BitcoinUnits::format(currentUnit, m_min_amount, false, BitcoinUnits::separatorAlways));
+ lineEdit()->setPlaceholderText(BitcoinUnits::format(currentUnit, m_min_amount, false, BitcoinUnits::SeparatorStyle::ALWAYS));
if(valid)
setValue(val);
else
@@ -122,7 +122,7 @@ public:
const QFontMetrics fm(fontMetrics());
int h = lineEdit()->minimumSizeHint().height();
- int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
+ int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS));
w += 2; // cursor blinking space
QStyleOptionSpinBox opt;
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 3a1fdc22a6..ebcc04a5eb 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -34,9 +34,10 @@
#include <chainparams.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
-#include <ui_interface.h>
+#include <node/ui_interface.h>
#include <util/system.h>
#include <util/translation.h>
+#include <validation.h>
#include <QAction>
#include <QApplication>
@@ -111,6 +112,8 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
Q_EMIT consoleShown(rpcConsole);
}
+ modalOverlay = new ModalOverlay(enableWallet, this->centralWidget());
+
// Accept D&D of URIs
setAcceptDrops(true);
@@ -200,7 +203,6 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
openOptionsDialogWithTab(OptionsDialog::TAB_NETWORK);
});
- modalOverlay = new ModalOverlay(enableWallet, this->centralWidget());
connect(labelBlocksIcon, &GUIUtil::ClickableLabel::clicked, this, &BitcoinGUI::showModalOverlay);
connect(progressBar, &GUIUtil::ClickableProgressBar::clicked, this, &BitcoinGUI::showModalOverlay);
#ifdef ENABLE_WALLET
@@ -237,6 +239,7 @@ BitcoinGUI::~BitcoinGUI()
void BitcoinGUI::createActions()
{
QActionGroup *tabGroup = new QActionGroup(this);
+ connect(modalOverlay, &ModalOverlay::triggered, tabGroup, &QActionGroup::setEnabled);
overviewAction = new QAction(platformStyle->SingleColorIcon(":/icons/overview"), tr("&Overview"), this);
overviewAction->setStatusTip(tr("Show general overview of wallet"));
@@ -320,8 +323,10 @@ void BitcoinGUI::createActions()
signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them"));
verifyMessageAction = new QAction(tr("&Verify message..."), this);
verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses"));
- m_load_psbt_action = new QAction(tr("Load PSBT..."), this);
+ m_load_psbt_action = new QAction(tr("&Load PSBT from file..."), this);
m_load_psbt_action->setStatusTip(tr("Load Partially Signed Bitcoin Transaction"));
+ m_load_psbt_clipboard_action = new QAction(tr("Load PSBT from clipboard..."), this);
+ m_load_psbt_clipboard_action->setStatusTip(tr("Load Partially Signed Bitcoin Transaction from clipboard"));
openRPCConsoleAction = new QAction(tr("Node window"), this);
openRPCConsoleAction->setStatusTip(tr("Open node debugging and diagnostic console"));
@@ -349,10 +354,18 @@ void BitcoinGUI::createActions()
m_create_wallet_action->setEnabled(false);
m_create_wallet_action->setStatusTip(tr("Create a new wallet"));
+ m_close_all_wallets_action = new QAction(tr("Close All Wallets..."), this);
+ m_close_all_wallets_action->setStatusTip(tr("Close all wallets"));
+
showHelpMessageAction = new QAction(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(PACKAGE_NAME));
+ m_mask_values_action = new QAction(tr("&Mask values"), this);
+ m_mask_values_action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M));
+ m_mask_values_action->setStatusTip(tr("Mask the values in the Overview tab"));
+ m_mask_values_action->setCheckable(true);
+
connect(quitAction, &QAction::triggered, qApp, QApplication::quit);
connect(aboutAction, &QAction::triggered, this, &BitcoinGUI::aboutClicked);
connect(aboutQtAction, &QAction::triggered, qApp, QApplication::aboutQt);
@@ -372,6 +385,7 @@ void BitcoinGUI::createActions()
connect(signMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(signMessageAction, &QAction::triggered, [this]{ gotoSignMessageTab(); });
connect(m_load_psbt_action, &QAction::triggered, [this]{ gotoLoadPSBT(); });
+ connect(m_load_psbt_clipboard_action, &QAction::triggered, [this]{ gotoLoadPSBT(true); });
connect(verifyMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(verifyMessageAction, &QAction::triggered, [this]{ gotoVerifyMessageTab(); });
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
@@ -415,6 +429,10 @@ void BitcoinGUI::createActions()
connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
activity->create();
});
+ connect(m_close_all_wallets_action, &QAction::triggered, [this] {
+ m_wallet_controller->closeAllWallets(this);
+ });
+ connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::setPrivacy);
}
#endif // ENABLE_WALLET
@@ -439,12 +457,14 @@ void BitcoinGUI::createMenuBar()
file->addAction(m_create_wallet_action);
file->addAction(m_open_wallet_action);
file->addAction(m_close_wallet_action);
+ file->addAction(m_close_all_wallets_action);
file->addSeparator();
file->addAction(openAction);
file->addAction(backupWalletAction);
file->addAction(signMessageAction);
file->addAction(verifyMessageAction);
file->addAction(m_load_psbt_action);
+ file->addAction(m_load_psbt_clipboard_action);
file->addSeparator();
}
file->addAction(quitAction);
@@ -455,6 +475,8 @@ void BitcoinGUI::createMenuBar()
settings->addAction(encryptWalletAction);
settings->addAction(changePassphraseAction);
settings->addSeparator();
+ settings->addAction(m_mask_values_action);
+ settings->addSeparator();
}
settings->addAction(optionsAction);
@@ -567,7 +589,7 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
connect(_clientModel, &ClientModel::networkActiveChanged, this, &BitcoinGUI::setNetworkActive);
modalOverlay->setKnownBestHeight(_clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(_clientModel->getHeaderTipTime()));
- setNumBlocks(m_node.getNumBlocks(), QDateTime::fromTime_t(m_node.getLastBlockTime()), m_node.getVerificationProgress(), false);
+ setNumBlocks(m_node.getNumBlocks(), QDateTime::fromTime_t(m_node.getLastBlockTime()), m_node.getVerificationProgress(), false, SynchronizationState::INIT_DOWNLOAD);
connect(_clientModel, &ClientModel::numBlocksChanged, this, &BitcoinGUI::setNumBlocks);
// Receive and report messages from client model
@@ -663,6 +685,7 @@ void BitcoinGUI::removeWallet(WalletModel* walletModel)
m_wallet_selector->removeItem(index);
if (m_wallet_selector->count() == 0) {
setWalletActionsEnabled(false);
+ overviewAction->setChecked(true);
} else if (m_wallet_selector->count() == 1) {
m_wallet_selector_label_action->setVisible(false);
m_wallet_selector_action->setVisible(false);
@@ -717,6 +740,7 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled)
usedReceivingAddressesAction->setEnabled(enabled);
openAction->setEnabled(enabled);
m_close_wallet_action->setEnabled(enabled);
+ m_close_all_wallets_action->setEnabled(enabled);
}
void BitcoinGUI::createTrayIcon()
@@ -861,9 +885,9 @@ void BitcoinGUI::gotoVerifyMessageTab(QString addr)
{
if (walletFrame) walletFrame->gotoVerifyMessageTab(addr);
}
-void BitcoinGUI::gotoLoadPSBT()
+void BitcoinGUI::gotoLoadPSBT(bool from_clipboard)
{
- if (walletFrame) walletFrame->gotoLoadPSBT();
+ if (walletFrame) walletFrame->gotoLoadPSBT(from_clipboard);
}
#endif // ENABLE_WALLET
@@ -926,11 +950,15 @@ void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab)
dlg.exec();
}
-void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header)
+void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state)
{
// Disabling macOS App Nap on initial sync, disk and reindex operations.
#ifdef Q_OS_MAC
- (m_node.isInitialBlockDownload() || m_node.getReindex() || m_node.getImporting()) ? m_app_nap_inhibitor->disableAppNap() : m_app_nap_inhibitor->enableAppNap();
+ if (sync_state == SynchronizationState::POST_INIT) {
+ m_app_nap_inhibitor->enableAppNap();
+ } else {
+ m_app_nap_inhibitor->disableAppNap();
+ }
#endif
if (modalOverlay)
@@ -1012,7 +1040,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
if(count != prevBlocks)
{
labelBlocksIcon->setPixmap(platformStyle->SingleColorIcon(QString(
- ":/movies/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0')))
+ ":/animation/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0')))
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES;
}
@@ -1048,9 +1076,6 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty
int nMBoxIcon = QMessageBox::Information;
int nNotifyIcon = Notificator::Information;
- bool prefix = !(style & CClientUIInterface::MSG_NOPREFIX);
- style &= ~CClientUIInterface::MSG_NOPREFIX;
-
QString msgType;
if (!title.isEmpty()) {
msgType = title;
@@ -1058,11 +1083,11 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty
switch (style) {
case CClientUIInterface::MSG_ERROR:
msgType = tr("Error");
- if (prefix) message = tr("Error: %1").arg(message);
+ message = tr("Error: %1").arg(message);
break;
case CClientUIInterface::MSG_WARNING:
msgType = tr("Warning");
- if (prefix) message = tr("Warning: %1").arg(message);
+ message = tr("Warning: %1").arg(message);
break;
case CClientUIInterface::MSG_INFORMATION:
msgType = tr("Information");
@@ -1246,7 +1271,7 @@ void BitcoinGUI::setEncryptionStatus(int status)
labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
encryptWalletAction->setChecked(true);
changePassphraseAction->setEnabled(true);
- encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
+ encryptWalletAction->setEnabled(false);
break;
case WalletModel::Locked:
labelWalletEncryptionIcon->show();
@@ -1254,7 +1279,7 @@ void BitcoinGUI::setEncryptionStatus(int status)
labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
encryptWalletAction->setChecked(true);
changePassphraseAction->setEnabled(true);
- encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
+ encryptWalletAction->setEnabled(false);
break;
}
}
@@ -1409,6 +1434,12 @@ void BitcoinGUI::unsubscribeFromCoreSignals()
m_handler_question->disconnect();
}
+bool BitcoinGUI::isPrivacyModeActivated() const
+{
+ assert(m_mask_values_action);
+ return m_mask_values_action->isChecked();
+}
+
UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) :
optionsModel(nullptr),
menu(nullptr)
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 6733585f68..697e83e772 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -38,6 +38,7 @@ class WalletFrame;
class WalletModel;
class HelpMessageDialog;
class ModalOverlay;
+enum class SynchronizationState;
namespace interfaces {
class Handler;
@@ -98,6 +99,8 @@ public:
/** Disconnect core signals from GUI client */
void unsubscribeFromCoreSignals();
+ bool isPrivacyModeActivated() const;
+
protected:
void changeEvent(QEvent *e) override;
void closeEvent(QCloseEvent *event) override;
@@ -136,6 +139,7 @@ private:
QAction* signMessageAction = nullptr;
QAction* verifyMessageAction = nullptr;
QAction* m_load_psbt_action = nullptr;
+ QAction* m_load_psbt_clipboard_action = nullptr;
QAction* aboutAction = nullptr;
QAction* receiveCoinsAction = nullptr;
QAction* receiveCoinsMenuAction = nullptr;
@@ -152,8 +156,10 @@ private:
QAction* m_open_wallet_action{nullptr};
QMenu* m_open_wallet_menu{nullptr};
QAction* m_close_wallet_action{nullptr};
+ QAction* m_close_all_wallets_action{nullptr};
QAction* m_wallet_selector_label_action = nullptr;
QAction* m_wallet_selector_action = nullptr;
+ QAction* m_mask_values_action{nullptr};
QLabel *m_wallet_selector_label = nullptr;
QComboBox* m_wallet_selector = nullptr;
@@ -206,6 +212,7 @@ Q_SIGNALS:
void receivedURI(const QString &uri);
/** Signal raised when RPC console shown */
void consoleShown(RPCConsole* console);
+ void setPrivacy(bool privacy);
public Q_SLOTS:
/** Set number of connections shown in the UI */
@@ -213,7 +220,7 @@ public Q_SLOTS:
/** Set network state shown in the UI */
void setNetworkActive(bool networkActive);
/** Set number of blocks and last block date shown in the UI */
- void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers);
+ void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state);
/** Notify the user of an event from the core network or transaction handling code.
@param[in] title the message box / notification title
@@ -272,8 +279,8 @@ public Q_SLOTS:
void gotoSignMessageTab(QString addr = "");
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
- /** Show load Partially Signed Bitcoin Transaction dialog */
- void gotoLoadPSBT();
+ /** Load Partially Signed Bitcoin Transaction from file or clipboard */
+ void gotoLoadPSBT(bool from_clipboard = false);
/** Show open dialog */
void openClicked();
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index d9711af123..fd55c547fc 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -6,6 +6,8 @@
#include <QStringList>
+#include <cassert>
+
BitcoinUnits::BitcoinUnits(QObject *parent):
QAbstractListModel(parent),
unitlist(availableUnits())
@@ -94,7 +96,7 @@ int BitcoinUnits::decimals(int unit)
}
}
-QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators)
+QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators, bool justify)
{
// Note: not using straight sprintf here because we do NOT want
// localized number formatting.
@@ -106,12 +108,13 @@ QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, Separator
qint64 n_abs = (n > 0 ? n : -n);
qint64 quotient = n_abs / coin;
QString quotient_str = QString::number(quotient);
+ if (justify) quotient_str = quotient_str.rightJustified(16 - num_decimals, ' ');
// Use SI-style thin space separators as these are locale independent and can't be
// confused with the decimal marker.
QChar thin_sp(THIN_SP_CP);
int q_size = quotient_str.size();
- if (separators == separatorAlways || (separators == separatorStandard && q_size > 4))
+ if (separators == SeparatorStyle::ALWAYS || (separators == SeparatorStyle::STANDARD && q_size > 4))
for (int i = 3; i < q_size; i += 3)
quotient_str.insert(q_size - i, thin_sp);
@@ -150,6 +153,17 @@ QString BitcoinUnits::formatHtmlWithUnit(int unit, const CAmount& amount, bool p
return QString("<span style='white-space: nowrap;'>%1</span>").arg(str);
}
+QString BitcoinUnits::formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy)
+{
+ assert(amount >= 0);
+ QString value;
+ if (privacy) {
+ value = format(unit, 0, false, separators, true).replace('0', '#');
+ } else {
+ value = format(unit, amount, false, separators, true);
+ }
+ return value + QString(" ") + shortName(unit);
+}
bool BitcoinUnits::parse(int unit, const QString &value, CAmount *val_out)
{
diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h
index 1ff4702117..e22ba0a938 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -46,11 +46,11 @@ public:
SAT
};
- enum SeparatorStyle
+ enum class SeparatorStyle
{
- separatorNever,
- separatorStandard,
- separatorAlways
+ NEVER,
+ STANDARD,
+ ALWAYS
};
//! @name Static API
@@ -72,11 +72,13 @@ public:
//! Number of decimals left
static int decimals(int unit);
//! Format as string
- static QString format(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
+ static QString format(int unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false);
//! Format as string (with unit)
- static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
+ static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD);
//! Format as HTML string (with unit)
- static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
+ static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD);
+ //! Format as string (with unit) of fixed length to preserve privacy, if it is set.
+ static QString formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy);
//! Parse string to coin amount
static bool parse(int unit, const QString &value, CAmount *val_out);
//! Gets title for amount column including current display unit if optionsModel reference available */
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index b94fcc9865..7822d4c5f3 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -15,6 +15,7 @@
#include <net.h>
#include <netbase.h>
#include <util/system.h>
+#include <validation.h>
#include <stdint.h>
@@ -113,6 +114,29 @@ int ClientModel::getNumBlocks() const
return m_cached_num_blocks;
}
+uint256 ClientModel::getBestBlockHash()
+{
+ uint256 tip{WITH_LOCK(m_cached_tip_mutex, return m_cached_tip_blocks)};
+
+ if (!tip.IsNull()) {
+ return tip;
+ }
+
+ // Lock order must be: first `cs_main`, then `m_cached_tip_mutex`.
+ // The following will lock `cs_main` (and release it), so we must not
+ // own `m_cached_tip_mutex` here.
+ tip = m_node.getBestBlockHash();
+
+ LOCK(m_cached_tip_mutex);
+ // We checked that `m_cached_tip_blocks` is not null above, but then we
+ // released the mutex `m_cached_tip_mutex`, so it could have changed in the
+ // meantime. Thus, check again.
+ if (m_cached_tip_blocks.IsNull()) {
+ m_cached_tip_blocks = tip;
+ }
+ return m_cached_tip_blocks;
+}
+
void ClientModel::updateNumConnections(int numConnections)
{
Q_EMIT numConnectionsChanged(numConnections);
@@ -142,7 +166,7 @@ enum BlockSource ClientModel::getBlockSource() const
QString ClientModel::getStatusBarWarnings() const
{
- return QString::fromStdString(m_node.getWarnings());
+ return QString::fromStdString(m_node.getWarnings().translated);
}
OptionsModel *ClientModel::getOptionsModel()
@@ -234,36 +258,33 @@ static void BannedListChanged(ClientModel *clientmodel)
assert(invoked);
}
-static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int height, int64_t blockTime, double verificationProgress, bool fHeader)
+static void BlockTipChanged(ClientModel* clientmodel, SynchronizationState sync_state, interfaces::BlockTip tip, double verificationProgress, bool fHeader)
{
- // lock free async UI updates in case we have a new block tip
- // during initial sync, only update the UI if the last update
- // was > 250ms (MODEL_UPDATE_DELAY) ago
- int64_t now = 0;
- if (initialSync)
- now = GetTimeMillis();
-
- int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
-
if (fHeader) {
// cache best headers time and height to reduce future cs_main locks
- clientmodel->cachedBestHeaderHeight = height;
- clientmodel->cachedBestHeaderTime = blockTime;
+ clientmodel->cachedBestHeaderHeight = tip.block_height;
+ clientmodel->cachedBestHeaderTime = tip.block_time;
} else {
- clientmodel->m_cached_num_blocks = height;
+ clientmodel->m_cached_num_blocks = tip.block_height;
+ WITH_LOCK(clientmodel->m_cached_tip_mutex, clientmodel->m_cached_tip_blocks = tip.block_hash;);
}
- // During initial sync, block notifications, and header notifications from reindexing are both throttled.
- if (!initialSync || (fHeader && !clientmodel->node().getReindex()) || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) {
- //pass an async signal to the UI thread
- bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
- Q_ARG(int, height),
- Q_ARG(QDateTime, QDateTime::fromTime_t(blockTime)),
- Q_ARG(double, verificationProgress),
- Q_ARG(bool, fHeader));
- assert(invoked);
- nLastUpdateNotification = now;
+ // Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.
+ const bool throttle = (sync_state != SynchronizationState::POST_INIT && !fHeader) || sync_state == SynchronizationState::INIT_REINDEX;
+ const int64_t now = throttle ? GetTimeMillis() : 0;
+ int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
+ if (throttle && now < nLastUpdateNotification + MODEL_UPDATE_DELAY) {
+ return;
}
+
+ bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
+ Q_ARG(int, tip.block_height),
+ Q_ARG(QDateTime, QDateTime::fromTime_t(tip.block_time)),
+ Q_ARG(double, verificationProgress),
+ Q_ARG(bool, fHeader),
+ Q_ARG(SynchronizationState, sync_state));
+ assert(invoked);
+ nLastUpdateNotification = now;
}
void ClientModel::subscribeToCoreSignals()
@@ -274,8 +295,8 @@ void ClientModel::subscribeToCoreSignals()
m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(std::bind(NotifyNetworkActiveChanged, this, std::placeholders::_1));
m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(std::bind(NotifyAlertChanged, this));
m_handler_banned_list_changed = m_node.handleBannedListChanged(std::bind(BannedListChanged, this));
- m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, false));
- m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, true));
+ m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, false));
+ m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, true));
}
void ClientModel::unsubscribeFromCoreSignals()
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index 7ac4120a8f..7f12cce1d9 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -10,12 +10,14 @@
#include <atomic>
#include <memory>
+#include <sync.h>
+#include <uint256.h>
class BanTableModel;
+class CBlockIndex;
class OptionsModel;
class PeerTableModel;
-
-class CBlockIndex;
+enum class SynchronizationState;
namespace interfaces {
class Handler;
@@ -57,6 +59,7 @@ public:
//! Return number of connections, default is in- and outbound (total)
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
int getNumBlocks() const;
+ uint256 getBestBlockHash();
int getHeaderTipHeight() const;
int64_t getHeaderTipTime() const;
@@ -74,11 +77,14 @@ public:
bool getProxyInfo(std::string& ip_port) const;
- // caches for the best header, number of blocks
+ // caches for the best header: hash, number of blocks and block time
mutable std::atomic<int> cachedBestHeaderHeight;
mutable std::atomic<int64_t> cachedBestHeaderTime;
mutable std::atomic<int> m_cached_num_blocks{-1};
+ Mutex m_cached_tip_mutex;
+ uint256 m_cached_tip_blocks GUARDED_BY(m_cached_tip_mutex){};
+
private:
interfaces::Node& m_node;
std::unique_ptr<interfaces::Handler> m_handler_show_progress;
@@ -100,7 +106,7 @@ private:
Q_SIGNALS:
void numConnectionsChanged(int count);
- void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, bool header);
+ void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state);
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes);
void networkActiveChanged(bool networkActive);
void alertsChanged(const QString &warnings);
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index db77c17df0..7c72858501 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -400,7 +400,6 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
// nPayAmount
CAmount nPayAmount = 0;
bool fDust = false;
- CMutableTransaction txDummy;
for (const CAmount &amount : CoinControlDialog::payAmounts)
{
nPayAmount += amount;
@@ -409,7 +408,6 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
{
// Assumes a p2pkh script size
CTxOut txout(amount, CScript() << std::vector<unsigned char>(24, 0));
- txDummy.vout.push_back(txout);
fDust |= IsDust(txout, model->node().getDustRelayFee());
}
}
@@ -458,7 +456,7 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
{
CPubKey pubkey;
PKHash *pkhash = boost::get<PKHash>(&address);
- if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, CKeyID(*pkhash), pubkey))
+ if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, ToKeyID(*pkhash), pubkey))
{
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
}
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 8b70800838..93840b4169 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -1078,22 +1078,16 @@
<height>426</height>
</rect>
</property>
- <property name="minimumSize">
- <size>
- <width>300</width>
- <height>0</height>
- </size>
- </property>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1">
<item row="0" column="0">
<widget class="QLabel" name="label_30">
<property name="text">
- <string>Whitelisted</string>
+ <string>Permissions</string>
</property>
</widget>
</item>
<item row="0" column="1">
- <widget class="QLabel" name="peerWhitelisted">
+ <widget class="QLabel" name="peerPermissions">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
@@ -1270,36 +1264,13 @@
</widget>
</item>
<item row="8" column="0">
- <widget class="QLabel" name="label_24">
- <property name="text">
- <string>Ban Score</string>
- </property>
- </widget>
- </item>
- <item row="8" column="1">
- <widget class="QLabel" name="peerBanScore">
- <property name="cursor">
- <cursorShape>IBeamCursor</cursorShape>
- </property>
- <property name="text">
- <string>N/A</string>
- </property>
- <property name="textFormat">
- <enum>Qt::PlainText</enum>
- </property>
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
- </property>
- </widget>
- </item>
- <item row="9" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Connection Time</string>
</property>
</widget>
</item>
- <item row="9" column="1">
+ <item row="8" column="1">
<widget class="QLabel" name="peerConnTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1315,14 +1286,14 @@
</property>
</widget>
</item>
- <item row="10" column="0">
+ <item row="9" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Last Send</string>
</property>
</widget>
</item>
- <item row="10" column="1">
+ <item row="9" column="1">
<widget class="QLabel" name="peerLastSend">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1338,14 +1309,14 @@
</property>
</widget>
</item>
- <item row="11" column="0">
+ <item row="10" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Last Receive</string>
</property>
</widget>
</item>
- <item row="11" column="1">
+ <item row="10" column="1">
<widget class="QLabel" name="peerLastRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1361,14 +1332,14 @@
</property>
</widget>
</item>
- <item row="12" column="0">
+ <item row="11" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Sent</string>
</property>
</widget>
</item>
- <item row="12" column="1">
+ <item row="11" column="1">
<widget class="QLabel" name="peerBytesSent">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1384,14 +1355,14 @@
</property>
</widget>
</item>
- <item row="13" column="0">
+ <item row="12" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Received</string>
</property>
</widget>
</item>
- <item row="13" column="1">
+ <item row="12" column="1">
<widget class="QLabel" name="peerBytesRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1407,14 +1378,14 @@
</property>
</widget>
</item>
- <item row="14" column="0">
+ <item row="13" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Ping Time</string>
</property>
</widget>
</item>
- <item row="14" column="1">
+ <item row="13" column="1">
<widget class="QLabel" name="peerPingTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1430,7 +1401,7 @@
</property>
</widget>
</item>
- <item row="15" column="0">
+ <item row="14" column="0">
<widget class="QLabel" name="peerPingWaitLabel">
<property name="toolTip">
<string>The duration of a currently outstanding ping.</string>
@@ -1440,7 +1411,7 @@
</property>
</widget>
</item>
- <item row="15" column="1">
+ <item row="14" column="1">
<widget class="QLabel" name="peerPingWait">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1456,14 +1427,14 @@
</property>
</widget>
</item>
- <item row="16" column="0">
+ <item row="15" column="0">
<widget class="QLabel" name="peerMinPingLabel">
<property name="text">
<string>Min Ping</string>
</property>
</widget>
</item>
- <item row="16" column="1">
+ <item row="15" column="1">
<widget class="QLabel" name="peerMinPing">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1479,14 +1450,14 @@
</property>
</widget>
</item>
- <item row="17" column="0">
+ <item row="16" column="0">
<widget class="QLabel" name="label_timeoffset">
<property name="text">
<string>Time Offset</string>
</property>
</widget>
</item>
- <item row="17" column="1">
+ <item row="16" column="1">
<widget class="QLabel" name="timeoffset">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1502,7 +1473,7 @@
</property>
</widget>
</item>
- <item row="18" column="0">
+ <item row="17" column="0">
<widget class="QLabel" name="peerMappedASLabel">
<property name="toolTip">
<string>The mapped Autonomous System used for diversifying peer selection.</string>
@@ -1512,7 +1483,7 @@
</property>
</widget>
</item>
- <item row="18" column="1">
+ <item row="17" column="1">
<widget class="QLabel" name="peerMappedAS">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1528,7 +1499,7 @@
</property>
</widget>
</item>
- <item row="19" column="0">
+ <item row="18" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui
index 710801ee96..4d3f90c484 100644
--- a/src/qt/forms/overviewpage.ui
+++ b/src/qt/forms/overviewpage.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>596</width>
- <height>342</height>
+ <width>798</width>
+ <height>318</height>
</rect>
</property>
<property name="windowTitle">
@@ -118,6 +118,7 @@
<widget class="QLabel" name="labelWatchPending">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -129,7 +130,7 @@
<string>Unconfirmed transactions to watch-only addresses</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">0.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -143,6 +144,7 @@
<widget class="QLabel" name="labelUnconfirmed">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -154,7 +156,7 @@
<string>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">0.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -168,6 +170,7 @@
<widget class="QLabel" name="labelWatchImmature">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -179,7 +182,7 @@
<string>Mined balance in watch-only addresses that has not yet matured</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">0.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -226,6 +229,7 @@
<widget class="QLabel" name="labelImmature">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -237,7 +241,7 @@
<string>Mined balance that has not yet matured</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">0.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -271,6 +275,7 @@
<widget class="QLabel" name="labelTotal">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -282,7 +287,7 @@
<string>Your current total balance</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">21 000 000.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -296,6 +301,7 @@
<widget class="QLabel" name="labelWatchTotal">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -307,7 +313,7 @@
<string>Current total balance in watch-only addresses</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">21 000 000.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -338,6 +344,7 @@
<widget class="QLabel" name="labelBalance">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -349,7 +356,7 @@
<string>Your current spendable balance</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">21 000 000.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -363,6 +370,7 @@
<widget class="QLabel" name="labelWatchAvailable">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -374,7 +382,7 @@
<string>Your current balance in watch-only addresses</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">21 000 000.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
diff --git a/src/qt/forms/psbtoperationsdialog.ui b/src/qt/forms/psbtoperationsdialog.ui
new file mode 100644
index 0000000000..c2e2f5035b
--- /dev/null
+++ b/src/qt/forms/psbtoperationsdialog.ui
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PSBTOperationsDialog</class>
+ <widget class="QDialog" name="PSBTOperationsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>585</width>
+ <height>327</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>12</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="bottomMargin">
+ <number>12</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="mainDialogLayout">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="statusBar">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTextEdit" name="transactionDescription">
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonRowLayout">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="signTransactionButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <weight>50</weight>
+ <bold>false</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Sign Tx</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>false</bool>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="broadcastTransactionButton">
+ <property name="text">
+ <string>Broadcast Tx</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="copyToClipboardButton">
+ <property name="text">
+ <string>Copy to Clipboard</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="saveButton">
+ <property name="text">
+ <string>Save...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closeButton">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui
index 9f896ee3b1..f6d4723465 100644
--- a/src/qt/forms/receiverequestdialog.ui
+++ b/src/qt/forms/receiverequestdialog.ui
@@ -6,68 +6,233 @@
<rect>
<x>0</x>
<y>0</y>
- <width>487</width>
- <height>597</height>
+ <width>413</width>
+ <height>229</height>
</rect>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <item>
- <widget class="QRImageWidget" name="lblQRCode">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>300</width>
- <height>320</height>
- </size>
- </property>
- <property name="toolTip">
- <string>QR Code</string>
+ <property name="windowTitle">
+ <string>Request payment to ...</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetFixedSize</enum>
+ </property>
+ <item row="0" column="0" colspan="2" alignment="Qt::AlignHCenter">
+ <widget class="QRImageWidget" name="qr_code">
+ <property name="text">
+ <string notr="true">QR image</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QLabel" name="payment_header">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Payment information</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
+ <widget class="QLabel" name="uri_tag">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string notr="true">URI:</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" alignment="Qt::AlignTop">
+ <widget class="QLabel" name="uri_content">
+ <property name="text">
+ <string notr="true">bitcoin:BC1...</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
+ <widget class="QLabel" name="address_tag">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Address:</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
</widget>
</item>
- <item>
- <widget class="QTextEdit" name="outUri">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>50</height>
- </size>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Plain</enum>
- </property>
- <property name="tabChangesFocus">
+ <item row="3" column="1" alignment="Qt::AlignTop">
+ <widget class="QLabel" name="address_content">
+ <property name="text">
+ <string notr="true">bc1...</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
+ <widget class="QLabel" name="amount_tag">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Amount:</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" alignment="Qt::AlignTop">
+ <widget class="QLabel" name="amount_content">
+ <property name="text">
+ <string notr="true">0.00000000 BTC</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
+ <widget class="QLabel" name="label_tag">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Label:</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" alignment="Qt::AlignTop">
+ <widget class="QLabel" name="label_content">
+ <property name="text">
+ <string notr="true">label content</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
+ <widget class="QLabel" name="message_tag">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Message:</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1" alignment="Qt::AlignTop">
+ <widget class="QLabel" name="message_content">
+ <property name="text">
+ <string notr="true">message content</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
+ <widget class="QLabel" name="wallet_tag">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Wallet:</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1" alignment="Qt::AlignTop">
+ <widget class="QLabel" name="wallet_content">
+ <property name="text">
+ <string notr="true">wallet name</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
- <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ <set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
- <item>
+ <item row="8" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btnCopyURI">
@@ -114,8 +279,11 @@
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
<property name="standardButtons">
- <set>QDialogButtonBox::Close</set>
+ <set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
@@ -130,37 +298,27 @@
<header>qt/qrimagewidget.h</header>
</customwidget>
</customwidgets>
+ <tabstops>
+ <tabstop>buttonBox</tabstop>
+ <tabstop>btnCopyURI</tabstop>
+ <tabstop>btnCopyAddress</tabstop>
+ <tabstop>btnSaveAs</tabstop>
+ </tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
- <signal>rejected()</signal>
- <receiver>ReceiveRequestDialog</receiver>
- <slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>452</x>
- <y>573</y>
- </hint>
- <hint type="destinationlabel">
- <x>243</x>
- <y>298</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ReceiveRequestDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
- <x>452</x>
- <y>573</y>
+ <x>135</x>
+ <y>230</y>
</hint>
<hint type="destinationlabel">
- <x>243</x>
- <y>298</y>
+ <x>135</x>
+ <y>126</y>
</hint>
</hints>
</connection>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index af86fe5d27..7f439fa45e 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -48,13 +48,14 @@
#include <QKeyEvent>
#include <QLineEdit>
#include <QList>
+#include <QMenu>
#include <QMouseEvent>
#include <QProgressDialog>
#include <QScreen>
#include <QSettings>
+#include <QShortcut>
#include <QSize>
#include <QString>
-#include <QShortcut>
#include <QTextDocument> // for Qt::mightBeRichText
#include <QThread>
#include <QUrlQuery>
@@ -187,7 +188,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info)
if (info.amount)
{
- ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount, false, BitcoinUnits::separatorNever));
+ ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount, false, BitcoinUnits::SeparatorStyle::NEVER));
paramCount++;
}
@@ -231,7 +232,7 @@ QString HtmlEscape(const std::string& str, bool fMultiLine)
return HtmlEscape(QString::fromStdString(str), fMultiLine);
}
-void copyEntryData(QAbstractItemView *view, int column, int role)
+void copyEntryData(const QAbstractItemView *view, int column, int role)
{
if(!view || !view->selectionModel())
return;
@@ -244,13 +245,20 @@ void copyEntryData(QAbstractItemView *view, int column, int role)
}
}
-QList<QModelIndex> getEntryData(QAbstractItemView *view, int column)
+QList<QModelIndex> getEntryData(const QAbstractItemView *view, int column)
{
if(!view || !view->selectionModel())
return QList<QModelIndex>();
return view->selectionModel()->selectedRows(column);
}
+bool hasEntryData(const QAbstractItemView *view, int column, int role)
+{
+ QModelIndexList selection = getEntryData(view, column);
+ if (selection.isEmpty()) return false;
+ return !selection.at(0).data(role).toString().isEmpty();
+}
+
QString getDefaultDataDirectory()
{
return boostPathToQString(GetDefaultDataDir());
@@ -442,6 +450,28 @@ bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
return QObject::eventFilter(obj, evt);
}
+LabelOutOfFocusEventFilter::LabelOutOfFocusEventFilter(QObject* parent)
+ : QObject(parent)
+{
+}
+
+bool LabelOutOfFocusEventFilter::eventFilter(QObject* watched, QEvent* event)
+{
+ if (event->type() == QEvent::FocusOut) {
+ auto focus_out = static_cast<QFocusEvent*>(event);
+ if (focus_out->reason() != Qt::PopupFocusReason) {
+ auto label = qobject_cast<QLabel*>(watched);
+ if (label) {
+ auto flags = label->textInteractionFlags();
+ label->setTextInteractionFlags(Qt::NoTextInteraction);
+ label->setTextInteractionFlags(flags);
+ }
+ }
+ }
+
+ return QObject::eventFilter(watched, event);
+}
+
void TableViewLastColumnResizingFixer::connectViewHeadersSignals()
{
connect(tableView->horizontalHeader(), &QHeaderView::sectionResized, this, &TableViewLastColumnResizingFixer::on_sectionResized);
@@ -743,34 +773,12 @@ QString formatDurationStr(int secs)
return strList.join(" ");
}
-QString serviceFlagToStr(const quint64 mask, const int bit)
-{
- switch (ServiceFlags(mask)) {
- case NODE_NONE: abort(); // impossible
- case NODE_NETWORK: return "NETWORK";
- case NODE_GETUTXO: return "GETUTXO";
- case NODE_BLOOM: return "BLOOM";
- case NODE_WITNESS: return "WITNESS";
- case NODE_NETWORK_LIMITED: return "NETWORK_LIMITED";
- // Not using default, so we get warned when a case is missing
- }
- if (bit < 8) {
- return QString("%1[%2]").arg("UNKNOWN").arg(mask);
- } else {
- return QString("%1[2^%2]").arg("UNKNOWN").arg(bit);
- }
-}
-
QString formatServicesStr(quint64 mask)
{
QStringList strList;
- for (int i = 0; i < 64; i++) {
- uint64_t check = 1LL << i;
- if (mask & check)
- {
- strList.append(serviceFlagToStr(check, i));
- }
+ for (const auto& flag : serviceFlagsToStr(mask)) {
+ strList.append(QString::fromStdString(flag));
}
if (strList.size())
@@ -910,4 +918,11 @@ void LogQtInfo()
}
}
+void PopupMenu(QMenu* menu, const QPoint& point, QAction* at_action)
+{
+ // The qminimal plugin does not provide window system integration.
+ if (QApplication::platformName() == "minimal") return;
+ menu->popup(point, at_action);
+}
+
} // namespace GUIUtil
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 8b9fca4fb1..2bd94b5eb3 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -28,9 +28,12 @@ namespace interfaces
QT_BEGIN_NAMESPACE
class QAbstractItemView;
+class QAction;
class QDateTime;
class QFont;
class QLineEdit;
+class QMenu;
+class QPoint;
class QProgressDialog;
class QUrl;
class QWidget;
@@ -68,14 +71,21 @@ namespace GUIUtil
@param[in] role Data role to extract from the model
@see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress
*/
- void copyEntryData(QAbstractItemView *view, int column, int role=Qt::EditRole);
+ void copyEntryData(const QAbstractItemView *view, int column, int role=Qt::EditRole);
/** Return a field of the currently selected entry as a QString. Does nothing if nothing
is selected.
@param[in] column Data column to extract from the model
@see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress
*/
- QList<QModelIndex> getEntryData(QAbstractItemView *view, int column);
+ QList<QModelIndex> getEntryData(const QAbstractItemView *view, int column);
+
+ /** Returns true if the specified field of the currently selected view entry is not empty.
+ @param[in] column Data column to extract from the model
+ @param[in] role Data role to extract from the model
+ @see TransactionView::contextualMenu
+ */
+ bool hasEntryData(const QAbstractItemView *view, int column, int role);
void setClipboard(const QString& str);
@@ -152,6 +162,21 @@ namespace GUIUtil
};
/**
+ * Qt event filter that intercepts QEvent::FocusOut events for QLabel objects, and
+ * resets their `textInteractionFlags' property to get rid of the visible cursor.
+ *
+ * This is a temporary fix of QTBUG-59514.
+ */
+ class LabelOutOfFocusEventFilter : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ explicit LabelOutOfFocusEventFilter(QObject* parent);
+ bool eventFilter(QObject* watched, QEvent* event) override;
+ };
+
+ /**
* Makes a QTableView last column feel as if it was being resized from its left border.
* Also makes sure the column widths are never larger than the table's viewport.
* In Qt, all columns are resizable from the right, but it's not intuitive resizing the last column from the right.
@@ -273,6 +298,11 @@ namespace GUIUtil
* Writes to debug.log short info about the used Qt and the host system.
*/
void LogQtInfo();
+
+ /**
+ * Call QMenu::popup() only on supported QT_QPA_PLATFORM.
+ */
+ void PopupMenu(QMenu* menu, const QPoint& point, QAction* at_action = nullptr);
} // namespace GUIUtil
#endif // BITCOIN_QT_GUIUTIL_H
diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp
index 49a1992803..8070aa627c 100644
--- a/src/qt/modaloverlay.cpp
+++ b/src/qt/modaloverlay.cpp
@@ -5,12 +5,12 @@
#include <qt/modaloverlay.h>
#include <qt/forms/ui_modaloverlay.h>
-#include <qt/guiutil.h>
-
#include <chainparams.h>
+#include <qt/guiutil.h>
-#include <QResizeEvent>
+#include <QEasingCurve>
#include <QPropertyAnimation>
+#include <QResizeEvent>
ModalOverlay::ModalOverlay(bool enable_wallet, QWidget *parent) :
QWidget(parent),
@@ -33,6 +33,11 @@ userClosed(false)
ui->infoText->setVisible(false);
ui->infoTextStrong->setText(tr("%1 is currently syncing. It will download headers and blocks from peers and validate them until reaching the tip of the block chain.").arg(PACKAGE_NAME));
}
+
+ m_animation.setTargetObject(this);
+ m_animation.setPropertyName("pos");
+ m_animation.setDuration(300 /* ms */);
+ m_animation.setEasingCurve(QEasingCurve::OutQuad);
}
ModalOverlay::~ModalOverlay()
@@ -48,6 +53,9 @@ bool ModalOverlay::eventFilter(QObject * obj, QEvent * ev) {
if (!layerIsVisible)
setGeometry(0, height(), width(), height());
+ if (m_animation.endValue().toPoint().y() > 0) {
+ m_animation.setEndValue(QPoint(0, height()));
+ }
}
else if (ev->type() == QEvent::ChildAdded) {
raise();
@@ -163,17 +171,15 @@ void ModalOverlay::showHide(bool hide, bool userRequested)
if ( (layerIsVisible && !hide) || (!layerIsVisible && hide) || (!hide && userClosed && !userRequested))
return;
+ Q_EMIT triggered(hide);
+
if (!isVisible() && !hide)
setVisible(true);
- setGeometry(0, hide ? 0 : height(), width(), height());
-
- QPropertyAnimation* animation = new QPropertyAnimation(this, "pos");
- animation->setDuration(300);
- animation->setStartValue(QPoint(0, hide ? 0 : this->height()));
- animation->setEndValue(QPoint(0, hide ? this->height() : 0));
- animation->setEasingCurve(QEasingCurve::OutQuad);
- animation->start(QAbstractAnimation::DeleteWhenStopped);
+ m_animation.setStartValue(QPoint(0, hide ? 0 : height()));
+ // The eventFilter() updates the endValue if it is required for QEvent::Resize.
+ m_animation.setEndValue(QPoint(0, hide ? height() : 0));
+ m_animation.start(QAbstractAnimation::KeepWhenStopped);
layerIsVisible = !hide;
}
diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h
index a565d7d8f3..7b07777641 100644
--- a/src/qt/modaloverlay.h
+++ b/src/qt/modaloverlay.h
@@ -6,6 +6,7 @@
#define BITCOIN_QT_MODALOVERLAY_H
#include <QDateTime>
+#include <QPropertyAnimation>
#include <QWidget>
//! The required delta of headers to the estimated number of available headers until we show the IBD progress
@@ -24,16 +25,20 @@ public:
explicit ModalOverlay(bool enable_wallet, QWidget *parent);
~ModalOverlay();
-public Q_SLOTS:
void tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress);
void setKnownBestHeight(int count, const QDateTime& blockDate);
- void toggleVisibility();
// will show or hide the modal layer
void showHide(bool hide = false, bool userRequested = false);
- void closeClicked();
bool isLayerVisible() const { return layerIsVisible; }
+public Q_SLOTS:
+ void toggleVisibility();
+ void closeClicked();
+
+Q_SIGNALS:
+ void triggered(bool hidden);
+
protected:
bool eventFilter(QObject * obj, QEvent * ev) override;
bool event(QEvent* ev) override;
@@ -45,6 +50,7 @@ private:
QVector<QPair<qint64, double> > blockProcessTime;
bool layerIsVisible;
bool userClosed;
+ QPropertyAnimation m_animation;
void UpdateHeaderSyncLabel();
};
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 6ca5ac9d75..14fdf9046e 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -6,6 +6,7 @@
#define BITCOIN_QT_OPTIONSMODEL_H
#include <amount.h>
+#include <cstdint>
#include <qt/guiconstants.h>
#include <QAbstractListModel>
@@ -15,7 +16,7 @@ class Node;
}
extern const char *DEFAULT_GUI_PROXY_HOST;
-static constexpr unsigned short DEFAULT_GUI_PROXY_PORT = 9050;
+static constexpr uint16_t DEFAULT_GUI_PROXY_PORT = 9050;
/**
* Convert configured prune target MiB to displayed GB. Round up to avoid underestimating max disk usage.
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index e20ec229fc..b536567c8b 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -16,7 +16,9 @@
#include <qt/walletmodel.h>
#include <QAbstractItemDelegate>
+#include <QApplication>
#include <QPainter>
+#include <QStatusTipEvent>
#define DECORATION_SIZE 54
#define NUM_ITEMS 5
@@ -86,7 +88,7 @@ public:
foreground = option.palette.color(QPalette::Text);
}
painter->setPen(foreground);
- QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::separatorAlways);
+ QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::SeparatorStyle::ALWAYS);
if(!confirmed)
{
amountText = QString("[") + amountText + QString("]");
@@ -152,6 +154,21 @@ void OverviewPage::handleOutOfSyncWarningClicks()
Q_EMIT outOfSyncWarningClicked();
}
+void OverviewPage::setPrivacy(bool privacy)
+{
+ m_privacy = privacy;
+ if (m_balances.balance != -1) {
+ setBalance(m_balances);
+ }
+
+ ui->listTransactions->setVisible(!m_privacy);
+
+ const QString status_tip = m_privacy ? tr("Privacy mode activated for the Overview tab. To unmask the values, uncheck Settings->Mask values.") : "";
+ setStatusTip(status_tip);
+ QStatusTipEvent event(status_tip);
+ QApplication::sendEvent(this, &event);
+}
+
OverviewPage::~OverviewPage()
{
delete ui;
@@ -163,25 +180,25 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances)
m_balances = balances;
if (walletModel->wallet().isLegacy()) {
if (walletModel->wallet().privateKeysDisabled()) {
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
} else {
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.balance, false, BitcoinUnits::separatorAlways));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways));
- ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_balance, false, BitcoinUnits::separatorAlways));
- ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelWatchAvailable->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelWatchPending->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelWatchImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelWatchTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
}
} else {
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.balance, false, BitcoinUnits::separatorAlways));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways));
- ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_balance, false, BitcoinUnits::separatorAlways));
- ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
}
// only show immature (newly mined) balance if it's non-zero, so as not to complicate things
// for the non-mining users
diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h
index 00ba7ad4ce..4cf673b6a6 100644
--- a/src/qt/overviewpage.h
+++ b/src/qt/overviewpage.h
@@ -39,6 +39,7 @@ public:
public Q_SLOTS:
void setBalance(const interfaces::WalletBalances& balances);
+ void setPrivacy(bool privacy);
Q_SIGNALS:
void transactionClicked(const QModelIndex &index);
@@ -49,6 +50,7 @@ private:
ClientModel *clientModel;
WalletModel *walletModel;
interfaces::WalletBalances m_balances;
+ bool m_privacy{false};
TxViewDelegate *txdelegate;
std::unique_ptr<TransactionFilterProxy> filter;
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index beca78a021..a1da85bda7 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -14,9 +14,9 @@
#include <chainparams.h>
#include <interfaces/node.h>
-#include <policy/policy.h>
#include <key_io.h>
-#include <ui_interface.h>
+#include <node/ui_interface.h>
+#include <policy/policy.h>
#include <util/system.h>
#include <wallet/wallet.h>
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
new file mode 100644
index 0000000000..58167d4bb4
--- /dev/null
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -0,0 +1,268 @@
+// Copyright (c) 2011-2020 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/psbtoperationsdialog.h>
+
+#include <core_io.h>
+#include <interfaces/node.h>
+#include <key_io.h>
+#include <node/psbt.h>
+#include <policy/policy.h>
+#include <qt/bitcoinunits.h>
+#include <qt/forms/ui_psbtoperationsdialog.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
+#include <util/strencodings.h>
+
+#include <iostream>
+
+
+PSBTOperationsDialog::PSBTOperationsDialog(
+ QWidget* parent, WalletModel* wallet_model, ClientModel* client_model) : QDialog(parent),
+ m_ui(new Ui::PSBTOperationsDialog),
+ m_wallet_model(wallet_model),
+ m_client_model(client_model)
+{
+ m_ui->setupUi(this);
+ setWindowTitle("PSBT Operations");
+
+ connect(m_ui->signTransactionButton, &QPushButton::clicked, this, &PSBTOperationsDialog::signTransaction);
+ connect(m_ui->broadcastTransactionButton, &QPushButton::clicked, this, &PSBTOperationsDialog::broadcastTransaction);
+ connect(m_ui->copyToClipboardButton, &QPushButton::clicked, this, &PSBTOperationsDialog::copyToClipboard);
+ connect(m_ui->saveButton, &QPushButton::clicked, this, &PSBTOperationsDialog::saveTransaction);
+
+ connect(m_ui->closeButton, &QPushButton::clicked, this, &PSBTOperationsDialog::close);
+
+ m_ui->signTransactionButton->setEnabled(false);
+ m_ui->broadcastTransactionButton->setEnabled(false);
+}
+
+PSBTOperationsDialog::~PSBTOperationsDialog()
+{
+ delete m_ui;
+}
+
+void PSBTOperationsDialog::openWithPSBT(PartiallySignedTransaction psbtx)
+{
+ m_transaction_data = psbtx;
+
+ bool complete;
+ size_t n_could_sign;
+ FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_could_sign);
+ if (err != TransactionError::OK) {
+ showStatus(tr("Failed to load transaction: %1")
+ .arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
+ return;
+ }
+
+ m_ui->broadcastTransactionButton->setEnabled(complete);
+ m_ui->signTransactionButton->setEnabled(!complete && !m_wallet_model->wallet().privateKeysDisabled() && n_could_sign > 0);
+
+ updateTransactionDisplay();
+}
+
+void PSBTOperationsDialog::signTransaction()
+{
+ bool complete;
+ size_t n_signed;
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_signed);
+
+ if (err != TransactionError::OK) {
+ showStatus(tr("Failed to sign transaction: %1")
+ .arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
+ return;
+ }
+
+ updateTransactionDisplay();
+
+ if (!complete && n_signed < 1) {
+ showStatus(tr("Could not sign any more inputs."), StatusLevel::WARN);
+ } else if (!complete) {
+ showStatus(tr("Signed %1 inputs, but more signatures are still required.").arg(n_signed),
+ StatusLevel::INFO);
+ } else {
+ showStatus(tr("Signed transaction successfully. Transaction is ready to broadcast."),
+ StatusLevel::INFO);
+ m_ui->broadcastTransactionButton->setEnabled(true);
+ }
+}
+
+void PSBTOperationsDialog::broadcastTransaction()
+{
+ CMutableTransaction mtx;
+ if (!FinalizeAndExtractPSBT(m_transaction_data, mtx)) {
+ // This is never expected to fail unless we were given a malformed PSBT
+ // (e.g. with an invalid signature.)
+ showStatus(tr("Unknown error processing transaction."), StatusLevel::ERR);
+ return;
+ }
+
+ CTransactionRef tx = MakeTransactionRef(mtx);
+ std::string err_string;
+ TransactionError error = BroadcastTransaction(
+ *m_client_model->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* await_callback */ false);
+
+ if (error == TransactionError::OK) {
+ showStatus(tr("Transaction broadcast successfully! Transaction ID: %1")
+ .arg(QString::fromStdString(tx->GetHash().GetHex())), StatusLevel::INFO);
+ } else {
+ showStatus(tr("Transaction broadcast failed: %1")
+ .arg(QString::fromStdString(TransactionErrorString(error).translated)), StatusLevel::ERR);
+ }
+}
+
+void PSBTOperationsDialog::copyToClipboard() {
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << m_transaction_data;
+ GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
+ showStatus(tr("PSBT copied to clipboard."), StatusLevel::INFO);
+}
+
+void PSBTOperationsDialog::saveTransaction() {
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << m_transaction_data;
+
+ QString selected_filter;
+ QString filename_suggestion = "";
+ bool first = true;
+ for (const CTxOut& out : m_transaction_data.tx->vout) {
+ if (!first) {
+ filename_suggestion.append("-");
+ }
+ CTxDestination address;
+ ExtractDestination(out.scriptPubKey, address);
+ QString amount = BitcoinUnits::format(m_wallet_model->getOptionsModel()->getDisplayUnit(), out.nValue);
+ QString address_str = QString::fromStdString(EncodeDestination(address));
+ filename_suggestion.append(address_str + "-" + amount);
+ first = false;
+ }
+ filename_suggestion.append(".psbt");
+ QString filename = GUIUtil::getSaveFileName(this,
+ tr("Save Transaction Data"), filename_suggestion,
+ tr("Partially Signed Transaction (Binary) (*.psbt)"), &selected_filter);
+ if (filename.isEmpty()) {
+ return;
+ }
+ std::ofstream out(filename.toLocal8Bit().data());
+ out << ssTx.str();
+ out.close();
+ showStatus(tr("PSBT saved to disk."), StatusLevel::INFO);
+}
+
+void PSBTOperationsDialog::updateTransactionDisplay() {
+ m_ui->transactionDescription->setText(QString::fromStdString(renderTransaction(m_transaction_data)));
+ showTransactionStatus(m_transaction_data);
+}
+
+std::string PSBTOperationsDialog::renderTransaction(const PartiallySignedTransaction &psbtx)
+{
+ QString tx_description = "";
+ CAmount totalAmount = 0;
+ for (const CTxOut& out : psbtx.tx->vout) {
+ CTxDestination address;
+ ExtractDestination(out.scriptPubKey, address);
+ totalAmount += out.nValue;
+ tx_description.append(tr(" * Sends %1 to %2")
+ .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, out.nValue))
+ .arg(QString::fromStdString(EncodeDestination(address))));
+ tx_description.append("<br>");
+ }
+
+ PSBTAnalysis analysis = AnalyzePSBT(psbtx);
+ tx_description.append(" * ");
+ if (!*analysis.fee) {
+ // This happens if the transaction is missing input UTXO information.
+ tx_description.append(tr("Unable to calculate transaction fee or total transaction amount."));
+ } else {
+ tx_description.append(tr("Pays transaction fee: "));
+ tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, *analysis.fee));
+
+ // add total amount in all subdivision units
+ tx_description.append("<hr />");
+ QStringList alternativeUnits;
+ for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits())
+ {
+ if(u != m_client_model->getOptionsModel()->getDisplayUnit()) {
+ alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
+ }
+ }
+ tx_description.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount"))
+ .arg(BitcoinUnits::formatHtmlWithUnit(m_client_model->getOptionsModel()->getDisplayUnit(), totalAmount)));
+ tx_description.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
+ .arg(alternativeUnits.join(" " + tr("or") + " ")));
+ }
+
+ size_t num_unsigned = CountPSBTUnsignedInputs(psbtx);
+ if (num_unsigned > 0) {
+ tx_description.append("<br><br>");
+ tx_description.append(tr("Transaction has %1 unsigned inputs.").arg(QString::number(num_unsigned)));
+ }
+
+ return tx_description.toStdString();
+}
+
+void PSBTOperationsDialog::showStatus(const QString &msg, StatusLevel level) {
+ m_ui->statusBar->setText(msg);
+ switch (level) {
+ case StatusLevel::INFO: {
+ m_ui->statusBar->setStyleSheet("QLabel { background-color : lightgreen }");
+ break;
+ }
+ case StatusLevel::WARN: {
+ m_ui->statusBar->setStyleSheet("QLabel { background-color : orange }");
+ break;
+ }
+ case StatusLevel::ERR: {
+ m_ui->statusBar->setStyleSheet("QLabel { background-color : red }");
+ break;
+ }
+ }
+ m_ui->statusBar->show();
+}
+
+size_t PSBTOperationsDialog::couldSignInputs(const PartiallySignedTransaction &psbtx) {
+ size_t n_signed;
+ bool complete;
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, m_transaction_data, complete, &n_signed);
+
+ if (err != TransactionError::OK) {
+ return 0;
+ }
+ return n_signed;
+}
+
+void PSBTOperationsDialog::showTransactionStatus(const PartiallySignedTransaction &psbtx) {
+ PSBTAnalysis analysis = AnalyzePSBT(psbtx);
+ size_t n_could_sign = couldSignInputs(psbtx);
+
+ switch (analysis.next) {
+ case PSBTRole::UPDATER: {
+ showStatus(tr("Transaction is missing some information about inputs."), StatusLevel::WARN);
+ break;
+ }
+ case PSBTRole::SIGNER: {
+ QString need_sig_text = tr("Transaction still needs signature(s).");
+ StatusLevel level = StatusLevel::INFO;
+ if (m_wallet_model->wallet().privateKeysDisabled()) {
+ need_sig_text += " " + tr("(But this wallet cannot sign transactions.)");
+ level = StatusLevel::WARN;
+ } else if (n_could_sign < 1) {
+ need_sig_text += " " + tr("(But this wallet does not have the right keys.)"); // XXX wording
+ level = StatusLevel::WARN;
+ }
+ showStatus(need_sig_text, level);
+ break;
+ }
+ case PSBTRole::FINALIZER:
+ case PSBTRole::EXTRACTOR: {
+ showStatus(tr("Transaction is fully signed and ready for broadcast."), StatusLevel::INFO);
+ break;
+ }
+ default: {
+ showStatus(tr("Transaction status is unknown."), StatusLevel::ERR);
+ break;
+ }
+ }
+}
diff --git a/src/qt/psbtoperationsdialog.h b/src/qt/psbtoperationsdialog.h
new file mode 100644
index 0000000000..f37bdbe39a
--- /dev/null
+++ b/src/qt/psbtoperationsdialog.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011-2020 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_PSBTOPERATIONSDIALOG_H
+#define BITCOIN_QT_PSBTOPERATIONSDIALOG_H
+
+#include <QDialog>
+
+#include <psbt.h>
+#include <qt/clientmodel.h>
+#include <qt/walletmodel.h>
+
+namespace Ui {
+class PSBTOperationsDialog;
+}
+
+/** Dialog showing transaction details. */
+class PSBTOperationsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit PSBTOperationsDialog(QWidget* parent, WalletModel* walletModel, ClientModel* clientModel);
+ ~PSBTOperationsDialog();
+
+ void openWithPSBT(PartiallySignedTransaction psbtx);
+
+public Q_SLOTS:
+ void signTransaction();
+ void broadcastTransaction();
+ void copyToClipboard();
+ void saveTransaction();
+
+private:
+ Ui::PSBTOperationsDialog* m_ui;
+ PartiallySignedTransaction m_transaction_data;
+ WalletModel* m_wallet_model;
+ ClientModel* m_client_model;
+
+ enum class StatusLevel {
+ INFO,
+ WARN,
+ ERR
+ };
+
+ size_t couldSignInputs(const PartiallySignedTransaction &psbtx);
+ void updateTransactionDisplay();
+ std::string renderTransaction(const PartiallySignedTransaction &psbtx);
+ void showStatus(const QString &msg, StatusLevel level);
+ void showTransactionStatus(const PartiallySignedTransaction &psbtx);
+};
+
+#endif // BITCOIN_QT_PSBTOPERATIONSDIALOG_H
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index 30bd5c6a5a..d385c42821 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -8,10 +8,11 @@
#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
+#include <qt/qrimagewidget.h>
#include <qt/walletmodel.h>
-#include <QClipboard>
-#include <QPixmap>
+#include <QDialog>
+#include <QString>
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h> /* for USE_QRCODE */
@@ -23,14 +24,6 @@ ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) :
model(nullptr)
{
ui->setupUi(this);
-
-#ifndef USE_QRCODE
- ui->btnSaveAs->setVisible(false);
- ui->lblQRCode->setVisible(false);
-#endif
-
- connect(ui->btnSaveAs, &QPushButton::clicked, ui->lblQRCode, &QRImageWidget::saveImage);
-
GUIUtil::handleCloseWindowShortcut(this);
}
@@ -44,7 +37,7 @@ void ReceiveRequestDialog::setModel(WalletModel *_model)
this->model = _model;
if (_model)
- connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &ReceiveRequestDialog::update);
+ connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &ReceiveRequestDialog::updateDisplayUnit);
// update the display unit if necessary
update();
@@ -53,40 +46,55 @@ void ReceiveRequestDialog::setModel(WalletModel *_model)
void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &_info)
{
this->info = _info;
- update();
-}
+ setWindowTitle(tr("Request payment to %1").arg(info.label.isEmpty() ? info.address : info.label));
+ QString uri = GUIUtil::formatBitcoinURI(info);
-void ReceiveRequestDialog::update()
-{
- if(!model)
- return;
- QString target = info.label;
- if(target.isEmpty())
- target = info.address;
- setWindowTitle(tr("Request payment to %1").arg(target));
+#ifdef USE_QRCODE
+ if (ui->qr_code->setQR(uri, info.address)) {
+ connect(ui->btnSaveAs, &QPushButton::clicked, ui->qr_code, &QRImageWidget::saveImage);
+ } else {
+ ui->btnSaveAs->setEnabled(false);
+ }
+#else
+ ui->btnSaveAs->hide();
+ ui->qr_code->hide();
+#endif
- QString uri = GUIUtil::formatBitcoinURI(info);
- ui->btnSaveAs->setEnabled(false);
- QString html;
- html += "<html><font face='verdana, arial, helvetica, sans-serif'>";
- html += "<b>"+tr("Payment information")+"</b><br>";
- html += "<b>"+tr("URI")+"</b>: ";
- html += "<a href=\""+uri+"\">" + GUIUtil::HtmlEscape(uri) + "</a><br>";
- html += "<b>"+tr("Address")+"</b>: " + GUIUtil::HtmlEscape(info.address) + "<br>";
- if(info.amount)
- html += "<b>"+tr("Amount")+"</b>: " + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), info.amount) + "<br>";
- if(!info.label.isEmpty())
- html += "<b>"+tr("Label")+"</b>: " + GUIUtil::HtmlEscape(info.label) + "<br>";
- if(!info.message.isEmpty())
- html += "<b>"+tr("Message")+"</b>: " + GUIUtil::HtmlEscape(info.message) + "<br>";
- if(model->isMultiwallet()) {
- html += "<b>"+tr("Wallet")+"</b>: " + GUIUtil::HtmlEscape(model->getWalletName()) + "<br>";
+ ui->uri_content->setText("<a href=\"" + uri + "\">" + GUIUtil::HtmlEscape(uri) + "</a>");
+ ui->address_content->setText(info.address);
+
+ if (!info.amount) {
+ ui->amount_tag->hide();
+ ui->amount_content->hide();
+ } // Amount is set in updateDisplayUnit() slot.
+ updateDisplayUnit();
+
+ if (!info.label.isEmpty()) {
+ ui->label_content->setText(info.label);
+ } else {
+ ui->label_tag->hide();
+ ui->label_content->hide();
}
- ui->outUri->setText(html);
- if (ui->lblQRCode->setQR(uri, info.address)) {
- ui->btnSaveAs->setEnabled(true);
+ if (!info.message.isEmpty()) {
+ ui->message_content->setText(info.message);
+ } else {
+ ui->message_tag->hide();
+ ui->message_content->hide();
}
+
+ if (!model->getWalletName().isEmpty()) {
+ ui->wallet_content->setText(model->getWalletName());
+ } else {
+ ui->wallet_tag->hide();
+ ui->wallet_content->hide();
+ }
+}
+
+void ReceiveRequestDialog::updateDisplayUnit()
+{
+ if (!model) return;
+ ui->amount_content->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), info.amount));
}
void ReceiveRequestDialog::on_btnCopyURI_clicked()
diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h
index 40e3d5ffa8..846478643d 100644
--- a/src/qt/receiverequestdialog.h
+++ b/src/qt/receiverequestdialog.h
@@ -29,8 +29,7 @@ public:
private Q_SLOTS:
void on_btnCopyURI_clicked();
void on_btnCopyAddress_clicked();
-
- void update();
+ void updateDisplayUnit();
private:
Ui::ReceiveRequestDialog *ui;
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 7419297a96..3e20368a36 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -82,7 +82,7 @@ QVariant RecentRequestsTableModel::data(const QModelIndex &index, int role) cons
if (rec->recipient.amount == 0 && role == Qt::DisplayRole)
return tr("(no amount requested)");
else if (role == Qt::EditRole)
- return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount, false, BitcoinUnits::separatorNever);
+ return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount, false, BitcoinUnits::SeparatorStyle::NEVER);
else
return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount);
}
diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h
index addf5ad0ae..c0bd3461bb 100644
--- a/src/qt/recentrequeststablemodel.h
+++ b/src/qt/recentrequeststablemodel.h
@@ -24,19 +24,11 @@ public:
QDateTime date;
SendCoinsRecipient recipient;
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- unsigned int nDate = date.toTime_t();
-
- READWRITE(this->nVersion);
- READWRITE(id);
- READWRITE(nDate);
- READWRITE(recipient);
-
- if (ser_action.ForRead())
- date = QDateTime::fromTime_t(nDate);
+ SERIALIZE_METHODS(RecentRequestEntry, obj) {
+ unsigned int date_timet;
+ SER_WRITE(obj, date_timet = obj.date.toTime_t());
+ READWRITE(obj.nVersion, obj.id, date_timet, obj.recipient);
+ SER_READ(obj, obj.date = QDateTime::fromTime_t(date_timet));
}
};
diff --git a/src/qt/res/movies/makespinner.sh b/src/qt/res/animation/makespinner.sh
index 4fa8dadf86..4fa8dadf86 100755
--- a/src/qt/res/movies/makespinner.sh
+++ b/src/qt/res/animation/makespinner.sh
diff --git a/src/qt/res/movies/spinner-000.png b/src/qt/res/animation/spinner-000.png
index 0dc48d0d8c..0dc48d0d8c 100644
--- a/src/qt/res/movies/spinner-000.png
+++ b/src/qt/res/animation/spinner-000.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-001.png b/src/qt/res/animation/spinner-001.png
index d167f20541..d167f20541 100644
--- a/src/qt/res/movies/spinner-001.png
+++ b/src/qt/res/animation/spinner-001.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-002.png b/src/qt/res/animation/spinner-002.png
index 4a1f1f8e56..4a1f1f8e56 100644
--- a/src/qt/res/movies/spinner-002.png
+++ b/src/qt/res/animation/spinner-002.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-003.png b/src/qt/res/animation/spinner-003.png
index fb1c2cd4ad..fb1c2cd4ad 100644
--- a/src/qt/res/movies/spinner-003.png
+++ b/src/qt/res/animation/spinner-003.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-004.png b/src/qt/res/animation/spinner-004.png
index 4df2132344..4df2132344 100644
--- a/src/qt/res/movies/spinner-004.png
+++ b/src/qt/res/animation/spinner-004.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-005.png b/src/qt/res/animation/spinner-005.png
index 5d6f41e0dc..5d6f41e0dc 100644
--- a/src/qt/res/movies/spinner-005.png
+++ b/src/qt/res/animation/spinner-005.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-006.png b/src/qt/res/animation/spinner-006.png
index c1f7d18899..c1f7d18899 100644
--- a/src/qt/res/movies/spinner-006.png
+++ b/src/qt/res/animation/spinner-006.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-007.png b/src/qt/res/animation/spinner-007.png
index 1e794b2626..1e794b2626 100644
--- a/src/qt/res/movies/spinner-007.png
+++ b/src/qt/res/animation/spinner-007.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-008.png b/src/qt/res/animation/spinner-008.png
index df12ea8719..df12ea8719 100644
--- a/src/qt/res/movies/spinner-008.png
+++ b/src/qt/res/animation/spinner-008.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-009.png b/src/qt/res/animation/spinner-009.png
index 18fc3a7d16..18fc3a7d16 100644
--- a/src/qt/res/movies/spinner-009.png
+++ b/src/qt/res/animation/spinner-009.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-010.png b/src/qt/res/animation/spinner-010.png
index a79c845fe8..a79c845fe8 100644
--- a/src/qt/res/movies/spinner-010.png
+++ b/src/qt/res/animation/spinner-010.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-011.png b/src/qt/res/animation/spinner-011.png
index 57baf66895..57baf66895 100644
--- a/src/qt/res/movies/spinner-011.png
+++ b/src/qt/res/animation/spinner-011.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-012.png b/src/qt/res/animation/spinner-012.png
index 9deae7853a..9deae7853a 100644
--- a/src/qt/res/movies/spinner-012.png
+++ b/src/qt/res/animation/spinner-012.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-013.png b/src/qt/res/animation/spinner-013.png
index 0659d48dec..0659d48dec 100644
--- a/src/qt/res/movies/spinner-013.png
+++ b/src/qt/res/animation/spinner-013.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-014.png b/src/qt/res/animation/spinner-014.png
index bc1ef51bde..bc1ef51bde 100644
--- a/src/qt/res/movies/spinner-014.png
+++ b/src/qt/res/animation/spinner-014.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-015.png b/src/qt/res/animation/spinner-015.png
index 24b57b62c2..24b57b62c2 100644
--- a/src/qt/res/movies/spinner-015.png
+++ b/src/qt/res/animation/spinner-015.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-016.png b/src/qt/res/animation/spinner-016.png
index d622872651..d622872651 100644
--- a/src/qt/res/movies/spinner-016.png
+++ b/src/qt/res/animation/spinner-016.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-017.png b/src/qt/res/animation/spinner-017.png
index f48f688db2..f48f688db2 100644
--- a/src/qt/res/movies/spinner-017.png
+++ b/src/qt/res/animation/spinner-017.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-018.png b/src/qt/res/animation/spinner-018.png
index a2c8f38b1d..a2c8f38b1d 100644
--- a/src/qt/res/movies/spinner-018.png
+++ b/src/qt/res/animation/spinner-018.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-019.png b/src/qt/res/animation/spinner-019.png
index 9d7cc35d82..9d7cc35d82 100644
--- a/src/qt/res/movies/spinner-019.png
+++ b/src/qt/res/animation/spinner-019.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-020.png b/src/qt/res/animation/spinner-020.png
index 1a07acc454..1a07acc454 100644
--- a/src/qt/res/movies/spinner-020.png
+++ b/src/qt/res/animation/spinner-020.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-021.png b/src/qt/res/animation/spinner-021.png
index 9cea8f2543..9cea8f2543 100644
--- a/src/qt/res/movies/spinner-021.png
+++ b/src/qt/res/animation/spinner-021.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-022.png b/src/qt/res/animation/spinner-022.png
index 60250f6dea..60250f6dea 100644
--- a/src/qt/res/movies/spinner-022.png
+++ b/src/qt/res/animation/spinner-022.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-023.png b/src/qt/res/animation/spinner-023.png
index fc290a0cf2..fc290a0cf2 100644
--- a/src/qt/res/movies/spinner-023.png
+++ b/src/qt/res/animation/spinner-023.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-024.png b/src/qt/res/animation/spinner-024.png
index c5dcf1eae9..c5dcf1eae9 100644
--- a/src/qt/res/movies/spinner-024.png
+++ b/src/qt/res/animation/spinner-024.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-025.png b/src/qt/res/animation/spinner-025.png
index 7f3577a4de..7f3577a4de 100644
--- a/src/qt/res/movies/spinner-025.png
+++ b/src/qt/res/animation/spinner-025.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-026.png b/src/qt/res/animation/spinner-026.png
index 1663ddf44c..1663ddf44c 100644
--- a/src/qt/res/movies/spinner-026.png
+++ b/src/qt/res/animation/spinner-026.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-027.png b/src/qt/res/animation/spinner-027.png
index d0e6da4503..d0e6da4503 100644
--- a/src/qt/res/movies/spinner-027.png
+++ b/src/qt/res/animation/spinner-027.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-028.png b/src/qt/res/animation/spinner-028.png
index 2a7aba50e2..2a7aba50e2 100644
--- a/src/qt/res/movies/spinner-028.png
+++ b/src/qt/res/animation/spinner-028.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-029.png b/src/qt/res/animation/spinner-029.png
index c8ca15c1e1..c8ca15c1e1 100644
--- a/src/qt/res/movies/spinner-029.png
+++ b/src/qt/res/animation/spinner-029.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-030.png b/src/qt/res/animation/spinner-030.png
index c847c99a93..c847c99a93 100644
--- a/src/qt/res/movies/spinner-030.png
+++ b/src/qt/res/animation/spinner-030.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-031.png b/src/qt/res/animation/spinner-031.png
index 403443144e..403443144e 100644
--- a/src/qt/res/movies/spinner-031.png
+++ b/src/qt/res/animation/spinner-031.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-032.png b/src/qt/res/animation/spinner-032.png
index f9db080567..f9db080567 100644
--- a/src/qt/res/movies/spinner-032.png
+++ b/src/qt/res/animation/spinner-032.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-033.png b/src/qt/res/animation/spinner-033.png
index 43f57719e7..43f57719e7 100644
--- a/src/qt/res/movies/spinner-033.png
+++ b/src/qt/res/animation/spinner-033.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-034.png b/src/qt/res/animation/spinner-034.png
index c26656ff17..c26656ff17 100644
--- a/src/qt/res/movies/spinner-034.png
+++ b/src/qt/res/animation/spinner-034.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-035.png b/src/qt/res/animation/spinner-035.png
index e471f950a3..e471f950a3 100644
--- a/src/qt/res/movies/spinner-035.png
+++ b/src/qt/res/animation/spinner-035.png
Binary files differ
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 2d4af3f9e6..29fd720244 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -24,24 +24,23 @@
#include <univalue.h>
#ifdef ENABLE_WALLET
+#include <wallet/bdb.h>
#include <wallet/db.h>
#include <wallet/wallet.h>
#endif
+#include <QFont>
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
-#include <QScrollBar>
#include <QScreen>
+#include <QScrollBar>
#include <QSettings>
#include <QString>
#include <QStringList>
#include <QTime>
#include <QTimer>
-// TODO: add a scrollback limit, as there is currently none
-// TODO: make it possible to filter out categories (esp debug messages when implemented)
-// TODO: receive errors and debug messages through ClientModel
const int CONSOLE_HISTORY = 50;
const int INITIAL_TRAFFIC_GRAPH_MINS = 30;
@@ -468,6 +467,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
// Install event filter for up and down arrow
ui->lineEdit->installEventFilter(this);
+ ui->lineEdit->setMaxLength(16 * 1024 * 1024);
ui->messagesWidget->installEventFilter(this);
connect(ui->clearButton, &QPushButton::clicked, this, &RPCConsole::clear);
@@ -497,7 +497,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
- consoleFontSize = settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()).toInt();
+ consoleFontSize = settings.value(fontSizeSettingsKey, QFont().pointSize()).toInt();
clear();
GUIUtil::handleCloseWindowShortcut(this);
@@ -1120,15 +1120,20 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound"));
ui->peerHeight->setText(QString::number(stats->nodeStats.nStartingHeight));
- ui->peerWhitelisted->setText(stats->nodeStats.m_legacyWhitelisted ? tr("Yes") : tr("No"));
+ if (stats->nodeStats.m_permissionFlags == PF_NONE) {
+ ui->peerPermissions->setText(tr("N/A"));
+ } else {
+ QStringList permissions;
+ for (const auto& permission : NetPermissions::ToStrings(stats->nodeStats.m_permissionFlags)) {
+ permissions.append(QString::fromStdString(permission));
+ }
+ ui->peerPermissions->setText(permissions.join(" & "));
+ }
ui->peerMappedAS->setText(stats->nodeStats.m_mapped_as != 0 ? QString::number(stats->nodeStats.m_mapped_as) : tr("N/A"));
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.
if (stats->fNodeStateStatsAvailable) {
- // Ban score is init to 0
- ui->peerBanScore->setText(QString("%1").arg(stats->nodeStateStats.nMisbehavior));
-
// Sync height is init to -1
if (stats->nodeStateStats.nSyncHeight > -1)
ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight));
@@ -1219,7 +1224,7 @@ void RPCConsole::banSelectedNode(int bantime)
// Find possible nodes, ban it and clear the selected node
const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
if (stats) {
- m_node.ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime);
+ m_node.ban(stats->nodeStats.addr, bantime);
m_node.disconnectByAddress(stats->nodeStats.addr);
}
}
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 9e23fe78d8..97fb88d71c 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -21,9 +21,9 @@
#include <chainparams.h>
#include <interfaces/node.h>
#include <key_io.h>
+#include <node/ui_interface.h>
#include <policy/fees.h>
#include <txmempool.h>
-#include <ui_interface.h>
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
#include <wallet/wallet.h>
@@ -392,7 +392,7 @@ void SendCoinsDialog::on_sendButton_clicked()
CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete);
+ const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
assert(!complete);
assert(err == TransactionError::OK);
// Serialize the PSBT
diff --git a/src/qt/sendcoinsrecipient.h b/src/qt/sendcoinsrecipient.h
index 12279fab64..6619faf417 100644
--- a/src/qt/sendcoinsrecipient.h
+++ b/src/qt/sendcoinsrecipient.h
@@ -44,30 +44,21 @@ public:
static const int CURRENT_VERSION = 1;
int nVersion;
- ADD_SERIALIZE_METHODS;
+ SERIALIZE_METHODS(SendCoinsRecipient, obj)
+ {
+ std::string address_str, label_str, message_str, auth_merchant_str;
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- std::string sAddress = address.toStdString();
- std::string sLabel = label.toStdString();
- std::string sMessage = message.toStdString();
- std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString();
+ SER_WRITE(obj, address_str = obj.address.toStdString());
+ SER_WRITE(obj, label_str = obj.label.toStdString());
+ SER_WRITE(obj, message_str = obj.message.toStdString());
+ SER_WRITE(obj, auth_merchant_str = obj.authenticatedMerchant.toStdString());
- READWRITE(this->nVersion);
- READWRITE(sAddress);
- READWRITE(sLabel);
- READWRITE(amount);
- READWRITE(sMessage);
- READWRITE(sPaymentRequest);
- READWRITE(sAuthenticatedMerchant);
+ READWRITE(obj.nVersion, address_str, label_str, obj.amount, message_str, obj.sPaymentRequest, auth_merchant_str);
- if (ser_action.ForRead())
- {
- address = QString::fromStdString(sAddress);
- label = QString::fromStdString(sLabel);
- message = QString::fromStdString(sMessage);
- authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant);
- }
+ SER_READ(obj, obj.address = QString::fromStdString(address_str));
+ SER_READ(obj, obj.label = QString::fromStdString(label_str));
+ SER_READ(obj, obj.message = QString::fromStdString(message_str));
+ SER_READ(obj, obj.authenticatedMerchant = QString::fromStdString(auth_merchant_str));
}
};
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 5cfbb67449..4835dd7954 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -91,6 +91,7 @@ void SignVerifyMessageDialog::on_addressBookButton_SM_clicked()
{
if (model && model->getAddressTableModel())
{
+ model->refresh(/* pk_hash_only */ true);
AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this);
dlg.setModel(model->getAddressTableModel());
if (dlg.exec())
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index ced6a299d5..6e6b2b8466 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -14,7 +14,6 @@
#include <interfaces/wallet.h>
#include <qt/guiutil.h>
#include <qt/networkstyle.h>
-#include <ui_interface.h>
#include <util/system.h>
#include <util/translation.h>
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 476128520c..9347ff9e42 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -59,7 +59,7 @@ void EditAddressAndSubmit(
void TestAddAddressesToSendBook(interfaces::Node& node)
{
TestChain100Setup test;
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), WalletDatabase::CreateMock());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
bool firstRun;
wallet->LoadWallet(firstRun);
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index f88d57c716..443e2d047d 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -62,9 +62,10 @@ void AppTests::appTests()
}
#endif
- BasicTestingSetup test{CBaseChainParams::REGTEST}; // Create a temp data directory to backup the gui settings to
- ECC_Stop(); // Already started by the common test setup, so stop it to avoid interference
- LogInstance().DisconnectTestLogger();
+ fs::create_directories([] {
+ BasicTestingSetup test{CBaseChainParams::REGTEST}; // Create a temp data directory to backup the gui settings to
+ return GetDataDir() / "blocks";
+ }());
m_app.parameterSetup();
m_app.createOptionsModel(true /* reset settings */);
@@ -80,6 +81,7 @@ void AppTests::appTests()
m_app.exec();
// Reset global state to avoid interfering with later tests.
+ LogInstance().DisconnectTestLogger();
AbortShutdown();
UnloadBlockIndex();
WITH_LOCK(::cs_main, g_chainman.Reset());
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index aefdcd2716..12efca2503 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -40,7 +40,7 @@ Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
// This is all you need to run all the tests
-int main(int argc, char *argv[])
+int main(int argc, char* argv[])
{
// Initialize persistent globals with the testing setup state for sanity.
// E.g. -datadir in gArgs is set to a temp directory dummy value (instead
@@ -70,6 +70,8 @@ int main(int argc, char *argv[])
BitcoinApplication app(*node);
app.setApplicationName("Bitcoin-Qt-test");
+ node->setupServerArgs(); // Make gArgs available in the NodeContext
+ node->context()->args->ClearArgs(); // Clear added args again
AppTests app_tests(app);
if (QTest::qExec(&app_tests) != 0) {
fInvalid = true;
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 2ee9ae0d86..6648029bae 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -140,7 +140,7 @@ void TestGUI(interfaces::Node& node)
}
node.context()->connman = std::move(test.m_node.connman);
node.context()->mempool = std::move(test.m_node.mempool);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), WalletDatabase::CreateMock());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase());
bool firstRun;
wallet->LoadWallet(firstRun);
{
@@ -178,7 +178,7 @@ void TestGUI(interfaces::Node& node)
QString balanceText = balanceLabel->text();
int unit = walletModel.getOptionsModel()->getDisplayUnit();
CAmount balance = walletModel.wallet().getBalance();
- QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways);
+ QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS);
QCOMPARE(balanceText, balanceComparison);
}
@@ -201,10 +201,10 @@ void TestGUI(interfaces::Node& node)
OverviewPage overviewPage(platformStyle.get());
overviewPage.setWalletModel(&walletModel);
QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance");
- QString balanceText = balanceLabel->text();
+ QString balanceText = balanceLabel->text().trimmed();
int unit = walletModel.getOptionsModel()->getDisplayUnit();
CAmount balance = walletModel.wallet().getBalance();
- QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways);
+ QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS);
QCOMPARE(balanceText, balanceComparison);
// Check Request Payment button
@@ -229,15 +229,23 @@ void TestGUI(interfaces::Node& node)
for (QWidget* widget : QApplication::topLevelWidgets()) {
if (widget->inherits("ReceiveRequestDialog")) {
ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget);
- QTextEdit* rlist = receiveRequestDialog->QObject::findChild<QTextEdit*>("outUri");
- QString paymentText = rlist->toPlainText();
- QStringList paymentTextList = paymentText.split('\n');
- QCOMPARE(paymentTextList.at(0), QString("Payment information"));
- QVERIFY(paymentTextList.at(1).indexOf(QString("URI: bitcoin:")) != -1);
- QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1);
- QCOMPARE(paymentTextList.at(3), QString("Amount: 0.00000001 ") + QString::fromStdString(CURRENCY_UNIT));
- QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1"));
- QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1"));
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("payment_header")->text(), QString("Payment information"));
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("uri_tag")->text(), QString("URI:"));
+ QString uri = receiveRequestDialog->QObject::findChild<QLabel*>("uri_content")->text();
+ QCOMPARE(uri.count("bitcoin:"), 2);
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("address_tag")->text(), QString("Address:"));
+
+ QCOMPARE(uri.count("amount=0.00000001"), 2);
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_tag")->text(), QString("Amount:"));
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_content")->text(), QString("0.00000001 ") + QString::fromStdString(CURRENCY_UNIT));
+
+ QCOMPARE(uri.count("label=TEST_LABEL_1"), 2);
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("label_tag")->text(), QString("Label:"));
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("label_content")->text(), QString("TEST_LABEL_1"));
+
+ QCOMPARE(uri.count("message=TEST_MESSAGE_1"), 2);
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("message_tag")->text(), QString("Message:"));
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("message_content")->text(), QString("TEST_MESSAGE_1"));
}
}
diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp
index 757648f485..6428fc4daf 100644
--- a/src/qt/trafficgraphwidget.cpp
+++ b/src/qt/trafficgraphwidget.cpp
@@ -7,6 +7,7 @@
#include <qt/clientmodel.h>
#include <QPainter>
+#include <QPainterPath>
#include <QColor>
#include <QTimer>
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index a32d218fc9..632a18de5c 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -47,7 +47,6 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
if(mine)
{
TransactionRecord sub(hash, nTime);
- CTxDestination address;
sub.idx = i; // vout index
sub.credit = txout.nValue;
sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;
@@ -162,7 +161,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
return parts;
}
-void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int numBlocks, int64_t block_time)
+void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, const uint256& block_hash, int numBlocks, int64_t block_time)
{
// Determine transaction status
@@ -174,7 +173,7 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int
idx);
status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0);
status.depth = wtx.depth_in_main_chain;
- status.cur_num_blocks = numBlocks;
+ status.m_cur_block_hash = block_hash;
const bool up_to_date = ((int64_t)QDateTime::currentMSecsSinceEpoch() / 1000 - block_time < MAX_BLOCK_TIME_GAP);
if (up_to_date && !wtx.is_final) {
@@ -233,9 +232,10 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int
status.needsUpdate = false;
}
-bool TransactionRecord::statusUpdateNeeded(int numBlocks) const
+bool TransactionRecord::statusUpdateNeeded(const uint256& block_hash) const
{
- return status.cur_num_blocks != numBlocks || status.needsUpdate;
+ assert(!block_hash.IsNull());
+ return status.m_cur_block_hash != block_hash || status.needsUpdate;
}
QString TransactionRecord::getTxHash() const
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index 3f64cefd09..c983c527c0 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -23,9 +23,8 @@ struct WalletTxStatus;
class TransactionStatus
{
public:
- TransactionStatus():
- countsForBalance(false), sortKey(""),
- matures_in(0), status(Unconfirmed), depth(0), open_for(0), cur_num_blocks(-1)
+ TransactionStatus() : countsForBalance(false), sortKey(""),
+ matures_in(0), status(Unconfirmed), depth(0), open_for(0)
{ }
enum Status {
@@ -61,8 +60,8 @@ public:
finalization */
/**@}*/
- /** Current number of blocks (to know whether cached status is still valid) */
- int cur_num_blocks;
+ /** Current block hash (to know whether cached status is still valid) */
+ uint256 m_cur_block_hash{};
bool needsUpdate;
};
@@ -138,11 +137,11 @@ public:
/** Update status from core wallet tx.
*/
- void updateStatus(const interfaces::WalletTxStatus& wtx, int numBlocks, int64_t block_time);
+ void updateStatus(const interfaces::WalletTxStatus& wtx, const uint256& block_hash, int numBlocks, int64_t block_time);
/** Return whether a status update is needed.
*/
- bool statusUpdateNeeded(int numBlocks) const;
+ bool statusUpdateNeeded(const uint256& block_hash) const;
};
#endif // BITCOIN_QT_TRANSACTIONRECORD_H
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 18554aef1f..c560dc58e7 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -176,24 +176,19 @@ public:
return cachedWallet.size();
}
- TransactionRecord *index(interfaces::Wallet& wallet, const int cur_num_blocks, const int idx)
+ TransactionRecord* index(interfaces::Wallet& wallet, const uint256& cur_block_hash, const int idx)
{
- if(idx >= 0 && idx < cachedWallet.size())
- {
+ if (idx >= 0 && idx < cachedWallet.size()) {
TransactionRecord *rec = &cachedWallet[idx];
- // Get required locks upfront. This avoids the GUI from getting
- // stuck if the core is holding the locks for a longer time - for
- // example, during a wallet rescan.
- //
// If a status update is needed (blocks came in since last check),
- // update the status of this transaction from the wallet. Otherwise,
- // simply re-use the cached status.
+ // try to update the status of this transaction from the wallet.
+ // Otherwise, simply re-use the cached status.
interfaces::WalletTxStatus wtx;
int numBlocks;
int64_t block_time;
- if (rec->statusUpdateNeeded(cur_num_blocks) && wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, block_time)) {
- rec->updateStatus(wtx, numBlocks, block_time);
+ if (!cur_block_hash.IsNull() && rec->statusUpdateNeeded(cur_block_hash) && wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, block_time)) {
+ rec->updateStatus(wtx, cur_block_hash, numBlocks, block_time);
}
return rec;
}
@@ -524,7 +519,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case ToAddress:
return formatTxToAddress(rec, false);
case Amount:
- return formatTxAmount(rec, true, BitcoinUnits::separatorAlways);
+ return formatTxAmount(rec, true, BitcoinUnits::SeparatorStyle::ALWAYS);
}
break;
case Qt::EditRole:
@@ -614,14 +609,14 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
details.append(QString::fromStdString(rec->address));
details.append(" ");
}
- details.append(formatTxAmount(rec, false, BitcoinUnits::separatorNever));
+ details.append(formatTxAmount(rec, false, BitcoinUnits::SeparatorStyle::NEVER));
return details;
}
case ConfirmedRole:
- return rec->status.countsForBalance;
+ return rec->status.status == TransactionStatus::Status::Confirming || rec->status.status == TransactionStatus::Status::Confirmed;
case FormattedAmountRole:
// Used for copy/export, so don't include separators
- return formatTxAmount(rec, false, BitcoinUnits::separatorNever);
+ return formatTxAmount(rec, false, BitcoinUnits::SeparatorStyle::NEVER);
case StatusRole:
return rec->status.status;
}
@@ -664,7 +659,7 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat
QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
- TransactionRecord *data = priv->index(walletModel->wallet(), walletModel->clientModel().getNumBlocks(), row);
+ TransactionRecord* data = priv->index(walletModel->wallet(), walletModel->getLastBlockProcessed(), row);
if(data)
{
return createIndex(row, column, data);
diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h
index f06f0ea15f..4b699d4d7d 100644
--- a/src/qt/transactiontablemodel.h
+++ b/src/qt/transactiontablemodel.h
@@ -101,7 +101,7 @@ private:
QString formatTxDate(const TransactionRecord *wtx) const;
QString formatTxType(const TransactionRecord *wtx) const;
QString formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const;
- QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::separatorStandard) const;
+ QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::SeparatorStyle::STANDARD) const;
QString formatTooltip(const TransactionRecord *rec) const;
QVariant txStatusDecoration(const TransactionRecord *wtx) const;
QVariant txWatchonlyDecoration(const TransactionRecord *wtx) const;
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 3c638fb358..54ecfc38ec 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -8,6 +8,7 @@
#include <qt/bitcoinunits.h>
#include <qt/csvmodelwriter.h>
#include <qt/editaddressdialog.h>
+#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/transactiondescdialog.h>
@@ -16,7 +17,7 @@
#include <qt/transactiontablemodel.h>
#include <qt/walletmodel.h>
-#include <ui_interface.h>
+#include <node/ui_interface.h>
#include <QApplication>
#include <QComboBox>
@@ -36,8 +37,7 @@
#include <QVBoxLayout>
TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) :
- QWidget(parent), model(nullptr), transactionProxyModel(nullptr),
- transactionView(nullptr), abandonAction(nullptr), bumpFeeAction(nullptr), columnResizingFixer(nullptr)
+ QWidget(parent)
{
// Build filter row
setContentsMargins(0,0,0,0);
@@ -152,8 +152,8 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
abandonAction = new QAction(tr("Abandon transaction"), this);
bumpFeeAction = new QAction(tr("Increase transaction fee"), this);
bumpFeeAction->setObjectName("bumpFeeAction");
- QAction *copyAddressAction = new QAction(tr("Copy address"), this);
- QAction *copyLabelAction = new QAction(tr("Copy label"), this);
+ copyAddressAction = new QAction(tr("Copy address"), this);
+ copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
QAction *copyTxIDAction = new QAction(tr("Copy transaction ID"), this);
QAction *copyTxHexAction = new QAction(tr("Copy raw transaction"), this);
@@ -395,10 +395,11 @@ void TransactionView::contextualMenu(const QPoint &point)
hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString());
abandonAction->setEnabled(model->wallet().transactionCanBeAbandoned(hash));
bumpFeeAction->setEnabled(model->wallet().transactionCanBeBumped(hash));
+ copyAddressAction->setEnabled(GUIUtil::hasEntryData(transactionView, 0, TransactionTableModel::AddressRole));
+ copyLabelAction->setEnabled(GUIUtil::hasEntryData(transactionView, 0, TransactionTableModel::LabelRole));
- if(index.isValid())
- {
- contextMenu->popup(transactionView->viewport()->mapToGlobal(point));
+ if (index.isValid()) {
+ GUIUtil::PopupMenu(contextMenu, transactionView->viewport()->mapToGlobal(point));
}
}
diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h
index 268e3751b3..9ce7f4ad97 100644
--- a/src/qt/transactionview.h
+++ b/src/qt/transactionview.h
@@ -60,9 +60,9 @@ public:
};
private:
- WalletModel *model;
- TransactionFilterProxy *transactionProxyModel;
- QTableView *transactionView;
+ WalletModel *model{nullptr};
+ TransactionFilterProxy *transactionProxyModel{nullptr};
+ QTableView *transactionView{nullptr};
QComboBox *dateWidget;
QComboBox *typeWidget;
@@ -75,12 +75,14 @@ private:
QFrame *dateRangeWidget;
QDateTimeEdit *dateFrom;
QDateTimeEdit *dateTo;
- QAction *abandonAction;
- QAction *bumpFeeAction;
+ QAction *abandonAction{nullptr};
+ QAction *bumpFeeAction{nullptr};
+ QAction *copyAddressAction{nullptr};
+ QAction *copyLabelAction{nullptr};
QWidget *createDateRangeWidget();
- GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer;
+ GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer{nullptr};
virtual void resizeEvent(QResizeEvent* event) override;
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 20f2ef5b5f..3aed98e0e8 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -92,6 +92,23 @@ void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)
removeAndDeleteWallet(wallet_model);
}
+void WalletController::closeAllWallets(QWidget* parent)
+{
+ QMessageBox::StandardButton button = QMessageBox::question(parent, tr("Close all wallets"),
+ tr("Are you sure you wish to close all wallets?"),
+ QMessageBox::Yes|QMessageBox::Cancel,
+ QMessageBox::Yes);
+ if (button != QMessageBox::Yes) return;
+
+ QMutexLocker locker(&m_mutex);
+ for (WalletModel* wallet_model : m_wallets) {
+ wallet_model->wallet().remove();
+ Q_EMIT walletRemoved(wallet_model);
+ delete wallet_model;
+ }
+ m_wallets.clear();
+}
+
WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet)
{
QMutexLocker locker(&m_mutex);
@@ -245,7 +262,7 @@ void CreateWalletActivity::finish()
{
destroyProgressDialog();
- if (!m_error_message.original.empty()) {
+ if (!m_error_message.empty()) {
QMessageBox::critical(m_parent_widget, tr("Create wallet failed"), QString::fromStdString(m_error_message.translated));
} else if (!m_warning_message.empty()) {
QMessageBox::warning(m_parent_widget, tr("Create wallet warning"), QString::fromStdString(Join(m_warning_message, Untranslated("\n")).translated));
@@ -286,7 +303,7 @@ void OpenWalletActivity::finish()
{
destroyProgressDialog();
- if (!m_error_message.original.empty()) {
+ if (!m_error_message.empty()) {
QMessageBox::critical(m_parent_widget, tr("Open wallet failed"), QString::fromStdString(m_error_message.translated));
} else if (!m_warning_message.empty()) {
QMessageBox::warning(m_parent_widget, tr("Open wallet warning"), QString::fromStdString(Join(m_warning_message, Untranslated("\n")).translated));
diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h
index 24dd83adf7..f7e366878d 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -62,6 +62,7 @@ public:
std::map<std::string, bool> listWalletDir() const;
void closeWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
+ void closeAllWallets(QWidget* parent = nullptr);
Q_SIGNALS:
void walletAdded(WalletModel* wallet_model);
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 02a9583ae9..ec56f2755f 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -53,6 +53,7 @@ bool WalletFrame::addWallet(WalletModel *walletModel)
walletView->setClientModel(clientModel);
walletView->setWalletModel(walletModel);
walletView->showOutOfSyncWarning(bOutOfSync);
+ walletView->setPrivacy(gui->isPrivacyModeActivated());
WalletView* current_wallet_view = currentWalletView();
if (current_wallet_view) {
@@ -73,6 +74,7 @@ bool WalletFrame::addWallet(WalletModel *walletModel)
connect(walletView, &WalletView::encryptionStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
connect(walletView, &WalletView::incomingTransaction, gui, &BitcoinGUI::incomingTransaction);
connect(walletView, &WalletView::hdEnabledStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
+ connect(gui, &BitcoinGUI::setPrivacy, walletView, &WalletView::setPrivacy);
return true;
}
@@ -163,11 +165,11 @@ void WalletFrame::gotoVerifyMessageTab(QString addr)
walletView->gotoVerifyMessageTab(addr);
}
-void WalletFrame::gotoLoadPSBT()
+void WalletFrame::gotoLoadPSBT(bool from_clipboard)
{
WalletView *walletView = currentWalletView();
if (walletView) {
- walletView->gotoLoadPSBT();
+ walletView->gotoLoadPSBT(from_clipboard);
}
}
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index d90ade5005..2b5f263468 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -79,7 +79,7 @@ public Q_SLOTS:
void gotoVerifyMessageTab(QString addr = "");
/** Load Partially Signed Bitcoin Transaction */
- void gotoLoadPSBT();
+ void gotoLoadPSBT(bool from_clipboard = false);
/** Encrypt the wallet */
void encryptWallet(bool status);
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 70ee7f4917..e374dd191c 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -21,8 +21,8 @@
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <key_io.h>
+#include <node/ui_interface.h>
#include <psbt.h>
-#include <ui_interface.h>
#include <util/system.h> // for GetBoolArg
#include <util/translation.h>
#include <wallet/coincontrol.h>
@@ -39,14 +39,14 @@
WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel& client_model, const PlatformStyle *platformStyle, QObject *parent) :
QObject(parent),
m_wallet(std::move(wallet)),
- m_client_model(client_model),
+ m_client_model(&client_model),
m_node(client_model.node()),
optionsModel(client_model.getOptionsModel()),
addressTableModel(nullptr),
transactionTableModel(nullptr),
recentRequestsTableModel(nullptr),
cachedEncryptionStatus(Unencrypted),
- cachedNumBlocks(0)
+ timer(new QTimer(this))
{
fHaveWatchOnly = m_wallet->haveWatchOnly();
addressTableModel = new AddressTableModel(this);
@@ -64,11 +64,16 @@ WalletModel::~WalletModel()
void WalletModel::startPollBalance()
{
// This timer will be fired repeatedly to update the balance
- QTimer* timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &WalletModel::pollBalanceChanged);
timer->start(MODEL_UPDATE_DELAY);
}
+void WalletModel::setClientModel(ClientModel* client_model)
+{
+ m_client_model = client_model;
+ if (!m_client_model) timer->stop();
+}
+
void WalletModel::updateStatus()
{
EncryptionStatus newEncryptionStatus = getEncryptionStatus();
@@ -80,24 +85,30 @@ void WalletModel::updateStatus()
void WalletModel::pollBalanceChanged()
{
+ // Avoid recomputing wallet balances unless a TransactionChanged or
+ // BlockTip notification was received.
+ if (!fForceCheckBalanceChanged && m_cached_last_update_tip == getLastBlockProcessed()) return;
+
// Try to get balances and return early if locks can't be acquired. This
// avoids the GUI from getting stuck on periodical polls if the core is
// holding the locks for a longer time - for example, during a wallet
// rescan.
interfaces::WalletBalances new_balances;
- int numBlocks = -1;
- if (!m_wallet->tryGetBalances(new_balances, numBlocks, fForceCheckBalanceChanged, cachedNumBlocks)) {
+ uint256 block_hash;
+ if (!m_wallet->tryGetBalances(new_balances, block_hash)) {
return;
}
- fForceCheckBalanceChanged = false;
+ if (fForceCheckBalanceChanged || block_hash != m_cached_last_update_tip) {
+ fForceCheckBalanceChanged = false;
- // Balance and number of transactions might have changed
- cachedNumBlocks = numBlocks;
+ // Balance and number of transactions might have changed
+ m_cached_last_update_tip = block_hash;
- checkBalanceChanged(new_balances);
- if(transactionTableModel)
- transactionTableModel->updateConfirmations();
+ checkBalanceChanged(new_balances);
+ if(transactionTableModel)
+ transactionTableModel->updateConfirmations();
+ }
}
void WalletModel::checkBalanceChanged(const interfaces::WalletBalances& new_balances)
@@ -304,16 +315,10 @@ WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const
bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase)
{
- if(encrypted)
- {
- // Encrypt
+ if (encrypted) {
return m_wallet->encryptWallet(passphrase);
}
- else
- {
- // Decrypt -- TODO; not supported yet
- return false;
- }
+ return false;
}
bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase)
@@ -531,7 +536,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
if (create_psbt) {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete);
+ const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
if (err != TransactionError::OK || complete) {
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't draft transaction."));
return false;
@@ -578,3 +583,13 @@ bool WalletModel::isMultiwallet()
{
return m_node.getWallets().size() > 1;
}
+
+void WalletModel::refresh(bool pk_hash_only)
+{
+ addressTableModel = new AddressTableModel(this, pk_hash_only);
+}
+
+uint256 WalletModel::getLastBlockProcessed() const
+{
+ return m_client_model ? m_client_model->getBestBlockHash() : uint256{};
+}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 07004b7c6b..fd52db2da3 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -144,7 +144,8 @@ public:
interfaces::Node& node() const { return m_node; }
interfaces::Wallet& wallet() const { return *m_wallet; }
- ClientModel& clientModel() const { return m_client_model; }
+ ClientModel& clientModel() const { return *m_client_model; }
+ void setClientModel(ClientModel* client_model);
QString getWalletName() const;
QString getDisplayName() const;
@@ -152,6 +153,11 @@ public:
bool isMultiwallet();
AddressTableModel* getAddressTableModel() const { return addressTableModel; }
+
+ void refresh(bool pk_hash_only = false);
+
+ uint256 getLastBlockProcessed() const;
+
private:
std::unique_ptr<interfaces::Wallet> m_wallet;
std::unique_ptr<interfaces::Handler> m_handler_unload;
@@ -161,7 +167,7 @@ private:
std::unique_ptr<interfaces::Handler> m_handler_show_progress;
std::unique_ptr<interfaces::Handler> m_handler_watch_only_changed;
std::unique_ptr<interfaces::Handler> m_handler_can_get_addrs_changed;
- ClientModel& m_client_model;
+ ClientModel* m_client_model;
interfaces::Node& m_node;
bool fHaveWatchOnly;
@@ -178,7 +184,10 @@ private:
// Cache some values to be able to detect changes
interfaces::WalletBalances m_cached_balances;
EncryptionStatus cachedEncryptionStatus;
- int cachedNumBlocks;
+ QTimer* timer;
+
+ // Block hash denoting when the last balance update was done.
+ uint256 m_cached_last_update_tip{};
void subscribeToCoreSignals();
void unsubscribeFromCoreSignals();
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 5d9b420df7..2fc883a5f5 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -4,13 +4,11 @@
#include <qt/walletview.h>
-#include <node/psbt.h>
-#include <node/transaction.h>
-#include <policy/policy.h>
#include <qt/addressbookpage.h>
#include <qt/askpassphrasedialog.h>
#include <qt/clientmodel.h>
#include <qt/guiutil.h>
+#include <qt/psbtoperationsdialog.h>
#include <qt/optionsmodel.h>
#include <qt/overviewpage.h>
#include <qt/platformstyle.h>
@@ -22,11 +20,14 @@
#include <qt/walletmodel.h>
#include <interfaces/node.h>
-#include <ui_interface.h>
+#include <node/ui_interface.h>
+#include <psbt.h>
#include <util/strencodings.h>
#include <QAction>
#include <QActionGroup>
+#include <QApplication>
+#include <QClipboard>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QProgressDialog>
@@ -85,6 +86,8 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
connect(sendCoinsPage, &SendCoinsDialog::message, this, &WalletView::message);
// Pass through messages from transactionView
connect(transactionView, &TransactionView::message, this, &WalletView::message);
+
+ connect(this, &WalletView::setPrivacy, overviewPage, &OverviewPage::setPrivacy);
}
WalletView::~WalletView()
@@ -97,6 +100,7 @@ void WalletView::setClientModel(ClientModel *_clientModel)
overviewPage->setClientModel(_clientModel);
sendCoinsPage->setClientModel(_clientModel);
+ if (walletModel) walletModel->setClientModel(_clientModel);
}
void WalletView::setWalletModel(WalletModel *_walletModel)
@@ -201,78 +205,42 @@ void WalletView::gotoVerifyMessageTab(QString addr)
signVerifyMessageDialog->setAddress_VM(addr);
}
-void WalletView::gotoLoadPSBT()
+void WalletView::gotoLoadPSBT(bool from_clipboard)
{
- QString filename = GUIUtil::getOpenFileName(this,
- tr("Load Transaction Data"), QString(),
- tr("Partially Signed Transaction (*.psbt)"), nullptr);
- if (filename.isEmpty()) return;
- if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
- Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
- return;
+ std::string data;
+
+ if (from_clipboard) {
+ std::string raw = QApplication::clipboard()->text().toStdString();
+ bool invalid;
+ data = DecodeBase64(raw, &invalid);
+ if (invalid) {
+ Q_EMIT message(tr("Error"), tr("Unable to decode PSBT from clipboard (invalid base64)"), CClientUIInterface::MSG_ERROR);
+ return;
+ }
+ } else {
+ QString filename = GUIUtil::getOpenFileName(this,
+ tr("Load Transaction Data"), QString(),
+ tr("Partially Signed Transaction (*.psbt)"), nullptr);
+ if (filename.isEmpty()) return;
+ if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
+ Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
+ return;
+ }
+ std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
+ data = std::string(std::istreambuf_iterator<char>{in}, {});
}
- std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
- std::string data(std::istreambuf_iterator<char>{in}, {});
std::string error;
PartiallySignedTransaction psbtx;
if (!DecodeRawPSBT(psbtx, data, error)) {
- Q_EMIT message(tr("Error"), tr("Unable to decode PSBT file") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR);
+ Q_EMIT message(tr("Error"), tr("Unable to decode PSBT") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR);
return;
}
- CMutableTransaction mtx;
- bool complete = false;
- PSBTAnalysis analysis = AnalyzePSBT(psbtx);
- QMessageBox msgBox;
- msgBox.setText("PSBT");
- switch (analysis.next) {
- case PSBTRole::CREATOR:
- case PSBTRole::UPDATER:
- msgBox.setInformativeText("PSBT is incomplete. Copy to clipboard for manual inspection?");
- break;
- case PSBTRole::SIGNER:
- msgBox.setInformativeText("Transaction needs more signatures. Copy to clipboard?");
- break;
- case PSBTRole::FINALIZER:
- case PSBTRole::EXTRACTOR:
- complete = FinalizeAndExtractPSBT(psbtx, mtx);
- if (complete) {
- msgBox.setInformativeText(tr("Would you like to send this transaction?"));
- } else {
- // The analyzer missed something, e.g. if there are final_scriptSig/final_scriptWitness
- // but with invalid signatures.
- msgBox.setInformativeText(tr("There was an unexpected problem processing the PSBT. Copy to clipboard for manual inspection?"));
- }
- }
-
- msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
- switch (msgBox.exec()) {
- case QMessageBox::Yes: {
- if (complete) {
- std::string err_string;
- CTransactionRef tx = MakeTransactionRef(mtx);
-
- TransactionError result = BroadcastTransaction(*clientModel->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* wait_callback */ false);
- if (result == TransactionError::OK) {
- Q_EMIT message(tr("Success"), tr("Broadcasted transaction sucessfully."), CClientUIInterface::MSG_INFORMATION | CClientUIInterface::MODAL);
- } else {
- Q_EMIT message(tr("Error"), QString::fromStdString(err_string), CClientUIInterface::MSG_ERROR);
- }
- } else {
- // Serialize the PSBT
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << psbtx;
- GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
- Q_EMIT message(tr("PSBT copied"), "Copied to clipboard", CClientUIInterface::MSG_INFORMATION);
- return;
- }
- }
- case QMessageBox::Cancel:
- break;
- default:
- assert(false);
- }
+ PSBTOperationsDialog* dlg = new PSBTOperationsDialog(this, walletModel, clientModel);
+ dlg->openWithPSBT(psbtx);
+ dlg->setAttribute(Qt::WA_DeleteOnClose);
+ dlg->exec();
}
bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient)
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index 11f894e7f8..f186554758 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -84,7 +84,7 @@ public Q_SLOTS:
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
/** Load Partially Signed Bitcoin Transaction */
- void gotoLoadPSBT();
+ void gotoLoadPSBT(bool from_clipboard = false);
/** Show incoming transaction notification for new transactions.
@@ -115,6 +115,7 @@ public Q_SLOTS:
void requestedSyncWarningInfo();
Q_SIGNALS:
+ void setPrivacy(bool privacy);
void transactionClicked();
void coinsSent();
/** Fired when a message should be reported to the user */
diff --git a/src/rest.cpp b/src/rest.cpp
index 5f99e26bad..8cb594a03b 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -18,6 +18,7 @@
#include <sync.h>
#include <txmempool.h>
#include <util/check.h>
+#include <util/ref.h>
#include <util/strencodings.h>
#include <validation.h>
#include <version.h>
@@ -49,18 +50,13 @@ struct CCoin {
uint32_t nHeight;
CTxOut out;
- ADD_SERIALIZE_METHODS;
-
CCoin() : nHeight(0) {}
explicit CCoin(Coin&& in) : nHeight(in.nHeight), out(std::move(in.out)) {}
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action)
+ SERIALIZE_METHODS(CCoin, obj)
{
uint32_t nTxVerDummy = 0;
- READWRITE(nTxVerDummy);
- READWRITE(nHeight);
- READWRITE(out);
+ READWRITE(nTxVerDummy, obj.nHeight, obj.out);
}
};
@@ -80,13 +76,14 @@ static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string me
* @param[in] req the HTTP request
* return pointer to the mempool or nullptr if no mempool found
*/
-static CTxMemPool* GetMemPool(HTTPRequest* req)
+static CTxMemPool* GetMemPool(const util::Ref& context, HTTPRequest* req)
{
- if (!g_rpc_node || !g_rpc_node->mempool) {
+ NodeContext* node = context.Has<NodeContext>() ? &context.Get<NodeContext>() : nullptr;
+ if (!node || !node->mempool) {
RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found");
return nullptr;
}
- return g_rpc_node->mempool;
+ return node->mempool;
}
static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
@@ -134,7 +131,8 @@ static bool CheckWarmup(HTTPRequest* req)
return true;
}
-static bool rest_headers(HTTPRequest* req,
+static bool rest_headers(const util::Ref& context,
+ HTTPRequest* req,
const std::string& strURIPart)
{
if (!CheckWarmup(req))
@@ -190,7 +188,7 @@ static bool rest_headers(HTTPRequest* req,
ssHeader << pindex->GetBlockHeader();
}
- std::string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n";
+ std::string strHex = HexStr(ssHeader) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
return true;
@@ -255,7 +253,7 @@ static bool rest_block(HTTPRequest* req,
case RetFormat::HEX: {
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ssBlock << block;
- std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";
+ std::string strHex = HexStr(ssBlock) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
return true;
@@ -275,12 +273,12 @@ static bool rest_block(HTTPRequest* req,
}
}
-static bool rest_block_extended(HTTPRequest* req, const std::string& strURIPart)
+static bool rest_block_extended(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{
return rest_block(req, strURIPart, true);
}
-static bool rest_block_notxdetails(HTTPRequest* req, const std::string& strURIPart)
+static bool rest_block_notxdetails(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{
return rest_block(req, strURIPart, false);
}
@@ -288,7 +286,7 @@ static bool rest_block_notxdetails(HTTPRequest* req, const std::string& strURIPa
// A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
UniValue getblockchaininfo(const JSONRPCRequest& request);
-static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart)
+static bool rest_chaininfo(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{
if (!CheckWarmup(req))
return false;
@@ -297,7 +295,7 @@ static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart)
switch (rf) {
case RetFormat::JSON: {
- JSONRPCRequest jsonRequest;
+ JSONRPCRequest jsonRequest(context);
jsonRequest.params = UniValue(UniValue::VARR);
UniValue chainInfoObject = getblockchaininfo(jsonRequest);
std::string strJSON = chainInfoObject.write() + "\n";
@@ -311,11 +309,11 @@ static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart)
}
}
-static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart)
+static bool rest_mempool_info(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{
if (!CheckWarmup(req))
return false;
- const CTxMemPool* mempool = GetMemPool(req);
+ const CTxMemPool* mempool = GetMemPool(context, req);
if (!mempool) return false;
std::string param;
const RetFormat rf = ParseDataFormat(param, strURIPart);
@@ -335,10 +333,10 @@ static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart)
}
}
-static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPart)
+static bool rest_mempool_contents(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{
if (!CheckWarmup(req)) return false;
- const CTxMemPool* mempool = GetMemPool(req);
+ const CTxMemPool* mempool = GetMemPool(context, req);
if (!mempool) return false;
std::string param;
const RetFormat rf = ParseDataFormat(param, strURIPart);
@@ -358,7 +356,7 @@ static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPar
}
}
-static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
+static bool rest_tx(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{
if (!CheckWarmup(req))
return false;
@@ -393,7 +391,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ssTx << tx;
- std::string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";
+ std::string strHex = HexStr(ssTx) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
return true;
@@ -414,7 +412,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
}
}
-static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
+static bool rest_getutxos(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{
if (!CheckWarmup(req))
return false;
@@ -523,7 +521,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
};
if (fCheckMemPool) {
- const CTxMemPool* mempool = GetMemPool(req);
+ const CTxMemPool* mempool = GetMemPool(context, req);
if (!mempool) return false;
// use db+mempool as cache backend in case user likes to query mempool
LOCK2(cs_main, mempool->cs);
@@ -558,7 +556,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;
- std::string strHex = HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + "\n";
+ std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
@@ -600,7 +598,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
}
}
-static bool rest_blockhash_by_height(HTTPRequest* req,
+static bool rest_blockhash_by_height(const util::Ref& context, HTTPRequest* req,
const std::string& str_uri_part)
{
if (!CheckWarmup(req)) return false;
@@ -648,7 +646,7 @@ static bool rest_blockhash_by_height(HTTPRequest* req,
static const struct {
const char* prefix;
- bool (*handler)(HTTPRequest* req, const std::string& strReq);
+ bool (*handler)(const util::Ref& context, HTTPRequest* req, const std::string& strReq);
} uri_prefixes[] = {
{"/rest/tx/", rest_tx},
{"/rest/block/notxdetails/", rest_block_notxdetails},
@@ -661,10 +659,12 @@ static const struct {
{"/rest/blockhashbyheight/", rest_blockhash_by_height},
};
-void StartREST()
+void StartREST(const util::Ref& context)
{
- for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++)
- RegisterHTTPHandler(uri_prefixes[i].prefix, false, uri_prefixes[i].handler);
+ for (const auto& up : uri_prefixes) {
+ auto handler = [&context, up](HTTPRequest* req, const std::string& prefix) { return up.handler(context, req, prefix); };
+ RegisterHTTPHandler(up.prefix, false, handler);
+ }
}
void InterruptREST()
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index f7ccbae706..2afc9a3d4a 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -29,8 +29,10 @@
#include <txdb.h>
#include <txmempool.h>
#include <undo.h>
+#include <util/ref.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <warnings.h>
@@ -51,15 +53,32 @@ struct CUpdatedBlock
static Mutex cs_blockchange;
static std::condition_variable cond_blockchange;
-static CUpdatedBlock latestblock;
+static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange);
-CTxMemPool& EnsureMemPool()
+NodeContext& EnsureNodeContext(const util::Ref& context)
{
- CHECK_NONFATAL(g_rpc_node);
- if (!g_rpc_node->mempool) {
+ if (!context.Has<NodeContext>()) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Node context not found");
+ }
+ return context.Get<NodeContext>();
+}
+
+CTxMemPool& EnsureMemPool(const util::Ref& context)
+{
+ NodeContext& node = EnsureNodeContext(context);
+ if (!node.mempool) {
throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found");
}
- return *g_rpc_node->mempool;
+ return *node.mempool;
+}
+
+ChainstateManager& EnsureChainman(const util::Ref& context)
+{
+ NodeContext& node = EnsureNodeContext(context);
+ if (!node.chainman) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found");
+ }
+ return *node.chainman;
}
/* Calculate the difficulty for a given block index.
@@ -205,10 +224,10 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
return ::ChainActive().Tip()->GetBlockHash().GetHex();
}
-void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
+void RPCNotifyBlockChange(const CBlockIndex* pindex)
{
if(pindex) {
- std::lock_guard<std::mutex> lock(cs_blockchange);
+ LOCK(cs_blockchange);
latestblock.hash = pindex->GetBlockHash();
latestblock.height = pindex->nHeight;
}
@@ -243,9 +262,9 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
WAIT_LOCK(cs_blockchange, lock);
block = latestblock;
if(timeout)
- cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
else
- cond_blockchange.wait(lock, [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
+ cond_blockchange.wait(lock, [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
block = latestblock;
}
UniValue ret(UniValue::VOBJ);
@@ -285,9 +304,9 @@ static UniValue waitforblock(const JSONRPCRequest& request)
{
WAIT_LOCK(cs_blockchange, lock);
if(timeout)
- cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]{return latestblock.hash == hash || !IsRPCRunning();});
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.hash == hash || !IsRPCRunning();});
else
- cond_blockchange.wait(lock, [&hash]{return latestblock.hash == hash || !IsRPCRunning(); });
+ cond_blockchange.wait(lock, [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.hash == hash || !IsRPCRunning(); });
block = latestblock;
}
@@ -329,9 +348,9 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
{
WAIT_LOCK(cs_blockchange, lock);
if(timeout)
- cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]{return latestblock.height >= height || !IsRPCRunning();});
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning();});
else
- cond_blockchange.wait(lock, [&height]{return latestblock.height >= height || !IsRPCRunning(); });
+ cond_blockchange.wait(lock, [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning(); });
block = latestblock;
}
UniValue ret(UniValue::VOBJ);
@@ -399,6 +418,7 @@ static std::vector<RPCResult> MempoolEntryDescription() { return {
RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction could be replaced due to BIP125 (replace-by-fee)"},
+ RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
};}
static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
@@ -460,6 +480,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
}
info.pushKV("bip125-replaceable", rbfStatus);
+ info.pushKV("unbroadcast", pool.IsUnbroadcastTx(tx.GetHash()));
}
UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose)
@@ -519,7 +540,7 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
if (!request.params[0].isNull())
fVerbose = request.params[0].get_bool();
- return MempoolToJSON(EnsureMemPool(), fVerbose);
+ return MempoolToJSON(EnsureMemPool(request.context), fVerbose);
}
static UniValue getmempoolancestors(const JSONRPCRequest& request)
@@ -549,7 +570,7 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
uint256 hash = ParseHashV(request.params[0], "parameter 1");
- const CTxMemPool& mempool = EnsureMemPool();
+ const CTxMemPool& mempool = EnsureMemPool(request.context);
LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash);
@@ -612,7 +633,7 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
uint256 hash = ParseHashV(request.params[0], "parameter 1");
- const CTxMemPool& mempool = EnsureMemPool();
+ const CTxMemPool& mempool = EnsureMemPool(request.context);
LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash);
@@ -662,7 +683,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
uint256 hash = ParseHashV(request.params[0], "parameter 1");
- const CTxMemPool& mempool = EnsureMemPool();
+ const CTxMemPool& mempool = EnsureMemPool(request.context);
LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash);
@@ -761,7 +782,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
{
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
ssBlock << pblockindex->GetBlockHeader();
- std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
+ std::string strHex = HexStr(ssBlock);
return strHex;
}
@@ -777,10 +798,8 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) {
// Block not found on disk. This could be because we have the block
- // header in our index but don't have the block (for example if a
- // non-whitelisted node sends us an unrequested long chain of valid
- // blocks, we add the headers to our index, but don't accept the
- // block).
+ // header in our index but not yet have the block or did not accept the
+ // block.
throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
}
@@ -887,7 +906,7 @@ static UniValue getblock(const JSONRPCRequest& request)
{
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ssBlock << block;
- std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
+ std::string strHex = HexStr(ssBlock);
return strHex;
}
@@ -954,7 +973,9 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
RPCHelpMan{"gettxoutsetinfo",
"\nReturns statistics about the unspent transaction output set.\n"
"Note this call may take some time.\n",
- {},
+ {
+ {"hash_type", RPCArg::Type::STR, /* default */ "hash_serialized_2", "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'none'."},
+ },
RPCResult{
RPCResult::Type::OBJ, "", "",
{
@@ -963,7 +984,7 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs"},
{RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
{RPCResult::Type::NUM, "bogosize", "A meaningless metric for UTXO set size"},
- {RPCResult::Type::STR_HEX, "hash_serialized_2", "The serialized hash"},
+ {RPCResult::Type::STR_HEX, "hash_serialized_2", "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
{RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk"},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount"},
}},
@@ -978,14 +999,19 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
CCoinsStats stats;
::ChainstateActive().ForceFlushStateToDisk();
+ const CoinStatsHashType hash_type = ParseHashType(request.params[0], CoinStatsHashType::HASH_SERIALIZED);
+
CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB());
- if (GetUTXOStats(coins_view, stats)) {
+ NodeContext& node = EnsureNodeContext(request.context);
+ if (GetUTXOStats(coins_view, stats, hash_type, node.rpc_interruption_point)) {
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
ret.pushKV("transactions", (int64_t)stats.nTransactions);
ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
ret.pushKV("bogosize", (int64_t)stats.nBogoSize);
- ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
+ if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
+ ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
+ }
ret.pushKV("disk_size", stats.nDiskSize);
ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount));
} else {
@@ -1045,7 +1071,7 @@ UniValue gettxout(const JSONRPCRequest& request)
CCoinsViewCache* coins_view = &::ChainstateActive().CoinsTip();
if (fMempool) {
- const CTxMemPool& mempool = EnsureMemPool();
+ const CTxMemPool& mempool = EnsureMemPool(request.context);
LOCK(mempool.cs);
CCoinsViewMemPool view(coins_view, mempool);
if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
@@ -1078,7 +1104,8 @@ static UniValue verifychain(const JSONRPCRequest& request)
RPCHelpMan{"verifychain",
"\nVerifies blockchain database.\n",
{
- {"checklevel", RPCArg::Type::NUM, /* default */ strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL), "How thorough the block verification is."},
+ {"checklevel", RPCArg::Type::NUM, /* default */ strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL),
+ strprintf("How thorough the block verification is:\n - %s", Join(CHECKLEVEL_DOC, "\n- "))},
{"nblocks", RPCArg::Type::NUM, /* default */ strprintf("%d, 0=all", DEFAULT_CHECKBLOCKS), "The number of blocks to check."},
},
RPCResult{
@@ -1260,7 +1287,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
obj.pushKV("softforks", softforks);
- obj.pushKV("warnings", GetWarnings(false));
+ obj.pushKV("warnings", GetWarnings(false).original);
return obj;
}
@@ -1306,50 +1333,48 @@ static UniValue getchaintips(const JSONRPCRequest& request)
},
}.Check(request);
+ ChainstateManager& chainman = EnsureChainman(request.context);
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 the active chain tip, plus orphan blocks which do not have another orphan building off of them.
* Algorithm:
* - Make one pass through BlockIndex(), 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 the active chain tip
*/
std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
std::set<const CBlockIndex*> setOrphans;
std::set<const CBlockIndex*> setPrevs;
- for (const std::pair<const uint256, CBlockIndex*>& item : ::BlockIndex())
- {
- if (!::ChainActive().Contains(item.second)) {
+ for (const std::pair<const uint256, CBlockIndex*>& item : chainman.BlockIndex()) {
+ if (!chainman.ActiveChain().Contains(item.second)) {
setOrphans.insert(item.second);
setPrevs.insert(item.second->pprev);
}
}
- for (std::set<const CBlockIndex*>::iterator it = setOrphans.begin(); it != setOrphans.end(); ++it)
- {
+ for (std::set<const CBlockIndex*>::iterator it = setOrphans.begin(); it != setOrphans.end(); ++it) {
if (setPrevs.erase(*it) == 0) {
setTips.insert(*it);
}
}
// Always report the currently active tip.
- setTips.insert(::ChainActive().Tip());
+ setTips.insert(chainman.ActiveChain().Tip());
/* Construct the output array. */
UniValue res(UniValue::VARR);
- for (const CBlockIndex* block : setTips)
- {
+ for (const CBlockIndex* block : setTips) {
UniValue obj(UniValue::VOBJ);
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 - chainman.ActiveChain().FindFork(block)->nHeight;
obj.pushKV("branchlen", branchLen);
std::string status;
- if (::ChainActive().Contains(block)) {
+ if (chainman.ActiveChain().Contains(block)) {
// This block is part of the currently active chain.
status = "active";
} else if (block->nStatus & BLOCK_FAILED_MASK) {
@@ -1389,7 +1414,7 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
ret.pushKV("maxmempool", (int64_t) maxmempool);
ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
-
+ ret.pushKV("unbroadcastcount", uint64_t{pool.GetUnbroadcastTxs().size()});
return ret;
}
@@ -1408,6 +1433,7 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
{RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
{RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
+ {RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"}
}},
RPCExamples{
HelpExampleCli("getmempoolinfo", "")
@@ -1415,7 +1441,7 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
},
}.Check(request);
- return MempoolInfoToJSON(EnsureMemPool());
+ return MempoolInfoToJSON(EnsureMemPool(request.context));
}
static UniValue preciousblock(const JSONRPCRequest& request)
@@ -1934,7 +1960,7 @@ static UniValue savemempool(const JSONRPCRequest& request)
},
}.Check(request);
- const CTxMemPool& mempool = EnsureMemPool();
+ const CTxMemPool& mempool = EnsureMemPool(request.context);
if (!mempool.IsLoaded()) {
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
@@ -1947,8 +1973,10 @@ static UniValue savemempool(const JSONRPCRequest& request)
return NullUniValue;
}
+namespace {
//! Search for a given set of pubkey scripts
-bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
+bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results, std::function<void()>& interruption_point)
+{
scan_progress = 0;
count = 0;
while (cursor->Valid()) {
@@ -1956,6 +1984,7 @@ bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>&
Coin coin;
if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
if (++count % 8192 == 0) {
+ interruption_point();
if (should_abort) {
// allow to abort the scan via the abort reference
return false;
@@ -1974,9 +2003,9 @@ bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>&
scan_progress = 100;
return true;
}
+} // namespace
/** RAII object to prevent concurrency issue when scanning the txout set */
-static std::mutex g_utxosetscan;
static std::atomic<int> g_scan_progress;
static std::atomic<bool> g_scan_in_progress;
static std::atomic<bool> g_should_abort_scan;
@@ -1989,18 +2018,15 @@ public:
bool reserve() {
CHECK_NONFATAL(!m_could_reserve);
- std::lock_guard<std::mutex> lock(g_utxosetscan);
- if (g_scan_in_progress) {
+ if (g_scan_in_progress.exchange(true)) {
return false;
}
- g_scan_in_progress = true;
m_could_reserve = true;
return true;
}
~CoinsViewScanReserver() {
if (m_could_reserve) {
- std::lock_guard<std::mutex> lock(g_utxosetscan);
g_scan_in_progress = false;
}
}
@@ -2126,7 +2152,8 @@ UniValue scantxoutset(const JSONRPCRequest& request)
tip = ::ChainActive().Tip();
CHECK_NONFATAL(tip);
}
- bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);
+ NodeContext& node = EnsureNodeContext(request.context);
+ bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins, node.rpc_interruption_point);
result.pushKV("success", res);
result.pushKV("txouts", count);
result.pushKV("height", tip->nHeight);
@@ -2142,7 +2169,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
UniValue unspent(UniValue::VOBJ);
unspent.pushKV("txid", outpoint.hash.GetHex());
unspent.pushKV("vout", (int32_t)outpoint.n);
- unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end()));
+ unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey));
unspent.pushKV("desc", descriptors[txo.scriptPubKey]);
unspent.pushKV("amount", ValueFromAmount(txo.nValue));
unspent.pushKV("height", (int32_t)coin.nHeight);
@@ -2281,6 +2308,7 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
std::unique_ptr<CCoinsViewCursor> pcursor;
CCoinsStats stats;
CBlockIndex* tip;
+ NodeContext& node = EnsureNodeContext(request.context);
{
// We need to lock cs_main to ensure that the coinsdb isn't written to
@@ -2299,7 +2327,7 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
::ChainstateActive().ForceFlushStateToDisk();
- if (!GetUTXOStats(&::ChainstateActive().CoinsDB(), stats)) {
+ if (!GetUTXOStats(&::ChainstateActive().CoinsDB(), stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
@@ -2317,9 +2345,7 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
unsigned int iter{0};
while (pcursor->Valid()) {
- if (iter % 5000 == 0 && !IsRPCRunning()) {
- throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
- }
+ if (iter % 5000 == 0) node.rpc_interruption_point();
++iter;
if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
afile << key;
@@ -2362,7 +2388,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "getmempoolinfo", &getmempoolinfo, {} },
{ "blockchain", "getrawmempool", &getrawmempool, {"verbose"} },
{ "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} },
- { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {} },
+ { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {"hash_type"} },
{ "blockchain", "pruneblockchain", &pruneblockchain, {"height"} },
{ "blockchain", "savemempool", &savemempool, {} },
{ "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} },
@@ -2385,5 +2411,3 @@ static const CRPCCommand commands[] =
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
-
-NodeContext* g_rpc_node = nullptr;
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index a02e5fae0e..5c9a43b13e 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -16,8 +16,12 @@ extern RecursiveMutex cs_main;
class CBlock;
class CBlockIndex;
class CTxMemPool;
+class ChainstateManager;
class UniValue;
struct NodeContext;
+namespace util {
+class Ref;
+} // namespace util
static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5;
@@ -30,7 +34,7 @@ static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5;
double GetDifficulty(const CBlockIndex* blockindex);
/** Callback for when block tip changed. */
-void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
+void RPCNotifyBlockChange(const CBlockIndex*);
/** Block description to JSON */
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails = false) LOCKS_EXCLUDED(cs_main);
@@ -47,11 +51,8 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
/** 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);
-//! Pointer to node state that needs to be declared as a global to be accessible
-//! RPC methods. Due to limitations of the RPC framework, there's currently no
-//! direct way to pass in state to RPC methods without globals.
-extern NodeContext* g_rpc_node;
-
-CTxMemPool& EnsureMemPool();
+NodeContext& EnsureNodeContext(const util::Ref& context);
+CTxMemPool& EnsureMemPool(const util::Ref& context);
+ChainstateManager& EnsureChainman(const util::Ref& context);
#endif
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 3045a74d7a..66ace7263a 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -217,7 +217,7 @@ UniValue ParseNonRFCJSONValue(const std::string& strVal)
UniValue jVal;
if (!jVal.read(std::string("[")+strVal+std::string("]")) ||
!jVal.isArray() || jVal.size()!=1)
- throw std::runtime_error(std::string("Error parsing JSON:")+strVal);
+ throw std::runtime_error(std::string("Error parsing JSON: ") + strVal);
return jVal[0];
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 8e752e5e83..fee6a893eb 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -17,6 +17,7 @@
#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/descriptor.h>
@@ -29,6 +30,7 @@
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
+#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
@@ -101,7 +103,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);
}
-static bool GenerateBlock(CBlock& block, uint64_t& max_tries, unsigned int& extra_nonce, uint256& block_hash)
+static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t& max_tries, unsigned int& extra_nonce, uint256& block_hash)
{
block_hash.SetNull();
@@ -124,14 +126,15 @@ static bool GenerateBlock(CBlock& block, uint64_t& max_tries, unsigned int& extr
}
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
- if (!ProcessNewBlock(chainparams, shared_pblock, true, nullptr))
+ if (!chainman.ProcessNewBlock(chainparams, shared_pblock, true, nullptr)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
+ }
block_hash = block.GetHash();
return true;
}
-static UniValue generateBlocks(const CTxMemPool& mempool, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
+static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& mempool, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
{
int nHeightEnd = 0;
int nHeight = 0;
@@ -151,7 +154,7 @@ static UniValue generateBlocks(const CTxMemPool& mempool, const CScript& coinbas
CBlock *pblock = &pblocktemplate->block;
uint256 block_hash;
- if (!GenerateBlock(*pblock, nMaxTries, nExtraNonce, block_hash)) {
+ if (!GenerateBlock(chainman, *pblock, nMaxTries, nExtraNonce, block_hash)) {
break;
}
@@ -178,7 +181,7 @@ static bool getScriptFromDescriptor(const std::string& descriptor, CScript& scri
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys"));
}
- // Combo desriptors can have 2 or 4 scripts, so we can't just check scripts.size() == 1
+ // Combo descriptors can have 2 or 4 scripts, so we can't just check scripts.size() == 1
CHECK_NONFATAL(scripts.size() > 0 && scripts.size() <= 4);
if (scripts.size() == 1) {
@@ -205,7 +208,7 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
{
{"num_blocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor to send the newly generated bitcoin to."},
- {"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."},
+ {"maxtries", RPCArg::Type::NUM, /* default */ ToString(DEFAULT_MAX_TRIES), "How many iterations to try."},
},
RPCResult{
RPCResult::Type::ARR, "", "hashes of blocks generated",
@@ -219,7 +222,7 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
.Check(request);
const int num_blocks{request.params[0].get_int()};
- const int64_t max_tries{request.params[2].isNull() ? 1000000 : request.params[2].get_int()};
+ const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
CScript coinbase_script;
std::string error;
@@ -227,9 +230,10 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
- const CTxMemPool& mempool = EnsureMemPool();
+ const CTxMemPool& mempool = EnsureMemPool(request.context);
+ ChainstateManager& chainman = EnsureChainman(request.context);
- return generateBlocks(mempool, coinbase_script, num_blocks, max_tries);
+ return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
}
static UniValue generatetoaddress(const JSONRPCRequest& request)
@@ -239,7 +243,7 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
{
{"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."},
+ {"maxtries", RPCArg::Type::NUM, /* default */ ToString(DEFAULT_MAX_TRIES), "How many iterations to try."},
},
RPCResult{
RPCResult::Type::ARR, "", "hashes of blocks generated",
@@ -249,27 +253,25 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
RPCExamples{
"\nGenerate 11 blocks to myaddress\n"
+ HelpExampleCli("generatetoaddress", "11 \"myaddress\"")
- + "If you are running the bitcoin core wallet, you can get a new address to send the newly generated bitcoin to with:\n"
+ + "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated bitcoin to with:\n"
+ HelpExampleCli("getnewaddress", "")
},
}.Check(request);
- int nGenerate = request.params[0].get_int();
- uint64_t nMaxTries = 1000000;
- if (!request.params[2].isNull()) {
- nMaxTries = request.params[2].get_int();
- }
+ const int num_blocks{request.params[0].get_int()};
+ const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
CTxDestination destination = DecodeDestination(request.params[1].get_str());
if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
}
- const CTxMemPool& mempool = EnsureMemPool();
+ const CTxMemPool& mempool = EnsureMemPool(request.context);
+ ChainstateManager& chainman = EnsureChainman(request.context);
CScript coinbase_script = GetScriptForDestination(destination);
- return generateBlocks(mempool, coinbase_script, nGenerate, nMaxTries);
+ return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
}
static UniValue generateblock(const JSONRPCRequest& request)
@@ -311,7 +313,7 @@ static UniValue generateblock(const JSONRPCRequest& request)
coinbase_script = GetScriptForDestination(destination);
}
- const CTxMemPool& mempool = EnsureMemPool();
+ const CTxMemPool& mempool = EnsureMemPool(request.context);
std::vector<CTransactionRef> txs;
const auto raw_txs_or_txids = request.params[1].get_array();
@@ -367,10 +369,10 @@ static UniValue generateblock(const JSONRPCRequest& request)
}
uint256 block_hash;
- uint64_t max_tries{1000000};
+ uint64_t max_tries{DEFAULT_MAX_TRIES};
unsigned int extra_nonce{0};
- if (!GenerateBlock(block, max_tries, extra_nonce, block_hash) || block_hash.IsNull()) {
+ if (!GenerateBlock(EnsureChainman(request.context), block, max_tries, extra_nonce, block_hash) || block_hash.IsNull()) {
throw JSONRPCError(RPC_MISC_ERROR, "Failed to make block.");
}
@@ -403,7 +405,7 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
}.Check(request);
LOCK(cs_main);
- const CTxMemPool& mempool = EnsureMemPool();
+ const CTxMemPool& mempool = EnsureMemPool(request.context);
UniValue obj(UniValue::VOBJ);
obj.pushKV("blocks", (int)::ChainActive().Height());
@@ -413,7 +415,7 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
obj.pushKV("networkhashps", getnetworkhashps(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
obj.pushKV("chain", Params().NetworkIDString());
- obj.pushKV("warnings", GetWarnings(false));
+ obj.pushKV("warnings", GetWarnings(false).original);
return obj;
}
@@ -449,7 +451,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.");
}
- EnsureMemPool().PrioritiseTransaction(hash, nAmount);
+ EnsureMemPool(request.context).PrioritiseTransaction(hash, nAmount);
return true;
}
@@ -635,17 +637,18 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
if (strMode != "template")
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
- if(!g_rpc_node->connman)
+ NodeContext& node = EnsureNodeContext(request.context);
+ if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- if (g_rpc_node->connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0)
+ if (node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0)
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
if (::ChainstateActive().IsInitialBlockDownload())
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks...");
static unsigned int nTransactionsUpdatedLast;
- const CTxMemPool& mempool = EnsureMemPool();
+ const CTxMemPool& mempool = EnsureMemPool(request.context);
if (!lpval.isNull())
{
@@ -787,6 +790,8 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
result.pushKV("capabilities", aCaps);
UniValue aRules(UniValue::VARR);
+ aRules.push_back("csv");
+ if (!fPreSegWit) aRules.push_back("!segwit");
UniValue vbavailable(UniValue::VOBJ);
for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
Consensus::DeploymentPos pos = Consensus::DeploymentPos(j);
@@ -868,7 +873,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
result.pushKV("height", (int64_t)(pindexPrev->nHeight+1));
if (!pblocktemplate->vchCoinbaseCommitment.empty()) {
- result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end()));
+ result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment));
}
return result;
@@ -944,7 +949,7 @@ static UniValue submitblock(const JSONRPCRequest& request)
bool new_block;
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
RegisterSharedValidationInterface(sc);
- bool accepted = ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block);
+ bool accepted = EnsureChainman(request.context).ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block);
UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) {
return "duplicate";
@@ -983,7 +988,7 @@ static UniValue submitheader(const JSONRPCRequest& request)
}
BlockValidationState state;
- ProcessNewBlockHeaders({h}, state, Params());
+ EnsureChainman(request.context).ProcessNewBlockHeaders({h}, state, Params());
if (state.IsValid()) return NullUniValue;
if (state.IsError()) {
throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
diff --git a/src/rpc/mining.h b/src/rpc/mining.h
new file mode 100644
index 0000000000..acc74e1dcc
--- /dev/null
+++ b/src/rpc/mining.h
@@ -0,0 +1,11 @@
+// Copyright (c) 2020 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
+
+/** Default max iterations to try in RPC generatetodescriptor, generatetoaddress, and generateblock. */
+static const uint64_t DEFAULT_MAX_TRIES{1000000};
+
+#endif // BITCOIN_RPC_MINING_H
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index f3c5fed858..53d38f4e11 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -15,6 +15,7 @@
#include <script/descriptor.h>
#include <util/check.h>
#include <util/message.h> // For MessageSign(), MessageVerify()
+#include <util/ref.h>
#include <util/strencodings.h>
#include <util/system.h>
@@ -62,7 +63,7 @@ static UniValue validateaddress(const JSONRPCRequest& request)
ret.pushKV("address", currentAddress);
CScript scriptPubKey = GetScriptForDestination(dest);
- ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
+ ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
UniValue detail = DescribeAddress(dest);
ret.pushKVs(detail);
@@ -130,7 +131,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(dest));
- result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
+ result.pushKV("redeemScript", HexStr(inner));
result.pushKV("descriptor", descriptor->ToString());
return result;
@@ -366,8 +367,8 @@ static UniValue setmocktime(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VNUM});
int64_t time = request.params[0].get_int64();
SetMockTime(time);
- if (g_rpc_node) {
- for (const auto& chain_client : g_rpc_node->chain_clients) {
+ if (request.context.Has<NodeContext>()) {
+ for (const auto& chain_client : request.context.Get<NodeContext>().chain_clients) {
chain_client->setMockTime(time);
}
}
@@ -398,9 +399,10 @@ static UniValue mockscheduler(const JSONRPCRequest& request)
}
// protect against null pointer dereference
- CHECK_NONFATAL(g_rpc_node);
- CHECK_NONFATAL(g_rpc_node->scheduler);
- g_rpc_node->scheduler->MockForward(std::chrono::seconds(delta_seconds));
+ CHECK_NONFATAL(request.context.Has<NodeContext>());
+ NodeContext& node = request.context.Get<NodeContext>();
+ CHECK_NONFATAL(node.scheduler);
+ node.scheduler->MockForward(std::chrono::seconds(delta_seconds));
return NullUniValue;
}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index d6d15f8b56..9981ea35df 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -22,6 +22,7 @@
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
+#include <util/translation.h>
#include <validation.h>
#include <version.h>
#include <warnings.h>
@@ -42,10 +43,11 @@ static UniValue getconnectioncount(const JSONRPCRequest& request)
},
}.Check(request);
- if(!g_rpc_node->connman)
+ NodeContext& node = EnsureNodeContext(request.context);
+ if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- return (int)g_rpc_node->connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
+ return (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
}
static UniValue ping(const JSONRPCRequest& request)
@@ -62,11 +64,12 @@ static UniValue ping(const JSONRPCRequest& request)
},
}.Check(request);
- if(!g_rpc_node->connman)
+ NodeContext& node = EnsureNodeContext(request.context);
+ if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
// Request that each node send a ping during next message processing pass
- g_rpc_node->connman->ForEachNode([](CNode* pnode) {
+ node.connman->ForEachNode([](CNode* pnode) {
pnode->fPingQueued = true;
});
return NullUniValue;
@@ -109,7 +112,7 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
{RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"},
{RPCResult::Type::BOOL, "addnode", "Whether connection was due to addnode/-connect or if it was an automatic/inbound connection"},
{RPCResult::Type::NUM, "startingheight", "The starting height (block) of the peer"},
- {RPCResult::Type::NUM, "banscore", "The ban score"},
+ {RPCResult::Type::NUM, "banscore", "The ban score (DEPRECATED, returned only if config option -deprecatedrpc=banscore is passed)"},
{RPCResult::Type::NUM, "synced_headers", "The last header we have in common with this peer"},
{RPCResult::Type::NUM, "synced_blocks", "The last block we have in common with this peer"},
{RPCResult::Type::ARR, "inflight", "",
@@ -139,11 +142,12 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
},
}.Check(request);
- if(!g_rpc_node->connman)
+ NodeContext& node = EnsureNodeContext(request.context);
+ if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
std::vector<CNodeStats> vstats;
- g_rpc_node->connman->GetNodeStats(vstats);
+ node.connman->GetNodeStats(vstats);
UniValue ret(UniValue::VARR);
@@ -187,7 +191,10 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
obj.pushKV("addnode", stats.m_manual_connection);
obj.pushKV("startingheight", stats.nStartingHeight);
if (fStateStats) {
- obj.pushKV("banscore", statestats.nMisbehavior);
+ if (IsDeprecatedRPCEnabled("banscore")) {
+ // banscore is deprecated in v0.21 for removal in v0.22
+ obj.pushKV("banscore", statestats.nMisbehavior);
+ }
obj.pushKV("synced_headers", statestats.nSyncHeight);
obj.pushKV("synced_blocks", statestats.nCommonHeight);
UniValue heights(UniValue::VARR);
@@ -248,7 +255,8 @@ static UniValue addnode(const JSONRPCRequest& request)
},
}.ToString());
- if(!g_rpc_node->connman)
+ NodeContext& node = EnsureNodeContext(request.context);
+ if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
std::string strNode = request.params[0].get_str();
@@ -256,18 +264,18 @@ static UniValue addnode(const JSONRPCRequest& request)
if (strCommand == "onetry")
{
CAddress addr;
- g_rpc_node->connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), false, false, true);
+ node.connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), false, false, true);
return NullUniValue;
}
if (strCommand == "add")
{
- if(!g_rpc_node->connman->AddNode(strNode))
+ if(!node.connman->AddNode(strNode))
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added");
}
else if(strCommand == "remove")
{
- if(!g_rpc_node->connman->RemoveAddedNode(strNode))
+ if(!node.connman->RemoveAddedNode(strNode))
throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added.");
}
@@ -293,7 +301,8 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
},
}.Check(request);
- if(!g_rpc_node->connman)
+ NodeContext& node = EnsureNodeContext(request.context);
+ if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
bool success;
@@ -302,11 +311,11 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
if (!address_arg.isNull() && id_arg.isNull()) {
/* handle disconnect-by-address */
- success = g_rpc_node->connman->DisconnectNode(address_arg.get_str());
+ success = node.connman->DisconnectNode(address_arg.get_str());
} else if (!id_arg.isNull() && (address_arg.isNull() || (address_arg.isStr() && address_arg.get_str().empty()))) {
/* handle disconnect-by-id */
NodeId nodeid = (NodeId) id_arg.get_int64();
- success = g_rpc_node->connman->DisconnectNode(nodeid);
+ success = node.connman->DisconnectNode(nodeid);
} else {
throw JSONRPCError(RPC_INVALID_PARAMS, "Only one of address and nodeid should be provided.");
}
@@ -350,10 +359,11 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
},
}.Check(request);
- if(!g_rpc_node->connman)
+ NodeContext& node = EnsureNodeContext(request.context);
+ if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- std::vector<AddedNodeInfo> vInfo = g_rpc_node->connman->GetAddedNodeInfo();
+ std::vector<AddedNodeInfo> vInfo = node.connman->GetAddedNodeInfo();
if (!request.params[0].isNull()) {
bool found = false;
@@ -417,21 +427,22 @@ static UniValue getnettotals(const JSONRPCRequest& request)
+ HelpExampleRpc("getnettotals", "")
},
}.Check(request);
- if(!g_rpc_node->connman)
+ NodeContext& node = EnsureNodeContext(request.context);
+ if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
UniValue obj(UniValue::VOBJ);
- obj.pushKV("totalbytesrecv", g_rpc_node->connman->GetTotalBytesRecv());
- obj.pushKV("totalbytessent", g_rpc_node->connman->GetTotalBytesSent());
+ obj.pushKV("totalbytesrecv", node.connman->GetTotalBytesRecv());
+ obj.pushKV("totalbytessent", node.connman->GetTotalBytesSent());
obj.pushKV("timemillis", GetTimeMillis());
UniValue outboundLimit(UniValue::VOBJ);
- outboundLimit.pushKV("timeframe", g_rpc_node->connman->GetMaxOutboundTimeframe());
- outboundLimit.pushKV("target", g_rpc_node->connman->GetMaxOutboundTarget());
- outboundLimit.pushKV("target_reached", g_rpc_node->connman->OutboundTargetReached(false));
- outboundLimit.pushKV("serve_historical_blocks", !g_rpc_node->connman->OutboundTargetReached(true));
- outboundLimit.pushKV("bytes_left_in_cycle", g_rpc_node->connman->GetOutboundTargetBytesLeft());
- outboundLimit.pushKV("time_left_in_cycle", g_rpc_node->connman->GetMaxOutboundTimeLeftInCycle());
+ outboundLimit.pushKV("timeframe", node.connman->GetMaxOutboundTimeframe());
+ outboundLimit.pushKV("target", node.connman->GetMaxOutboundTarget());
+ outboundLimit.pushKV("target_reached", node.connman->OutboundTargetReached(false));
+ outboundLimit.pushKV("serve_historical_blocks", !node.connman->OutboundTargetReached(true));
+ outboundLimit.pushKV("bytes_left_in_cycle", node.connman->GetOutboundTargetBytesLeft());
+ outboundLimit.pushKV("time_left_in_cycle", node.connman->GetMaxOutboundTimeLeftInCycle());
obj.pushKV("uploadtarget", outboundLimit);
return obj;
}
@@ -513,16 +524,17 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
obj.pushKV("version", CLIENT_VERSION);
obj.pushKV("subversion", strSubVersion);
obj.pushKV("protocolversion",PROTOCOL_VERSION);
- if (g_rpc_node->connman) {
- ServiceFlags services = g_rpc_node->connman->GetLocalServices();
+ NodeContext& node = EnsureNodeContext(request.context);
+ if (node.connman) {
+ ServiceFlags services = node.connman->GetLocalServices();
obj.pushKV("localservices", strprintf("%016x", services));
obj.pushKV("localservicesnames", GetServicesNames(services));
}
obj.pushKV("localrelay", g_relay_txes);
obj.pushKV("timeoffset", GetTimeOffset());
- if (g_rpc_node->connman) {
- obj.pushKV("networkactive", g_rpc_node->connman->GetNetworkActive());
- obj.pushKV("connections", (int)g_rpc_node->connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
+ if (node.connman) {
+ obj.pushKV("networkactive", node.connman->GetNetworkActive());
+ obj.pushKV("connections", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
}
obj.pushKV("networks", GetNetworksInfo());
obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
@@ -540,7 +552,7 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
}
}
obj.pushKV("localaddresses", localAddresses);
- obj.pushKV("warnings", GetWarnings(false));
+ obj.pushKV("warnings", GetWarnings(false).original);
return obj;
}
@@ -567,7 +579,8 @@ static UniValue setban(const JSONRPCRequest& request)
if (request.fHelp || !help.IsValidNumArgs(request.params.size()) || (strCommand != "add" && strCommand != "remove")) {
throw std::runtime_error(help.ToString());
}
- if (!g_rpc_node->banman) {
+ NodeContext& node = EnsureNodeContext(request.context);
+ if (!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
}
@@ -591,7 +604,7 @@ static UniValue setban(const JSONRPCRequest& request)
if (strCommand == "add")
{
- if (isSubnet ? g_rpc_node->banman->IsBanned(subNet) : g_rpc_node->banman->IsBanned(netAddr)) {
+ if (isSubnet ? node.banman->IsBanned(subNet) : node.banman->IsBanned(netAddr)) {
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
}
@@ -604,21 +617,21 @@ static UniValue setban(const JSONRPCRequest& request)
absolute = true;
if (isSubnet) {
- g_rpc_node->banman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute);
- if (g_rpc_node->connman) {
- g_rpc_node->connman->DisconnectNode(subNet);
+ node.banman->Ban(subNet, banTime, absolute);
+ if (node.connman) {
+ node.connman->DisconnectNode(subNet);
}
} else {
- g_rpc_node->banman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
- if (g_rpc_node->connman) {
- g_rpc_node->connman->DisconnectNode(netAddr);
+ node.banman->Ban(netAddr, banTime, absolute);
+ if (node.connman) {
+ node.connman->DisconnectNode(netAddr);
}
}
}
else if(strCommand == "remove")
{
- if (!( isSubnet ? g_rpc_node->banman->Unban(subNet) : g_rpc_node->banman->Unban(netAddr) )) {
- throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet was not previously banned.");
+ if (!( isSubnet ? node.banman->Unban(subNet) : node.banman->Unban(netAddr) )) {
+ throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet was not previously manually banned.");
}
}
return NullUniValue;
@@ -627,7 +640,7 @@ static UniValue setban(const JSONRPCRequest& request)
static UniValue listbanned(const JSONRPCRequest& request)
{
RPCHelpMan{"listbanned",
- "\nList all banned IPs/Subnets.\n",
+ "\nList all manually banned IPs/Subnets.\n",
{},
RPCResult{RPCResult::Type::ARR, "", "",
{
@@ -636,7 +649,6 @@ static UniValue listbanned(const JSONRPCRequest& request)
{RPCResult::Type::STR, "address", ""},
{RPCResult::Type::NUM_TIME, "banned_until", ""},
{RPCResult::Type::NUM_TIME, "ban_created", ""},
- {RPCResult::Type::STR, "ban_reason", ""},
}},
}},
RPCExamples{
@@ -645,12 +657,13 @@ static UniValue listbanned(const JSONRPCRequest& request)
},
}.Check(request);
- if(!g_rpc_node->banman) {
+ NodeContext& node = EnsureNodeContext(request.context);
+ if(!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
}
banmap_t banMap;
- g_rpc_node->banman->GetBanned(banMap);
+ node.banman->GetBanned(banMap);
UniValue bannedAddresses(UniValue::VARR);
for (const auto& entry : banMap)
@@ -660,7 +673,6 @@ static UniValue listbanned(const JSONRPCRequest& request)
rec.pushKV("address", entry.first.ToString());
rec.pushKV("banned_until", banEntry.nBanUntil);
rec.pushKV("ban_created", banEntry.nCreateTime);
- rec.pushKV("ban_reason", banEntry.banReasonToString());
bannedAddresses.push_back(rec);
}
@@ -679,11 +691,12 @@ static UniValue clearbanned(const JSONRPCRequest& request)
+ HelpExampleRpc("clearbanned", "")
},
}.Check(request);
- if (!g_rpc_node->banman) {
+ NodeContext& node = EnsureNodeContext(request.context);
+ if (!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
}
- g_rpc_node->banman->ClearBanned();
+ node.banman->ClearBanned();
return NullUniValue;
}
@@ -699,13 +712,14 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
RPCExamples{""},
}.Check(request);
- if (!g_rpc_node->connman) {
+ NodeContext& node = EnsureNodeContext(request.context);
+ if (!node.connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
- g_rpc_node->connman->SetNetworkActive(request.params[0].get_bool());
+ node.connman->SetNetworkActive(request.params[0].get_bool());
- return g_rpc_node->connman->GetNetworkActive();
+ return node.connman->GetNetworkActive();
}
static UniValue getnodeaddresses(const JSONRPCRequest& request)
@@ -732,7 +746,8 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
+ HelpExampleRpc("getnodeaddresses", "8")
},
}.Check(request);
- if (!g_rpc_node->connman) {
+ NodeContext& node = EnsureNodeContext(request.context);
+ if (!node.connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
@@ -744,7 +759,7 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
}
}
// returns a shuffled list of CAddress
- std::vector<CAddress> vAddr = g_rpc_node->connman->GetAddresses();
+ std::vector<CAddress> vAddr = node.connman->GetAddresses();
UniValue ret(UniValue::VARR);
int address_return_count = std::min<int>(count, vAddr.size());
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 063ee1697c..d5e902cadd 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -28,6 +28,7 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <uint256.h>
+#include <util/bip32.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
#include <util/string.h>
@@ -305,7 +306,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
CMerkleBlock mb(block, setTxids);
ssMB << mb;
- std::string strHex = HexStr(ssMB.begin(), ssMB.end());
+ std::string strHex = HexStr(ssMB);
return strHex;
}
@@ -511,12 +512,12 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
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));
+ std::vector<std::string> ret;
+ using U = std::underlying_type<TxoutType>::type;
+ for (U i = (U)TxoutType::NONSTANDARD; i <= (U)TxoutType::WITNESS_UNKNOWN; ++i) {
+ ret.emplace_back(GetTxnOutputType(static_cast<TxoutType>(i)));
}
- return ret;
+ return Join(ret, ", ");
}
static UniValue decodescript(const JSONRPCRequest& request)
@@ -580,10 +581,10 @@ static UniValue decodescript(const JSONRPCRequest& request)
// 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") {
std::vector<std::vector<unsigned char>> solutions_data;
- txnouttype which_type = Solver(script, solutions_data);
+ TxoutType which_type = Solver(script, solutions_data);
// Uncompressed pubkeys cannot be used with segwit checksigs.
// If the script contains an uncompressed pubkey, skip encoding of a segwit program.
- if ((which_type == TX_PUBKEY) || (which_type == TX_MULTISIG)) {
+ if ((which_type == TxoutType::PUBKEY) || (which_type == TxoutType::MULTISIG)) {
for (const auto& solution : solutions_data) {
if ((solution.size() != 1) && !CPubKey(solution).IsCompressed()) {
return r;
@@ -592,10 +593,10 @@ static UniValue decodescript(const JSONRPCRequest& request)
}
UniValue sr(UniValue::VOBJ);
CScript segwitScr;
- if (which_type == TX_PUBKEY) {
+ if (which_type == TxoutType::PUBKEY) {
segwitScr = GetScriptForDestination(WitnessV0KeyHash(Hash160(solutions_data[0].begin(), solutions_data[0].end())));
- } else if (which_type == TX_PUBKEYHASH) {
- segwitScr = GetScriptForDestination(WitnessV0KeyHash(solutions_data[0]));
+ } else if (which_type == TxoutType::PUBKEYHASH) {
+ segwitScr = GetScriptForDestination(WitnessV0KeyHash(uint160{solutions_data[0]}));
} else {
// Scripts that are not fit for P2WPKH are encoded as P2WSH.
// Newer segwit program versions should be considered when then become available.
@@ -653,7 +654,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
- const CTxMemPool& mempool = EnsureMemPool();
+ const CTxMemPool& mempool = EnsureMemPool(request.context);
LOCK(cs_main);
LOCK(mempool.cs);
CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
@@ -778,7 +779,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
for (const CTxIn& txin : mtx.vin) {
coins[txin.prevout]; // Create empty map entry keyed by prevout.
}
- FindCoins(*g_rpc_node, coins);
+ NodeContext& node = EnsureNodeContext(request.context);
+ FindCoins(node, coins);
// Parse the prevtxs array
ParsePrevouts(request.params[2], &keystore, coins);
@@ -837,7 +839,8 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
std::string err_string;
AssertLockNotHeld(cs_main);
- const TransactionError err = BroadcastTransaction(*g_rpc_node, tx, err_string, max_raw_tx_fee, /*relay*/ true, /*wait_callback*/ true);
+ NodeContext& node = EnsureNodeContext(request.context);
+ const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee, /*relay*/ true, /*wait_callback*/ true);
if (TransactionError::OK != err) {
throw JSONRPCTransactionError(err, err_string);
}
@@ -904,7 +907,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
DEFAULT_MAX_RAW_TX_FEE_RATE :
CFeeRate(AmountFromValue(request.params[1]));
- CTxMemPool& mempool = EnsureMemPool();
+ CTxMemPool& mempool = EnsureMemPool(request.context);
int64_t virtual_size = GetVirtualTransactionSize(*tx);
CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
@@ -936,25 +939,6 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
return result;
}
-static std::string WriteHDKeypath(std::vector<uint32_t>& keypath)
-{
- std::string keypath_str = "m";
- for (uint32_t num : keypath) {
- keypath_str += "/";
- bool hardened = false;
- if (num & 0x80000000) {
- hardened = true;
- num &= ~0x80000000;
- }
-
- keypath_str += ToString(num);
- if (hardened) {
- keypath_str += "'";
- }
- }
- return keypath_str;
-}
-
UniValue decodepsbt(const JSONRPCRequest& request)
{
RPCHelpMan{"decodepsbt",
@@ -1102,30 +1086,34 @@ UniValue decodepsbt(const JSONRPCRequest& request)
const PSBTInput& input = psbtx.inputs[i];
UniValue in(UniValue::VOBJ);
// UTXOs
+ bool have_a_utxo = false;
+ CTxOut txout;
if (!input.witness_utxo.IsNull()) {
- const CTxOut& txout = input.witness_utxo;
-
- UniValue out(UniValue::VOBJ);
-
- out.pushKV("amount", ValueFromAmount(txout.nValue));
- if (MoneyRange(txout.nValue) && MoneyRange(total_in + txout.nValue)) {
- total_in += txout.nValue;
- } else {
- // Hack to just not show fee later
- have_all_utxos = false;
- }
+ txout = input.witness_utxo;
UniValue o(UniValue::VOBJ);
ScriptToUniv(txout.scriptPubKey, o, true);
+
+ UniValue out(UniValue::VOBJ);
+ out.pushKV("amount", ValueFromAmount(txout.nValue));
out.pushKV("scriptPubKey", o);
+
in.pushKV("witness_utxo", out);
- } else if (input.non_witness_utxo) {
+
+ have_a_utxo = true;
+ }
+ if (input.non_witness_utxo) {
+ txout = input.non_witness_utxo->vout[psbtx.tx->vin[i].prevout.n];
+
UniValue non_wit(UniValue::VOBJ);
TxToUniv(*input.non_witness_utxo, uint256(), non_wit, false);
in.pushKV("non_witness_utxo", non_wit);
- CAmount utxo_val = input.non_witness_utxo->vout[psbtx.tx->vin[i].prevout.n].nValue;
- if (MoneyRange(utxo_val) && MoneyRange(total_in + utxo_val)) {
- total_in += utxo_val;
+
+ have_a_utxo = true;
+ }
+ if (have_a_utxo) {
+ if (MoneyRange(txout.nValue) && MoneyRange(total_in + txout.nValue)) {
+ total_in += txout.nValue;
} else {
// Hack to just not show fee later
have_all_utxos = false;
@@ -1184,7 +1172,7 @@ UniValue decodepsbt(const JSONRPCRequest& request)
if (!input.final_script_witness.IsNull()) {
UniValue txinwitness(UniValue::VARR);
for (const auto& item : input.final_script_witness.stack) {
- txinwitness.push_back(HexStr(item.begin(), item.end()));
+ txinwitness.push_back(HexStr(item));
}
in.pushKV("final_scriptwitness", txinwitness);
}
@@ -1555,7 +1543,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
- const CTxMemPool& mempool = EnsureMemPool();
+ const CTxMemPool& mempool = EnsureMemPool(request.context);
LOCK2(cs_main, mempool.cs);
CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool);
@@ -1626,7 +1614,7 @@ UniValue joinpsbts(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "At least two PSBTs are required to join PSBTs.");
}
- int32_t best_version = 1;
+ uint32_t best_version = 1;
uint32_t best_locktime = 0xffffffff;
for (unsigned int i = 0; i < txs.size(); ++i) {
PartiallySignedTransaction psbtx;
@@ -1636,8 +1624,8 @@ UniValue joinpsbts(const JSONRPCRequest& request)
}
psbtxs.push_back(psbtx);
// Choose the highest version number
- if (psbtx.tx->nVersion > best_version) {
- best_version = psbtx.tx->nVersion;
+ if (static_cast<uint32_t>(psbtx.tx->nVersion) > best_version) {
+ best_version = static_cast<uint32_t>(psbtx.tx->nVersion);
}
// Choose the lowest lock time
if (psbtx.tx->nLockTime < best_locktime) {
@@ -1648,7 +1636,7 @@ UniValue joinpsbts(const JSONRPCRequest& request)
// 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->nVersion = static_cast<int32_t>(best_version);
merged_psbt.tx->nLockTime = best_locktime;
// Merge
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index bd82773bf2..1031716b4a 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -138,10 +138,10 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
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()));
+ witness.push_back(HexStr(txin.scriptWitness.stack[i]));
}
entry.pushKV("witness", witness);
- entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
+ entry.pushKV("scriptSig", HexStr(txin.scriptSig));
entry.pushKV("sequence", (uint64_t)txin.nSequence);
entry.pushKV("error", strMessage);
vErrorsRet.push_back(entry);
diff --git a/src/rpc/request.cpp b/src/rpc/request.cpp
index 56cac6661e..7fef45f50e 100644
--- a/src/rpc/request.cpp
+++ b/src/rpc/request.cpp
@@ -130,20 +130,20 @@ void DeleteAuthCookie()
}
}
-std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num)
+std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in)
{
if (!in.isArray()) {
throw std::runtime_error("Batch must be an array");
}
+ const size_t num {in.size()};
std::vector<UniValue> batch(num);
- for (size_t i=0; i<in.size(); ++i) {
- const UniValue &rec = in[i];
+ for (const UniValue& rec : in.getValues()) {
if (!rec.isObject()) {
- throw std::runtime_error("Batch member must be object");
+ throw std::runtime_error("Batch member must be an object");
}
size_t id = rec["id"].get_int();
if (id >= num) {
- throw std::runtime_error("Batch member id larger than size");
+ throw std::runtime_error("Batch member id is larger than batch size");
}
batch[id] = rec;
}
diff --git a/src/rpc/request.h b/src/rpc/request.h
index 99eb4f9354..4761e9e371 100644
--- a/src/rpc/request.h
+++ b/src/rpc/request.h
@@ -10,6 +10,10 @@
#include <univalue.h>
+namespace util {
+class Ref;
+} // namespace util
+
UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id);
std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id);
@@ -22,7 +26,7 @@ bool GetAuthCookie(std::string *cookie_out);
/** Delete RPC authentication cookie from disk */
void DeleteAuthCookie();
/** Parse JSON-RPC batch reply into a vector */
-std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num);
+std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in);
class JSONRPCRequest
{
@@ -34,8 +38,19 @@ public:
std::string URI;
std::string authUser;
std::string peerAddr;
+ const util::Ref& context;
+
+ JSONRPCRequest(const util::Ref& context) : id(NullUniValue), params(NullUniValue), fHelp(false), context(context) {}
+
+ //! Initializes request information from another request object and the
+ //! given context. The implementation should be updated if any members are
+ //! added or removed above.
+ JSONRPCRequest(const JSONRPCRequest& other, const util::Ref& context)
+ : id(other.id), strMethod(other.strMethod), params(other.params), fHelp(other.fHelp), URI(other.URI),
+ authUser(other.authUser), peerAddr(other.peerAddr), context(context)
+ {
+ }
- JSONRPCRequest() : id(NullUniValue), params(NullUniValue), fHelp(false) {}
void parse(const UniValue& valRequest);
};
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 219979f095..e5f6b1b9f1 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -11,17 +11,19 @@
#include <util/strencodings.h>
#include <util/system.h>
-#include <boost/signals2/signal.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
+#include <boost/signals2/signal.hpp>
+#include <cassert>
#include <memory> // for unique_ptr
+#include <mutex>
#include <unordered_map>
-static RecursiveMutex cs_rpcWarmup;
+static Mutex g_rpc_warmup_mutex;
static std::atomic<bool> g_rpc_running{false};
-static bool fRPCInWarmup GUARDED_BY(cs_rpcWarmup) = true;
-static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server started";
+static bool fRPCInWarmup GUARDED_BY(g_rpc_warmup_mutex) = true;
+static std::string rpcWarmupStatus GUARDED_BY(g_rpc_warmup_mutex) = "RPC server started";
/* Timer-creating functions */
static RPCTimerInterface* timerInterface = nullptr;
/* Map of name to timer. */
@@ -128,11 +130,9 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest&
return strRet;
}
-UniValue help(const JSONRPCRequest& jsonRequest)
+static RPCHelpMan help()
{
- if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
- throw std::runtime_error(
- RPCHelpMan{"help",
+ return RPCHelpMan{"help",
"\nList all commands, or get help for a specified command.\n",
{
{"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"},
@@ -141,32 +141,32 @@ UniValue help(const JSONRPCRequest& jsonRequest)
RPCResult::Type::STR, "", "The help text"
},
RPCExamples{""},
- }.ToString()
- );
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
+{
std::string strCommand;
if (jsonRequest.params.size() > 0)
strCommand = jsonRequest.params[0].get_str();
return tableRPC.help(strCommand, jsonRequest);
+},
+ };
}
-
-UniValue stop(const JSONRPCRequest& jsonRequest)
+static RPCHelpMan stop()
{
static const std::string RESULT{PACKAGE_NAME " stopping"};
- // Accept the deprecated and ignored 'detach' boolean argument
+ return RPCHelpMan{"stop",
// Also accept the hidden 'wait' integer argument (milliseconds)
// For instance, 'stop 1000' makes the call wait 1 second before returning
// to the client (intended for testing)
- if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
- throw std::runtime_error(
- RPCHelpMan{"stop",
"\nRequest a graceful shutdown of " PACKAGE_NAME ".",
- {},
+ {
+ {"wait", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "how long to wait in ms", "", {}, /* hidden */ true},
+ },
RPCResult{RPCResult::Type::STR, "", "A string with the content '" + RESULT + "'"},
RPCExamples{""},
- }.ToString());
+ [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
+{
// Event loop will exit after current HTTP requests have been handled, so
// this reply will get back to the client.
StartShutdown();
@@ -174,11 +174,13 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].get_int()});
}
return RESULT;
+},
+ };
}
-static UniValue uptime(const JSONRPCRequest& jsonRequest)
+static RPCHelpMan uptime()
{
- RPCHelpMan{"uptime",
+ return RPCHelpMan{"uptime",
"\nReturns the total uptime of the server.\n",
{},
RPCResult{
@@ -188,14 +190,16 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest)
HelpExampleCli("uptime", "")
+ HelpExampleRpc("uptime", "")
},
- }.Check(jsonRequest);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
return GetTime() - GetStartupTime();
}
+ };
+}
-static UniValue getrpcinfo(const JSONRPCRequest& request)
+static RPCHelpMan getrpcinfo()
{
- RPCHelpMan{"getrpcinfo",
+ return RPCHelpMan{"getrpcinfo",
"\nReturns details of the RPC server.\n",
{},
RPCResult{
@@ -215,8 +219,8 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("getrpcinfo", "")
+ HelpExampleRpc("getrpcinfo", "")},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(g_rpc_server_info.mutex);
UniValue active_commands(UniValue::VARR);
for (const RPCCommandExecutionInfo& info : g_rpc_server_info.active_commands) {
@@ -235,6 +239,8 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
return result;
}
+ };
+}
// clang-format off
static const CRPCCommand vRPCCommands[] =
@@ -291,17 +297,26 @@ void StartRPC()
void InterruptRPC()
{
- LogPrint(BCLog::RPC, "Interrupting RPC\n");
- // Interrupt e.g. running longpolls
- g_rpc_running = false;
+ static std::once_flag g_rpc_interrupt_flag;
+ // This function could be called twice if the GUI has been started with -server=1.
+ std::call_once(g_rpc_interrupt_flag, []() {
+ LogPrint(BCLog::RPC, "Interrupting RPC\n");
+ // Interrupt e.g. running longpolls
+ g_rpc_running = false;
+ });
}
void StopRPC()
{
- LogPrint(BCLog::RPC, "Stopping RPC\n");
- WITH_LOCK(g_deadline_timers_mutex, deadlineTimers.clear());
- DeleteAuthCookie();
- g_rpcSignals.Stopped();
+ static std::once_flag g_rpc_stop_flag;
+ // This function could be called twice if the GUI has been started with -server=1.
+ assert(!g_rpc_running);
+ std::call_once(g_rpc_stop_flag, []() {
+ LogPrint(BCLog::RPC, "Stopping RPC\n");
+ WITH_LOCK(g_deadline_timers_mutex, deadlineTimers.clear());
+ DeleteAuthCookie();
+ g_rpcSignals.Stopped();
+ });
}
bool IsRPCRunning()
@@ -309,22 +324,27 @@ bool IsRPCRunning()
return g_rpc_running;
}
+void RpcInterruptionPoint()
+{
+ if (!IsRPCRunning()) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
+}
+
void SetRPCWarmupStatus(const std::string& newStatus)
{
- LOCK(cs_rpcWarmup);
+ LOCK(g_rpc_warmup_mutex);
rpcWarmupStatus = newStatus;
}
void SetRPCWarmupFinished()
{
- LOCK(cs_rpcWarmup);
+ LOCK(g_rpc_warmup_mutex);
assert(fRPCInWarmup);
fRPCInWarmup = false;
}
bool RPCIsInWarmup(std::string *outStatus)
{
- LOCK(cs_rpcWarmup);
+ LOCK(g_rpc_warmup_mutex);
if (outStatus)
*outStatus = rpcWarmupStatus;
return fRPCInWarmup;
@@ -423,7 +443,7 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const
{
// Return immediately if in warmup
{
- LOCK(cs_rpcWarmup);
+ LOCK(g_rpc_warmup_mutex);
if (fRPCInWarmup)
throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
}
diff --git a/src/rpc/server.h b/src/rpc/server.h
index c91bf1f613..6da3e94ea2 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -8,11 +8,12 @@
#include <amount.h>
#include <rpc/request.h>
+#include <rpc/util.h>
+#include <functional>
#include <map>
#include <stdint.h>
#include <string>
-#include <functional>
#include <univalue.h>
@@ -29,6 +30,9 @@ namespace RPCServer
/** Query whether RPC is running */
bool IsRPCRunning();
+/** Throw JSONRPCError if RPC is not running */
+void RpcInterruptionPoint();
+
/**
* Set the RPC warmup status. When this is done, all RPC calls will error out
* immediately with RPC_IN_WARMUP.
@@ -82,6 +86,7 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface);
void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds);
typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest);
+typedef RPCHelpMan (*RpcMethodFnType)();
class CRPCCommand
{
@@ -98,6 +103,19 @@ public:
{
}
+ //! Simplified constructor taking plain RpcMethodFnType function pointer.
+ CRPCCommand(std::string category, std::string name_in, RpcMethodFnType fn, std::vector<std::string> args_in)
+ : CRPCCommand(
+ category,
+ fn().m_name,
+ [fn](const JSONRPCRequest& request, UniValue& result, bool) { result = fn().HandleRequest(request); return true; },
+ fn().GetArgNames(),
+ intptr_t(fn))
+ {
+ CHECK_NONFATAL(fn().m_name == name_in);
+ CHECK_NONFATAL(fn().GetArgNames() == args_in);
+ }
+
//! 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,
@@ -114,7 +132,7 @@ public:
};
/**
- * Bitcoin RPC command dispatcher.
+ * RPC command dispatcher.
*/
class CRPCTable
{
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 860fa198d5..9f4c7bee9c 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -10,6 +10,7 @@
#include <tinyformat.h>
#include <util/strencodings.h>
#include <util/string.h>
+#include <util/translation.h>
#include <tuple>
@@ -112,6 +113,23 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
return ParseHexV(find_value(o, strKey), strKey);
}
+CoinStatsHashType ParseHashType(const UniValue& param, const CoinStatsHashType default_type)
+{
+ if (param.isNull()) {
+ return default_type;
+ } else {
+ std::string hash_type_input = param.get_str();
+
+ if (hash_type_input == "hash_serialized_2") {
+ return CoinStatsHashType::HASH_SERIALIZED;
+ } else if (hash_type_input == "none") {
+ return CoinStatsHashType::NONE;
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%d is not a valid hash_type", hash_type_input));
+ }
+ }
+}
+
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
{
return "> bitcoin-cli " + methodname + " " + args + "\n";
@@ -223,7 +241,7 @@ public:
obj.pushKV("isscript", false);
obj.pushKV("iswitness", true);
obj.pushKV("witness_version", 0);
- obj.pushKV("witness_program", HexStr(id.begin(), id.end()));
+ obj.pushKV("witness_program", HexStr(id));
return obj;
}
@@ -233,7 +251,7 @@ public:
obj.pushKV("isscript", true);
obj.pushKV("iswitness", true);
obj.pushKV("witness_version", 0);
- obj.pushKV("witness_program", HexStr(id.begin(), id.end()));
+ obj.pushKV("witness_program", HexStr(id));
return obj;
}
@@ -285,7 +303,7 @@ UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_s
if (err_string.length() > 0) {
return JSONRPCError(RPCErrorFromTransactionError(terr), err_string);
} else {
- return JSONRPCError(RPCErrorFromTransactionError(terr), TransactionErrorString(terr));
+ return JSONRPCError(RPCErrorFromTransactionError(terr), TransactionErrorString(terr).original);
}
}
@@ -367,9 +385,7 @@ struct Sections {
PushSection({indent + "]" + (outer_type != OuterType::NONE ? "," : ""), ""});
break;
}
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
}
/**
@@ -380,6 +396,9 @@ struct Sections {
std::string ret;
const size_t pad = m_max_pad + 4;
for (const auto& s : m_sections) {
+ // The left part of a section is assumed to be a single line, usually it is the name of the JSON struct or a
+ // brace like {, }, [, or ]
+ CHECK_NONFATAL(s.m_left.find('\n') == std::string::npos);
if (s.m_right.empty()) {
ret += s.m_left;
ret += "\n";
@@ -414,7 +433,11 @@ struct Sections {
};
RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples)
+ : RPCHelpMan{std::move(name), std::move(description), std::move(args), std::move(results), std::move(examples), nullptr} {}
+
+RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun)
: m_name{std::move(name)},
+ m_fun{std::move(fun)},
m_description{std::move(description)},
m_args{std::move(args)},
m_results{std::move(results)},
@@ -463,6 +486,16 @@ bool RPCHelpMan::IsValidNumArgs(size_t num_args) const
}
return num_required_args <= num_args && num_args <= m_args.size();
}
+
+std::vector<std::string> RPCHelpMan::GetArgNames() const
+{
+ std::vector<std::string> ret;
+ for (const auto& arg : m_args) {
+ ret.emplace_back(arg.m_names);
+ }
+ return ret;
+}
+
std::string RPCHelpMan::ToString() const
{
std::string ret;
@@ -471,6 +504,7 @@ std::string RPCHelpMan::ToString() const
ret += m_name;
bool was_optional{false};
for (const auto& arg : m_args) {
+ if (arg.m_hidden) continue;
const bool optional = arg.IsOptional();
ret += " ";
if (optional) {
@@ -492,6 +526,7 @@ std::string RPCHelpMan::ToString() const
Sections sections;
for (size_t i{0}; i < m_args.size(); ++i) {
const auto& arg = m_args.at(i);
+ if (arg.m_hidden) continue;
if (i == 0) ret += "\nArguments:\n";
@@ -571,9 +606,7 @@ std::string RPCArg::ToDescriptionString() const
ret += "json array";
break;
}
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
}
if (m_fallback.which() == 1) {
ret += ", optional, default=" + boost::get<std::string>(m_fallback);
@@ -591,9 +624,7 @@ std::string RPCArg::ToDescriptionString() const
ret += ", required";
break;
}
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
}
ret += ")";
ret += m_description.empty() ? "" : " " + m_description;
@@ -688,10 +719,7 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
sections.PushSection({indent + "}" + maybe_separator, ""});
return;
}
-
- // no default case, so the compiler can warn about missing cases
- }
-
+ } // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false);
}
@@ -728,9 +756,7 @@ std::string RPCArg::ToStringObj(const bool oneline) const
case Type::OBJ_USER_KEYS:
// Currently unused, so avoid writing dead code
CHECK_NONFATAL(false);
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false);
}
@@ -765,9 +791,7 @@ std::string RPCArg::ToString(const bool oneline) const
}
return "[" + res + "...]";
}
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false);
}
@@ -843,16 +867,9 @@ UniValue GetServicesNames(ServiceFlags services)
{
UniValue servicesNames(UniValue::VARR);
- if (services & NODE_NETWORK)
- servicesNames.push_back("NETWORK");
- if (services & NODE_GETUTXO)
- servicesNames.push_back("GETUTXO");
- if (services & NODE_BLOOM)
- servicesNames.push_back("BLOOM");
- if (services & NODE_WITNESS)
- servicesNames.push_back("WITNESS");
- if (services & NODE_NETWORK_LIMITED)
- servicesNames.push_back("NETWORK_LIMITED");
+ for (const auto& flag : serviceFlagsToStr(services)) {
+ servicesNames.push_back(flag);
+ }
return servicesNames;
}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 53dce2c397..45b0bb0c7e 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_RPC_UTIL_H
#define BITCOIN_RPC_UTIL_H
+#include <node/coinstats.h>
#include <node/transaction.h>
#include <outputtype.h>
#include <protocol.h>
@@ -77,6 +78,8 @@ 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);
+CoinStatsHashType ParseHashType(const UniValue& param, const CoinStatsHashType default_type);
+
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);
@@ -144,6 +147,7 @@ struct RPCArg {
using Fallback = boost::variant<Optional, /* default value for optional args */ std::string>;
const std::string m_names; //!< The name of the arg (can be empty for inner args, can contain multiple aliases separated by | for named request arguments)
const Type m_type;
+ const bool m_hidden;
const std::vector<RPCArg> m_inner; //!< Only used for arrays or dicts
const Fallback m_fallback;
const std::string m_description;
@@ -156,9 +160,11 @@ struct RPCArg {
const Fallback fallback,
const std::string description,
const std::string oneline_description = "",
- const std::vector<std::string> type_str = {})
+ const std::vector<std::string> type_str = {},
+ const bool hidden = false)
: m_names{std::move(name)},
m_type{std::move(type)},
+ m_hidden{hidden},
m_fallback{std::move(fallback)},
m_description{std::move(description)},
m_oneline_description{std::move(oneline_description)},
@@ -177,6 +183,7 @@ struct RPCArg {
const std::vector<std::string> type_str = {})
: m_names{std::move(name)},
m_type{std::move(type)},
+ m_hidden{false},
m_inner{std::move(inner)},
m_fallback{std::move(fallback)},
m_description{std::move(description)},
@@ -326,8 +333,15 @@ class RPCHelpMan
{
public:
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples);
+ using RPCMethodImpl = std::function<UniValue(const RPCHelpMan&, const JSONRPCRequest&)>;
+ RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun);
std::string ToString() const;
+ UniValue HandleRequest(const JSONRPCRequest& request)
+ {
+ Check(request);
+ return m_fun(*this, request);
+ }
/** If the supplied number of args is neither too small nor too high */
bool IsValidNumArgs(size_t num_args) const;
/**
@@ -340,8 +354,12 @@ public:
}
}
-private:
+ std::vector<std::string> GetArgNames() const;
+
const std::string m_name;
+
+private:
+ const RPCMethodImpl m_fun;
const std::string m_description;
const std::vector<RPCArg> m_args;
const RPCResults m_results;
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index c4bd47310b..7c361bf26f 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -30,9 +30,6 @@ void CScheduler::serviceQueue()
// is called.
while (!shouldStop()) {
try {
- if (!shouldStop() && taskQueue.empty()) {
- REVERSE_LOCK(lock);
- }
while (!shouldStop() && taskQueue.empty()) {
// Wait until there is something to do.
newTaskScheduled.wait(lock);
@@ -71,18 +68,6 @@ void CScheduler::serviceQueue()
newTaskScheduled.notify_one();
}
-void CScheduler::stop(bool drain)
-{
- {
- LOCK(newTaskMutex);
- if (drain)
- stopWhenEmpty = true;
- else
- stopRequested = true;
- }
- newTaskScheduled.notify_all();
-}
-
void CScheduler::schedule(CScheduler::Function f, std::chrono::system_clock::time_point t)
{
{
@@ -125,8 +110,8 @@ void CScheduler::scheduleEvery(CScheduler::Function f, std::chrono::milliseconds
scheduleFromNow([=] { Repeat(*this, f, delta); }, delta);
}
-size_t CScheduler::getQueueInfo(std::chrono::system_clock::time_point &first,
- std::chrono::system_clock::time_point &last) const
+size_t CScheduler::getQueueInfo(std::chrono::system_clock::time_point& first,
+ std::chrono::system_clock::time_point& last) const
{
LOCK(newTaskMutex);
size_t result = taskQueue.size();
@@ -137,13 +122,15 @@ size_t CScheduler::getQueueInfo(std::chrono::system_clock::time_point &first,
return result;
}
-bool CScheduler::AreThreadsServicingQueue() const {
+bool CScheduler::AreThreadsServicingQueue() const
+{
LOCK(newTaskMutex);
return nThreadsServicingQueue;
}
-void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() {
+void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue()
+{
{
LOCK(m_cs_callbacks_pending);
// Try to avoid scheduling too many copies here, but if we
@@ -155,8 +142,9 @@ void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() {
m_pscheduler->schedule(std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this), std::chrono::system_clock::now());
}
-void SingleThreadedSchedulerClient::ProcessQueue() {
- std::function<void ()> callback;
+void SingleThreadedSchedulerClient::ProcessQueue()
+{
+ std::function<void()> callback;
{
LOCK(m_cs_callbacks_pending);
if (m_are_callbacks_running) return;
@@ -172,7 +160,8 @@ void SingleThreadedSchedulerClient::ProcessQueue() {
struct RAIICallbacksRunning {
SingleThreadedSchedulerClient* instance;
explicit RAIICallbacksRunning(SingleThreadedSchedulerClient* _instance) : instance(_instance) {}
- ~RAIICallbacksRunning() {
+ ~RAIICallbacksRunning()
+ {
{
LOCK(instance->m_cs_callbacks_pending);
instance->m_are_callbacks_running = false;
@@ -184,7 +173,8 @@ void SingleThreadedSchedulerClient::ProcessQueue() {
callback();
}
-void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void ()> func) {
+void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void()> func)
+{
assert(m_pscheduler);
{
@@ -194,7 +184,8 @@ void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void ()> fun
MaybeScheduleProcessQueue();
}
-void SingleThreadedSchedulerClient::EmptyQueue() {
+void SingleThreadedSchedulerClient::EmptyQueue()
+{
assert(!m_pscheduler->AreThreadsServicingQueue());
bool should_continue = true;
while (should_continue) {
@@ -204,7 +195,8 @@ void SingleThreadedSchedulerClient::EmptyQueue() {
}
}
-size_t SingleThreadedSchedulerClient::CallbacksPending() {
+size_t SingleThreadedSchedulerClient::CallbacksPending()
+{
LOCK(m_cs_callbacks_pending);
return m_callbacks_pending.size();
}
diff --git a/src/scheduler.h b/src/scheduler.h
index 1e64195484..d7fe00d1b4 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -5,11 +5,6 @@
#ifndef BITCOIN_SCHEDULER_H
#define BITCOIN_SCHEDULER_H
-//
-// NOTE:
-// boost::thread should be ported to std::thread
-// when we support C++11.
-//
#include <condition_variable>
#include <functional>
#include <list>
@@ -17,24 +12,23 @@
#include <sync.h>
-//
-// Simple class for background tasks that should be run
-// periodically or once "after a while"
-//
-// Usage:
-//
-// CScheduler* s = new CScheduler();
-// s->scheduleFromNow(doSomething, std::chrono::milliseconds{11}); // Assuming a: void doSomething() { }
-// s->scheduleFromNow([=] { this->func(argument); }, std::chrono::milliseconds{3});
-// boost::thread* t = new boost::thread(std::bind(CScheduler::serviceQueue, s));
-//
-// ... then at program shutdown, make sure to call stop() to clean up the thread(s) running serviceQueue:
-// s->stop();
-// t->join();
-// delete t;
-// delete s; // Must be done after thread is interrupted/joined.
-//
-
+/**
+ * Simple class for background tasks that should be run
+ * periodically or once "after a while"
+ *
+ * Usage:
+ *
+ * CScheduler* s = new CScheduler();
+ * s->scheduleFromNow(doSomething, std::chrono::milliseconds{11}); // Assuming a: void doSomething() { }
+ * s->scheduleFromNow([=] { this->func(argument); }, std::chrono::milliseconds{3});
+ * std::thread* t = new std::thread([&] { s->serviceQueue(); });
+ *
+ * ... then at program shutdown, make sure to call stop() to clean up the thread(s) running serviceQueue:
+ * s->stop();
+ * t->join();
+ * delete t;
+ * delete s; // Must be done after thread is interrupted/joined.
+ */
class CScheduler
{
public:
@@ -43,7 +37,7 @@ public:
typedef std::function<void()> Function;
- // Call func at/after time t
+ /** Call func at/after time t */
void schedule(Function f, std::chrono::system_clock::time_point t);
/** Call f once after the delta has passed */
@@ -67,23 +61,33 @@ public:
*/
void MockForward(std::chrono::seconds delta_seconds);
- // To keep things as simple as possible, there is no unschedule.
-
- // Services the queue 'forever'. Should be run in a thread,
- // and interrupted using boost::interrupt_thread
+ /**
+ * Services the queue 'forever'. Should be run in a thread,
+ * and interrupted using boost::interrupt_thread
+ */
void serviceQueue();
- // Tell any threads running serviceQueue to stop as soon as they're
- // done servicing whatever task they're currently servicing (drain=false)
- // or when there is no work left to be done (drain=true)
- void stop(bool drain=false);
+ /** Tell any threads running serviceQueue to stop as soon as the current task is done */
+ void stop()
+ {
+ WITH_LOCK(newTaskMutex, stopRequested = true);
+ newTaskScheduled.notify_all();
+ }
+ /** Tell any threads running serviceQueue to stop when there is no work left to be done */
+ void StopWhenDrained()
+ {
+ WITH_LOCK(newTaskMutex, stopWhenEmpty = true);
+ newTaskScheduled.notify_all();
+ }
- // Returns number of tasks waiting to be serviced,
- // and first and last task times
- size_t getQueueInfo(std::chrono::system_clock::time_point &first,
- std::chrono::system_clock::time_point &last) const;
+ /**
+ * Returns number of tasks waiting to be serviced,
+ * and first and last task times
+ */
+ size_t getQueueInfo(std::chrono::system_clock::time_point& first,
+ std::chrono::system_clock::time_point& last) const;
- // Returns true if there are threads actively running in serviceQueue()
+ /** Returns true if there are threads actively running in serviceQueue() */
bool AreThreadsServicingQueue() const;
private:
@@ -106,19 +110,20 @@ private:
* B() will be able to observe all of the effects of callback A() which executed
* before it.
*/
-class SingleThreadedSchedulerClient {
+class SingleThreadedSchedulerClient
+{
private:
- CScheduler *m_pscheduler;
+ CScheduler* m_pscheduler;
RecursiveMutex m_cs_callbacks_pending;
- std::list<std::function<void ()>> m_callbacks_pending GUARDED_BY(m_cs_callbacks_pending);
+ std::list<std::function<void()>> m_callbacks_pending GUARDED_BY(m_cs_callbacks_pending);
bool m_are_callbacks_running GUARDED_BY(m_cs_callbacks_pending) = false;
void MaybeScheduleProcessQueue();
void ProcessQueue();
public:
- explicit SingleThreadedSchedulerClient(CScheduler *pschedulerIn) : m_pscheduler(pschedulerIn) {}
+ explicit SingleThreadedSchedulerClient(CScheduler* pschedulerIn) : m_pscheduler(pschedulerIn) {}
/**
* Add a callback to be executed. Callbacks are executed serially
@@ -126,10 +131,12 @@ public:
* Practically, this means that callbacks can behave as if they are executed
* in order by a single thread.
*/
- void AddToProcessQueue(std::function<void ()> func);
+ void AddToProcessQueue(std::function<void()> func);
- // Processes all remaining queue members on the calling thread, blocking until queue is empty
- // Must be called after the CScheduler has no remaining processing threads!
+ /**
+ * Processes all remaining queue members on the calling thread, blocking until queue is empty
+ * Must be called after the CScheduler has no remaining processing threads!
+ */
void EmptyQueue();
size_t CallbacksPending();
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index ed0175bb10..5fa128d62d 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -139,7 +139,7 @@ std::string DescriptorChecksum(const Span<const char>& span)
return ret;
}
-std::string AddChecksum(const std::string& str) { return str + "#" + DescriptorChecksum(MakeSpan(str)); }
+std::string AddChecksum(const std::string& str) { return str + "#" + DescriptorChecksum(str); }
////////////////////////////////////////////////////////////////////////////
// Internal representation //
@@ -235,7 +235,7 @@ public:
}
bool IsRange() const override { return false; }
size_t GetSize() const override { return m_pubkey.size(); }
- std::string ToString() const override { return HexStr(m_pubkey.begin(), m_pubkey.end()); }
+ std::string ToString() const override { return HexStr(m_pubkey); }
bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override
{
CKey key;
@@ -583,7 +583,7 @@ class RawDescriptor final : public DescriptorImpl
{
const CScript m_script;
protected:
- std::string ToStringExtra() const override { return HexStr(m_script.begin(), m_script.end()); }
+ std::string ToStringExtra() const override { return HexStr(m_script); }
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Vector(m_script); }
public:
RawDescriptor(CScript script) : DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {}
@@ -985,15 +985,15 @@ std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptCo
std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
{
std::vector<std::vector<unsigned char>> data;
- txnouttype txntype = Solver(script, data);
+ TxoutType txntype = Solver(script, data);
- if (txntype == TX_PUBKEY) {
+ if (txntype == TxoutType::PUBKEY) {
CPubKey pubkey(data[0].begin(), data[0].end());
if (pubkey.IsValid()) {
return MakeUnique<PKDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
- if (txntype == TX_PUBKEYHASH) {
+ if (txntype == TxoutType::PUBKEYHASH) {
uint160 hash(data[0]);
CKeyID keyid(hash);
CPubKey pubkey;
@@ -1001,7 +1001,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
return MakeUnique<PKHDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
- if (txntype == TX_WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) {
+ if (txntype == TxoutType::WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) {
uint160 hash(data[0]);
CKeyID keyid(hash);
CPubKey pubkey;
@@ -1009,7 +1009,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
return MakeUnique<WPKHDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
- if (txntype == TX_MULTISIG) {
+ if (txntype == TxoutType::MULTISIG) {
std::vector<std::unique_ptr<PubkeyProvider>> providers;
for (size_t i = 1; i + 1 < data.size(); ++i) {
CPubKey pubkey(data[i].begin(), data[i].end());
@@ -1017,7 +1017,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
}
return MakeUnique<MultisigDescriptor>((int)data[0][0], std::move(providers));
}
- if (txntype == TX_SCRIPTHASH && ctx == ParseScriptContext::TOP) {
+ if (txntype == TxoutType::SCRIPTHASH && ctx == ParseScriptContext::TOP) {
uint160 hash(data[0]);
CScriptID scriptid(hash);
CScript subscript;
@@ -1026,7 +1026,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
if (sub) return MakeUnique<SHDescriptor>(std::move(sub));
}
}
- if (txntype == TX_WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) {
+ if (txntype == TxoutType::WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) {
CScriptID scriptid;
CRIPEMD160().Write(data[0].data(), data[0].size()).Finalize(scriptid.begin());
CScript subscript;
@@ -1087,7 +1087,7 @@ bool CheckChecksum(Span<const char>& sp, bool require_checksum, std::string& err
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum)
{
- Span<const char> sp(descriptor.data(), descriptor.size());
+ Span<const char> sp{descriptor};
if (!CheckChecksum(sp, require_checksum, error)) return nullptr;
auto ret = ParseScript(0, sp, ParseScriptContext::TOP, out, error);
if (sp.size() == 0 && ret) return std::unique_ptr<Descriptor>(std::move(ret));
@@ -1098,7 +1098,7 @@ std::string GetDescriptorChecksum(const std::string& descriptor)
{
std::string ret;
std::string error;
- Span<const char> sp(descriptor.data(), descriptor.size());
+ Span<const char> sp{descriptor};
if (!CheckChecksum(sp, false, error, &ret)) return "";
return ret;
}
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 23d5b72a5c..9415bba585 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1522,7 +1522,7 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
{
CScript scriptPubKey;
- Span<const valtype> stack = MakeSpan(witness.stack);
+ Span<const valtype> stack{witness.stack};
if (witversion == 0) {
if (program.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
diff --git a/src/script/keyorigin.h b/src/script/keyorigin.h
index 467605ce46..a318ff0f9d 100644
--- a/src/script/keyorigin.h
+++ b/src/script/keyorigin.h
@@ -18,13 +18,7 @@ struct KeyOriginInfo
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);
- }
+ SERIALIZE_METHODS(KeyOriginInfo, obj) { READWRITE(obj.fingerprint, obj.path); }
void clear()
{
diff --git a/src/script/script.cpp b/src/script/script.cpp
index ae0de1d24e..92c6fe7785 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -7,7 +7,9 @@
#include <util/strencodings.h>
-const char* GetOpName(opcodetype opcode)
+#include <string>
+
+std::string GetOpName(opcodetype opcode)
{
switch (opcode)
{
diff --git a/src/script/script.h b/src/script/script.h
index 773ffbb985..c1f2b66921 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -193,7 +193,7 @@ enum opcodetype
// Maximum value that an opcode can be
static const unsigned int MAX_OPCODE = OP_NOP10;
-const char* GetOpName(opcodetype opcode);
+std::string GetOpName(opcodetype opcode);
class scriptnum_error : public std::runtime_error
{
@@ -412,12 +412,7 @@ public:
CScript(std::vector<unsigned char>::const_iterator pbegin, std::vector<unsigned char>::const_iterator pend) : CScriptBase(pbegin, pend) { }
CScript(const unsigned char* pbegin, const unsigned char* pend) : CScriptBase(pbegin, pend) { }
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITEAS(CScriptBase, *this);
- }
+ SERIALIZE_METHODS(CScript, obj) { READWRITEAS(CScriptBase, obj); }
explicit CScript(int64_t b) { operator<<(b); }
explicit CScript(opcodetype b) { operator<<(b); }
diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp
index 57e8fee539..69e14803f1 100644
--- a/src/script/script_error.cpp
+++ b/src/script/script_error.cpp
@@ -5,7 +5,9 @@
#include <script/script_error.h>
-const char* ScriptErrorString(const ScriptError serror)
+#include <string>
+
+std::string ScriptErrorString(const ScriptError serror)
{
switch (serror)
{
diff --git a/src/script/script_error.h b/src/script/script_error.h
index 400f63ff0f..2978c147e1 100644
--- a/src/script/script_error.h
+++ b/src/script/script_error.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_SCRIPT_SCRIPT_ERROR_H
#define BITCOIN_SCRIPT_SCRIPT_ERROR_H
+#include <string>
+
typedef enum ScriptError_t
{
SCRIPT_ERR_OK = 0,
@@ -73,6 +75,6 @@ typedef enum ScriptError_t
#define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT
-const char* ScriptErrorString(const ScriptError error);
+std::string ScriptErrorString(const ScriptError error);
#endif // BITCOIN_SCRIPT_SCRIPT_ERROR_H
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index e7b6df3ce8..aaecab1ef2 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -11,7 +11,7 @@
#include <util/system.h>
#include <cuckoocache.h>
-#include <boost/thread.hpp>
+#include <boost/thread/shared_mutex.hpp>
namespace {
/**
@@ -23,7 +23,7 @@ class CSignatureCache
{
private:
//! Entries are SHA256(nonce || signature hash || public key || signature):
- uint256 nonce;
+ CSHA256 m_salted_hasher;
typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
map_type setValid;
boost::shared_mutex cs_sigcache;
@@ -31,13 +31,19 @@ private:
public:
CSignatureCache()
{
- GetRandBytes(nonce.begin(), 32);
+ uint256 nonce = GetRandHash();
+ // We want the nonce to be 64 bytes long to force the hasher to process
+ // this chunk, which makes later hash computations more efficient. We
+ // just write our 32-byte entropy twice to fill the 64 bytes.
+ m_salted_hasher.Write(nonce.begin(), 32);
+ m_salted_hasher.Write(nonce.begin(), 32);
}
void
ComputeEntry(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey)
{
- CSHA256().Write(nonce.begin(), 32).Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
+ CSHA256 hasher = m_salted_hasher;
+ hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
}
bool
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 1e00afcf89..f425215549 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -92,11 +92,11 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat
/**
* Sign scriptPubKey using signature made with creator.
* Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed),
- * unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script.
+ * unless whichTypeRet is TxoutType::SCRIPTHASH, in which case scriptSigRet is the redemption script.
* Returns false if scriptPubKey could not be completely satisfied.
*/
static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey,
- std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion, SignatureData& sigdata)
+ std::vector<valtype>& ret, TxoutType& whichTypeRet, SigVersion sigversion, SignatureData& sigdata)
{
CScript scriptRet;
uint160 h160;
@@ -108,15 +108,15 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
switch (whichTypeRet)
{
- case TX_NONSTANDARD:
- case TX_NULL_DATA:
- case TX_WITNESS_UNKNOWN:
+ case TxoutType::NONSTANDARD:
+ case TxoutType::NULL_DATA:
+ case TxoutType::WITNESS_UNKNOWN:
return false;
- case TX_PUBKEY:
+ case TxoutType::PUBKEY:
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
ret.push_back(std::move(sig));
return true;
- case TX_PUBKEYHASH: {
+ case TxoutType::PUBKEYHASH: {
CKeyID keyID = CKeyID(uint160(vSolutions[0]));
CPubKey pubkey;
if (!GetPubKey(provider, sigdata, keyID, pubkey)) {
@@ -129,9 +129,9 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
ret.push_back(ToByteVector(pubkey));
return true;
}
- case TX_SCRIPTHASH:
+ case TxoutType::SCRIPTHASH:
h160 = uint160(vSolutions[0]);
- if (GetCScript(provider, sigdata, h160, scriptRet)) {
+ if (GetCScript(provider, sigdata, CScriptID{h160}, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
@@ -139,7 +139,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
sigdata.missing_redeem_script = h160;
return false;
- case TX_MULTISIG: {
+ case TxoutType::MULTISIG: {
size_t required = vSolutions.front()[0];
ret.push_back(valtype()); // workaround CHECKMULTISIG bug
for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
@@ -159,13 +159,13 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
}
return ok;
}
- case TX_WITNESS_V0_KEYHASH:
+ case TxoutType::WITNESS_V0_KEYHASH:
ret.push_back(vSolutions[0]);
return true;
- case TX_WITNESS_V0_SCRIPTHASH:
+ case TxoutType::WITNESS_V0_SCRIPTHASH:
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin());
- if (GetCScript(provider, sigdata, h160, scriptRet)) {
+ if (GetCScript(provider, sigdata, CScriptID{h160}, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
@@ -198,44 +198,44 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
if (sigdata.complete) return true;
std::vector<valtype> result;
- txnouttype whichType;
+ TxoutType whichType;
bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
bool P2SH = false;
CScript subscript;
sigdata.scriptWitness.stack.clear();
- if (solved && whichType == TX_SCRIPTHASH)
+ if (solved && whichType == TxoutType::SCRIPTHASH)
{
// Solver returns the subscript that needs to be evaluated;
// the final scriptSig is the signatures from that
// and then the serialized subscript:
subscript = CScript(result[0].begin(), result[0].end());
sigdata.redeem_script = subscript;
- solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TX_SCRIPTHASH;
+ solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TxoutType::SCRIPTHASH;
P2SH = true;
}
- if (solved && whichType == TX_WITNESS_V0_KEYHASH)
+ if (solved && whichType == TxoutType::WITNESS_V0_KEYHASH)
{
CScript witnessscript;
witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
- txnouttype subType;
+ TxoutType subType;
solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata);
sigdata.scriptWitness.stack = result;
sigdata.witness = true;
result.clear();
}
- else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH)
+ else if (solved && whichType == TxoutType::WITNESS_V0_SCRIPTHASH)
{
CScript witnessscript(result[0].begin(), result[0].end());
sigdata.witness_script = witnessscript;
- txnouttype subType;
- solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
+ TxoutType subType;
+ solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TxoutType::SCRIPTHASH && subType != TxoutType::WITNESS_V0_SCRIPTHASH && subType != TxoutType::WITNESS_V0_KEYHASH;
result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end()));
sigdata.scriptWitness.stack = result;
sigdata.witness = true;
result.clear();
- } else if (solved && whichType == TX_WITNESS_UNKNOWN) {
+ } else if (solved && whichType == TxoutType::WITNESS_UNKNOWN) {
sigdata.witness = true;
}
@@ -301,11 +301,11 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
// Get scripts
std::vector<std::vector<unsigned char>> solutions;
- txnouttype script_type = Solver(txout.scriptPubKey, solutions);
+ TxoutType script_type = Solver(txout.scriptPubKey, solutions);
SigVersion sigversion = SigVersion::BASE;
CScript next_script = txout.scriptPubKey;
- if (script_type == TX_SCRIPTHASH && !stack.script.empty() && !stack.script.back().empty()) {
+ if (script_type == TxoutType::SCRIPTHASH && !stack.script.empty() && !stack.script.back().empty()) {
// Get the redeemScript
CScript redeem_script(stack.script.back().begin(), stack.script.back().end());
data.redeem_script = redeem_script;
@@ -315,7 +315,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
script_type = Solver(next_script, solutions);
stack.script.pop_back();
}
- if (script_type == TX_WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) {
+ if (script_type == TxoutType::WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) {
// Get the witnessScript
CScript witness_script(stack.witness.back().begin(), stack.witness.back().end());
data.witness_script = witness_script;
@@ -328,7 +328,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
stack.witness.clear();
sigversion = SigVersion::WITNESS_V0;
}
- if (script_type == TX_MULTISIG && !stack.script.empty()) {
+ if (script_type == TxoutType::MULTISIG && !stack.script.empty()) {
// Build a map of pubkey -> signature by matching sigs to pubkeys:
assert(solutions.size() > 1);
unsigned int num_pubkeys = solutions.size()-2;
@@ -454,13 +454,13 @@ bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
{
std::vector<valtype> solutions;
auto whichtype = Solver(script, solutions);
- if (whichtype == TX_WITNESS_V0_SCRIPTHASH || whichtype == TX_WITNESS_V0_KEYHASH || whichtype == TX_WITNESS_UNKNOWN) return true;
- if (whichtype == TX_SCRIPTHASH) {
+ if (whichtype == TxoutType::WITNESS_V0_SCRIPTHASH || whichtype == TxoutType::WITNESS_V0_KEYHASH || whichtype == TxoutType::WITNESS_UNKNOWN) return true;
+ if (whichtype == TxoutType::SCRIPTHASH) {
auto h160 = uint160(solutions[0]);
CScript subscript;
- if (provider.GetCScript(h160, subscript)) {
+ if (provider.GetCScript(CScriptID{h160}, subscript)) {
whichtype = Solver(subscript, solutions);
- if (whichtype == TX_WITNESS_V0_SCRIPTHASH || whichtype == TX_WITNESS_V0_KEYHASH || whichtype == TX_WITNESS_UNKNOWN) return true;
+ if (whichtype == TxoutType::WITNESS_V0_SCRIPTHASH || whichtype == TxoutType::WITNESS_V0_KEYHASH || whichtype == TxoutType::WITNESS_UNKNOWN) return true;
}
}
return false;
diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp
index 01757e2f65..2d8dc7d471 100644
--- a/src/script/signingprovider.cpp
+++ b/src/script/signingprovider.cpp
@@ -180,10 +180,10 @@ CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination&
// Only supports destinations which map to single public keys, i.e. P2PKH,
// P2WPKH, and P2SH-P2WPKH.
if (auto id = boost::get<PKHash>(&dest)) {
- return CKeyID(*id);
+ return ToKeyID(*id);
}
if (auto witness_id = boost::get<WitnessV0KeyHash>(&dest)) {
- return CKeyID(*witness_id);
+ return ToKeyID(*witness_id);
}
if (auto script_hash = boost::get<ScriptHash>(&dest)) {
CScript script;
@@ -191,7 +191,7 @@ CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination&
CTxDestination 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);
+ return ToKeyID(*inner_witness_id);
}
}
}
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 7d89a336fb..1c4990791c 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -9,37 +9,55 @@
#include <pubkey.h>
#include <script/script.h>
+#include <string>
+
typedef std::vector<unsigned char> valtype;
bool fAcceptDatacarrier = DEFAULT_ACCEPT_DATACARRIER;
unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
-CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
+CScriptID::CScriptID(const CScript& in) : BaseHash(Hash160(in.begin(), in.end())) {}
+CScriptID::CScriptID(const ScriptHash& in) : BaseHash(static_cast<uint160>(in)) {}
+
+ScriptHash::ScriptHash(const CScript& in) : BaseHash(Hash160(in.begin(), in.end())) {}
+ScriptHash::ScriptHash(const CScriptID& in) : BaseHash(static_cast<uint160>(in)) {}
+
+PKHash::PKHash(const CPubKey& pubkey) : BaseHash(pubkey.GetID()) {}
+PKHash::PKHash(const CKeyID& pubkey_id) : BaseHash(pubkey_id) {}
-ScriptHash::ScriptHash(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
+WitnessV0KeyHash::WitnessV0KeyHash(const CPubKey& pubkey) : BaseHash(pubkey.GetID()) {}
+WitnessV0KeyHash::WitnessV0KeyHash(const PKHash& pubkey_hash) : BaseHash(static_cast<uint160>(pubkey_hash)) {}
-PKHash::PKHash(const CPubKey& pubkey) : uint160(pubkey.GetID()) {}
+CKeyID ToKeyID(const PKHash& key_hash)
+{
+ return CKeyID{static_cast<uint160>(key_hash)};
+}
+
+CKeyID ToKeyID(const WitnessV0KeyHash& key_hash)
+{
+ return CKeyID{static_cast<uint160>(key_hash)};
+}
WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
{
CSHA256().Write(in.data(), in.size()).Finalize(begin());
}
-const char* GetTxnOutputType(txnouttype t)
+std::string GetTxnOutputType(TxoutType t)
{
switch (t)
{
- case TX_NONSTANDARD: return "nonstandard";
- case TX_PUBKEY: return "pubkey";
- case TX_PUBKEYHASH: return "pubkeyhash";
- case TX_SCRIPTHASH: return "scripthash";
- case TX_MULTISIG: return "multisig";
- case TX_NULL_DATA: return "nulldata";
- case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
- case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
- case TX_WITNESS_UNKNOWN: return "witness_unknown";
- }
- return nullptr;
+ case TxoutType::NONSTANDARD: return "nonstandard";
+ case TxoutType::PUBKEY: return "pubkey";
+ case TxoutType::PUBKEYHASH: return "pubkeyhash";
+ case TxoutType::SCRIPTHASH: return "scripthash";
+ case TxoutType::MULTISIG: return "multisig";
+ case TxoutType::NULL_DATA: return "nulldata";
+ case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
+ case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
+ case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
@@ -88,7 +106,7 @@ static bool MatchMultisig(const CScript& script, unsigned int& required, std::ve
return (it + 1 == script.end());
}
-txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
+TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
{
vSolutionsRet.clear();
@@ -98,7 +116,7 @@ txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned
{
std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
vSolutionsRet.push_back(hashBytes);
- return TX_SCRIPTHASH;
+ return TxoutType::SCRIPTHASH;
}
int witnessversion;
@@ -106,18 +124,18 @@ txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
vSolutionsRet.push_back(witnessprogram);
- return TX_WITNESS_V0_KEYHASH;
+ return TxoutType::WITNESS_V0_KEYHASH;
}
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
vSolutionsRet.push_back(witnessprogram);
- return TX_WITNESS_V0_SCRIPTHASH;
+ return TxoutType::WITNESS_V0_SCRIPTHASH;
}
if (witnessversion != 0) {
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
vSolutionsRet.push_back(std::move(witnessprogram));
- return TX_WITNESS_UNKNOWN;
+ return TxoutType::WITNESS_UNKNOWN;
}
- return TX_NONSTANDARD;
+ return TxoutType::NONSTANDARD;
}
// Provably prunable, data-carrying output
@@ -126,18 +144,18 @@ txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned
// byte passes the IsPushOnly() test we don't care what exactly is in the
// script.
if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
- return TX_NULL_DATA;
+ return TxoutType::NULL_DATA;
}
std::vector<unsigned char> data;
if (MatchPayToPubkey(scriptPubKey, data)) {
vSolutionsRet.push_back(std::move(data));
- return TX_PUBKEY;
+ return TxoutType::PUBKEY;
}
if (MatchPayToPubkeyHash(scriptPubKey, data)) {
vSolutionsRet.push_back(std::move(data));
- return TX_PUBKEYHASH;
+ return TxoutType::PUBKEYHASH;
}
unsigned int required;
@@ -146,19 +164,19 @@ txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned
vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..16
vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..16
- return TX_MULTISIG;
+ return TxoutType::MULTISIG;
}
vSolutionsRet.clear();
- return TX_NONSTANDARD;
+ return TxoutType::NONSTANDARD;
}
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
{
std::vector<valtype> vSolutions;
- txnouttype whichType = Solver(scriptPubKey, vSolutions);
+ TxoutType whichType = Solver(scriptPubKey, vSolutions);
- if (whichType == TX_PUBKEY) {
+ if (whichType == TxoutType::PUBKEY) {
CPubKey pubKey(vSolutions[0]);
if (!pubKey.IsValid())
return false;
@@ -166,26 +184,26 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
addressRet = PKHash(pubKey);
return true;
}
- else if (whichType == TX_PUBKEYHASH)
+ else if (whichType == TxoutType::PUBKEYHASH)
{
addressRet = PKHash(uint160(vSolutions[0]));
return true;
}
- else if (whichType == TX_SCRIPTHASH)
+ else if (whichType == TxoutType::SCRIPTHASH)
{
addressRet = ScriptHash(uint160(vSolutions[0]));
return true;
- } else if (whichType == TX_WITNESS_V0_KEYHASH) {
+ } else if (whichType == TxoutType::WITNESS_V0_KEYHASH) {
WitnessV0KeyHash hash;
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
addressRet = hash;
return true;
- } else if (whichType == TX_WITNESS_V0_SCRIPTHASH) {
+ } else if (whichType == TxoutType::WITNESS_V0_SCRIPTHASH) {
WitnessV0ScriptHash hash;
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
addressRet = hash;
return true;
- } else if (whichType == TX_WITNESS_UNKNOWN) {
+ } else if (whichType == TxoutType::WITNESS_UNKNOWN) {
WitnessUnknown unk;
unk.version = vSolutions[0][0];
std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
@@ -197,19 +215,19 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
return false;
}
-bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet)
+bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet)
{
addressRet.clear();
std::vector<valtype> vSolutions;
typeRet = Solver(scriptPubKey, vSolutions);
- if (typeRet == TX_NONSTANDARD) {
+ if (typeRet == TxoutType::NONSTANDARD) {
return false;
- } else if (typeRet == TX_NULL_DATA) {
+ } else if (typeRet == TxoutType::NULL_DATA) {
// This is data, not addresses
return false;
}
- if (typeRet == TX_MULTISIG)
+ if (typeRet == TxoutType::MULTISIG)
{
nRequiredRet = vSolutions.front()[0];
for (unsigned int i = 1; i < vSolutions.size()-1; i++)
@@ -239,59 +257,44 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::
namespace
{
-class CScriptVisitor : public boost::static_visitor<bool>
+class CScriptVisitor : public boost::static_visitor<CScript>
{
-private:
- CScript *script;
public:
- explicit CScriptVisitor(CScript *scriptin) { script = scriptin; }
-
- bool operator()(const CNoDestination &dest) const {
- script->clear();
- return false;
+ CScript operator()(const CNoDestination& dest) const
+ {
+ return CScript();
}
- bool operator()(const PKHash &keyID) const {
- script->clear();
- *script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
- return true;
+ CScript operator()(const PKHash& keyID) const
+ {
+ return CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
}
- bool operator()(const ScriptHash &scriptID) const {
- script->clear();
- *script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
- return true;
+ CScript operator()(const ScriptHash& scriptID) const
+ {
+ return CScript() << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
}
- bool operator()(const WitnessV0KeyHash& id) const
+ CScript operator()(const WitnessV0KeyHash& id) const
{
- script->clear();
- *script << OP_0 << ToByteVector(id);
- return true;
+ return CScript() << OP_0 << ToByteVector(id);
}
- bool operator()(const WitnessV0ScriptHash& id) const
+ CScript operator()(const WitnessV0ScriptHash& id) const
{
- script->clear();
- *script << OP_0 << ToByteVector(id);
- return true;
+ return CScript() << OP_0 << ToByteVector(id);
}
- bool operator()(const WitnessUnknown& id) const
+ CScript operator()(const WitnessUnknown& id) const
{
- script->clear();
- *script << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length);
- return true;
+ return CScript() << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length);
}
};
} // namespace
CScript GetScriptForDestination(const CTxDestination& dest)
{
- CScript script;
-
- boost::apply_visitor(CScriptVisitor(&script), dest);
- return script;
+ return boost::apply_visitor(CScriptVisitor(), dest);
}
CScript GetScriptForRawPubKey(const CPubKey& pubKey)
@@ -313,11 +316,11 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
CScript GetScriptForWitness(const CScript& redeemscript)
{
std::vector<std::vector<unsigned char> > vSolutions;
- txnouttype typ = Solver(redeemscript, vSolutions);
- if (typ == TX_PUBKEY) {
+ TxoutType typ = Solver(redeemscript, vSolutions);
+ if (typ == TxoutType::PUBKEY) {
return GetScriptForDestination(WitnessV0KeyHash(Hash160(vSolutions[0].begin(), vSolutions[0].end())));
- } else if (typ == TX_PUBKEYHASH) {
- return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0]));
+ } else if (typ == TxoutType::PUBKEYHASH) {
+ return GetScriptForDestination(WitnessV0KeyHash(uint160{vSolutions[0]}));
}
return GetScriptForDestination(WitnessV0ScriptHash(redeemscript));
}
diff --git a/src/script/standard.h b/src/script/standard.h
index 49a45f3eba..fd29353886 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -11,19 +11,84 @@
#include <boost/variant.hpp>
+#include <string>
+
static const bool DEFAULT_ACCEPT_DATACARRIER = true;
class CKeyID;
class CScript;
+struct ScriptHash;
+
+template<typename HashType>
+class BaseHash
+{
+protected:
+ HashType m_hash;
+
+public:
+ BaseHash() : m_hash() {}
+ BaseHash(const HashType& in) : m_hash(in) {}
+
+ unsigned char* begin()
+ {
+ return m_hash.begin();
+ }
+
+ const unsigned char* begin() const
+ {
+ return m_hash.begin();
+ }
+
+ unsigned char* end()
+ {
+ return m_hash.end();
+ }
+
+ const unsigned char* end() const
+ {
+ return m_hash.end();
+ }
+
+ operator std::vector<unsigned char>() const
+ {
+ return std::vector<unsigned char>{m_hash.begin(), m_hash.end()};
+ }
+
+ std::string ToString() const
+ {
+ return m_hash.ToString();
+ }
+
+ bool operator==(const BaseHash<HashType>& other) const noexcept
+ {
+ return m_hash == other.m_hash;
+ }
+
+ bool operator!=(const BaseHash<HashType>& other) const noexcept
+ {
+ return !(m_hash == other.m_hash);
+ }
+
+ bool operator<(const BaseHash<HashType>& other) const noexcept
+ {
+ return m_hash < other.m_hash;
+ }
+
+ size_t size() const
+ {
+ return m_hash.size();
+ }
+};
/** A reference to a CScript: the Hash160 of its serialization (see script.h) */
-class CScriptID : public uint160
+class CScriptID : public BaseHash<uint160>
{
public:
- CScriptID() : uint160() {}
+ CScriptID() : BaseHash() {}
explicit CScriptID(const CScript& in);
- CScriptID(const uint160& in) : uint160(in) {}
+ explicit CScriptID(const uint160& in) : BaseHash(in) {}
+ explicit CScriptID(const ScriptHash& in);
};
/**
@@ -34,36 +99,34 @@ static const unsigned int MAX_OP_RETURN_RELAY = 83;
/**
* A data carrying output is an unspendable output containing data. The script
- * type is designated as TX_NULL_DATA.
+ * type is designated as TxoutType::NULL_DATA.
*/
extern bool fAcceptDatacarrier;
-/** Maximum size of TX_NULL_DATA scripts that this node considers standard. */
+/** Maximum size of TxoutType::NULL_DATA scripts that this node considers standard. */
extern unsigned nMaxDatacarrierBytes;
/**
* Mandatory script verification flags that all new blocks must comply with for
* them to be valid. (but old blocks may not comply with) Currently just P2SH,
- * but in the future other flags may be added, such as a soft-fork to enforce
- * strict DER encoding.
+ * but in the future other flags may be added.
*
* Failing one of these tests may trigger a DoS ban - see CheckInputScripts() for
* details.
*/
static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH;
-enum txnouttype
-{
- TX_NONSTANDARD,
+enum class TxoutType {
+ NONSTANDARD,
// 'standard' transaction types:
- TX_PUBKEY,
- TX_PUBKEYHASH,
- TX_SCRIPTHASH,
- TX_MULTISIG,
- TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data
- TX_WITNESS_V0_SCRIPTHASH,
- TX_WITNESS_V0_KEYHASH,
- TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
+ PUBKEY,
+ PUBKEYHASH,
+ SCRIPTHASH,
+ MULTISIG,
+ NULL_DATA, //!< unspendable OP_RETURN script that carries data
+ WITNESS_V0_SCRIPTHASH,
+ WITNESS_V0_KEYHASH,
+ WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
};
class CNoDestination {
@@ -72,41 +135,44 @@ public:
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
};
-struct PKHash : public uint160
+struct PKHash : public BaseHash<uint160>
{
- PKHash() : uint160() {}
- explicit PKHash(const uint160& hash) : uint160(hash) {}
+ PKHash() : BaseHash() {}
+ explicit PKHash(const uint160& hash) : BaseHash(hash) {}
explicit PKHash(const CPubKey& pubkey);
- using uint160::uint160;
+ explicit PKHash(const CKeyID& pubkey_id);
};
+CKeyID ToKeyID(const PKHash& key_hash);
struct WitnessV0KeyHash;
-struct ScriptHash : public uint160
+struct ScriptHash : public BaseHash<uint160>
{
- ScriptHash() : uint160() {}
+ ScriptHash() : BaseHash() {}
// These don't do what you'd expect.
// Use ScriptHash(GetScriptForDestination(...)) instead.
explicit ScriptHash(const WitnessV0KeyHash& hash) = delete;
explicit ScriptHash(const PKHash& hash) = delete;
- explicit ScriptHash(const uint160& hash) : uint160(hash) {}
+
+ explicit ScriptHash(const uint160& hash) : BaseHash(hash) {}
explicit ScriptHash(const CScript& script);
- using uint160::uint160;
+ explicit ScriptHash(const CScriptID& script);
};
-struct WitnessV0ScriptHash : public uint256
+struct WitnessV0ScriptHash : public BaseHash<uint256>
{
- WitnessV0ScriptHash() : uint256() {}
- explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {}
+ WitnessV0ScriptHash() : BaseHash() {}
+ explicit WitnessV0ScriptHash(const uint256& hash) : BaseHash(hash) {}
explicit WitnessV0ScriptHash(const CScript& script);
- using uint256::uint256;
};
-struct WitnessV0KeyHash : public uint160
+struct WitnessV0KeyHash : public BaseHash<uint160>
{
- WitnessV0KeyHash() : uint160() {}
- explicit WitnessV0KeyHash(const uint160& hash) : uint160(hash) {}
- using uint160::uint160;
+ WitnessV0KeyHash() : BaseHash() {}
+ explicit WitnessV0KeyHash(const uint160& hash) : BaseHash(hash) {}
+ explicit WitnessV0KeyHash(const CPubKey& pubkey);
+ explicit WitnessV0KeyHash(const PKHash& pubkey_hash);
};
+CKeyID ToKeyID(const WitnessV0KeyHash& key_hash);
//! CTxDestination subtype to encode any future Witness version
struct WitnessUnknown
@@ -133,11 +199,11 @@ struct WitnessUnknown
/**
* A txout script template with a specific destination. It is either:
* * CNoDestination: no destination set
- * * 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???)
+ * * PKHash: TxoutType::PUBKEYHASH destination (P2PKH)
+ * * ScriptHash: TxoutType::SCRIPTHASH destination (P2SH)
+ * * WitnessV0ScriptHash: TxoutType::WITNESS_V0_SCRIPTHASH destination (P2WSH)
+ * * WitnessV0KeyHash: TxoutType::WITNESS_V0_KEYHASH destination (P2WPKH)
+ * * WitnessUnknown: TxoutType::WITNESS_UNKNOWN destination (P2W???)
* A CTxDestination is the internal data type encoded in a bitcoin address
*/
typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
@@ -145,8 +211,8 @@ typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash,
/** Check whether a CTxDestination is a CNoDestination. */
bool IsValidDestination(const CTxDestination& dest);
-/** Get the name of a txnouttype as a C string, or nullptr if unknown. */
-const char* GetTxnOutputType(txnouttype t);
+/** Get the name of a TxoutType as a string */
+std::string GetTxnOutputType(TxoutType t);
/**
* Parse a scriptPubKey and identify script type for standard scripts. If
@@ -156,9 +222,9 @@ const char* GetTxnOutputType(txnouttype t);
*
* @param[in] scriptPubKey Script to parse
* @param[out] vSolutionsRet Vector of parsed pubkeys and hashes
- * @return The script type. TX_NONSTANDARD represents a failed solve.
+ * @return The script type. TxoutType::NONSTANDARD represents a failed solve.
*/
-txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet);
+TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet);
/**
* Parse a standard scriptPubKey for the destination address. Assigns result to
@@ -179,7 +245,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
* encodable as an address) with key identifiers (of keys involved in a
* CScript), and its use should be phased out.
*/
-bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
+bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
/**
* Generate a Bitcoin scriptPubKey for the given CTxDestination. Returns a P2PKH
diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore
index 55d325aeef..cb4331aa90 100644
--- a/src/secp256k1/.gitignore
+++ b/src/secp256k1/.gitignore
@@ -9,6 +9,7 @@ bench_internal
tests
exhaustive_tests
gen_context
+valgrind_ctime_test
*.exe
*.so
*.a
diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml
index 74f658f4d1..a6ad6fb27e 100644
--- a/src/secp256k1/.travis.yml
+++ b/src/secp256k1/.travis.yml
@@ -1,18 +1,23 @@
language: c
-os: linux
+os:
+ - linux
+ - osx
+
+dist: bionic
+# Valgrind currently supports upto macOS 10.13, the latest xcode of that version is 10.1
+osx_image: xcode10.1
addons:
apt:
- packages: libgmp-dev
+ packages:
+ - libgmp-dev
+ - valgrind
+ - libtool-bin
compiler:
- clang
- gcc
-cache:
- directories:
- - 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 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
+ - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2
matrix:
- SCALAR=32bit RECOVERY=yes
- SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes
@@ -26,43 +31,78 @@ env:
- BIGNUM=no
- BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes
- BIGNUM=no STATICPRECOMPUTATION=no
- - BUILD=distcheck
- - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
- - EXTRAFLAGS=CFLAGS=-O0
- - BUILD=check-java JNI=yes ECDH=yes EXPERIMENTAL=yes
+ - BUILD=distcheck CTIMETEST= BENCH=
+ - CPPFLAGS=-DDETERMINISTIC
+ - CFLAGS=-O0 CTIMETEST=
+ - ECMULTGENPRECISION=2
+ - ECMULTGENPRECISION=8
+ - VALGRIND=yes ENDOMORPHISM=yes BIGNUM=no ASM=x86_64 EXPERIMENTAL=yes ECDH=yes RECOVERY=yes EXTRAFLAGS="--disable-openssl-tests" CPPFLAGS=-DVALGRIND BUILD=
+ - VALGRIND=yes BIGNUM=no ASM=x86_64 EXPERIMENTAL=yes ECDH=yes RECOVERY=yes EXTRAFLAGS="--disable-openssl-tests" CPPFLAGS=-DVALGRIND BUILD=
matrix:
fast_finish: true
include:
- compiler: clang
+ os: linux
env: HOST=i686-linux-gnu ENDOMORPHISM=yes
addons:
apt:
packages:
- gcc-multilib
- libgmp-dev:i386
+ - valgrind
+ - libtool-bin
+ - libc6-dbg:i386
- compiler: clang
env: HOST=i686-linux-gnu
+ os: linux
addons:
apt:
packages:
- gcc-multilib
+ - valgrind
+ - libtool-bin
+ - libc6-dbg:i386
- compiler: gcc
env: HOST=i686-linux-gnu ENDOMORPHISM=yes
+ os: linux
addons:
apt:
packages:
- gcc-multilib
+ - valgrind
+ - libtool-bin
+ - libc6-dbg:i386
- compiler: gcc
+ os: linux
env: HOST=i686-linux-gnu
addons:
apt:
packages:
- gcc-multilib
- libgmp-dev:i386
-before_install: mkdir -p `dirname $GUAVA_JAR`
-install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi
+ - valgrind
+ - libtool-bin
+ - libc6-dbg:i386
+
+# We use this to install macOS dependencies instead of the built in `homebrew` plugin,
+# because in xcode earlier than 11 they have a bug requiring updating the system which overall takes ~8 minutes.
+# https://travis-ci.community/t/macos-build-fails-because-of-homebrew-bundle-unknown-command/7296
+before_install:
+ - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install gmp valgrind gcc@9; fi
+
before_script: ./autogen.sh
+
+# travis auto terminates jobs that go for 10 minutes without printing to stdout, but travis_wait doesn't work well with forking programs like valgrind (https://docs.travis-ci.com/user/common-build-problems/#build-times-out-because-no-output-was-received https://github.com/bitcoin-core/secp256k1/pull/750#issuecomment-623476860)
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 --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
+ - function keep_alive() { while true; do echo -en "\a"; sleep 60; done }
+ - keep_alive &
+ - ./contrib/travis.sh
+ - kill %keep_alive
+
+after_script:
+ - cat ./tests.log
+ - cat ./exhaustive_tests.log
+ - cat ./valgrind_ctime_test.log
+ - cat ./bench.log
+ - $CC --version
+ - valgrind --version
diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am
index 9e5b7dcce0..d8c1c79e8c 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -1,13 +1,8 @@
ACLOCAL_AMFLAGS = -I build-aux/m4
lib_LTLIBRARIES = libsecp256k1.la
-if USE_JNI
-JNI_LIB = libsecp256k1_jni.la
-noinst_LTLIBRARIES = $(JNI_LIB)
-else
-JNI_LIB =
-endif
include_HEADERS = include/secp256k1.h
+include_HEADERS += include/secp256k1_preallocated.h
noinst_HEADERS =
noinst_HEADERS += src/scalar.h
noinst_HEADERS += src/scalar_4x64.h
@@ -39,8 +34,6 @@ noinst_HEADERS += src/field_5x52.h
noinst_HEADERS += src/field_5x52_impl.h
noinst_HEADERS += src/field_5x52_int128_impl.h
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
@@ -74,16 +67,19 @@ endif
libsecp256k1_la_SOURCES = src/secp256k1.c
libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
-libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB)
+libsecp256k1_la_LIBADD = $(SECP_LIBS) $(COMMON_LIB)
-libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c
-libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES)
+if VALGRIND_ENABLED
+libsecp256k1_la_CPPFLAGS += -DVALGRIND
+endif
noinst_PROGRAMS =
if USE_BENCHMARK
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)
+# SECP_TEST_INCLUDES are only used here for CRYPTO_CPPFLAGS
+bench_verify_CPPFLAGS = -DSECP256K1_BUILD $(SECP_TEST_INCLUDES)
bench_sign_SOURCES = src/bench_sign.c
bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_internal_SOURCES = src/bench_internal.c
@@ -99,6 +95,12 @@ if USE_TESTS
noinst_PROGRAMS += tests
tests_SOURCES = src/tests.c
tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
+if VALGRIND_ENABLED
+tests_CPPFLAGS += -DVALGRIND
+noinst_PROGRAMS += valgrind_ctime_test
+valgrind_ctime_test_SOURCES = src/valgrind_ctime_test.c
+valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
+endif
if !ENABLE_COVERAGE
tests_CPPFLAGS += -DVERIFY
endif
@@ -119,42 +121,12 @@ exhaustive_tests_LDFLAGS = -static
TESTS += exhaustive_tests
endif
-JAVAROOT=src/java
-JAVAORG=org/bitcoin
-JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar
-CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA)
-JAVA_FILES= \
- $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \
- $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \
- $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \
- $(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java
-
-if USE_JNI
-
-$(JAVA_GUAVA):
- @echo Guava is missing. Fetch it via: \
- wget https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar -O $(@)
- @false
-
-.stamp-java: $(JAVA_FILES)
- @echo Compiling $^
- $(AM_V_at)$(CLASSPATH_ENV) javac $^
- @touch $@
-
-if USE_TESTS
-
-check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java
- $(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test
-
-endif
-endif
-
if USE_ECMULT_STATIC_PRECOMPUTATION
-CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)
+CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) -I$(builddir)/src
gen_context_OBJECTS = gen_context.o
gen_context_BIN = gen_context$(BUILD_EXEEXT)
-gen_%.o: src/gen_%.c
+gen_%.o: src/gen_%.c src/libsecp256k1-config.h
$(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@
$(gen_context_BIN): $(gen_context_OBJECTS)
@@ -168,10 +140,10 @@ $(bench_ecmult_OBJECTS): src/ecmult_static_context.h
src/ecmult_static_context.h: $(gen_context_BIN)
./$(gen_context_BIN)
-CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java
+CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h
endif
-EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES)
+EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h
if ENABLE_MODULE_ECDH
include src/modules/ecdh/Makefile.am.include
diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md
index 8cd344ea81..434178b372 100644
--- a/src/secp256k1/README.md
+++ b/src/secp256k1/README.md
@@ -3,17 +3,22 @@ libsecp256k1
[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1)
-Optimized C library for EC operations on curve secp256k1.
+Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1.
-This library is a work in progress and is being used to research best practices. Use at your own risk.
+This library is intended to be the highest quality publicly available library for cryptography on the secp256k1 curve. However, the primary focus of its development has been for usage in the Bitcoin system and usage unlike Bitcoin's may be less well tested, verified, or suffer from a less well thought out interface. Correct usage requires some care and consideration that the library is fit for your application's purpose.
Features:
* secp256k1 ECDSA signing/verification and key generation.
-* Adding/multiplying private/public keys.
-* Serialization/parsing of private keys, public keys, signatures.
-* Constant time, constant memory access signing and pubkey generation.
-* Derandomized DSA (via RFC6979 or with a caller provided function.)
+* Additive and multiplicative tweaking of secret/public keys.
+* Serialization/parsing of secret keys, public keys, signatures.
+* Constant time, constant memory access signing and public key generation.
+* Derandomized ECDSA (via RFC6979 or with a caller provided function.)
* Very efficient implementation.
+* Suitable for embedded systems.
+* Optional module for public key recovery.
+* Optional module for ECDH key exchange (experimental).
+
+Experimental features have not received enough scrutiny to satisfy the standard of quality of this library but are made available for testing and review by the community. The APIs of these features should not be considered stable.
Implementation details
----------------------
@@ -23,11 +28,12 @@ Implementation details
* Extensive testing infrastructure.
* Structured to facilitate review and analysis.
* Intended to be portable to any system with a C89 compiler and uint64_t support.
+ * No use of floating types.
* Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.")
* Field operations
* Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1).
* Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys).
- * Using 10 26-bit limbs.
+ * Using 10 26-bit limbs (including hand-optimized assembly for 32-bit ARM, by Wladimir J. van der Laan).
* Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman).
* Scalar operations
* Optimized implementation without data-dependent branches of arithmetic modulo the curve's order.
@@ -45,9 +51,11 @@ Implementation details
* Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones.
* Point multiplication for signing
* Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions.
- * Access the table with branch-free conditional moves so memory access is uniform.
- * No data-dependent branches
- * The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally.
+ * Intended to be completely free of timing sidechannels for secret-key operations (on reasonable hardware/toolchains)
+ * Access the table with branch-free conditional moves so memory access is uniform.
+ * No data-dependent branches
+ * Optional runtime blinding which attempts to frustrate differential power analysis.
+ * The precomputed tables add and eventually subtract points for which no known scalar (secret key) is known, preventing even an attacker with control over the secret key used to control the data internally.
Build steps
-----------
@@ -57,5 +65,40 @@ libsecp256k1 is built using autotools:
$ ./autogen.sh
$ ./configure
$ make
- $ ./tests
+ $ make check
$ sudo make install # optional
+
+Exhaustive tests
+-----------
+
+ $ ./exhaustive_tests
+
+With valgrind, you might need to increase the max stack size:
+
+ $ valgrind --max-stackframe=2500000 ./exhaustive_tests
+
+Test coverage
+-----------
+
+This library aims to have full coverage of the reachable lines and branches.
+
+To create a test coverage report, configure with `--enable-coverage` (use of GCC is necessary):
+
+ $ ./configure --enable-coverage
+
+Run the tests:
+
+ $ make check
+
+To create a report, `gcovr` is recommended, as it includes branch coverage reporting:
+
+ $ gcovr --exclude 'src/bench*' --print-summary
+
+To create a HTML report with coloured and annotated source code:
+
+ $ gcovr --exclude 'src/bench*' --html --html-details -o coverage.html
+
+Reporting a vulnerability
+------------
+
+See [SECURITY.md](SECURITY.md)
diff --git a/src/secp256k1/SECURITY.md b/src/secp256k1/SECURITY.md
new file mode 100644
index 0000000000..0e4d588030
--- /dev/null
+++ b/src/secp256k1/SECURITY.md
@@ -0,0 +1,15 @@
+# Security Policy
+
+## Reporting a Vulnerability
+
+To report security issues send an email to secp256k1-security@bitcoincore.org (not for support).
+
+The following keys may be used to communicate sensitive information to developers:
+
+| Name | Fingerprint |
+|------|-------------|
+| Pieter Wuille | 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320 |
+| Andrew Poelstra | 699A 63EF C17A D3A9 A34C FFC0 7AD0 A91C 40BD 0091 |
+| Tim Ruffing | 09E0 3F87 1092 E40E 106E 902B 33BC 86AB 80FF 5516 |
+
+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/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
deleted file mode 100644
index cdc78d87d4..0000000000
--- a/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
+++ /dev/null
@@ -1,145 +0,0 @@
-# ===========================================================================
-# https://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_JNI_INCLUDE_DIR
-#
-# DESCRIPTION
-#
-# AX_JNI_INCLUDE_DIR finds include directories needed for compiling
-# programs using the JNI interface.
-#
-# JNI include directories are usually in the Java distribution. This is
-# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in
-# that order. When this macro completes, a list of directories is left in
-# the variable JNI_INCLUDE_DIRS.
-#
-# Example usage follows:
-#
-# AX_JNI_INCLUDE_DIR
-#
-# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS
-# do
-# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR"
-# done
-#
-# If you want to force a specific compiler:
-#
-# - at the configure.in level, set JAVAC=yourcompiler before calling
-# AX_JNI_INCLUDE_DIR
-#
-# - at the configure level, setenv JAVAC
-#
-# Note: This macro can work with the autoconf M4 macros for Java programs.
-# This particular macro is not part of the original set of macros.
-#
-# LICENSE
-#
-# Copyright (c) 2008 Don Anderson <dda@sleepycat.com>
-#
-# Copying and distribution of this file, with or without modification, are
-# permitted in any medium without royalty provided the copyright notice
-# and this notice are preserved. This file is offered as-is, without any
-# warranty.
-
-#serial 14
-
-AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR])
-AC_DEFUN([AX_JNI_INCLUDE_DIR],[
-
-JNI_INCLUDE_DIRS=""
-
-if test "x$JAVA_HOME" != x; then
- _JTOPDIR="$JAVA_HOME"
-else
- if test "x$JAVAC" = x; then
- JAVAC=javac
- fi
- AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no])
- if test "x$_ACJNI_JAVAC" = xno; then
- AC_MSG_WARN([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME])
- fi
- _ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC")
- _JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'`
-fi
-
-case "$host_os" in
- 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])
-
-# 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"
- JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
- else
- ac_cv_jni_header_path=none
- fi
- fi
-])
-
-# get the likely subdirectories for system specific java includes
-case "$host_os" in
-bsdi*) _JNI_INC_SUBDIRS="bsdos";;
-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";;
-mingw*) _JNI_INC_SUBDIRS="win32";;
-cygwin*) _JNI_INC_SUBDIRS="win32";;
-*) _JNI_INC_SUBDIRS="genunix";;
-esac
-
-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
- done
-fi
-])
-
-# _ACJNI_FOLLOW_SYMLINKS <path>
-# Follows symbolic links on <path>,
-# finally setting variable _ACJNI_FOLLOWED
-# ----------------------------------------
-AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[
-# find the include directory relative to the javac executable
-_cur="$1"
-while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do
- AC_MSG_CHECKING([symlink for $_cur])
- _slink=`ls -ld "$_cur" | sed 's/.* -> //'`
- case "$_slink" in
- /*) _cur="$_slink";;
- # 'X' avoids triggering unwanted echo options.
- *) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";;
- esac
- AC_MSG_RESULT([$_cur])
-done
-_ACJNI_FOLLOWED="$_cur"
-])# _ACJNI
diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
index 3b3975cbdd..1b2b71e6ab 100644
--- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4
+++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
@@ -38,6 +38,8 @@ AC_DEFUN([SECP_OPENSSL_CHECK],[
fi
if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
AC_MSG_CHECKING(for EC functions in libcrypto)
+ CPPFLAGS_TEMP="$CPPFLAGS"
+ CPPFLAGS="$CRYPTO_CPPFLAGS $CPPFLAGS"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
@@ -51,6 +53,7 @@ if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
ECDSA_SIG_free(sig_openssl);
]])],[has_openssl_ec=yes],[has_openssl_ec=no])
AC_MSG_RESULT([$has_openssl_ec])
+ CPPFLAGS="$CPPFLAGS_TEMP"
fi
])
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index 3b7a328c8a..6021b760b5 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -7,6 +7,11 @@ AH_TOP([#ifndef LIBSECP256K1_CONFIG_H])
AH_TOP([#define LIBSECP256K1_CONFIG_H])
AH_BOTTOM([#endif /*LIBSECP256K1_CONFIG_H*/])
AM_INIT_AUTOMAKE([foreign subdir-objects])
+
+# Set -g if CFLAGS are not already set, which matches the default autoconf
+# behavior (see PROG_CC in the Autoconf manual) with the exception that we don't
+# set -O2 here because we set it in any case (see further down).
+: ${CFLAGS="-g"}
LT_INIT
dnl make the compilation flags quiet unless V=1 is used
@@ -19,10 +24,6 @@ AC_PATH_TOOL(RANLIB, ranlib)
AC_PATH_TOOL(STRIP, strip)
AX_PROG_CC_FOR_BUILD
-if test "x$CFLAGS" = "x"; then
- CFLAGS="-g"
-fi
-
AM_PROG_CC_C_O
AC_PROG_CC_C89
@@ -45,6 +46,7 @@ case $host_os in
if test x$openssl_prefix != x; then
PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH"
export PKG_CONFIG_PATH
+ CRYPTO_CPPFLAGS="-I$openssl_prefix/include"
fi
if test x$gmp_prefix != x; then
GMP_CPPFLAGS="-I$gmp_prefix/include"
@@ -63,11 +65,11 @@ case $host_os in
;;
esac
-CFLAGS="$CFLAGS -W"
+CFLAGS="-W $CFLAGS"
warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wno-unused-function -Wno-long-long -Wno-overlength-strings"
saved_CFLAGS="$CFLAGS"
-CFLAGS="$CFLAGS $warn_CFLAGS"
+CFLAGS="$warn_CFLAGS $CFLAGS"
AC_MSG_CHECKING([if ${CC} supports ${warn_CFLAGS}])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
[ AC_MSG_RESULT([yes]) ],
@@ -76,7 +78,7 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
])
saved_CFLAGS="$CFLAGS"
-CFLAGS="$CFLAGS -fvisibility=hidden"
+CFLAGS="-fvisibility=hidden $CFLAGS"
AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
[ AC_MSG_RESULT([yes]) ],
@@ -85,42 +87,42 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
])
AC_ARG_ENABLE(benchmark,
- AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is yes)]),
+ AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]),
[use_benchmark=$enableval],
[use_benchmark=yes])
AC_ARG_ENABLE(coverage,
- AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]),
+ AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis [default=no]]),
[enable_coverage=$enableval],
[enable_coverage=no])
AC_ARG_ENABLE(tests,
- AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]),
+ AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]),
[use_tests=$enableval],
[use_tests=yes])
AC_ARG_ENABLE(openssl_tests,
- AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]),
+ AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests [default=auto]]),
[enable_openssl_tests=$enableval],
[enable_openssl_tests=auto])
AC_ARG_ENABLE(experimental,
- AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]),
+ AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]),
[use_experimental=$enableval],
[use_experimental=no])
AC_ARG_ENABLE(exhaustive_tests,
- AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]),
+ AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]),
[use_exhaustive_tests=$enableval],
[use_exhaustive_tests=yes])
AC_ARG_ENABLE(endomorphism,
- AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]),
+ AS_HELP_STRING([--enable-endomorphism],[enable endomorphism [default=no]]),
[use_endomorphism=$enableval],
[use_endomorphism=no])
AC_ARG_ENABLE(ecmult_static_precomputation,
- AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]),
+ AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing [default=auto]]),
[use_ecmult_static_precomputation=$enableval],
[use_ecmult_static_precomputation=auto])
@@ -130,35 +132,55 @@ AC_ARG_ENABLE(module_ecdh,
[enable_module_ecdh=no])
AC_ARG_ENABLE(module_recovery,
- AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]),
+ AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]),
[enable_module_recovery=$enableval],
[enable_module_recovery=no])
-AC_ARG_ENABLE(jni,
- AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is no)]),
- [use_jni=$enableval],
- [use_jni=no])
+AC_ARG_ENABLE(external_default_callbacks,
+ AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]),
+ [use_external_default_callbacks=$enableval],
+ [use_external_default_callbacks=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])
+[finite field implementation to use [default=auto]])],[req_field=$withval], [req_field=auto])
AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
-[Specify Bignum Implementation. Default is auto])],[req_bignum=$withval], [req_bignum=auto])
+[bignum implementation to use [default=auto]])],[req_bignum=$withval], [req_bignum=auto])
AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto],
-[Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=auto])
-
-AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto]
-[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto])
+[scalar implementation to use [default=auto]])],[req_scalar=$withval], [req_scalar=auto])
+
+AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto],
+[assembly optimizations to use (experimental: arm) [default=auto]])],[req_asm=$withval], [req_asm=auto])
+
+AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE|auto],
+[window size for ecmult precomputation for verification, specified as integer in range [2..24].]
+[Larger values result in possibly better performance at the cost of an exponentially larger precomputed table.]
+[The table will store 2^(SIZE-2) * 64 bytes of data but can be larger in memory due to platform-specific padding and alignment.]
+[If the endomorphism optimization is enabled, two tables of this size are used instead of only one.]
+["auto" is a reasonable setting for desktop machines (currently 15). [default=auto]]
+)],
+[req_ecmult_window=$withval], [req_ecmult_window=auto])
+
+AC_ARG_WITH([ecmult-gen-precision], [AS_HELP_STRING([--with-ecmult-gen-precision=2|4|8|auto],
+[Precision bits to tune the precomputed table size for signing.]
+[The size of the table is 32kB for 2 bits, 64kB for 4 bits, 512kB for 8 bits of precision.]
+[A larger table size usually results in possible faster signing.]
+["auto" is a reasonable setting for desktop machines (currently 4). [default=auto]]
+)],
+[req_ecmult_gen_precision=$withval], [req_ecmult_gen_precision=auto])
AC_CHECK_TYPES([__int128])
+AC_CHECK_HEADER([valgrind/memcheck.h], [enable_valgrind=yes], [enable_valgrind=no], [])
+AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
+
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"
- LDFLAGS="--coverage"
+ CFLAGS="-O0 --coverage $CFLAGS"
+ LDFLAGS="--coverage $LDFLAGS"
else
- CFLAGS="$CFLAGS -O3"
+ CFLAGS="-O2 $CFLAGS"
fi
if test x"$use_ecmult_static_precomputation" != x"no"; then
@@ -176,7 +198,7 @@ if test x"$use_ecmult_static_precomputation" != x"no"; then
warn_CFLAGS_FOR_BUILD="-Wall -Wextra -Wno-unused-function"
saved_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS $warn_CFLAGS_FOR_BUILD"
+ CFLAGS="$warn_CFLAGS_FOR_BUILD $CFLAGS"
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]) ],
@@ -188,7 +210,7 @@ if test x"$use_ecmult_static_precomputation" != x"no"; then
AC_RUN_IFELSE(
[AC_LANG_PROGRAM([], [])],
[working_native_cc=yes],
- [working_native_cc=no],[dnl])
+ [working_native_cc=no],[:])
CFLAGS_FOR_BUILD="$CFLAGS"
@@ -387,12 +409,50 @@ case $set_scalar in
;;
esac
+#set ecmult window size
+if test x"$req_ecmult_window" = x"auto"; then
+ set_ecmult_window=15
+else
+ set_ecmult_window=$req_ecmult_window
+fi
+
+error_window_size=['window size for ecmult precomputation not an integer in range [2..24] or "auto"']
+case $set_ecmult_window in
+''|*[[!0-9]]*)
+ # no valid integer
+ AC_MSG_ERROR($error_window_size)
+ ;;
+*)
+ if test "$set_ecmult_window" -lt 2 -o "$set_ecmult_window" -gt 24 ; then
+ # not in range
+ AC_MSG_ERROR($error_window_size)
+ fi
+ AC_DEFINE_UNQUOTED(ECMULT_WINDOW_SIZE, $set_ecmult_window, [Set window size for ecmult precomputation])
+ ;;
+esac
+
+#set ecmult gen precision
+if test x"$req_ecmult_gen_precision" = x"auto"; then
+ set_ecmult_gen_precision=4
+else
+ set_ecmult_gen_precision=$req_ecmult_gen_precision
+fi
+
+case $set_ecmult_gen_precision in
+2|4|8)
+ AC_DEFINE_UNQUOTED(ECMULT_GEN_PREC_BITS, $set_ecmult_gen_precision, [Set ecmult gen precision bits])
+ ;;
+*)
+ AC_MSG_ERROR(['ecmult gen precision not 2, 4, 8 or "auto"'])
+ ;;
+esac
+
if test x"$use_tests" = x"yes"; then
SECP_OPENSSL_CHECK
if test x"$has_openssl_ec" = x"yes"; then
if test x"$enable_openssl_tests" != x"no"; then
AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available])
- SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS"
+ SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS $CRYPTO_CPPFLAGS"
SECP_TEST_LIBS="$CRYPTO_LIBS"
case $host in
@@ -412,29 +472,6 @@ else
fi
fi
-if test x"$use_jni" != x"no"; then
- AX_JNI_INCLUDE_DIR
- have_jni_dependencies=yes
- if test x"$enable_module_ecdh" = x"no"; then
- have_jni_dependencies=no
- fi
- if test "x$JNI_INCLUDE_DIRS" = "x"; then
- have_jni_dependencies=no
- fi
- if test "x$have_jni_dependencies" = "xno"; then
- if test x"$use_jni" = x"yes"; then
- AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and try again.])
- fi
- AC_MSG_WARN([jni headers/dependencies not found. jni support disabled])
- use_jni=no
- else
- use_jni=yes
- for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do
- JNI_INCLUDES="$JNI_INCLUDES -I$JNI_INCLUDE_DIR"
- done
- fi
-fi
-
if test x"$set_bignum" = x"gmp"; then
SECP_LIBS="$SECP_LIBS $GMP_LIBS"
SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS"
@@ -462,6 +499,10 @@ 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
+if test x"$use_external_default_callbacks" = x"yes"; then
+ AC_DEFINE(USE_EXTERNAL_DEFAULT_CALLBACKS, 1, [Define this symbol if an external implementation of the default callbacks is used])
+fi
+
if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([******])
AC_MSG_NOTICE([WARNING: experimental build])
@@ -479,7 +520,6 @@ fi
AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
-AC_SUBST(JNI_INCLUDES)
AC_SUBST(SECP_INCLUDES)
AC_SUBST(SECP_LIBS)
AC_SUBST(SECP_TEST_LIBS)
@@ -491,7 +531,6 @@ 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_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
@@ -504,21 +543,24 @@ 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 " with endomorphism = $use_endomorphism"
+echo " with ecmult precomp = $set_precomp"
+echo " with external callbacks = $use_external_default_callbacks"
+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 " asm = $set_asm"
+echo " bignum = $set_bignum"
+echo " field = $set_field"
+echo " scalar = $set_scalar"
+echo " ecmult window size = $set_ecmult_window"
+echo " ecmult gen prec. bits = $set_ecmult_gen_precision"
echo
-echo " CC = $CC"
-echo " CFLAGS = $CFLAGS"
-echo " CPPFLAGS = $CPPFLAGS"
-echo " LDFLAGS = $LDFLAGS"
+echo " valgrind = $enable_valgrind"
+echo " CC = $CC"
+echo " CFLAGS = $CFLAGS"
+echo " CPPFLAGS = $CPPFLAGS"
+echo " LDFLAGS = $LDFLAGS"
echo
diff --git a/src/secp256k1/contrib/lax_der_parsing.c b/src/secp256k1/contrib/lax_der_parsing.c
index 5b141a9948..e177a0562d 100644
--- a/src/secp256k1/contrib/lax_der_parsing.c
+++ b/src/secp256k1/contrib/lax_der_parsing.c
@@ -32,7 +32,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
- if (pos + lenbyte > inputlen) {
+ if (lenbyte > inputlen - pos) {
return 0;
}
pos += lenbyte;
@@ -51,7 +51,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
- if (pos + lenbyte > inputlen) {
+ if (lenbyte > inputlen - pos) {
return 0;
}
while (lenbyte > 0 && input[pos] == 0) {
@@ -89,7 +89,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
- if (pos + lenbyte > inputlen) {
+ if (lenbyte > inputlen - pos) {
return 0;
}
while (lenbyte > 0 && input[pos] == 0) {
diff --git a/src/secp256k1/contrib/travis.sh b/src/secp256k1/contrib/travis.sh
new file mode 100755
index 0000000000..3909d16a27
--- /dev/null
+++ b/src/secp256k1/contrib/travis.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+set -e
+set -x
+
+if [ -n "$HOST" ]
+then
+ export USE_HOST="--host=$HOST"
+fi
+if [ "$HOST" = "i686-linux-gnu" ]
+then
+ export CC="$CC -m32"
+fi
+if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$TRAVIS_COMPILER" = "gcc" ]
+then
+ export CC="gcc-9"
+fi
+
+./configure \
+ --enable-experimental="$EXPERIMENTAL" --enable-endomorphism="$ENDOMORPHISM" \
+ --with-field="$FIELD" --with-bignum="$BIGNUM" --with-asm="$ASM" --with-scalar="$SCALAR" \
+ --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \
+ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" "$EXTRAFLAGS" "$USE_HOST"
+
+if [ -n "$BUILD" ]
+then
+ make -j2 "$BUILD"
+fi
+if [ -n "$VALGRIND" ]
+then
+ make -j2
+ # the `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (http://valgrind.org/docs/manual/manual-core.html)
+ valgrind --error-exitcode=42 ./tests 16
+ valgrind --error-exitcode=42 ./exhaustive_tests
+fi
+if [ -n "$BENCH" ]
+then
+ if [ -n "$VALGRIND" ]
+ then
+ # Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
+ EXEC='./libtool --mode=execute valgrind --error-exitcode=42'
+ else
+ EXEC=
+ fi
+ # This limits the iterations in the benchmarks below to ITER(set in .travis.yml) iterations.
+ export SECP256K1_BENCH_ITERS="$ITERS"
+ {
+ $EXEC ./bench_ecmult
+ $EXEC ./bench_internal
+ $EXEC ./bench_sign
+ $EXEC ./bench_verify
+ } >> bench.log 2>&1
+ if [ "$RECOVERY" = "yes" ]
+ then
+ $EXEC ./bench_recover >> bench.log 2>&1
+ fi
+ if [ "$ECDH" = "yes" ]
+ then
+ $EXEC ./bench_ecdh >> bench.log 2>&1
+ fi
+fi
+if [ -n "$CTIMETEST" ]
+then
+ ./libtool --mode=execute valgrind --error-exitcode=42 ./valgrind_ctime_test > valgrind_ctime_test.log 2>&1
+fi
diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h
index 43af09c330..2ba2dca388 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -14,7 +14,7 @@ extern "C" {
* 2. Array lengths always immediately the follow the argument whose length
* they describe, even if this violates rule 1.
* 3. Within the OUT/OUTIN/IN groups, pointers to data that is typically generated
- * later go first. This means: signatures, public nonces, private nonces,
+ * later go first. This means: signatures, public nonces, secret nonces,
* messages, public keys, secret keys, tweaks.
* 4. Arguments that are not data pointers go last, from more complex to less
* complex: function pointers, algorithm names, messages, void pointers,
@@ -33,9 +33,10 @@ extern "C" {
* verification).
*
* A constructed context can safely be used from multiple threads
- * simultaneously, but API call that take a non-const pointer to a context
+ * simultaneously, but API calls that take a non-const pointer to a context
* need exclusive access to it. In particular this is the case for
- * secp256k1_context_destroy and secp256k1_context_randomize.
+ * secp256k1_context_destroy, secp256k1_context_preallocated_destroy,
+ * and secp256k1_context_randomize.
*
* Regarding randomization, either do it once at creation time (in which case
* you do not need any locking for the other calls), or use a read-write lock.
@@ -161,14 +162,17 @@ typedef int (*secp256k1_nonce_function)(
/** The higher bits contain the actual data. Do not use directly. */
#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8)
#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9)
+#define SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY (1 << 10)
#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8)
-/** Flags to pass to secp256k1_context_create. */
+/** Flags to pass to secp256k1_context_create, secp256k1_context_preallocated_size, and
+ * secp256k1_context_preallocated_create. */
#define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
#define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
+#define SECP256K1_CONTEXT_DECLASSIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY)
#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT)
-/** Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export. */
+/** Flag to pass to secp256k1_ec_pubkey_serialize. */
#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION)
@@ -186,7 +190,11 @@ typedef int (*secp256k1_nonce_function)(
*/
SECP256K1_API extern const secp256k1_context *secp256k1_context_no_precomp;
-/** Create a secp256k1 context object.
+/** Create a secp256k1 context object (in dynamically allocated memory).
+ *
+ * This function uses malloc to allocate memory. It is guaranteed that malloc is
+ * called at most once for every call of this function. If you need to avoid dynamic
+ * memory allocation entirely, see the functions in secp256k1_preallocated.h.
*
* Returns: a newly created context object.
* In: flags: which parts of the context to initialize.
@@ -197,7 +205,11 @@ SECP256K1_API secp256k1_context* secp256k1_context_create(
unsigned int flags
) SECP256K1_WARN_UNUSED_RESULT;
-/** Copies a secp256k1 context object.
+/** Copy a secp256k1 context object (into dynamically allocated memory).
+ *
+ * This function uses malloc to allocate memory. It is guaranteed that malloc is
+ * called at most once for every call of this function. If you need to avoid dynamic
+ * memory allocation entirely, see the functions in secp256k1_preallocated.h.
*
* Returns: a newly created context object.
* Args: ctx: an existing context to copy (cannot be NULL)
@@ -206,10 +218,18 @@ SECP256K1_API secp256k1_context* secp256k1_context_clone(
const secp256k1_context* ctx
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
-/** Destroy a secp256k1 context object.
+/** Destroy a secp256k1 context object (created in dynamically allocated memory).
*
* The context pointer may not be used afterwards.
- * Args: ctx: an existing context to destroy (cannot be NULL)
+ *
+ * The context to destroy must have been created using secp256k1_context_create
+ * or secp256k1_context_clone. If the context has instead been created using
+ * secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone, the
+ * behaviour is undefined. In that case, secp256k1_context_preallocated_destroy must
+ * be used instead.
+ *
+ * Args: ctx: an existing context to destroy, constructed using
+ * secp256k1_context_create or secp256k1_context_clone
*/
SECP256K1_API void secp256k1_context_destroy(
secp256k1_context* ctx
@@ -229,11 +249,28 @@ SECP256K1_API void secp256k1_context_destroy(
* to cause a crash, though its return value and output arguments are
* undefined.
*
+ * When this function has not been called (or called with fn==NULL), then the
+ * default handler will be used. The library provides a default handler which
+ * writes the message to stderr and calls abort. This default handler can be
+ * replaced at link time if the preprocessor macro
+ * USE_EXTERNAL_DEFAULT_CALLBACKS is defined, which is the case if the build
+ * has been configured with --enable-external-default-callbacks. Then the
+ * following two symbols must be provided to link against:
+ * - void secp256k1_default_illegal_callback_fn(const char* message, void* data);
+ * - void secp256k1_default_error_callback_fn(const char* message, void* data);
+ * The library can call these default handlers even before a proper callback data
+ * pointer could have been set using secp256k1_context_set_illegal_callback or
+ * secp256k1_context_set_error_callback, e.g., when the creation of a context
+ * fails. In this case, the corresponding default handler will be called with
+ * the data pointer argument set to NULL.
+ *
* Args: ctx: an existing context object (cannot be NULL)
* In: fun: a pointer to a function to call when an illegal argument is
- * passed to the API, taking a message and an opaque pointer
- * (NULL restores a default handler that calls abort).
+ * passed to the API, taking a message and an opaque pointer.
+ * (NULL restores the default handler.)
* data: the opaque pointer to pass to fun above.
+ *
+ * See also secp256k1_context_set_error_callback.
*/
SECP256K1_API void secp256k1_context_set_illegal_callback(
secp256k1_context* ctx,
@@ -253,9 +290,12 @@ SECP256K1_API void secp256k1_context_set_illegal_callback(
*
* Args: ctx: an existing context object (cannot be NULL)
* In: fun: a pointer to a function to call when an internal error occurs,
- * taking a message and an opaque pointer (NULL restores a default
- * handler that calls abort).
+ * taking a message and an opaque pointer (NULL restores the
+ * default handler, see secp256k1_context_set_illegal_callback
+ * for details).
* data: the opaque pointer to pass to fun above.
+ *
+ * See also secp256k1_context_set_illegal_callback.
*/
SECP256K1_API void secp256k1_context_set_error_callback(
secp256k1_context* ctx,
@@ -267,21 +307,24 @@ SECP256K1_API void secp256k1_context_set_error_callback(
*
* Returns: a newly created scratch space.
* Args: ctx: an existing context object (cannot be NULL)
- * In: max_size: maximum amount of memory to allocate
+ * In: size: amount of memory to be available as scratch space. Some extra
+ * (<100 bytes) will be allocated for extra accounting.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create(
const secp256k1_context* ctx,
- size_t max_size
+ size_t size
) SECP256K1_ARG_NONNULL(1);
/** Destroy a secp256k1 scratch space.
*
* The pointer may not be used afterwards.
- * Args: scratch: space to destroy
+ * Args: ctx: a secp256k1 context object.
+ * scratch: space to destroy
*/
SECP256K1_API void secp256k1_scratch_space_destroy(
+ const secp256k1_context* ctx,
secp256k1_scratch_space* scratch
-);
+) SECP256K1_ARG_NONNULL(1);
/** Parse a variable-length public key into the pubkey object.
*
@@ -488,7 +531,7 @@ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_def
/** Create an ECDSA signature.
*
* Returns: 1: signature created
- * 0: the nonce generation function failed, or the private key was invalid.
+ * 0: the nonce generation function failed, or the secret key was invalid.
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
* Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
* In: msg32: the 32-byte message hash being signed (cannot be NULL)
@@ -510,6 +553,11 @@ SECP256K1_API int secp256k1_ecdsa_sign(
/** Verify an ECDSA secret key.
*
+ * A secret key is valid if it is not 0 and less than the secp256k1 curve order
+ * when interpreted as an integer (most significant byte first). The
+ * probability of choosing a 32-byte string uniformly at random which is an
+ * invalid secret key is negligible.
+ *
* Returns: 1: secret key is valid
* 0: secret key is invalid
* Args: ctx: pointer to a context object (cannot be NULL)
@@ -526,7 +574,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify(
* 0: secret was invalid, try again
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
* Out: pubkey: pointer to the created public key (cannot be NULL)
- * In: seckey: pointer to a 32-byte private key (cannot be NULL)
+ * In: seckey: pointer to a 32-byte secret key (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
const secp256k1_context* ctx,
@@ -534,12 +582,24 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
-/** Negates a private key in place.
+/** Negates a secret key in place.
*
- * Returns: 1 always
- * Args: ctx: pointer to a context object
- * In/Out: seckey: pointer to the 32-byte private key to be negated (cannot be NULL)
+ * Returns: 0 if the given secret key is invalid according to
+ * secp256k1_ec_seckey_verify. 1 otherwise
+ * Args: ctx: pointer to a context object
+ * In/Out: seckey: pointer to the 32-byte secret key to be negated. If the
+ * secret key is invalid according to
+ * secp256k1_ec_seckey_verify, this function returns 0 and
+ * seckey will be set to some unspecified value. (cannot be
+ * NULL)
*/
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_negate(
+ const secp256k1_context* ctx,
+ unsigned char *seckey
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
+
+/** Same as secp256k1_ec_seckey_negate, but DEPRECATED. Will be removed in
+ * future versions. */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_negate(
const secp256k1_context* ctx,
unsigned char *seckey
@@ -556,15 +616,29 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate(
secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
-/** Tweak a private key by adding tweak to it.
- * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
- * uniformly random 32-byte arrays, or if the resulting private key
- * would be invalid (only when the tweak is the complement of the
- * private key). 1 otherwise.
- * Args: ctx: pointer to a context object (cannot be NULL).
- * In/Out: seckey: pointer to a 32-byte private key.
- * In: tweak: pointer to a 32-byte tweak.
- */
+/** Tweak a secret key by adding tweak to it.
+ *
+ * Returns: 0 if the arguments are invalid or the resulting secret key would be
+ * invalid (only when the tweak is the negation of the secret key). 1
+ * otherwise.
+ * Args: ctx: pointer to a context object (cannot be NULL).
+ * In/Out: seckey: pointer to a 32-byte secret key. If the secret key is
+ * invalid according to secp256k1_ec_seckey_verify, this
+ * function returns 0. seckey will be set to some unspecified
+ * value if this function returns 0. (cannot be NULL)
+ * In: tweak: pointer to a 32-byte tweak. If the tweak is invalid according to
+ * secp256k1_ec_seckey_verify, this function returns 0. For
+ * uniformly random 32-byte arrays the chance of being invalid
+ * is negligible (around 1 in 2^128) (cannot be NULL).
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add(
+ const secp256k1_context* ctx,
+ unsigned char *seckey,
+ const unsigned char *tweak
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Same as secp256k1_ec_seckey_tweak_add, but DEPRECATED. Will be removed in
+ * future versions. */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
const secp256k1_context* ctx,
unsigned char *seckey,
@@ -572,14 +646,18 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a public key by adding tweak times the generator to it.
- * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
- * uniformly random 32-byte arrays, or if the resulting public key
- * would be invalid (only when the tweak is the complement of the
- * corresponding private key). 1 otherwise.
- * Args: ctx: pointer to a context object initialized for validation
+ *
+ * Returns: 0 if the arguments are invalid or the resulting public key would be
+ * invalid (only when the tweak is the negation of the corresponding
+ * secret key). 1 otherwise.
+ * Args: ctx: pointer to a context object initialized for validation
* (cannot be NULL).
- * In/Out: pubkey: pointer to a public key object.
- * In: tweak: pointer to a 32-byte tweak.
+ * In/Out: pubkey: pointer to a public key object. pubkey will be set to an
+ * invalid value if this function returns 0 (cannot be NULL).
+ * In: tweak: pointer to a 32-byte tweak. If the tweak is invalid according to
+ * secp256k1_ec_seckey_verify, this function returns 0. For
+ * uniformly random 32-byte arrays the chance of being invalid
+ * is negligible (around 1 in 2^128) (cannot be NULL).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
const secp256k1_context* ctx,
@@ -587,13 +665,27 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
const unsigned char *tweak
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
-/** Tweak a private key by multiplying it by a tweak.
- * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
- * uniformly random 32-byte arrays, or equal to zero. 1 otherwise.
- * Args: ctx: pointer to a context object (cannot be NULL).
- * In/Out: seckey: pointer to a 32-byte private key.
- * In: tweak: pointer to a 32-byte tweak.
+/** Tweak a secret key by multiplying it by a tweak.
+ *
+ * Returns: 0 if the arguments are invalid. 1 otherwise.
+ * Args: ctx: pointer to a context object (cannot be NULL).
+ * In/Out: seckey: pointer to a 32-byte secret key. If the secret key is
+ * invalid according to secp256k1_ec_seckey_verify, this
+ * function returns 0. seckey will be set to some unspecified
+ * value if this function returns 0. (cannot be NULL)
+ * In: tweak: pointer to a 32-byte tweak. If the tweak is invalid according to
+ * secp256k1_ec_seckey_verify, this function returns 0. For
+ * uniformly random 32-byte arrays the chance of being invalid
+ * is negligible (around 1 in 2^128) (cannot be NULL).
*/
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_mul(
+ const secp256k1_context* ctx,
+ unsigned char *seckey,
+ const unsigned char *tweak
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Same as secp256k1_ec_seckey_tweak_mul, but DEPRECATED. Will be removed in
+ * future versions. */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
const secp256k1_context* ctx,
unsigned char *seckey,
@@ -601,12 +693,16 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a public key by multiplying it by a tweak value.
- * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
- * uniformly random 32-byte arrays, or equal to zero. 1 otherwise.
- * Args: ctx: pointer to a context object initialized for validation
- * (cannot be NULL).
- * In/Out: pubkey: pointer to a public key obkect.
- * In: tweak: pointer to a 32-byte tweak.
+ *
+ * Returns: 0 if the arguments are invalid. 1 otherwise.
+ * Args: ctx: pointer to a context object initialized for validation
+ * (cannot be NULL).
+ * In/Out: pubkey: pointer to a public key object. pubkey will be set to an
+ * invalid value if this function returns 0 (cannot be NULL).
+ * In: tweak: pointer to a 32-byte tweak. If the tweak is invalid according to
+ * secp256k1_ec_seckey_verify, this function returns 0. For
+ * uniformly random 32-byte arrays the chance of being invalid
+ * is negligible (around 1 in 2^128) (cannot be NULL).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
const secp256k1_context* ctx,
@@ -636,7 +732,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
* 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.
+ * secp256k1_context_clone (and secp256k1_context_preallocated_create or
+ * secp256k1_context_clone, resp.), and you may call this repeatedly afterwards.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
secp256k1_context* ctx,
@@ -644,6 +741,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
) SECP256K1_ARG_NONNULL(1);
/** Add a number of public keys together.
+ *
* Returns: 1: the sum of the public keys is valid.
* 0: the sum of the public keys is not valid.
* Args: ctx: pointer to a context object
diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h
index df5fde235c..4058e9c043 100644
--- a/src/secp256k1/include/secp256k1_ecdh.h
+++ b/src/secp256k1/include/secp256k1_ecdh.h
@@ -7,43 +7,50 @@
extern "C" {
#endif
-/** A pointer to a function that applies hash function to a point
+/** A pointer to a function that hashes an EC point to obtain an ECDH secret
*
- * 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
+ * Returns: 1 if the point was successfully hashed.
+ * 0 will cause secp256k1_ecdh to fail and return 0.
+ * Other return values are not allowed, and the behaviour of
+ * secp256k1_ecdh is undefined for other return values.
+ * Out: output: pointer to an array to be filled by the function
+ * In: x32: pointer to a 32-byte x coordinate
+ * y32: 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,
+ const unsigned char *x32,
+ const unsigned char *y32,
void *data
);
-/** An implementation of SHA256 hash function that applies to compressed public key. */
+/** An implementation of SHA256 hash function that applies to compressed public key.
+ * Populates the output parameter with 32 bytes. */
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). */
+/** A default ECDH hash function (currently equal to secp256k1_ecdh_hash_function_sha256).
+ * Populates the output parameter with 32 bytes. */
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)
+ * 0: scalar was invalid (zero or overflow) or hashfp returned 0
* Args: ctx: pointer to a context object (cannot be NULL)
- * Out: output: pointer to an array to be filled by the function
+ * Out: output: pointer to an array to be filled by hashfp
* In: pubkey: a pointer to a secp256k1_pubkey containing an
* initialized public key
- * privkey: a 32-byte scalar with which to multiply the point
+ * seckey: 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
+ * (in which case, 32 bytes will be written to output)
+ * data: arbitrary data pointer that is passed through to hashfp
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh(
const secp256k1_context* ctx,
unsigned char *output,
const secp256k1_pubkey *pubkey,
- const unsigned char *privkey,
+ const unsigned char *seckey,
secp256k1_ecdh_hash_function hashfp,
void *data
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
diff --git a/src/secp256k1/include/secp256k1_preallocated.h b/src/secp256k1/include/secp256k1_preallocated.h
new file mode 100644
index 0000000000..a9ae15d5ae
--- /dev/null
+++ b/src/secp256k1/include/secp256k1_preallocated.h
@@ -0,0 +1,128 @@
+#ifndef SECP256K1_PREALLOCATED_H
+#define SECP256K1_PREALLOCATED_H
+
+#include "secp256k1.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The module provided by this header file is intended for settings in which it
+ * is not possible or desirable to rely on dynamic memory allocation. It provides
+ * functions for creating, cloning, and destroying secp256k1 context objects in a
+ * contiguous fixed-size block of memory provided by the caller.
+ *
+ * Context objects created by functions in this module can be used like contexts
+ * objects created by functions in secp256k1.h, i.e., they can be passed to any
+ * API function that expects a context object (see secp256k1.h for details). The
+ * only exception is that context objects created by functions in this module
+ * must be destroyed using secp256k1_context_preallocated_destroy (in this
+ * module) instead of secp256k1_context_destroy (in secp256k1.h).
+ *
+ * It is guaranteed that functions in this module will not call malloc or its
+ * friends realloc, calloc, and free.
+ */
+
+/** Determine the memory size of a secp256k1 context object to be created in
+ * caller-provided memory.
+ *
+ * The purpose of this function is to determine how much memory must be provided
+ * to secp256k1_context_preallocated_create.
+ *
+ * Returns: the required size of the caller-provided memory block
+ * In: flags: which parts of the context to initialize.
+ */
+SECP256K1_API size_t secp256k1_context_preallocated_size(
+ unsigned int flags
+) SECP256K1_WARN_UNUSED_RESULT;
+
+/** Create a secp256k1 context object in caller-provided memory.
+ *
+ * The caller must provide a pointer to a rewritable contiguous block of memory
+ * of size at least secp256k1_context_preallocated_size(flags) bytes, suitably
+ * aligned to hold an object of any type.
+ *
+ * The block of memory is exclusively owned by the created context object during
+ * the lifetime of this context object, which begins with the call to this
+ * function and ends when a call to secp256k1_context_preallocated_destroy
+ * (which destroys the context object again) returns. During the lifetime of the
+ * context object, the caller is obligated not to access this block of memory,
+ * i.e., the caller may not read or write the memory, e.g., by copying the memory
+ * contents to a different location or trying to create a second context object
+ * in the memory. In simpler words, the prealloc pointer (or any pointer derived
+ * from it) should not be used during the lifetime of the context object.
+ *
+ * Returns: a newly created context object.
+ * In: prealloc: a pointer to a rewritable contiguous block of memory of
+ * size at least secp256k1_context_preallocated_size(flags)
+ * bytes, as detailed above (cannot be NULL)
+ * flags: which parts of the context to initialize.
+ *
+ * See also secp256k1_context_randomize (in secp256k1.h)
+ * and secp256k1_context_preallocated_destroy.
+ */
+SECP256K1_API secp256k1_context* secp256k1_context_preallocated_create(
+ void* prealloc,
+ unsigned int flags
+) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
+
+/** Determine the memory size of a secp256k1 context object to be copied into
+ * caller-provided memory.
+ *
+ * Returns: the required size of the caller-provided memory block.
+ * In: ctx: an existing context to copy (cannot be NULL)
+ */
+SECP256K1_API size_t secp256k1_context_preallocated_clone_size(
+ const secp256k1_context* ctx
+) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
+
+/** Copy a secp256k1 context object into caller-provided memory.
+ *
+ * The caller must provide a pointer to a rewritable contiguous block of memory
+ * of size at least secp256k1_context_preallocated_size(flags) bytes, suitably
+ * aligned to hold an object of any type.
+ *
+ * The block of memory is exclusively owned by the created context object during
+ * the lifetime of this context object, see the description of
+ * secp256k1_context_preallocated_create for details.
+ *
+ * Returns: a newly created context object.
+ * Args: ctx: an existing context to copy (cannot be NULL)
+ * In: prealloc: a pointer to a rewritable contiguous block of memory of
+ * size at least secp256k1_context_preallocated_size(flags)
+ * bytes, as detailed above (cannot be NULL)
+ */
+SECP256K1_API secp256k1_context* secp256k1_context_preallocated_clone(
+ const secp256k1_context* ctx,
+ void* prealloc
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_WARN_UNUSED_RESULT;
+
+/** Destroy a secp256k1 context object that has been created in
+ * caller-provided memory.
+ *
+ * The context pointer may not be used afterwards.
+ *
+ * The context to destroy must have been created using
+ * secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone.
+ * If the context has instead been created using secp256k1_context_create or
+ * secp256k1_context_clone, the behaviour is undefined. In that case,
+ * secp256k1_context_destroy must be used instead.
+ *
+ * If required, it is the responsibility of the caller to deallocate the block
+ * of memory properly after this function returns, e.g., by calling free on the
+ * preallocated pointer given to secp256k1_context_preallocated_create or
+ * secp256k1_context_preallocated_clone.
+ *
+ * Args: ctx: an existing context to destroy, constructed using
+ * secp256k1_context_preallocated_create or
+ * secp256k1_context_preallocated_clone (cannot be NULL)
+ */
+SECP256K1_API void secp256k1_context_preallocated_destroy(
+ secp256k1_context* ctx
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SECP256K1_PREALLOCATED_H */
diff --git a/src/secp256k1/include/secp256k1_recovery.h b/src/secp256k1/include/secp256k1_recovery.h
index cf6c5ed7f5..f8ccaecd3d 100644
--- a/src/secp256k1/include/secp256k1_recovery.h
+++ b/src/secp256k1/include/secp256k1_recovery.h
@@ -70,7 +70,7 @@ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact(
/** Create a recoverable ECDSA signature.
*
* Returns: 1: signature created
- * 0: the nonce generation function failed, or the private key was invalid.
+ * 0: the nonce generation function failed, or the secret key was invalid.
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
* Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
* In: msg32: the 32-byte message hash being signed (cannot be NULL)
diff --git a/src/secp256k1/src/asm/field_10x26_arm.s b/src/secp256k1/src/asm/field_10x26_arm.s
index 5a9cc3ffcf..9a5bd06721 100644
--- a/src/secp256k1/src/asm/field_10x26_arm.s
+++ b/src/secp256k1/src/asm/field_10x26_arm.s
@@ -16,15 +16,9 @@ Note:
*/
.syntax unified
- .arch armv7-a
@ eabi attributes - see readelf -A
- .eabi_attribute 8, 1 @ Tag_ARM_ISA_use = yes
- .eabi_attribute 9, 0 @ Tag_Thumb_ISA_use = no
- .eabi_attribute 10, 0 @ Tag_FP_arch = none
.eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP
- .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Aggressive Speed
- .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6
.text
@ Field constants
diff --git a/src/secp256k1/src/basic-config.h b/src/secp256k1/src/basic-config.h
index fc588061ca..e9be39d4ca 100644
--- a/src/secp256k1/src/basic-config.h
+++ b/src/secp256k1/src/basic-config.h
@@ -10,7 +10,10 @@
#ifdef USE_BASIC_CONFIG
#undef USE_ASM_X86_64
+#undef USE_ECMULT_STATIC_PRECOMPUTATION
#undef USE_ENDOMORPHISM
+#undef USE_EXTERNAL_ASM
+#undef USE_EXTERNAL_DEFAULT_CALLBACKS
#undef USE_FIELD_10X26
#undef USE_FIELD_5X52
#undef USE_FIELD_INV_BUILTIN
@@ -21,12 +24,14 @@
#undef USE_SCALAR_8X32
#undef USE_SCALAR_INV_BUILTIN
#undef USE_SCALAR_INV_NUM
+#undef ECMULT_WINDOW_SIZE
#define USE_NUM_NONE 1
#define USE_FIELD_INV_BUILTIN 1
#define USE_SCALAR_INV_BUILTIN 1
#define USE_FIELD_10X26 1
#define USE_SCALAR_8X32 1
+#define ECMULT_WINDOW_SIZE 15
#endif /* USE_BASIC_CONFIG */
diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h
index 5b59783f68..9bfed903e0 100644
--- a/src/secp256k1/src/bench.h
+++ b/src/secp256k1/src/bench.h
@@ -7,45 +7,87 @@
#ifndef SECP256K1_BENCH_H
#define SECP256K1_BENCH_H
+#include <stdint.h>
#include <stdio.h>
#include <string.h>
-#include <math.h>
#include "sys/time.h"
-static double gettimedouble(void) {
+static int64_t gettime_i64(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
- return tv.tv_usec * 0.000001 + tv.tv_sec;
+ return (int64_t)tv.tv_usec + (int64_t)tv.tv_sec * 1000000LL;
}
-void print_number(double x) {
- double y = x;
- int c = 0;
- if (y < 0.0) {
- y = -y;
+#define FP_EXP (6)
+#define FP_MULT (1000000LL)
+
+/* Format fixed point number. */
+void print_number(const int64_t x) {
+ int64_t x_abs, y;
+ int c, i, rounding;
+ size_t ptr;
+ char buffer[30];
+
+ if (x == INT64_MIN) {
+ /* Prevent UB. */
+ printf("ERR");
+ return;
}
- while (y > 0 && y < 100.0) {
- y *= 10.0;
+ x_abs = x < 0 ? -x : x;
+
+ /* Determine how many decimals we want to show (more than FP_EXP makes no
+ * sense). */
+ y = x_abs;
+ c = 0;
+ while (y > 0LL && y < 100LL * FP_MULT && c < FP_EXP) {
+ y *= 10LL;
c++;
}
- printf("%.*f", c, x);
+
+ /* Round to 'c' decimals. */
+ y = x_abs;
+ rounding = 0;
+ for (i = c; i < FP_EXP; ++i) {
+ rounding = (y % 10) >= 5;
+ y /= 10;
+ }
+ y += rounding;
+
+ /* Format and print the number. */
+ ptr = sizeof(buffer) - 1;
+ buffer[ptr] = 0;
+ if (c != 0) {
+ for (i = 0; i < c; ++i) {
+ buffer[--ptr] = '0' + (y % 10);
+ y /= 10;
+ }
+ buffer[--ptr] = '.';
+ }
+ do {
+ buffer[--ptr] = '0' + (y % 10);
+ y /= 10;
+ } while (y != 0);
+ if (x < 0) {
+ buffer[--ptr] = '-';
+ }
+ printf("%s", &buffer[ptr]);
}
-void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), void (*teardown)(void*), void* data, int count, int iter) {
+void run_benchmark(char *name, void (*benchmark)(void*, int), void (*setup)(void*), void (*teardown)(void*, int), void* data, int count, int iter) {
int i;
- double min = HUGE_VAL;
- double sum = 0.0;
- double max = 0.0;
+ int64_t min = INT64_MAX;
+ int64_t sum = 0;
+ int64_t max = 0;
for (i = 0; i < count; i++) {
- double begin, total;
+ int64_t begin, total;
if (setup != NULL) {
setup(data);
}
- begin = gettimedouble();
- benchmark(data);
- total = gettimedouble() - begin;
+ begin = gettime_i64();
+ benchmark(data, iter);
+ total = gettime_i64() - begin;
if (teardown != NULL) {
- teardown(data);
+ teardown(data, iter);
}
if (total < min) {
min = total;
@@ -56,11 +98,11 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v
sum += total;
}
printf("%s: min ", name);
- print_number(min * 1000000.0 / iter);
+ print_number(min * FP_MULT / iter);
printf("us / avg ");
- print_number((sum / count) * 1000000.0 / iter);
+ print_number(((sum * FP_MULT) / count) / iter);
printf("us / max ");
- print_number(max * 1000000.0 / iter);
+ print_number(max * FP_MULT / iter);
printf("us\n");
}
@@ -79,4 +121,13 @@ int have_flag(int argc, char** argv, char *flag) {
return 0;
}
+int get_iters(int default_iters) {
+ char* env = getenv("SECP256K1_BENCH_ITERS");
+ if (env) {
+ return strtol(env, NULL, 0);
+ } else {
+ return default_iters;
+ }
+}
+
#endif /* SECP256K1_BENCH_H */
diff --git a/src/secp256k1/src/bench_ecdh.c b/src/secp256k1/src/bench_ecdh.c
index c1dd5a6ac9..f099d33884 100644
--- a/src/secp256k1/src/bench_ecdh.c
+++ b/src/secp256k1/src/bench_ecdh.c
@@ -28,20 +28,18 @@ static void bench_ecdh_setup(void* arg) {
0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f
};
- /* create a context with no capabilities */
- data->ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT);
for (i = 0; i < 32; i++) {
data->scalar[i] = i + 1;
}
CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1);
}
-static void bench_ecdh(void* arg) {
+static void bench_ecdh(void* arg, int iters) {
int i;
unsigned char res[32];
bench_ecdh_data *data = (bench_ecdh_data*)arg;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar, NULL, NULL) == 1);
}
}
@@ -49,6 +47,13 @@ static void bench_ecdh(void* arg) {
int main(void) {
bench_ecdh_data data;
- run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, 20000);
+ int iters = get_iters(20000);
+
+ /* create a context with no capabilities */
+ data.ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT);
+
+ run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, iters);
+
+ secp256k1_context_destroy(data.ctx);
return 0;
}
diff --git a/src/secp256k1/src/bench_ecmult.c b/src/secp256k1/src/bench_ecmult.c
index 6d0ed1f436..facd07ef31 100644
--- a/src/secp256k1/src/bench_ecmult.c
+++ b/src/secp256k1/src/bench_ecmult.c
@@ -18,7 +18,6 @@
#include "secp256k1.c"
#define POINTS 32768
-#define ITERS 10000
typedef struct {
/* Setup once in advance */
@@ -55,16 +54,16 @@ static int bench_callback(secp256k1_scalar* sc, secp256k1_ge* ge, size_t idx, vo
return 1;
}
-static void bench_ecmult(void* arg) {
+static void bench_ecmult(void* arg, int iters) {
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;
+ int iter;
+ int count = data->count;
+ iters = iters / data->count;
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->ecmult_multi(&data->ctx->error_callback, &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;
}
@@ -76,10 +75,10 @@ static void bench_ecmult_setup(void* arg) {
data->offset2 = (data->count * 0x7f6f537b + 0x6a1a8f49) % POINTS;
}
-static void bench_ecmult_teardown(void* arg) {
+static void bench_ecmult_teardown(void* arg, int iters) {
bench_data* data = (bench_data*)arg;
- size_t iters = 1 + ITERS / data->count;
- size_t iter;
+ int iter;
+ iters = iters / data->count;
/* Verify the results in teardown, to avoid doing comparisons while benchmarking. */
for (iter = 0; iter < iters; ++iter) {
secp256k1_gej tmp;
@@ -104,10 +103,10 @@ static void generate_scalar(uint32_t num, secp256k1_scalar* scalar) {
CHECK(!overflow);
}
-static void run_test(bench_data* data, size_t count, int includes_g) {
+static void run_test(bench_data* data, size_t count, int includes_g, int num_iters) {
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 iters = 1 + num_iters / count;
size_t iter;
data->count = count;
@@ -130,7 +129,7 @@ static void run_test(bench_data* data, size_t count, int includes_g) {
/* 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));
+ run_benchmark(str, bench_ecmult, bench_ecmult_setup, bench_ecmult_teardown, data, 10, count * iters);
}
int main(int argc, char **argv) {
@@ -139,6 +138,8 @@ int main(int argc, char **argv) {
secp256k1_gej* pubkeys_gej;
size_t scratch_size;
+ int iters = get_iters(10000);
+
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);
@@ -154,7 +155,7 @@ int main(int argc, char **argv) {
} 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);
+ secp256k1_scratch_space_destroy(data.ctx, data.scratch);
data.scratch = NULL;
} else {
fprintf(stderr, "%s: unrecognized argument '%s'.\n", argv[0], argv[1]);
@@ -167,8 +168,8 @@ int main(int argc, char **argv) {
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));
+ 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);
@@ -185,18 +186,24 @@ int main(int argc, char **argv) {
free(pubkeys_gej);
for (i = 1; i <= 8; ++i) {
- run_test(&data, i, 1);
+ run_test(&data, i, 1, iters);
}
- for (p = 0; p <= 11; ++p) {
- for (i = 9; i <= 16; ++i) {
- run_test(&data, i << p, 1);
+ /* This is disabled with low count of iterations because the loop runs 77 times even with iters=1
+ * and the higher it goes the longer the computation takes(more points)
+ * So we don't run this benchmark with low iterations to prevent slow down */
+ if (iters > 2) {
+ for (p = 0; p <= 11; ++p) {
+ for (i = 9; i <= 16; ++i) {
+ run_test(&data, i << p, 1, iters);
+ }
}
}
- secp256k1_context_destroy(data.ctx);
+
if (data.scratch != NULL) {
- secp256k1_scratch_space_destroy(data.scratch);
+ secp256k1_scratch_space_destroy(data.ctx, data.scratch);
}
+ secp256k1_context_destroy(data.ctx);
free(data.scalars);
free(data.pubkeys);
free(data.seckeys);
diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c
index 9071724331..20759127d3 100644
--- a/src/secp256k1/src/bench_internal.c
+++ b/src/secp256k1/src/bench_internal.c
@@ -56,263 +56,272 @@ void bench_setup(void* arg) {
memcpy(data->data + 32, init_y, 32);
}
-void bench_scalar_add(void* arg) {
- int i;
+void bench_scalar_add(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 2000000; i++) {
- secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ for (i = 0; i < iters; i++) {
+ j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
+ CHECK(j <= iters);
}
-void bench_scalar_negate(void* arg) {
+void bench_scalar_negate(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 2000000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x);
}
}
-void bench_scalar_sqr(void* arg) {
+void bench_scalar_sqr(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x);
}
}
-void bench_scalar_mul(void* arg) {
+void bench_scalar_mul(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
}
#ifdef USE_ENDOMORPHISM
-void bench_scalar_split(void* arg) {
- int i;
+void bench_scalar_split(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 20000; i++) {
- secp256k1_scalar l, r;
- secp256k1_scalar_split_lambda(&l, &r, &data->scalar_x);
- secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ for (i = 0; i < iters; i++) {
+ secp256k1_scalar_split_lambda(&data->scalar_x, &data->scalar_y, &data->scalar_x);
+ j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
+ CHECK(j <= iters);
}
#endif
-void bench_scalar_inverse(void* arg) {
- int i;
+void bench_scalar_inverse(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 2000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x);
- secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
+ CHECK(j <= iters);
}
-void bench_scalar_inverse_var(void* arg) {
- int i;
+void bench_scalar_inverse_var(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 2000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x);
- secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
+ CHECK(j <= iters);
}
-void bench_field_normalize(void* arg) {
+void bench_field_normalize(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 2000000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_fe_normalize(&data->fe_x);
}
}
-void bench_field_normalize_weak(void* arg) {
+void bench_field_normalize_weak(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 2000000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_fe_normalize_weak(&data->fe_x);
}
}
-void bench_field_mul(void* arg) {
+void bench_field_mul(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y);
}
}
-void bench_field_sqr(void* arg) {
+void bench_field_sqr(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_fe_sqr(&data->fe_x, &data->fe_x);
}
}
-void bench_field_inverse(void* arg) {
+void bench_field_inverse(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_fe_inv(&data->fe_x, &data->fe_x);
secp256k1_fe_add(&data->fe_x, &data->fe_y);
}
}
-void bench_field_inverse_var(void* arg) {
+void bench_field_inverse_var(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_fe_inv_var(&data->fe_x, &data->fe_x);
secp256k1_fe_add(&data->fe_x, &data->fe_y);
}
}
-void bench_field_sqrt(void* arg) {
- int i;
+void bench_field_sqrt(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
secp256k1_fe t;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
t = data->fe_x;
- secp256k1_fe_sqrt(&data->fe_x, &t);
+ j += secp256k1_fe_sqrt(&data->fe_x, &t);
secp256k1_fe_add(&data->fe_x, &data->fe_y);
}
+ CHECK(j <= iters);
}
-void bench_group_double_var(void* arg) {
+void bench_group_double_var(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL);
}
}
-void bench_group_add_var(void* arg) {
+void bench_group_add_var(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL);
}
}
-void bench_group_add_affine(void* arg) {
+void bench_group_add_affine(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y);
}
}
-void bench_group_add_affine_var(void* arg) {
+void bench_group_add_affine_var(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL);
}
}
-void bench_group_jacobi_var(void* arg) {
- int i;
+void bench_group_jacobi_var(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 20000; i++) {
- secp256k1_gej_has_quad_y_var(&data->gej_x);
+ for (i = 0; i < iters; i++) {
+ j += secp256k1_gej_has_quad_y_var(&data->gej_x);
}
+ CHECK(j == iters);
}
-void bench_ecmult_wnaf(void* arg) {
- int i;
+void bench_ecmult_wnaf(void* arg, int iters) {
+ int i, bits = 0, overflow = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 20000; i++) {
- secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A);
- secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ for (i = 0; i < iters; i++) {
+ bits += secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A);
+ overflow += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
+ CHECK(overflow >= 0);
+ CHECK(bits <= 256*iters);
}
-void bench_wnaf_const(void* arg) {
- int i;
+void bench_wnaf_const(void* arg, int iters) {
+ int i, bits = 0, overflow = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 20000; i++) {
- secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A, 256);
- secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ for (i = 0; i < iters; i++) {
+ bits += secp256k1_wnaf_const(data->wnaf, &data->scalar_x, WINDOW_A, 256);
+ overflow += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
+ CHECK(overflow >= 0);
+ CHECK(bits <= 256*iters);
}
-void bench_sha256(void* arg) {
+void bench_sha256(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
secp256k1_sha256 sha;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, data->data, 32);
secp256k1_sha256_finalize(&sha, data->data);
}
}
-void bench_hmac_sha256(void* arg) {
+void bench_hmac_sha256(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
secp256k1_hmac_sha256 hmac;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_hmac_sha256_initialize(&hmac, data->data, 32);
secp256k1_hmac_sha256_write(&hmac, data->data, 32);
secp256k1_hmac_sha256_finalize(&hmac, data->data);
}
}
-void bench_rfc6979_hmac_sha256(void* arg) {
+void bench_rfc6979_hmac_sha256(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
secp256k1_rfc6979_hmac_sha256 rng;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64);
secp256k1_rfc6979_hmac_sha256_generate(&rng, data->data, 32);
}
}
-void bench_context_verify(void* arg) {
+void bench_context_verify(void* arg, int iters) {
int i;
(void)arg;
- for (i = 0; i < 20; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY));
}
}
-void bench_context_sign(void* arg) {
+void bench_context_sign(void* arg, int iters) {
int i;
(void)arg;
- for (i = 0; i < 200; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_SIGN));
}
}
#ifndef USE_NUM_NONE
-void bench_num_jacobi(void* arg) {
- int i;
+void bench_num_jacobi(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
secp256k1_num nx, norder;
@@ -320,50 +329,53 @@ void bench_num_jacobi(void* arg) {
secp256k1_scalar_order_get_num(&norder);
secp256k1_scalar_get_num(&norder, &data->scalar_y);
- for (i = 0; i < 200000; i++) {
- secp256k1_num_jacobi(&nx, &norder);
+ for (i = 0; i < iters; i++) {
+ j += secp256k1_num_jacobi(&nx, &norder);
}
+ CHECK(j <= iters);
}
#endif
int main(int argc, char **argv) {
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);
- if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, 200000);
+ int iters = get_iters(20000);
+
+ if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, iters*100);
+ if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, iters*100);
+ if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, iters*10);
+ if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, iters*10);
#ifdef USE_ENDOMORPHISM
- if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, 20000);
+ if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, iters);
#endif
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, 2000);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, 2000);
- if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, 2000000);
- if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, 2000000);
- if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, 200000);
- if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, 200000);
- if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, 20000);
- if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, 20000);
- if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, 20000);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, iters*100);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, iters*100);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, iters*10);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, iters*10);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, iters);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, iters);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, iters);
- if (have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, 200000);
- if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, 200000);
- if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, 200000);
- if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, 200000);
- if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, 20000);
+ if (have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, iters*10);
+ if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, iters*10);
+ if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, iters*10);
+ if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, iters*10);
+ if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, iters);
- if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, 20000);
- if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, 20000);
+ if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, iters);
+ if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, iters);
- if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "sha256")) run_benchmark("hash_sha256", bench_sha256, bench_setup, NULL, &data, 10, 20000);
- if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "hmac")) run_benchmark("hash_hmac_sha256", bench_hmac_sha256, bench_setup, NULL, &data, 10, 20000);
- if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "rng6979")) run_benchmark("hash_rfc6979_hmac_sha256", bench_rfc6979_hmac_sha256, bench_setup, NULL, &data, 10, 20000);
+ if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "sha256")) run_benchmark("hash_sha256", bench_sha256, bench_setup, NULL, &data, 10, iters);
+ if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "hmac")) run_benchmark("hash_hmac_sha256", bench_hmac_sha256, bench_setup, NULL, &data, 10, iters);
+ if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "rng6979")) run_benchmark("hash_rfc6979_hmac_sha256", bench_rfc6979_hmac_sha256, bench_setup, NULL, &data, 10, iters);
- if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 20);
- if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 200);
+ if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 1 + iters/1000);
+ if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 1 + iters/100);
#ifndef USE_NUM_NONE
- if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, 200000);
+ if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, iters*10);
#endif
return 0;
}
diff --git a/src/secp256k1/src/bench_recover.c b/src/secp256k1/src/bench_recover.c
index b806eed94e..e952ed1215 100644
--- a/src/secp256k1/src/bench_recover.c
+++ b/src/secp256k1/src/bench_recover.c
@@ -15,13 +15,13 @@ typedef struct {
unsigned char sig[64];
} bench_recover_data;
-void bench_recover(void* arg) {
+void bench_recover(void* arg, int iters) {
int i;
bench_recover_data *data = (bench_recover_data*)arg;
secp256k1_pubkey pubkey;
unsigned char pubkeyc[33];
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
int j;
size_t pubkeylen = 33;
secp256k1_ecdsa_recoverable_signature sig;
@@ -51,9 +51,11 @@ void bench_recover_setup(void* arg) {
int main(void) {
bench_recover_data data;
+ int iters = get_iters(20000);
+
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
- run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, 20000);
+ run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, iters);
secp256k1_context_destroy(data.ctx);
return 0;
diff --git a/src/secp256k1/src/bench_sign.c b/src/secp256k1/src/bench_sign.c
index 544b43963c..c6b2942cc0 100644
--- a/src/secp256k1/src/bench_sign.c
+++ b/src/secp256k1/src/bench_sign.c
@@ -26,12 +26,12 @@ static void bench_sign_setup(void* arg) {
}
}
-static void bench_sign_run(void* arg) {
+static void bench_sign_run(void* arg, int iters) {
int i;
bench_sign *data = (bench_sign*)arg;
unsigned char sig[74];
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
size_t siglen = 74;
int j;
secp256k1_ecdsa_signature signature;
@@ -47,9 +47,11 @@ static void bench_sign_run(void* arg) {
int main(void) {
bench_sign data;
+ int iters = get_iters(20000);
+
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
- run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, 20000);
+ run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, iters);
secp256k1_context_destroy(data.ctx);
return 0;
diff --git a/src/secp256k1/src/bench_verify.c b/src/secp256k1/src/bench_verify.c
index 418defa0aa..272d3e5cc4 100644
--- a/src/secp256k1/src/bench_verify.c
+++ b/src/secp256k1/src/bench_verify.c
@@ -17,6 +17,7 @@
#include <openssl/obj_mac.h>
#endif
+
typedef struct {
secp256k1_context *ctx;
unsigned char msg[32];
@@ -30,11 +31,11 @@ typedef struct {
#endif
} benchmark_verify_t;
-static void benchmark_verify(void* arg) {
+static void benchmark_verify(void* arg, int iters) {
int i;
benchmark_verify_t* data = (benchmark_verify_t*)arg;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_pubkey pubkey;
secp256k1_ecdsa_signature sig;
data->sig[data->siglen - 1] ^= (i & 0xFF);
@@ -50,11 +51,11 @@ static void benchmark_verify(void* arg) {
}
#ifdef ENABLE_OPENSSL_TESTS
-static void benchmark_verify_openssl(void* arg) {
+static void benchmark_verify_openssl(void* arg, int iters) {
int i;
benchmark_verify_t* data = (benchmark_verify_t*)arg;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
data->sig[data->siglen - 1] ^= (i & 0xFF);
data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF);
data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF);
@@ -85,6 +86,8 @@ int main(void) {
secp256k1_ecdsa_signature sig;
benchmark_verify_t data;
+ int iters = get_iters(20000);
+
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
for (i = 0; i < 32; i++) {
@@ -100,10 +103,10 @@ int main(void) {
data.pubkeylen = 33;
CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
- run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, 20000);
+ run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, iters);
#ifdef ENABLE_OPENSSL_TESTS
data.ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1);
- run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, 20000);
+ run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, iters);
EC_GROUP_free(data.ec_group);
#endif
diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h
index c3400042d8..5f54b59faa 100644
--- a/src/secp256k1/src/ecdsa_impl.h
+++ b/src/secp256k1/src/ecdsa_impl.h
@@ -46,68 +46,73 @@ static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CON
0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL
);
-static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned char *sigend) {
- int lenleft, b1;
- size_t ret = 0;
+static int secp256k1_der_read_len(size_t *len, const unsigned char **sigp, const unsigned char *sigend) {
+ size_t lenleft;
+ unsigned char b1;
+ VERIFY_CHECK(len != NULL);
+ *len = 0;
if (*sigp >= sigend) {
- return -1;
+ return 0;
}
b1 = *((*sigp)++);
if (b1 == 0xFF) {
/* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */
- return -1;
+ return 0;
}
if ((b1 & 0x80) == 0) {
/* X.690-0207 8.1.3.4 short form length octets */
- return b1;
+ *len = b1;
+ return 1;
}
if (b1 == 0x80) {
/* Indefinite length is not allowed in DER. */
- return -1;
+ return 0;
}
/* X.690-207 8.1.3.5 long form length octets */
- lenleft = b1 & 0x7F;
- if (lenleft > sigend - *sigp) {
- return -1;
+ lenleft = b1 & 0x7F; /* lenleft is at least 1 */
+ if (lenleft > (size_t)(sigend - *sigp)) {
+ return 0;
}
if (**sigp == 0) {
/* Not the shortest possible length encoding. */
- return -1;
+ return 0;
}
- if ((size_t)lenleft > sizeof(size_t)) {
+ if (lenleft > sizeof(size_t)) {
/* The resulting length would exceed the range of a size_t, so
* certainly longer than the passed array size.
*/
- return -1;
+ return 0;
}
while (lenleft > 0) {
- ret = (ret << 8) | **sigp;
- if (ret + lenleft > (size_t)(sigend - *sigp)) {
- /* Result exceeds the length of the passed array. */
- return -1;
- }
+ *len = (*len << 8) | **sigp;
(*sigp)++;
lenleft--;
}
- if (ret < 128) {
+ if (*len > (size_t)(sigend - *sigp)) {
+ /* Result exceeds the length of the passed array. */
+ return 0;
+ }
+ if (*len < 128) {
/* Not the shortest possible length encoding. */
- return -1;
+ return 0;
}
- return ret;
+ return 1;
}
static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) {
int overflow = 0;
unsigned char ra[32] = {0};
- int rlen;
+ size_t rlen;
if (*sig == sigend || **sig != 0x02) {
/* Not a primitive integer (X.690-0207 8.3.1). */
return 0;
}
(*sig)++;
- rlen = secp256k1_der_read_len(sig, sigend);
- if (rlen <= 0 || (*sig) + rlen > sigend) {
+ if (secp256k1_der_read_len(&rlen, sig, sigend) == 0) {
+ return 0;
+ }
+ if (rlen == 0 || *sig + rlen > sigend) {
/* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */
return 0;
}
@@ -123,8 +128,11 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char
/* Negative. */
overflow = 1;
}
- while (rlen > 0 && **sig == 0) {
- /* Skip leading zero bytes */
+ /* There is at most one leading zero byte:
+ * if there were two leading zero bytes, we would have failed and returned 0
+ * because of excessive 0x00 padding already. */
+ if (rlen > 0 && **sig == 0) {
+ /* Skip leading zero byte */
rlen--;
(*sig)++;
}
@@ -144,18 +152,16 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char
static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) {
const unsigned char *sigend = sig + size;
- int rlen;
+ size_t rlen;
if (sig == sigend || *(sig++) != 0x30) {
/* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */
return 0;
}
- rlen = secp256k1_der_read_len(&sig, sigend);
- if (rlen < 0 || sig + rlen > sigend) {
- /* Tuple exceeds bounds */
+ if (secp256k1_der_read_len(&rlen, &sig, sigend) == 0) {
return 0;
}
- if (sig + rlen != sigend) {
- /* Garbage after tuple. */
+ if (rlen != (size_t)(sigend - sig)) {
+ /* Tuple exceeds bounds or garage after tuple. */
return 0;
}
@@ -274,6 +280,7 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
secp256k1_ge r;
secp256k1_scalar n;
int overflow = 0;
+ int high;
secp256k1_ecmult_gen(ctx, &rp, nonce);
secp256k1_ge_set_gej(&r, &rp);
@@ -281,15 +288,11 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
secp256k1_fe_normalize(&r.y);
secp256k1_fe_get_b32(b, &r.x);
secp256k1_scalar_set_b32(sigr, b, &overflow);
- /* These two conditions should be checked before calling */
- VERIFY_CHECK(!secp256k1_scalar_is_zero(sigr));
- VERIFY_CHECK(overflow == 0);
-
if (recid) {
/* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log
* of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria.
*/
- *recid = (overflow ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0);
+ *recid = (overflow << 1) | secp256k1_fe_is_odd(&r.y);
}
secp256k1_scalar_mul(&n, sigr, seckey);
secp256k1_scalar_add(&n, &n, message);
@@ -298,16 +301,15 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
secp256k1_scalar_clear(&n);
secp256k1_gej_clear(&rp);
secp256k1_ge_clear(&r);
- if (secp256k1_scalar_is_zero(sigs)) {
- return 0;
- }
- if (secp256k1_scalar_is_high(sigs)) {
- secp256k1_scalar_negate(sigs, sigs);
- if (recid) {
- *recid ^= 1;
- }
+ high = secp256k1_scalar_is_high(sigs);
+ secp256k1_scalar_cond_negate(sigs, high);
+ if (recid) {
+ *recid ^= high;
}
- return 1;
+ /* P.x = order is on the curve, so technically sig->r could end up being zero, which would be an invalid signature.
+ * This is cryptographically unreachable as hitting it requires finding the discrete log of P.x = N.
+ */
+ return !secp256k1_scalar_is_zero(sigr) & !secp256k1_scalar_is_zero(sigs);
}
#endif /* SECP256K1_ECDSA_IMPL_H */
diff --git a/src/secp256k1/src/eckey_impl.h b/src/secp256k1/src/eckey_impl.h
index 7c5b789325..e2e72d9303 100644
--- a/src/secp256k1/src/eckey_impl.h
+++ b/src/secp256k1/src/eckey_impl.h
@@ -54,10 +54,7 @@ static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *p
static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak) {
secp256k1_scalar_add(key, key, tweak);
- if (secp256k1_scalar_is_zero(key)) {
- return 0;
- }
- return 1;
+ return !secp256k1_scalar_is_zero(key);
}
static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) {
@@ -75,12 +72,11 @@ static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx,
}
static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak) {
- if (secp256k1_scalar_is_zero(tweak)) {
- return 0;
- }
+ int ret;
+ ret = !secp256k1_scalar_is_zero(tweak);
secp256k1_scalar_mul(key, key, tweak);
- return 1;
+ return ret;
}
static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) {
diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h
index 3d75a960f4..c9b198239d 100644
--- a/src/secp256k1/src/ecmult.h
+++ b/src/secp256k1/src/ecmult.h
@@ -20,10 +20,10 @@ typedef struct {
#endif
} secp256k1_ecmult_context;
+static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx);
-static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb);
-static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst,
- const secp256k1_ecmult_context *src, const secp256k1_callback *cb);
+static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc);
+static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src);
static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx);
static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx);
@@ -43,6 +43,6 @@ typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge
* 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);
+static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, 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 d4804b8b68..03bb33257d 100644
--- a/src/secp256k1/src/ecmult_const.h
+++ b/src/secp256k1/src/ecmult_const.h
@@ -10,8 +10,11 @@
#include "scalar.h"
#include "group.h"
-/* 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. */
+/**
+ * Multiply: R = q*A (in constant-time)
+ * 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 8411752eb0..6d6d354aa4 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -14,16 +14,22 @@
/* This is like `ECMULT_TABLE_GET_GE` but is constant time */
#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \
- int m; \
- int abs_n = (n) * (((n) > 0) * 2 - 1); \
- int idx_n = abs_n / 2; \
+ int m = 0; \
+ /* Extract the sign-bit for a constant time absolute-value. */ \
+ int mask = (n) >> (sizeof(n) * CHAR_BIT - 1); \
+ int abs_n = ((n) + mask) ^ mask; \
+ int idx_n = abs_n >> 1; \
secp256k1_fe neg_y; \
VERIFY_CHECK(((n) & 1) == 1); \
VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \
VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \
VERIFY_SETUP(secp256k1_fe_clear(&(r)->x)); \
VERIFY_SETUP(secp256k1_fe_clear(&(r)->y)); \
- for (m = 0; m < ECMULT_TABLE_SIZE(w); m++) { \
+ /* Unconditionally set r->x = (pre)[m].x. r->y = (pre)[m].y. because it's either the correct one \
+ * or will get replaced in the later iterations, this is needed to make sure `r` is initialized. */ \
+ (r)->x = (pre)[m].x; \
+ (r)->y = (pre)[m].y; \
+ for (m = 1; m < ECMULT_TABLE_SIZE(w); m++) { \
/* This loop is used to avoid secret data in array indices. See
* the comment in ecmult_gen_impl.h for rationale. */ \
secp256k1_fe_cmov(&(r)->x, &(pre)[m].x, m == idx_n); \
@@ -44,11 +50,11 @@
*
* Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar
* Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.)
- * CT-RSA 2003, LNCS 2612, pp. 328-443, 2003. Springer-Verlagy Berlin Heidelberg 2003
+ * CT-RSA 2003, LNCS 2612, pp. 328-443, 2003. Springer-Verlag Berlin Heidelberg 2003
*
* 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, int size) {
+static int secp256k1_wnaf_const(int *wnaf, const secp256k1_scalar *scalar, int w, int size) {
int global_sign;
int skew = 0;
int word = 0;
@@ -59,8 +65,12 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
int flip;
int bit;
- secp256k1_scalar neg_s;
+ secp256k1_scalar s;
int not_neg_one;
+
+ VERIFY_CHECK(w > 0);
+ VERIFY_CHECK(size > 0);
+
/* Note that we cannot handle even numbers by negating them to be odd, as is
* done in other implementations, since if our scalars were specified to have
* width < 256 for performance reasons, their negations would have width 256
@@ -75,12 +85,13 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
* {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);
+ flip = secp256k1_scalar_is_high(scalar);
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
- bit = flip ^ !secp256k1_scalar_is_even(&s);
+ bit = flip ^ !secp256k1_scalar_is_even(scalar);
/* We check for negative one, since adding 2 to it will cause an overflow */
- secp256k1_scalar_negate(&neg_s, &s);
- not_neg_one = !secp256k1_scalar_is_one(&neg_s);
+ secp256k1_scalar_negate(&s, scalar);
+ not_neg_one = !secp256k1_scalar_is_one(&s);
+ s = *scalar;
secp256k1_scalar_cadd_bit(&s, bit, not_neg_one);
/* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects
* that we added two to it and flipped it. In fact for -1 these operations are
@@ -93,7 +104,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
/* 4 */
u_last = secp256k1_scalar_shr_int(&s, w);
- while (word * w < size) {
+ do {
int sign;
int even;
@@ -109,7 +120,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
wnaf[word++] = u_last * global_sign;
u_last = u;
- }
+ } while (word * w < size);
wnaf[word] = u * global_sign;
VERIFY_CHECK(secp256k1_scalar_is_zero(&s));
@@ -132,7 +143,6 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
int i;
- secp256k1_scalar sc = *scalar;
/* build wnaf representation for q. */
int rsize = size;
@@ -140,13 +150,13 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
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);
+ secp256k1_scalar_split_lambda(&q_1, &q_lam, scalar);
+ 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);
+ skew_1 = secp256k1_wnaf_const(wnaf_1, scalar, WINDOW_A - 1, size);
#ifdef USE_ENDOMORPHISM
skew_lam = 0;
#endif
@@ -168,6 +178,7 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
}
+
}
#endif
@@ -191,7 +202,7 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
int n;
int j;
for (j = 0; j < WINDOW_A - 1; ++j) {
- secp256k1_gej_double_nonzero(r, r, NULL);
+ secp256k1_gej_double_nonzero(r, r);
}
n = wnaf_1[i];
diff --git a/src/secp256k1/src/ecmult_gen.h b/src/secp256k1/src/ecmult_gen.h
index 7564b7015f..30815e5aa1 100644
--- a/src/secp256k1/src/ecmult_gen.h
+++ b/src/secp256k1/src/ecmult_gen.h
@@ -10,28 +10,35 @@
#include "scalar.h"
#include "group.h"
+#if ECMULT_GEN_PREC_BITS != 2 && ECMULT_GEN_PREC_BITS != 4 && ECMULT_GEN_PREC_BITS != 8
+# error "Set ECMULT_GEN_PREC_BITS to 2, 4 or 8."
+#endif
+#define ECMULT_GEN_PREC_B ECMULT_GEN_PREC_BITS
+#define ECMULT_GEN_PREC_G (1 << ECMULT_GEN_PREC_B)
+#define ECMULT_GEN_PREC_N (256 / ECMULT_GEN_PREC_B)
+
typedef struct {
/* For accelerating the computation of a*G:
* To harden against timing attacks, use the following mechanism:
- * * Break up the multiplicand into groups of 4 bits, called n_0, n_1, n_2, ..., n_63.
- * * Compute sum(n_i * 16^i * G + U_i, i=0..63), where:
- * * U_i = U * 2^i (for i=0..62)
- * * U_i = U * (1-2^63) (for i=63)
- * where U is a point with no known corresponding scalar. Note that sum(U_i, i=0..63) = 0.
- * For each i, and each of the 16 possible values of n_i, (n_i * 16^i * G + U_i) is
- * precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0..63).
+ * * Break up the multiplicand into groups of PREC_B bits, called n_0, n_1, n_2, ..., n_(PREC_N-1).
+ * * Compute sum(n_i * (PREC_G)^i * G + U_i, i=0 ... PREC_N-1), where:
+ * * U_i = U * 2^i, for i=0 ... PREC_N-2
+ * * U_i = U * (1-2^(PREC_N-1)), for i=PREC_N-1
+ * where U is a point with no known corresponding scalar. Note that sum(U_i, i=0 ... PREC_N-1) = 0.
+ * For each i, and each of the PREC_G possible values of n_i, (n_i * (PREC_G)^i * G + U_i) is
+ * precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0 ... PREC_N-1).
* None of the resulting prec group elements have a known scalar, and neither do any of
* the intermediate sums while computing a*G.
*/
- secp256k1_ge_storage (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */
+ secp256k1_ge_storage (*prec)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G]; /* prec[j][i] = (PREC_G)^j * i * G + U_i */
secp256k1_scalar blind;
secp256k1_gej initial;
} secp256k1_ecmult_gen_context;
+static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx);
-static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, const secp256k1_callback* cb);
-static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst,
- const secp256k1_ecmult_gen_context* src, const secp256k1_callback* cb);
+static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, void **prealloc);
+static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context* src);
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx);
static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx);
diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h
index d64505dc00..30ac16518b 100644
--- a/src/secp256k1/src/ecmult_gen_impl.h
+++ b/src/secp256k1/src/ecmult_gen_impl.h
@@ -7,6 +7,7 @@
#ifndef SECP256K1_ECMULT_GEN_IMPL_H
#define SECP256K1_ECMULT_GEN_IMPL_H
+#include "util.h"
#include "scalar.h"
#include "group.h"
#include "ecmult_gen.h"
@@ -14,23 +15,32 @@
#ifdef USE_ECMULT_STATIC_PRECOMPUTATION
#include "ecmult_static_context.h"
#endif
+
+#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
+ static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE = ROUND_TO_ALIGN(sizeof(*((secp256k1_ecmult_gen_context*) NULL)->prec));
+#else
+ static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE = 0;
+#endif
+
static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx) {
ctx->prec = NULL;
}
-static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, const secp256k1_callback* cb) {
+static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, void **prealloc) {
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
- secp256k1_ge prec[1024];
+ secp256k1_ge prec[ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G];
secp256k1_gej gj;
secp256k1_gej nums_gej;
int i, j;
+ size_t const prealloc_size = SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
+ void* const base = *prealloc;
#endif
if (ctx->prec != NULL) {
return;
}
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
- ctx->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*ctx->prec));
+ ctx->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])manual_alloc(prealloc, prealloc_size, base, prealloc_size);
/* get the generator */
secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g);
@@ -54,39 +64,39 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
/* compute prec. */
{
- secp256k1_gej precj[1024]; /* Jacobian versions of prec. */
+ secp256k1_gej precj[ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G]; /* Jacobian versions of prec. */
secp256k1_gej gbase;
secp256k1_gej numsbase;
- gbase = gj; /* 16^j * G */
+ gbase = gj; /* PREC_G^j * G */
numsbase = nums_gej; /* 2^j * nums. */
- for (j = 0; j < 64; j++) {
- /* Set precj[j*16 .. j*16+15] to (numsbase, numsbase + gbase, ..., numsbase + 15*gbase). */
- precj[j*16] = numsbase;
- for (i = 1; i < 16; i++) {
- secp256k1_gej_add_var(&precj[j*16 + i], &precj[j*16 + i - 1], &gbase, NULL);
+ for (j = 0; j < ECMULT_GEN_PREC_N; j++) {
+ /* Set precj[j*PREC_G .. j*PREC_G+(PREC_G-1)] to (numsbase, numsbase + gbase, ..., numsbase + (PREC_G-1)*gbase). */
+ precj[j*ECMULT_GEN_PREC_G] = numsbase;
+ for (i = 1; i < ECMULT_GEN_PREC_G; i++) {
+ secp256k1_gej_add_var(&precj[j*ECMULT_GEN_PREC_G + i], &precj[j*ECMULT_GEN_PREC_G + i - 1], &gbase, NULL);
}
- /* Multiply gbase by 16. */
- for (i = 0; i < 4; i++) {
+ /* Multiply gbase by PREC_G. */
+ for (i = 0; i < ECMULT_GEN_PREC_B; i++) {
secp256k1_gej_double_var(&gbase, &gbase, NULL);
}
/* Multiply numbase by 2. */
secp256k1_gej_double_var(&numsbase, &numsbase, NULL);
- if (j == 62) {
+ if (j == ECMULT_GEN_PREC_N - 2) {
/* In the last iteration, numsbase is (1 - 2^j) * nums instead. */
secp256k1_gej_neg(&numsbase, &numsbase);
secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL);
}
}
- secp256k1_ge_set_all_gej_var(prec, precj, 1024);
+ secp256k1_ge_set_all_gej_var(prec, precj, ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G);
}
- for (j = 0; j < 64; j++) {
- for (i = 0; i < 16; i++) {
- secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]);
+ for (j = 0; j < ECMULT_GEN_PREC_N; j++) {
+ for (i = 0; i < ECMULT_GEN_PREC_G; i++) {
+ secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*ECMULT_GEN_PREC_G + i]);
}
}
#else
- (void)cb;
- ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_static_context;
+ (void)prealloc;
+ ctx->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])secp256k1_ecmult_static_context;
#endif
secp256k1_ecmult_gen_blind(ctx, NULL);
}
@@ -95,27 +105,18 @@ static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_cont
return ctx->prec != NULL;
}
-static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst,
- const secp256k1_ecmult_gen_context *src, const secp256k1_callback* cb) {
- if (src->prec == NULL) {
- dst->prec = NULL;
- } else {
+static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context *src) {
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
- dst->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*dst->prec));
- memcpy(dst->prec, src->prec, sizeof(*dst->prec));
+ if (src->prec != NULL) {
+ /* We cast to void* first to suppress a -Wcast-align warning. */
+ dst->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])(void*)((unsigned char*)dst + ((unsigned char*)src->prec - (unsigned char*)src));
+ }
#else
- (void)cb;
- dst->prec = src->prec;
+ (void)dst, (void)src;
#endif
- dst->initial = src->initial;
- dst->blind = src->blind;
- }
}
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) {
-#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
- free(ctx->prec);
-#endif
secp256k1_scalar_clear(&ctx->blind);
secp256k1_gej_clear(&ctx->initial);
ctx->prec = NULL;
@@ -132,9 +133,9 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25
/* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */
secp256k1_scalar_add(&gnb, gn, &ctx->blind);
add.infinity = 0;
- for (j = 0; j < 64; j++) {
- bits = secp256k1_scalar_get_bits(&gnb, j * 4, 4);
- for (i = 0; i < 16; i++) {
+ for (j = 0; j < ECMULT_GEN_PREC_N; j++) {
+ bits = secp256k1_scalar_get_bits(&gnb, j * ECMULT_GEN_PREC_B, ECMULT_GEN_PREC_B);
+ for (i = 0; i < ECMULT_GEN_PREC_G; i++) {
/** This uses a conditional move to avoid any secret data in array indexes.
* _Any_ use of secret indexes has been demonstrated to result in timing
* sidechannels, even when the cache-line access patterns are uniform.
@@ -162,7 +163,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
secp256k1_fe s;
unsigned char nonce32[32];
secp256k1_rfc6979_hmac_sha256 rng;
- int retry;
+ int overflow;
unsigned char keydata[64] = {0};
if (seed32 == NULL) {
/* When seed is NULL, reset the initial point and blinding value. */
@@ -182,21 +183,18 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
}
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32);
memset(keydata, 0, sizeof(keydata));
- /* Retry for out of range results to achieve uniformity. */
- do {
- secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
- retry = !secp256k1_fe_set_b32(&s, nonce32);
- retry |= secp256k1_fe_is_zero(&s);
- } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */
+ /* Accept unobservably small non-uniformity. */
+ secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
+ overflow = !secp256k1_fe_set_b32(&s, nonce32);
+ overflow |= secp256k1_fe_is_zero(&s);
+ secp256k1_fe_cmov(&s, &secp256k1_fe_one, overflow);
/* Randomize the projection to defend against multiplier sidechannels. */
secp256k1_gej_rescale(&ctx->initial, &s);
secp256k1_fe_clear(&s);
- do {
- secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
- secp256k1_scalar_set_b32(&b, nonce32, &retry);
- /* A blinding value of 0 works, but would undermine the projection hardening. */
- retry |= secp256k1_scalar_is_zero(&b);
- } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */
+ secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
+ secp256k1_scalar_set_b32(&b, nonce32, NULL);
+ /* A blinding value of 0 works, but would undermine the projection hardening. */
+ secp256k1_scalar_cmov(&b, &secp256k1_scalar_one, secp256k1_scalar_is_zero(&b));
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
memset(nonce32, 0, 32);
secp256k1_ecmult_gen(ctx, &gb, &b);
diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h
index 1986914a4f..f03fa9469d 100644
--- a/src/secp256k1/src/ecmult_impl.h
+++ b/src/secp256k1/src/ecmult_impl.h
@@ -10,6 +10,7 @@
#include <string.h>
#include <stdint.h>
+#include "util.h"
#include "group.h"
#include "scalar.h"
#include "ecmult.h"
@@ -30,16 +31,32 @@
# endif
#else
/* optimal for 128-bit and 256-bit exponents. */
-#define WINDOW_A 5
-/** larger numbers may result in slightly better performance, at the cost of
- exponentially larger precomputed tables. */
-#ifdef USE_ENDOMORPHISM
-/** Two tables for window size 15: 1.375 MiB. */
-#define WINDOW_G 15
-#else
-/** One table for window size 16: 1.375 MiB. */
-#define WINDOW_G 16
+# define WINDOW_A 5
+/** Larger values for ECMULT_WINDOW_SIZE result in possibly better
+ * performance at the cost of an exponentially larger precomputed
+ * table. The exact table size is
+ * (1 << (WINDOW_G - 2)) * sizeof(secp256k1_ge_storage) bytes,
+ * where sizeof(secp256k1_ge_storage) is typically 64 bytes but can
+ * be larger due to platform-specific padding and alignment.
+ * If the endomorphism optimization is enabled (USE_ENDOMORMPHSIM)
+ * two tables of this size are used instead of only one.
+ */
+# define WINDOW_G ECMULT_WINDOW_SIZE
#endif
+
+/* Noone will ever need more than a window size of 24. The code might
+ * be correct for larger values of ECMULT_WINDOW_SIZE but this is not
+ * not tested.
+ *
+ * The following limitations are known, and there are probably more:
+ * If WINDOW_G > 27 and size_t has 32 bits, then the code is incorrect
+ * because the size of the memory object that we allocate (in bytes)
+ * will not fit in a size_t.
+ * If WINDOW_G > 31 and int has 32 bits, then the code is incorrect
+ * because certain expressions will overflow.
+ */
+#if ECMULT_WINDOW_SIZE < 2 || ECMULT_WINDOW_SIZE > 24
+# error Set ECMULT_WINDOW_SIZE to an integer in range [2..24].
#endif
#ifdef USE_ENDOMORPHISM
@@ -121,7 +138,7 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, sec
* It only operates on tables sized for WINDOW_A wnaf multiples.
* - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its
* resulting point set to actually affine points, and stores those in pre.
- * It operates on tables of any size, but uses heap-allocated temporaries.
+ * It operates on tables of any size.
*
* To compute a*P + b*G, we compute a table for P using the first function,
* and for G using the second (which requires an inverse, but it only needs to
@@ -294,6 +311,13 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(const int n, secp25
} \
} while(0)
+static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE =
+ ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G))
+#ifdef USE_ENDOMORPHISM
+ + ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G))
+#endif
+ ;
+
static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) {
ctx->pre_g = NULL;
#ifdef USE_ENDOMORPHISM
@@ -301,8 +325,10 @@ static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) {
#endif
}
-static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb) {
+static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc) {
secp256k1_gej gj;
+ void* const base = *prealloc;
+ size_t const prealloc_size = SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
if (ctx->pre_g != NULL) {
return;
@@ -311,7 +337,12 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
/* get the generator */
secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g);
- ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G));
+ {
+ size_t size = sizeof((*ctx->pre_g)[0]) * ((size_t)ECMULT_TABLE_SIZE(WINDOW_G));
+ /* check for overflow */
+ VERIFY_CHECK(size / sizeof((*ctx->pre_g)[0]) == ((size_t)ECMULT_TABLE_SIZE(WINDOW_G)));
+ ctx->pre_g = (secp256k1_ge_storage (*)[])manual_alloc(prealloc, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G), base, prealloc_size);
+ }
/* precompute the tables with odd multiples */
secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj);
@@ -321,7 +352,10 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
secp256k1_gej g_128j;
int i;
- ctx->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G));
+ size_t size = sizeof((*ctx->pre_g_128)[0]) * ((size_t) ECMULT_TABLE_SIZE(WINDOW_G));
+ /* check for overflow */
+ VERIFY_CHECK(size / sizeof((*ctx->pre_g_128)[0]) == ((size_t)ECMULT_TABLE_SIZE(WINDOW_G)));
+ ctx->pre_g_128 = (secp256k1_ge_storage (*)[])manual_alloc(prealloc, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G), base, prealloc_size);
/* calculate 2^128*generator */
g_128j = gj;
@@ -333,22 +367,14 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
#endif
}
-static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst,
- const secp256k1_ecmult_context *src, const secp256k1_callback *cb) {
- if (src->pre_g == NULL) {
- dst->pre_g = NULL;
- } else {
- size_t size = sizeof((*dst->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G);
- dst->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, size);
- memcpy(dst->pre_g, src->pre_g, size);
+static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src) {
+ if (src->pre_g != NULL) {
+ /* We cast to void* first to suppress a -Wcast-align warning. */
+ dst->pre_g = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g) - (unsigned char*)src));
}
#ifdef USE_ENDOMORPHISM
- if (src->pre_g_128 == NULL) {
- dst->pre_g_128 = NULL;
- } else {
- size_t size = sizeof((*dst->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G);
- dst->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, size);
- memcpy(dst->pre_g_128, src->pre_g_128, size);
+ if (src->pre_g_128 != NULL) {
+ dst->pre_g_128 = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g_128) - (unsigned char*)src));
}
#endif
}
@@ -358,10 +384,6 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx
}
static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) {
- free(ctx->pre_g);
-#ifdef USE_ENDOMORPHISM
- free(ctx->pre_g_128);
-#endif
secp256k1_ecmult_context_init(ctx);
}
@@ -373,7 +395,7 @@ static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) {
* than the number of bits in the (absolute value) of the input.
*/
static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, int w) {
- secp256k1_scalar s = *a;
+ secp256k1_scalar s;
int last_set_bit = -1;
int bit = 0;
int sign = 1;
@@ -386,6 +408,7 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a,
memset(wnaf, 0, len * sizeof(wnaf[0]));
+ s = *a;
if (secp256k1_scalar_get_bits(&s, 255, 1)) {
secp256k1_scalar_negate(&s, &s);
sign = -1;
@@ -418,7 +441,7 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a,
CHECK(carry == 0);
while (bit < 256) {
CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0);
- }
+ }
#endif
return last_set_bit + 1;
}
@@ -626,52 +649,55 @@ static size_t secp256k1_strauss_scratch_size(size_t n_points) {
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) {
+static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, 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;
+ const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
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));
+ points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej));
+ scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
+ state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
+ state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, 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 = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, 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));
+ state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, 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));
+ state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
+
+ if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL) {
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
+ return 0;
+ }
for (i = 0; i < n_points; i++) {
secp256k1_ge point;
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) {
- secp256k1_scratch_deallocate_frame(scratch);
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
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);
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
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 int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, 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(error_callback, 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);
+static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
+ return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
}
/** Convert a number to WNAF notation.
@@ -963,7 +989,8 @@ static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_windo
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) {
+static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_callback, 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) {
+ const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
/* 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. */
@@ -988,15 +1015,21 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
}
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)) {
+ points = (secp256k1_ge *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*points));
+ scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*scalars));
+ state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(error_callback, scratch, sizeof(*state_space));
+ if (points == NULL || scalars == NULL || state_space == NULL) {
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
+ return 0;
+ }
+
+ state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*state_space->ps));
+ state_space->wnaf_na = (int *) secp256k1_scratch_alloc(error_callback, scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
+ buckets = (secp256k1_gej *) secp256k1_scratch_alloc(error_callback, scratch, (1<<bucket_window) * sizeof(*buckets));
+ if (state_space->ps == NULL || state_space->wnaf_na == NULL || buckets == NULL) {
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
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;
@@ -1010,7 +1043,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
while (point_idx < n_points) {
if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) {
- secp256k1_scratch_deallocate_frame(scratch);
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
idx++;
@@ -1034,13 +1067,13 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
for(i = 0; i < 1<<bucket_window; i++) {
secp256k1_gej_clear(&buckets[i]);
}
- secp256k1_scratch_deallocate_frame(scratch);
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
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);
+static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_callback* error_callback, 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(error_callback, actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
}
/**
@@ -1048,8 +1081,8 @@ static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_ecmult_contex
* 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);
+static size_t secp256k1_pippenger_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
+ size_t max_alloc = secp256k1_scratch_max_allocation(error_callback, scratch, PIPPENGER_SCRATCH_OBJECTS);
int bucket_window;
size_t res = 0;
@@ -1131,11 +1164,11 @@ static int secp256k1_ecmult_multi_batch_size_helper(size_t *n_batches, size_t *n
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) {
+typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_callback* error_callback, 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_callback* error_callback, 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);
+ int (*f)(const secp256k1_callback* error_callback, 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;
@@ -1152,16 +1185,18 @@ static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp2
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;
+ /* Compute the batch sizes for Pippenger's algorithm given a scratch space. If it's greater than
+ * a threshold use Pippenger's algorithm. Otherwise use Strauss' algorithm.
+ * As a first step check if there's enough space for Pippenger's algo (which requires less space
+ * than Strauss' algo) and if not, use the simple algorithm. */
+ if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_pippenger_max_points(error_callback, scratch), n)) {
+ return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
}
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;
+ if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_strauss_max_points(error_callback, scratch), n)) {
+ return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
}
f = secp256k1_ecmult_strauss_batch;
}
@@ -1169,7 +1204,7 @@ static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp2
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)) {
+ if (!f(error_callback, ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) {
return 0;
}
secp256k1_gej_add_var(r, r, &tmp, NULL);
diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h
index bb6692ad57..7993a1f11e 100644
--- a/src/secp256k1/src/field.h
+++ b/src/secp256k1/src/field.h
@@ -32,10 +32,12 @@
#include "util.h"
-/** Normalize a field element. */
+/** Normalize a field element. This brings the field element to a canonical representation, reduces
+ * its magnitude to 1, and reduces it modulo field size `p`.
+ */
static void secp256k1_fe_normalize(secp256k1_fe *r);
-/** Weakly normalize a field element: reduce it magnitude to 1, but don't fully normalize. */
+/** Weakly normalize a field element: reduce its magnitude to 1, but don't fully normalize. */
static void secp256k1_fe_normalize_weak(secp256k1_fe *r);
/** Normalize a field element, without constant-time guarantee. */
@@ -123,10 +125,10 @@ static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe
/** Convert a field element back from the storage type. */
static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a);
-/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */
+/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/
static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag);
-/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */
+/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/
static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag);
#endif /* SECP256K1_FIELD_H */
diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h
index 4ae4fdcec8..651500ee8e 100644
--- a/src/secp256k1/src/field_10x26_impl.h
+++ b/src/secp256k1/src/field_10x26_impl.h
@@ -320,6 +320,7 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
}
static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
+ int ret;
r->n[0] = (uint32_t)a[31] | ((uint32_t)a[30] << 8) | ((uint32_t)a[29] << 16) | ((uint32_t)(a[28] & 0x3) << 24);
r->n[1] = (uint32_t)((a[28] >> 2) & 0x3f) | ((uint32_t)a[27] << 6) | ((uint32_t)a[26] << 14) | ((uint32_t)(a[25] & 0xf) << 22);
r->n[2] = (uint32_t)((a[25] >> 4) & 0xf) | ((uint32_t)a[24] << 4) | ((uint32_t)a[23] << 12) | ((uint32_t)(a[22] & 0x3f) << 20);
@@ -331,15 +332,17 @@ static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
r->n[8] = (uint32_t)a[5] | ((uint32_t)a[4] << 8) | ((uint32_t)a[3] << 16) | ((uint32_t)(a[2] & 0x3) << 24);
r->n[9] = (uint32_t)((a[2] >> 2) & 0x3f) | ((uint32_t)a[1] << 6) | ((uint32_t)a[0] << 14);
- if (r->n[9] == 0x3FFFFFUL && (r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL && (r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL) {
- return 0;
- }
+ ret = !((r->n[9] == 0x3FFFFFUL) & ((r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL) & ((r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL));
#ifdef VERIFY
r->magnitude = 1;
- r->normalized = 1;
- secp256k1_fe_verify(r);
+ if (ret) {
+ r->normalized = 1;
+ secp256k1_fe_verify(r);
+ } else {
+ r->normalized = 0;
+ }
#endif
- return 1;
+ return ret;
}
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
@@ -1094,6 +1097,7 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) {
static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) {
uint32_t mask0, mask1;
+ VG_CHECK_VERIFY(r->n, sizeof(r->n));
mask0 = flag + ~((uint32_t)0);
mask1 = ~mask0;
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
@@ -1107,15 +1111,16 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_
r->n[8] = (r->n[8] & mask0) | (a->n[8] & mask1);
r->n[9] = (r->n[9] & mask0) | (a->n[9] & mask1);
#ifdef VERIFY
- if (a->magnitude > r->magnitude) {
+ if (flag) {
r->magnitude = a->magnitude;
+ r->normalized = a->normalized;
}
- r->normalized &= a->normalized;
#endif
}
static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) {
uint32_t mask0, mask1;
+ VG_CHECK_VERIFY(r->n, sizeof(r->n));
mask0 = flag + ~((uint32_t)0);
mask1 = ~mask0;
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h
index f4263320d5..71a38f915b 100644
--- a/src/secp256k1/src/field_5x52_impl.h
+++ b/src/secp256k1/src/field_5x52_impl.h
@@ -283,6 +283,7 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
}
static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
+ int ret;
r->n[0] = (uint64_t)a[31]
| ((uint64_t)a[30] << 8)
| ((uint64_t)a[29] << 16)
@@ -317,15 +318,17 @@ static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
| ((uint64_t)a[2] << 24)
| ((uint64_t)a[1] << 32)
| ((uint64_t)a[0] << 40);
- if (r->n[4] == 0x0FFFFFFFFFFFFULL && (r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL && r->n[0] >= 0xFFFFEFFFFFC2FULL) {
- return 0;
- }
+ ret = !((r->n[4] == 0x0FFFFFFFFFFFFULL) & ((r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL) & (r->n[0] >= 0xFFFFEFFFFFC2FULL));
#ifdef VERIFY
r->magnitude = 1;
- r->normalized = 1;
- secp256k1_fe_verify(r);
+ if (ret) {
+ r->normalized = 1;
+ secp256k1_fe_verify(r);
+ } else {
+ r->normalized = 0;
+ }
#endif
- return 1;
+ return ret;
}
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
@@ -446,6 +449,7 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) {
static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) {
uint64_t mask0, mask1;
+ VG_CHECK_VERIFY(r->n, sizeof(r->n));
mask0 = flag + ~((uint64_t)0);
mask1 = ~mask0;
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
@@ -454,15 +458,16 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_
r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1);
r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1);
#ifdef VERIFY
- if (a->magnitude > r->magnitude) {
+ if (flag) {
r->magnitude = a->magnitude;
+ r->normalized = a->normalized;
}
- r->normalized &= a->normalized;
#endif
}
static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) {
uint64_t mask0, mask1;
+ VG_CHECK_VERIFY(r->n, sizeof(r->n));
mask0 = flag + ~((uint64_t)0);
mask1 = ~mask0;
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h
index 6070caccfe..485921a60e 100644
--- a/src/secp256k1/src/field_impl.h
+++ b/src/secp256k1/src/field_impl.h
@@ -315,4 +315,6 @@ static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) {
#endif
}
+static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
+
#endif /* SECP256K1_FIELD_IMPL_H */
diff --git a/src/secp256k1/src/gen_context.c b/src/secp256k1/src/gen_context.c
index 87d296ebf0..539f574bfd 100644
--- a/src/secp256k1/src/gen_context.c
+++ b/src/secp256k1/src/gen_context.c
@@ -4,10 +4,16 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
+// Autotools creates libsecp256k1-config.h, of which ECMULT_GEN_PREC_BITS is needed.
+// ifndef guard so downstream users can define their own if they do not use autotools.
+#if !defined(ECMULT_GEN_PREC_BITS)
+#include "libsecp256k1-config.h"
+#endif
#define USE_BASIC_CONFIG 1
-
#include "basic-config.h"
+
#include "include/secp256k1.h"
+#include "util.h"
#include "field_impl.h"
#include "scalar_impl.h"
#include "group_impl.h"
@@ -26,6 +32,7 @@ static const secp256k1_callback default_error_callback = {
int main(int argc, char **argv) {
secp256k1_ecmult_gen_context ctx;
+ void *prealloc, *base;
int inner;
int outer;
FILE* fp;
@@ -38,26 +45,31 @@ int main(int argc, char **argv) {
fprintf(stderr, "Could not open src/ecmult_static_context.h for writing!\n");
return -1;
}
-
+
fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\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");
+ fprintf(fp, "#if ECMULT_GEN_PREC_N != %d || ECMULT_GEN_PREC_G != %d\n", ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G);
+ fprintf(fp, " #error configuration mismatch, invalid ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G. Try deleting ecmult_static_context.h before the build.\n");
+ fprintf(fp, "#endif\n");
+ fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G] = {\n");
+ base = checked_malloc(&default_error_callback, SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE);
+ prealloc = base;
secp256k1_ecmult_gen_context_init(&ctx);
- secp256k1_ecmult_gen_context_build(&ctx, &default_error_callback);
- for(outer = 0; outer != 64; outer++) {
+ secp256k1_ecmult_gen_context_build(&ctx, &prealloc);
+ for(outer = 0; outer != ECMULT_GEN_PREC_N; outer++) {
fprintf(fp,"{\n");
- for(inner = 0; inner != 16; inner++) {
+ for(inner = 0; inner != ECMULT_GEN_PREC_G; inner++) {
fprintf(fp," SC(%uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu)", SECP256K1_GE_STORAGE_CONST_GET((*ctx.prec)[outer][inner]));
- if (inner != 15) {
+ if (inner != ECMULT_GEN_PREC_G - 1) {
fprintf(fp,",\n");
} else {
fprintf(fp,"\n");
}
}
- if (outer != 63) {
+ if (outer != ECMULT_GEN_PREC_N - 1) {
fprintf(fp,"},\n");
} else {
fprintf(fp,"}\n");
@@ -65,10 +77,11 @@ int main(int argc, char **argv) {
}
fprintf(fp,"};\n");
secp256k1_ecmult_gen_context_clear(&ctx);
-
+ free(base);
+
fprintf(fp, "#undef SC\n");
fprintf(fp, "#endif\n");
fclose(fp);
-
+
return 0;
}
diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h
index 8e122ab429..863644f0f0 100644
--- a/src/secp256k1/src/group.h
+++ b/src/secp256k1/src/group.h
@@ -95,14 +95,13 @@ static int secp256k1_gej_is_infinity(const secp256k1_gej *a);
/** Check whether a group element's y coordinate is a quadratic residue. */
static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a);
-/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0).
- * a may not be zero. Constant time. */
-static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);
+/** Set r equal to the double of a, a cannot be infinity. Constant time. */
+static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a);
-/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). */
+/** Set r equal to the double of a. If rzr is not-NULL this sets *rzr such that r->z == a->z * *rzr (where infinity means an implicit z = 0). */
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);
-/** Set r equal to the sum of a and b. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */
+/** Set r equal to the sum of a and b. If rzr is non-NULL this sets *rzr such that r->z == a->z * *rzr (a cannot be infinity in that case). */
static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr);
/** Set r equal to the sum of a and b (with b given in affine coordinates, and not infinity). */
@@ -110,7 +109,7 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const
/** Set r equal to the sum of a and b (with b given in affine coordinates). This is more efficient
than secp256k1_gej_add_var. It is identical to secp256k1_gej_add_ge but without constant-time
- guarantee, and b is allowed to be infinity. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */
+ guarantee, and b is allowed to be infinity. If rzr is non-NULL this sets *rzr such that r->z == a->z * *rzr (a cannot be infinity in that case). */
static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr);
/** Set r equal to the sum of a and b (with the inverse of b's Z coordinate passed as bzinv). */
@@ -133,7 +132,7 @@ static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge
/** Convert a group element back from the storage type. */
static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a);
-/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */
+/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/
static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag);
/** Rescale a jacobian point by b which must be non-zero. Constant-time. */
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index 9b93c39e92..43b039becf 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -303,7 +303,7 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) {
return secp256k1_fe_equal_var(&y2, &x3);
}
-static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
+static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a) {
/* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate.
*
* Note that there is an implementation described at
@@ -312,29 +312,9 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
* mainly because it requires more normalizations.
*/
secp256k1_fe t1,t2,t3,t4;
- /** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity,
- * Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have
- * y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p.
- *
- * Having said this, if this function receives a point on a sextic twist, e.g. by
- * a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6,
- * since -6 does have a cube root mod p. For this point, this function will not set
- * the infinity flag even though the point doubles to infinity, and the result
- * point will be gibberish (z = 0 but infinity = 0).
- */
- r->infinity = a->infinity;
- if (r->infinity) {
- if (rzr != NULL) {
- secp256k1_fe_set_int(rzr, 1);
- }
- return;
- }
- if (rzr != NULL) {
- *rzr = a->y;
- secp256k1_fe_normalize_weak(rzr);
- secp256k1_fe_mul_int(rzr, 2);
- }
+ VERIFY_CHECK(!secp256k1_gej_is_infinity(a));
+ r->infinity = 0;
secp256k1_fe_mul(&r->z, &a->z, &a->y);
secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */
@@ -358,9 +338,32 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */
}
-static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
- VERIFY_CHECK(!secp256k1_gej_is_infinity(a));
- secp256k1_gej_double_var(r, a, rzr);
+static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
+ /** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity,
+ * Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have
+ * y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p.
+ *
+ * Having said this, if this function receives a point on a sextic twist, e.g. by
+ * a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6,
+ * since -6 does have a cube root mod p. For this point, this function will not set
+ * the infinity flag even though the point doubles to infinity, and the result
+ * point will be gibberish (z = 0 but infinity = 0).
+ */
+ if (a->infinity) {
+ r->infinity = 1;
+ if (rzr != NULL) {
+ secp256k1_fe_set_int(rzr, 1);
+ }
+ return;
+ }
+
+ if (rzr != NULL) {
+ *rzr = a->y;
+ secp256k1_fe_normalize_weak(rzr);
+ secp256k1_fe_mul_int(rzr, 2);
+ }
+
+ secp256k1_gej_double_nonzero(r, a);
}
static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) {
diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h
index 009f26beba..782f97216c 100644
--- a/src/secp256k1/src/hash_impl.h
+++ b/src/secp256k1/src/hash_impl.h
@@ -131,7 +131,8 @@ static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) {
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) {
+ VERIFY_CHECK(hash->bytes >= len);
+ while (len >= 64 - bufsize) {
/* Fill the buffer, and process it. */
size_t chunk_len = 64 - bufsize;
memcpy(((unsigned char*)hash->buf) + bufsize, data, chunk_len);
diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java
deleted file mode 100644
index 1c67802fba..0000000000
--- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- * Copyright 2014-2016 the libsecp256k1 contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.bitcoin;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-import java.math.BigInteger;
-import com.google.common.base.Preconditions;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import static org.bitcoin.NativeSecp256k1Util.*;
-
-/**
- * <p>This class holds native methods to handle ECDSA verification.</p>
- *
- * <p>You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1</p>
- *
- * <p>To build secp256k1 for use with bitcoinj, run
- * `./configure --enable-jni --enable-experimental --enable-module-ecdh`
- * and `make` then copy `.libs/libsecp256k1.so` to your system library path
- * or point the JVM to the folder containing it with -Djava.library.path
- * </p>
- */
-public class NativeSecp256k1 {
-
- private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
- private static final Lock r = rwl.readLock();
- private static final Lock w = rwl.writeLock();
- private static ThreadLocal<ByteBuffer> nativeECDSABuffer = new ThreadLocal<ByteBuffer>();
- /**
- * Verifies the given secp256k1 signature in native code.
- * Calling when enabled == false is undefined (probably library not loaded)
- *
- * @param data The data which was signed, must be exactly 32 bytes
- * @param signature The signature
- * @param pub The public key which did the signing
- */
- public static boolean verify(byte[] data, byte[] signature, byte[] pub) throws AssertFailException{
- Preconditions.checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520);
-
- ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null || byteBuff.capacity() < 520) {
- byteBuff = ByteBuffer.allocateDirect(520);
- byteBuff.order(ByteOrder.nativeOrder());
- nativeECDSABuffer.set(byteBuff);
- }
- byteBuff.rewind();
- byteBuff.put(data);
- byteBuff.put(signature);
- byteBuff.put(pub);
-
- byte[][] retByteArray;
-
- r.lock();
- try {
- return secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(), signature.length, pub.length) == 1;
- } finally {
- r.unlock();
- }
- }
-
- /**
- * libsecp256k1 Create an ECDSA signature.
- *
- * @param data Message hash, 32 bytes
- * @param key Secret key, 32 bytes
- *
- * Return values
- * @param sig byte array of signature
- */
- public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{
- Preconditions.checkArgument(data.length == 32 && sec.length <= 32);
-
- ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null || byteBuff.capacity() < 32 + 32) {
- byteBuff = ByteBuffer.allocateDirect(32 + 32);
- byteBuff.order(ByteOrder.nativeOrder());
- nativeECDSABuffer.set(byteBuff);
- }
- byteBuff.rewind();
- byteBuff.put(data);
- byteBuff.put(sec);
-
- byte[][] retByteArray;
-
- r.lock();
- try {
- retByteArray = secp256k1_ecdsa_sign(byteBuff, Secp256k1Context.getContext());
- } finally {
- r.unlock();
- }
-
- byte[] sigArr = retByteArray[0];
- int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
- int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
-
- assertEquals(sigArr.length, sigLen, "Got bad signature length.");
-
- return retVal == 0 ? new byte[0] : sigArr;
- }
-
- /**
- * libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid
- *
- * @param seckey ECDSA Secret key, 32 bytes
- */
- public static boolean secKeyVerify(byte[] seckey) {
- Preconditions.checkArgument(seckey.length == 32);
-
- ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null || byteBuff.capacity() < seckey.length) {
- byteBuff = ByteBuffer.allocateDirect(seckey.length);
- byteBuff.order(ByteOrder.nativeOrder());
- nativeECDSABuffer.set(byteBuff);
- }
- byteBuff.rewind();
- byteBuff.put(seckey);
-
- r.lock();
- try {
- return secp256k1_ec_seckey_verify(byteBuff,Secp256k1Context.getContext()) == 1;
- } finally {
- r.unlock();
- }
- }
-
-
- /**
- * libsecp256k1 Compute Pubkey - computes public key from secret key
- *
- * @param seckey ECDSA Secret key, 32 bytes
- *
- * Return values
- * @param pubkey ECDSA Public key, 33 or 65 bytes
- */
- //TODO add a 'compressed' arg
- public static byte[] computePubkey(byte[] seckey) throws AssertFailException{
- Preconditions.checkArgument(seckey.length == 32);
-
- ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null || byteBuff.capacity() < seckey.length) {
- byteBuff = ByteBuffer.allocateDirect(seckey.length);
- byteBuff.order(ByteOrder.nativeOrder());
- nativeECDSABuffer.set(byteBuff);
- }
- byteBuff.rewind();
- byteBuff.put(seckey);
-
- byte[][] retByteArray;
-
- r.lock();
- try {
- retByteArray = secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext());
- } finally {
- r.unlock();
- }
-
- byte[] pubArr = retByteArray[0];
- int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
- int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
-
- assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
-
- return retVal == 0 ? new byte[0]: pubArr;
- }
-
- /**
- * libsecp256k1 Cleanup - This destroys the secp256k1 context object
- * This should be called at the end of the program for proper cleanup of the context.
- */
- public static synchronized void cleanup() {
- w.lock();
- try {
- secp256k1_destroy_context(Secp256k1Context.getContext());
- } finally {
- w.unlock();
- }
- }
-
- public static long cloneContext() {
- r.lock();
- try {
- return secp256k1_ctx_clone(Secp256k1Context.getContext());
- } finally { r.unlock(); }
- }
-
- /**
- * libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it
- *
- * @param tweak some bytes to tweak with
- * @param seckey 32-byte seckey
- */
- public static byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{
- Preconditions.checkArgument(privkey.length == 32);
-
- ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) {
- byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length);
- byteBuff.order(ByteOrder.nativeOrder());
- nativeECDSABuffer.set(byteBuff);
- }
- byteBuff.rewind();
- byteBuff.put(privkey);
- byteBuff.put(tweak);
-
- byte[][] retByteArray;
- r.lock();
- try {
- retByteArray = secp256k1_privkey_tweak_mul(byteBuff,Secp256k1Context.getContext());
- } finally {
- r.unlock();
- }
-
- byte[] privArr = retByteArray[0];
-
- int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
- int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
-
- assertEquals(privArr.length, privLen, "Got bad pubkey length.");
-
- assertEquals(retVal, 1, "Failed return value check.");
-
- return privArr;
- }
-
- /**
- * libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it
- *
- * @param tweak some bytes to tweak with
- * @param seckey 32-byte seckey
- */
- public static byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{
- Preconditions.checkArgument(privkey.length == 32);
-
- ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) {
- byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length);
- byteBuff.order(ByteOrder.nativeOrder());
- nativeECDSABuffer.set(byteBuff);
- }
- byteBuff.rewind();
- byteBuff.put(privkey);
- byteBuff.put(tweak);
-
- byte[][] retByteArray;
- r.lock();
- try {
- retByteArray = secp256k1_privkey_tweak_add(byteBuff,Secp256k1Context.getContext());
- } finally {
- r.unlock();
- }
-
- byte[] privArr = retByteArray[0];
-
- int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
- int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
-
- assertEquals(privArr.length, privLen, "Got bad pubkey length.");
-
- assertEquals(retVal, 1, "Failed return value check.");
-
- return privArr;
- }
-
- /**
- * libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it
- *
- * @param tweak some bytes to tweak with
- * @param pubkey 32-byte seckey
- */
- public static byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak) throws AssertFailException{
- Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65);
-
- ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) {
- byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length);
- byteBuff.order(ByteOrder.nativeOrder());
- nativeECDSABuffer.set(byteBuff);
- }
- byteBuff.rewind();
- byteBuff.put(pubkey);
- byteBuff.put(tweak);
-
- byte[][] retByteArray;
- r.lock();
- try {
- retByteArray = secp256k1_pubkey_tweak_add(byteBuff,Secp256k1Context.getContext(), pubkey.length);
- } finally {
- r.unlock();
- }
-
- byte[] pubArr = retByteArray[0];
-
- int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
- int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
-
- assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
-
- assertEquals(retVal, 1, "Failed return value check.");
-
- return pubArr;
- }
-
- /**
- * libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it
- *
- * @param tweak some bytes to tweak with
- * @param pubkey 32-byte seckey
- */
- public static byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak) throws AssertFailException{
- Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65);
-
- ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) {
- byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length);
- byteBuff.order(ByteOrder.nativeOrder());
- nativeECDSABuffer.set(byteBuff);
- }
- byteBuff.rewind();
- byteBuff.put(pubkey);
- byteBuff.put(tweak);
-
- byte[][] retByteArray;
- r.lock();
- try {
- retByteArray = secp256k1_pubkey_tweak_mul(byteBuff,Secp256k1Context.getContext(), pubkey.length);
- } finally {
- r.unlock();
- }
-
- byte[] pubArr = retByteArray[0];
-
- int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
- int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
-
- assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
-
- assertEquals(retVal, 1, "Failed return value check.");
-
- return pubArr;
- }
-
- /**
- * libsecp256k1 create ECDH secret - constant time ECDH calculation
- *
- * @param seckey byte array of secret key used in exponentiaion
- * @param pubkey byte array of public key used in exponentiaion
- */
- public static byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{
- Preconditions.checkArgument(seckey.length <= 32 && pubkey.length <= 65);
-
- ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) {
- byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length);
- byteBuff.order(ByteOrder.nativeOrder());
- nativeECDSABuffer.set(byteBuff);
- }
- byteBuff.rewind();
- byteBuff.put(seckey);
- byteBuff.put(pubkey);
-
- byte[][] retByteArray;
- r.lock();
- try {
- retByteArray = secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.length);
- } finally {
- r.unlock();
- }
-
- byte[] resArr = retByteArray[0];
- int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
-
- assertEquals(resArr.length, 32, "Got bad result length.");
- assertEquals(retVal, 1, "Failed return value check.");
-
- return resArr;
- }
-
- /**
- * libsecp256k1 randomize - updates the context randomization
- *
- * @param seed 32-byte random seed
- */
- public static synchronized boolean randomize(byte[] seed) throws AssertFailException{
- Preconditions.checkArgument(seed.length == 32 || seed == null);
-
- ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null || byteBuff.capacity() < seed.length) {
- byteBuff = ByteBuffer.allocateDirect(seed.length);
- byteBuff.order(ByteOrder.nativeOrder());
- nativeECDSABuffer.set(byteBuff);
- }
- byteBuff.rewind();
- byteBuff.put(seed);
-
- w.lock();
- try {
- return secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1;
- } finally {
- w.unlock();
- }
- }
-
- private static native long secp256k1_ctx_clone(long context);
-
- private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context);
-
- private static native byte[][] secp256k1_privkey_tweak_add(ByteBuffer byteBuff, long context);
-
- private static native byte[][] secp256k1_privkey_tweak_mul(ByteBuffer byteBuff, long context);
-
- private static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen);
-
- private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen);
-
- private static native void secp256k1_destroy_context(long context);
-
- private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen);
-
- private static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context);
-
- private static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context);
-
- private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context);
-
- private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen);
-
- private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen);
-
-}
diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
deleted file mode 100644
index d766a1029c..0000000000
--- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
+++ /dev/null
@@ -1,226 +0,0 @@
-package org.bitcoin;
-
-import com.google.common.io.BaseEncoding;
-import java.util.Arrays;
-import java.math.BigInteger;
-import javax.xml.bind.DatatypeConverter;
-import static org.bitcoin.NativeSecp256k1Util.*;
-
-/**
- * This class holds test cases defined for testing this library.
- */
-public class NativeSecp256k1Test {
-
- //TODO improve comments/add more tests
- /**
- * This tests verify() for a valid signature
- */
- public static void testVerifyPos() throws AssertFailException{
- boolean result = false;
- byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
- byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase());
- byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
-
- result = NativeSecp256k1.verify( data, sig, pub);
- assertEquals( result, true , "testVerifyPos");
- }
-
- /**
- * This tests verify() for a non-valid signature
- */
- public static void testVerifyNeg() throws AssertFailException{
- boolean result = false;
- byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()); //sha256hash of "testing"
- byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase());
- byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
-
- result = NativeSecp256k1.verify( data, sig, pub);
- //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
- assertEquals( result, false , "testVerifyNeg");
- }
-
- /**
- * This tests secret key verify() for a valid secretkey
- */
- public static void testSecKeyVerifyPos() throws AssertFailException{
- boolean result = false;
- byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
-
- result = NativeSecp256k1.secKeyVerify( sec );
- //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
- assertEquals( result, true , "testSecKeyVerifyPos");
- }
-
- /**
- * This tests secret key verify() for an invalid secretkey
- */
- public static void testSecKeyVerifyNeg() throws AssertFailException{
- boolean result = false;
- byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
-
- result = NativeSecp256k1.secKeyVerify( sec );
- //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
- assertEquals( result, false , "testSecKeyVerifyNeg");
- }
-
- /**
- * This tests public key create() for a valid secretkey
- */
- public static void testPubKeyCreatePos() throws AssertFailException{
- byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
-
- byte[] resultArr = NativeSecp256k1.computePubkey( sec);
- String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
- assertEquals( pubkeyString , "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6" , "testPubKeyCreatePos");
- }
-
- /**
- * This tests public key create() for a invalid secretkey
- */
- public static void testPubKeyCreateNeg() throws AssertFailException{
- byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
-
- byte[] resultArr = NativeSecp256k1.computePubkey( sec);
- String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
- assertEquals( pubkeyString, "" , "testPubKeyCreateNeg");
- }
-
- /**
- * This tests sign() for a valid secretkey
- */
- public static void testSignPos() throws AssertFailException{
-
- byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
- byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
-
- byte[] resultArr = NativeSecp256k1.sign(data, sec);
- String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
- assertEquals( sigString, "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos");
- }
-
- /**
- * This tests sign() for a invalid secretkey
- */
- public static void testSignNeg() throws AssertFailException{
- byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
- byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
-
- byte[] resultArr = NativeSecp256k1.sign(data, sec);
- String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
- assertEquals( sigString, "" , "testSignNeg");
- }
-
- /**
- * This tests private key tweak-add
- */
- public static void testPrivKeyTweakAdd_1() throws AssertFailException {
- byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
- byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
-
- byte[] resultArr = NativeSecp256k1.privKeyTweakAdd( sec , data );
- String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
- assertEquals( sigString , "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3" , "testPrivKeyAdd_1");
- }
-
- /**
- * This tests private key tweak-mul
- */
- public static void testPrivKeyTweakMul_1() throws AssertFailException {
- byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
- byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
-
- byte[] resultArr = NativeSecp256k1.privKeyTweakMul( sec , data );
- String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
- assertEquals( sigString , "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC" , "testPrivKeyMul_1");
- }
-
- /**
- * This tests private key tweak-add uncompressed
- */
- public static void testPrivKeyTweakAdd_2() throws AssertFailException {
- byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
- byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
-
- byte[] resultArr = NativeSecp256k1.pubKeyTweakAdd( pub , data );
- String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
- assertEquals( sigString , "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF" , "testPrivKeyAdd_2");
- }
-
- /**
- * This tests private key tweak-mul uncompressed
- */
- public static void testPrivKeyTweakMul_2() throws AssertFailException {
- byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
- byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
-
- byte[] resultArr = NativeSecp256k1.pubKeyTweakMul( pub , data );
- String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
- assertEquals( sigString , "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589" , "testPrivKeyMul_2");
- }
-
- /**
- * This tests seed randomization
- */
- public static void testRandomize() throws AssertFailException {
- byte[] seed = BaseEncoding.base16().lowerCase().decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()); //sha256hash of "random"
- boolean result = NativeSecp256k1.randomize(seed);
- assertEquals( result, true, "testRandomize");
- }
-
- public static void testCreateECDHSecret() throws AssertFailException{
-
- byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
- byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
-
- byte[] resultArr = NativeSecp256k1.createECDHSecret(sec, pub);
- String ecdhString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
- assertEquals( ecdhString, "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043" , "testCreateECDHSecret");
- }
-
- public static void main(String[] args) throws AssertFailException{
-
-
- System.out.println("\n libsecp256k1 enabled: " + Secp256k1Context.isEnabled() + "\n");
-
- assertEquals( Secp256k1Context.isEnabled(), true, "isEnabled" );
-
- //Test verify() success/fail
- testVerifyPos();
- testVerifyNeg();
-
- //Test secKeyVerify() success/fail
- testSecKeyVerifyPos();
- testSecKeyVerifyNeg();
-
- //Test computePubkey() success/fail
- testPubKeyCreatePos();
- testPubKeyCreateNeg();
-
- //Test sign() success/fail
- testSignPos();
- testSignNeg();
-
- //Test privKeyTweakAdd() 1
- testPrivKeyTweakAdd_1();
-
- //Test privKeyTweakMul() 2
- testPrivKeyTweakMul_1();
-
- //Test privKeyTweakAdd() 3
- testPrivKeyTweakAdd_2();
-
- //Test privKeyTweakMul() 4
- testPrivKeyTweakMul_2();
-
- //Test randomize()
- testRandomize();
-
- //Test ECDH
- testCreateECDHSecret();
-
- NativeSecp256k1.cleanup();
-
- System.out.println(" All tests passed." );
-
- }
-}
diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java
deleted file mode 100644
index 04732ba044..0000000000
--- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2014-2016 the libsecp256k1 contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.bitcoin;
-
-public class NativeSecp256k1Util{
-
- public static void assertEquals( int val, int val2, String message ) throws AssertFailException{
- if( val != val2 )
- throw new AssertFailException("FAIL: " + message);
- }
-
- public static void assertEquals( boolean val, boolean val2, String message ) throws AssertFailException{
- if( val != val2 )
- throw new AssertFailException("FAIL: " + message);
- else
- System.out.println("PASS: " + message);
- }
-
- public static void assertEquals( String val, String val2, String message ) throws AssertFailException{
- if( !val.equals(val2) )
- throw new AssertFailException("FAIL: " + message);
- else
- System.out.println("PASS: " + message);
- }
-
- public static class AssertFailException extends Exception {
- public AssertFailException(String message) {
- super( message );
- }
- }
-}
diff --git a/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java b/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java
deleted file mode 100644
index 216c986a8b..0000000000
--- a/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2014-2016 the libsecp256k1 contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.bitcoin;
-
-/**
- * This class holds the context reference used in native methods
- * to handle ECDSA operations.
- */
-public class Secp256k1Context {
- private static final boolean enabled; //true if the library is loaded
- private static final long context; //ref to pointer to context obj
-
- static { //static initializer
- boolean isEnabled = true;
- long contextRef = -1;
- try {
- System.loadLibrary("secp256k1");
- contextRef = secp256k1_init_context();
- } catch (UnsatisfiedLinkError e) {
- System.out.println("UnsatisfiedLinkError: " + e.toString());
- isEnabled = false;
- }
- enabled = isEnabled;
- context = contextRef;
- }
-
- public static boolean isEnabled() {
- return enabled;
- }
-
- public static long getContext() {
- if(!enabled) return -1; //sanity check
- return context;
- }
-
- private static native long secp256k1_init_context();
-}
diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
deleted file mode 100644
index b50970b4f2..0000000000
--- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
+++ /dev/null
@@ -1,379 +0,0 @@
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include "org_bitcoin_NativeSecp256k1.h"
-#include "include/secp256k1.h"
-#include "include/secp256k1_ecdh.h"
-#include "include/secp256k1_recovery.h"
-
-
-SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
- (JNIEnv* env, jclass classObject, jlong ctx_l)
-{
- const secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
-
- jlong ctx_clone_l = (uintptr_t) secp256k1_context_clone(ctx);
-
- (void)classObject;(void)env;
-
- return ctx_clone_l;
-
-}
-
-SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
-{
- secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
-
- const unsigned char* seed = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
-
- (void)classObject;
-
- return secp256k1_context_randomize(ctx, seed);
-
-}
-
-SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context
- (JNIEnv* env, jclass classObject, jlong ctx_l)
-{
- secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
-
- secp256k1_context_destroy(ctx);
-
- (void)classObject;(void)env;
-}
-
-SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint siglen, jint publen)
-{
- secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
-
- unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
- const unsigned char* sigdata = { (unsigned char*) (data + 32) };
- const unsigned char* pubdata = { (unsigned char*) (data + siglen + 32) };
-
- secp256k1_ecdsa_signature sig;
- secp256k1_pubkey pubkey;
-
- int ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen);
-
- if( ret ) {
- ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen);
-
- if( ret ) {
- ret = secp256k1_ecdsa_verify(ctx, &sig, data, &pubkey);
- }
- }
-
- (void)classObject;
-
- return ret;
-}
-
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
-{
- secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
- unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
- unsigned char* secKey = (unsigned char*) (data + 32);
-
- jobjectArray retArray;
- jbyteArray sigArray, intsByteArray;
- unsigned char intsarray[2];
-
- secp256k1_ecdsa_signature sig[72];
-
- int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL);
-
- unsigned char outputSer[72];
- size_t outputLen = 72;
-
- if( ret ) {
- int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, sig ); (void)ret2;
- }
-
- intsarray[0] = outputLen;
- intsarray[1] = ret;
-
- retArray = (*env)->NewObjectArray(env, 2,
- (*env)->FindClass(env, "[B"),
- (*env)->NewByteArray(env, 1));
-
- sigArray = (*env)->NewByteArray(env, outputLen);
- (*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer);
- (*env)->SetObjectArrayElement(env, retArray, 0, sigArray);
-
- intsByteArray = (*env)->NewByteArray(env, 2);
- (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
- (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
-
- (void)classObject;
-
- return retArray;
-}
-
-SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
-{
- secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
- unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
-
- (void)classObject;
-
- return secp256k1_ec_seckey_verify(ctx, secKey);
-}
-
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
-{
- secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
- const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
-
- secp256k1_pubkey pubkey;
-
- jobjectArray retArray;
- jbyteArray pubkeyArray, intsByteArray;
- unsigned char intsarray[2];
-
- int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey);
-
- unsigned char outputSer[65];
- size_t outputLen = 65;
-
- if( ret ) {
- int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
- }
-
- intsarray[0] = outputLen;
- intsarray[1] = ret;
-
- retArray = (*env)->NewObjectArray(env, 2,
- (*env)->FindClass(env, "[B"),
- (*env)->NewByteArray(env, 1));
-
- pubkeyArray = (*env)->NewByteArray(env, outputLen);
- (*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer);
- (*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray);
-
- intsByteArray = (*env)->NewByteArray(env, 2);
- (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
- (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
-
- (void)classObject;
-
- return retArray;
-
-}
-
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
-{
- secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
- unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
- const unsigned char* tweak = (unsigned char*) (privkey + 32);
-
- jobjectArray retArray;
- jbyteArray privArray, intsByteArray;
- unsigned char intsarray[2];
-
- int privkeylen = 32;
-
- int ret = secp256k1_ec_privkey_tweak_add(ctx, privkey, tweak);
-
- intsarray[0] = privkeylen;
- intsarray[1] = ret;
-
- retArray = (*env)->NewObjectArray(env, 2,
- (*env)->FindClass(env, "[B"),
- (*env)->NewByteArray(env, 1));
-
- privArray = (*env)->NewByteArray(env, privkeylen);
- (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey);
- (*env)->SetObjectArrayElement(env, retArray, 0, privArray);
-
- intsByteArray = (*env)->NewByteArray(env, 2);
- (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
- (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
-
- (void)classObject;
-
- return retArray;
-}
-
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
-{
- secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
- unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
- const unsigned char* tweak = (unsigned char*) (privkey + 32);
-
- jobjectArray retArray;
- jbyteArray privArray, intsByteArray;
- unsigned char intsarray[2];
-
- int privkeylen = 32;
-
- int ret = secp256k1_ec_privkey_tweak_mul(ctx, privkey, tweak);
-
- intsarray[0] = privkeylen;
- intsarray[1] = ret;
-
- retArray = (*env)->NewObjectArray(env, 2,
- (*env)->FindClass(env, "[B"),
- (*env)->NewByteArray(env, 1));
-
- privArray = (*env)->NewByteArray(env, privkeylen);
- (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey);
- (*env)->SetObjectArrayElement(env, retArray, 0, privArray);
-
- intsByteArray = (*env)->NewByteArray(env, 2);
- (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
- (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
-
- (void)classObject;
-
- return retArray;
-}
-
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
-{
- secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
-/* secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (*env)->GetDirectBufferAddress(env, byteBufferObject);*/
- unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject);
- const unsigned char* tweak = (unsigned char*) (pkey + publen);
-
- jobjectArray retArray;
- jbyteArray pubArray, intsByteArray;
- unsigned char intsarray[2];
- unsigned char outputSer[65];
- size_t outputLen = 65;
-
- secp256k1_pubkey pubkey;
- int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
-
- if( ret ) {
- ret = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, tweak);
- }
-
- if( ret ) {
- int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
- }
-
- intsarray[0] = outputLen;
- intsarray[1] = ret;
-
- retArray = (*env)->NewObjectArray(env, 2,
- (*env)->FindClass(env, "[B"),
- (*env)->NewByteArray(env, 1));
-
- pubArray = (*env)->NewByteArray(env, outputLen);
- (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer);
- (*env)->SetObjectArrayElement(env, retArray, 0, pubArray);
-
- intsByteArray = (*env)->NewByteArray(env, 2);
- (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
- (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
-
- (void)classObject;
-
- return retArray;
-}
-
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
-{
- secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
- unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject);
- const unsigned char* tweak = (unsigned char*) (pkey + publen);
-
- jobjectArray retArray;
- jbyteArray pubArray, intsByteArray;
- unsigned char intsarray[2];
- unsigned char outputSer[65];
- size_t outputLen = 65;
-
- secp256k1_pubkey pubkey;
- int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
-
- if ( ret ) {
- ret = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, tweak);
- }
-
- if( ret ) {
- int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
- }
-
- intsarray[0] = outputLen;
- intsarray[1] = ret;
-
- retArray = (*env)->NewObjectArray(env, 2,
- (*env)->FindClass(env, "[B"),
- (*env)->NewByteArray(env, 1));
-
- pubArray = (*env)->NewByteArray(env, outputLen);
- (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer);
- (*env)->SetObjectArrayElement(env, retArray, 0, pubArray);
-
- intsByteArray = (*env)->NewByteArray(env, 2);
- (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
- (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
-
- (void)classObject;
-
- return retArray;
-}
-
-SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1pubkey_1combine
- (JNIEnv * env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint numkeys)
-{
- (void)classObject;(void)env;(void)byteBufferObject;(void)ctx_l;(void)numkeys;
-
- return 0;
-}
-
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
-{
- secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
- const unsigned char* secdata = (*env)->GetDirectBufferAddress(env, byteBufferObject);
- const unsigned char* pubdata = (const unsigned char*) (secdata + 32);
-
- jobjectArray retArray;
- jbyteArray outArray, intsByteArray;
- unsigned char intsarray[1];
- secp256k1_pubkey pubkey;
- unsigned char nonce_res[32];
- size_t outputLen = 32;
-
- int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen);
-
- if (ret) {
- ret = secp256k1_ecdh(
- ctx,
- nonce_res,
- &pubkey,
- secdata,
- NULL,
- NULL
- );
- }
-
- intsarray[0] = ret;
-
- retArray = (*env)->NewObjectArray(env, 2,
- (*env)->FindClass(env, "[B"),
- (*env)->NewByteArray(env, 1));
-
- outArray = (*env)->NewByteArray(env, outputLen);
- (*env)->SetByteArrayRegion(env, outArray, 0, 32, (jbyte*)nonce_res);
- (*env)->SetObjectArrayElement(env, retArray, 0, outArray);
-
- intsByteArray = (*env)->NewByteArray(env, 1);
- (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray);
- (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
-
- (void)classObject;
-
- return retArray;
-}
diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h
deleted file mode 100644
index fe613c9e9e..0000000000
--- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-#include "include/secp256k1.h"
-/* Header for class org_bitcoin_NativeSecp256k1 */
-
-#ifndef _Included_org_bitcoin_NativeSecp256k1
-#define _Included_org_bitcoin_NativeSecp256k1
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_ctx_clone
- * Signature: (J)J
- */
-SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
- (JNIEnv *, jclass, jlong);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_context_randomize
- * Signature: (Ljava/nio/ByteBuffer;J)I
- */
-SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize
- (JNIEnv *, jclass, jobject, jlong);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_privkey_tweak_add
- * Signature: (Ljava/nio/ByteBuffer;J)[[B
- */
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add
- (JNIEnv *, jclass, jobject, jlong);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_privkey_tweak_mul
- * Signature: (Ljava/nio/ByteBuffer;J)[[B
- */
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul
- (JNIEnv *, jclass, jobject, jlong);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_pubkey_tweak_add
- * Signature: (Ljava/nio/ByteBuffer;JI)[[B
- */
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add
- (JNIEnv *, jclass, jobject, jlong, jint);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_pubkey_tweak_mul
- * Signature: (Ljava/nio/ByteBuffer;JI)[[B
- */
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul
- (JNIEnv *, jclass, jobject, jlong, jint);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_destroy_context
- * Signature: (J)V
- */
-SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context
- (JNIEnv *, jclass, jlong);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_ecdsa_verify
- * Signature: (Ljava/nio/ByteBuffer;JII)I
- */
-SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
- (JNIEnv *, jclass, jobject, jlong, jint, jint);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_ecdsa_sign
- * Signature: (Ljava/nio/ByteBuffer;J)[[B
- */
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
- (JNIEnv *, jclass, jobject, jlong);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_ec_seckey_verify
- * Signature: (Ljava/nio/ByteBuffer;J)I
- */
-SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
- (JNIEnv *, jclass, jobject, jlong);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_ec_pubkey_create
- * Signature: (Ljava/nio/ByteBuffer;J)[[B
- */
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
- (JNIEnv *, jclass, jobject, jlong);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_ec_pubkey_parse
- * Signature: (Ljava/nio/ByteBuffer;JI)[[B
- */
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse
- (JNIEnv *, jclass, jobject, jlong, jint);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_ecdh
- * Signature: (Ljava/nio/ByteBuffer;JI)[[B
- */
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen);
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c
deleted file mode 100644
index a52939e7e7..0000000000
--- a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <stdlib.h>
-#include <stdint.h>
-#include "org_bitcoin_Secp256k1Context.h"
-#include "include/secp256k1.h"
-
-SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context
- (JNIEnv* env, jclass classObject)
-{
- secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
-
- (void)classObject;(void)env;
-
- return (uintptr_t)ctx;
-}
-
diff --git a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h
deleted file mode 100644
index 0d2bc84b7f..0000000000
--- a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-#include "include/secp256k1.h"
-/* Header for class org_bitcoin_Secp256k1Context */
-
-#ifndef _Included_org_bitcoin_Secp256k1Context
-#define _Included_org_bitcoin_Secp256k1Context
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * Class: org_bitcoin_Secp256k1Context
- * Method: secp256k1_init_context
- * Signature: ()J
- */
-SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context
- (JNIEnv *, jclass);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h
index 44cb68e750..07a25b80d4 100644
--- a/src/secp256k1/src/modules/ecdh/main_impl.h
+++ b/src/secp256k1/src/modules/ecdh/main_impl.h
@@ -10,14 +10,14 @@
#include "include/secp256k1_ecdh.h"
#include "ecmult_const_impl.h"
-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;
+static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x32, const unsigned char *y32, void *data) {
+ unsigned char version = (y32[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_write(&sha, x32, 32);
secp256k1_sha256_finalize(&sha, output);
return 1;
@@ -32,36 +32,40 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const se
secp256k1_gej res;
secp256k1_ge pt;
secp256k1_scalar s;
+ unsigned char x[32];
+ unsigned char y[32];
+
VERIFY_CHECK(ctx != 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);
- if (overflow || secp256k1_scalar_is_zero(&s)) {
- ret = 0;
- } else {
- unsigned char x[32];
- unsigned char y[32];
-
- secp256k1_ecmult_const(&res, &pt, &s, 256);
- secp256k1_ge_set_gej(&pt, &res);
-
- /* Compute a hash of the point */
- secp256k1_fe_normalize(&pt.x);
- secp256k1_fe_normalize(&pt.y);
- secp256k1_fe_get_b32(x, &pt.x);
- secp256k1_fe_get_b32(y, &pt.y);
-
- ret = hashfp(output, x, y, data);
- }
+ overflow |= secp256k1_scalar_is_zero(&s);
+ secp256k1_scalar_cmov(&s, &secp256k1_scalar_one, overflow);
+
+ secp256k1_ecmult_const(&res, &pt, &s, 256);
+ secp256k1_ge_set_gej(&pt, &res);
+
+ /* Compute a hash of the point */
+ secp256k1_fe_normalize(&pt.x);
+ secp256k1_fe_normalize(&pt.y);
+ secp256k1_fe_get_b32(x, &pt.x);
+ secp256k1_fe_get_b32(y, &pt.y);
+
+ ret = hashfp(output, x, y, data);
+
+ memset(x, 0, 32);
+ memset(y, 0, 32);
secp256k1_scalar_clear(&s);
- return ret;
+
+ return !!ret & !overflow;
}
#endif /* SECP256K1_MODULE_ECDH_MAIN_H */
diff --git a/src/secp256k1/src/modules/recovery/main_impl.h b/src/secp256k1/src/modules/recovery/main_impl.h
index 2f6691c5a1..e2576aa953 100755..100644
--- a/src/secp256k1/src/modules/recovery/main_impl.h
+++ b/src/secp256k1/src/modules/recovery/main_impl.h
@@ -122,48 +122,15 @@ static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, cons
int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
secp256k1_scalar r, s;
- secp256k1_scalar sec, non, msg;
- int recid;
- int ret = 0;
- int overflow = 0;
+ int ret, recid;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(msg32 != NULL);
ARG_CHECK(signature != NULL);
ARG_CHECK(seckey != NULL);
- if (noncefp == NULL) {
- noncefp = secp256k1_nonce_function_default;
- }
- secp256k1_scalar_set_b32(&sec, seckey, &overflow);
- /* Fail if the secret key is invalid. */
- if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
- unsigned char nonce32[32];
- unsigned int count = 0;
- secp256k1_scalar_set_b32(&msg, msg32, NULL);
- while (1) {
- ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
- if (!ret) {
- break;
- }
- secp256k1_scalar_set_b32(&non, nonce32, &overflow);
- if (!secp256k1_scalar_is_zero(&non) && !overflow) {
- if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) {
- break;
- }
- }
- count++;
- }
- memset(nonce32, 0, 32);
- secp256k1_scalar_clear(&msg);
- secp256k1_scalar_clear(&non);
- secp256k1_scalar_clear(&sec);
- }
- if (ret) {
- secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid);
- } else {
- memset(signature, 0, sizeof(*signature));
- }
+ ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, msg32, seckey, noncefp, noncedata);
+ secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid);
return ret;
}
diff --git a/src/secp256k1/src/modules/recovery/tests_impl.h b/src/secp256k1/src/modules/recovery/tests_impl.h
index 5c9bbe8610..38a533a755 100644
--- a/src/secp256k1/src/modules/recovery/tests_impl.h
+++ b/src/secp256k1/src/modules/recovery/tests_impl.h
@@ -215,7 +215,7 @@ void test_ecdsa_recovery_edge_cases(void) {
};
const unsigned char sig64[64] = {
/* Generated by signing the above message with nonce 'This is the nonce we will use...'
- * and secret key 0 (which is not valid), resulting in recid 0. */
+ * and secret key 0 (which is not valid), resulting in recid 1. */
0x67, 0xCB, 0x28, 0x5F, 0x9C, 0xD1, 0x94, 0xE8,
0x40, 0xD6, 0x29, 0x39, 0x7A, 0xF5, 0x56, 0x96,
0x62, 0xFD, 0xE4, 0x46, 0x49, 0x99, 0x59, 0x63,
diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h
index 59304cb66e..2a74703523 100644
--- a/src/secp256k1/src/scalar.h
+++ b/src/secp256k1/src/scalar.h
@@ -32,9 +32,17 @@ static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigne
/** Access bits from a scalar. Not constant time. */
static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count);
-/** Set a scalar from a big endian byte array. */
+/** Set a scalar from a big endian byte array. The scalar will be reduced modulo group order `n`.
+ * In: bin: pointer to a 32-byte array.
+ * Out: r: scalar to be set.
+ * overflow: non-zero if the scalar was bigger or equal to `n` before reduction, zero otherwise (can be NULL).
+ */
static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *bin, int *overflow);
+/** Set a scalar from a big endian byte array and returns 1 if it is a valid
+ * seckey and 0 otherwise. */
+static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned char *bin);
+
/** Set a scalar to an unsigned integer. */
static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v);
@@ -103,4 +111,7 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar
/** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */
static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift);
+/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/
+static void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag);
+
#endif /* SECP256K1_SCALAR_H */
diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h
index d378335d99..8f539c4bc6 100644
--- a/src/secp256k1/src/scalar_4x64_impl.h
+++ b/src/secp256k1/src/scalar_4x64_impl.h
@@ -946,4 +946,15 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r,
secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1);
}
+static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) {
+ uint64_t mask0, mask1;
+ VG_CHECK_VERIFY(r->d, sizeof(r->d));
+ mask0 = flag + ~((uint64_t)0);
+ mask1 = ~mask0;
+ r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1);
+ r->d[1] = (r->d[1] & mask0) | (a->d[1] & mask1);
+ r->d[2] = (r->d[2] & mask0) | (a->d[2] & mask1);
+ r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1);
+}
+
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h
index 4f9ed61fea..3c372f34fe 100644
--- a/src/secp256k1/src/scalar_8x32_impl.h
+++ b/src/secp256k1/src/scalar_8x32_impl.h
@@ -718,4 +718,19 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r,
secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1);
}
+static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) {
+ uint32_t mask0, mask1;
+ VG_CHECK_VERIFY(r->d, sizeof(r->d));
+ mask0 = flag + ~((uint32_t)0);
+ mask1 = ~mask0;
+ r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1);
+ r->d[1] = (r->d[1] & mask0) | (a->d[1] & mask1);
+ r->d[2] = (r->d[2] & mask0) | (a->d[2] & mask1);
+ r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1);
+ r->d[4] = (r->d[4] & mask0) | (a->d[4] & mask1);
+ r->d[5] = (r->d[5] & mask0) | (a->d[5] & mask1);
+ r->d[6] = (r->d[6] & mask0) | (a->d[6] & mask1);
+ r->d[7] = (r->d[7] & mask0) | (a->d[7] & mask1);
+}
+
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h
index fa790570ff..70cd73db06 100644
--- a/src/secp256k1/src/scalar_impl.h
+++ b/src/secp256k1/src/scalar_impl.h
@@ -7,8 +7,8 @@
#ifndef SECP256K1_SCALAR_IMPL_H
#define SECP256K1_SCALAR_IMPL_H
-#include "group.h"
#include "scalar.h"
+#include "util.h"
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
@@ -24,6 +24,9 @@
#error "Please select scalar implementation"
#endif
+static const secp256k1_scalar secp256k1_scalar_one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1);
+static const secp256k1_scalar secp256k1_scalar_zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
+
#ifndef USE_NUM_NONE
static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a) {
unsigned char c[32];
@@ -52,6 +55,12 @@ static void secp256k1_scalar_order_get_num(secp256k1_num *r) {
}
#endif
+static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned char *bin) {
+ int overflow;
+ secp256k1_scalar_set_b32(r, bin, &overflow);
+ return (!overflow) & (!secp256k1_scalar_is_zero(r));
+}
+
static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
#if defined(EXHAUSTIVE_TEST_ORDER)
int i;
diff --git a/src/secp256k1/src/scalar_low.h b/src/secp256k1/src/scalar_low.h
index 5836febc5b..2794a7f171 100644
--- a/src/secp256k1/src/scalar_low.h
+++ b/src/secp256k1/src/scalar_low.h
@@ -12,4 +12,6 @@
/** A scalar modulo the group order of the secp256k1 curve. */
typedef uint32_t secp256k1_scalar;
+#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) (d0)
+
#endif /* SECP256K1_SCALAR_REPR_H */
diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h
index c80e70c5a2..b79cf1ff6c 100644
--- a/src/secp256k1/src/scalar_low_impl.h
+++ b/src/secp256k1/src/scalar_low_impl.h
@@ -38,8 +38,11 @@ static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a,
static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) {
if (flag && bit < 32)
- *r += (1 << bit);
+ *r += ((uint32_t)1 << bit);
#ifdef VERIFY
+ VERIFY_CHECK(bit < 32);
+ /* Verify that adding (1 << bit) will not overflow any in-range scalar *r by overflowing the underlying uint32_t. */
+ VERIFY_CHECK(((uint32_t)1 << bit) - 1 <= UINT32_MAX - EXHAUSTIVE_TEST_ORDER);
VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0);
#endif
}
@@ -111,4 +114,12 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const
return *a == *b;
}
+static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) {
+ uint32_t mask0, mask1;
+ VG_CHECK_VERIFY(r, sizeof(*r));
+ mask0 = flag + ~((uint32_t)0);
+ mask1 = ~mask0;
+ *r = (*r & mask0) | (*a & mask1);
+}
+
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scratch.h b/src/secp256k1/src/scratch.h
index fef377af0d..77b35d126b 100644
--- a/src/secp256k1/src/scratch.h
+++ b/src/secp256k1/src/scratch.h
@@ -7,33 +7,36 @@
#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;
+ /** guard against interpreting this object as other types */
+ unsigned char magic[8];
+ /** actual allocated data */
+ void *data;
+ /** amount that has been allocated (i.e. `data + offset` is the next
+ * available pointer) */
+ size_t alloc_size;
+ /** maximum size available to allocate */
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);
+static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, 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);
+/** Returns an opaque object used to "checkpoint" a scratch space. Used
+ * with `secp256k1_scratch_apply_checkpoint` to undo allocations. */
+static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch);
-/** Deallocates a stack frame */
-static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch);
+/** Applies a check point received from `secp256k1_scratch_checkpoint`,
+ * undoing all allocations since that point. */
+static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint);
/** Returns the maximum allocation the scratch space will allow */
-static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t n_objects);
+static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, 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);
+static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t n);
#endif
diff --git a/src/secp256k1/src/scratch_impl.h b/src/secp256k1/src/scratch_impl.h
index abed713b21..4cee700001 100644
--- a/src/secp256k1/src/scratch_impl.h
+++ b/src/secp256k1/src/scratch_impl.h
@@ -7,78 +7,80 @@
#ifndef _SECP256K1_SCRATCH_IMPL_H_
#define _SECP256K1_SCRATCH_IMPL_H_
+#include "util.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));
+static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t size) {
+ const size_t base_alloc = ((sizeof(secp256k1_scratch) + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
+ void *alloc = checked_malloc(error_callback, base_alloc + size);
+ secp256k1_scratch* ret = (secp256k1_scratch *)alloc;
if (ret != NULL) {
memset(ret, 0, sizeof(*ret));
- ret->max_size = max_size;
- ret->error_callback = error_callback;
+ memcpy(ret->magic, "scratch", 8);
+ ret->data = (void *) ((char *) alloc + base_alloc);
+ ret->max_size = size;
}
return ret;
}
-static void secp256k1_scratch_destroy(secp256k1_scratch* scratch) {
+static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) {
if (scratch != NULL) {
- VERIFY_CHECK(scratch->frame == 0);
+ VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */
+ if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ secp256k1_callback_call(error_callback, "invalid scratch space");
+ return;
+ }
+ memset(scratch->magic, 0, sizeof(scratch->magic));
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) {
+static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch) {
+ if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ secp256k1_callback_call(error_callback, "invalid scratch space");
return 0;
}
- return scratch->max_size - allocated - objects * ALIGNMENT;
+ return scratch->alloc_size;
}
-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_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint) {
+ if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ secp256k1_callback_call(error_callback, "invalid scratch space");
+ return;
+ }
+ if (checkpoint > scratch->alloc_size) {
+ secp256k1_callback_call(error_callback, "invalid checkpoint");
+ return;
}
+ scratch->alloc_size = checkpoint;
}
-static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch) {
- VERIFY_CHECK(scratch->frame > 0);
- scratch->frame -= 1;
- free(scratch->data[scratch->frame]);
+static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t objects) {
+ if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ secp256k1_callback_call(error_callback, "invalid scratch space");
+ return 0;
+ }
+ if (scratch->max_size - scratch->alloc_size <= objects * (ALIGNMENT - 1)) {
+ return 0;
+ }
+ return scratch->max_size - scratch->alloc_size - objects * (ALIGNMENT - 1);
}
-static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t size) {
+static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t size) {
void *ret;
- size_t frame = scratch->frame - 1;
- size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
+ size = ROUND_TO_ALIGN(size);
+
+ if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ secp256k1_callback_call(error_callback, "invalid scratch space");
+ return NULL;
+ }
- if (scratch->frame == 0 || size + scratch->offset[frame] > scratch->frame_size[frame]) {
+ if (size > scratch->max_size - scratch->alloc_size) {
return NULL;
}
- ret = (void *) ((unsigned char *) scratch->data[frame] + scratch->offset[frame]);
+ ret = (void *) ((char *) scratch->data + scratch->alloc_size);
memset(ret, 0, size);
- scratch->offset[frame] += size;
+ scratch->alloc_size += size;
return ret;
}
diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c
index 15981f46e2..b03a6e6345 100644
--- a/src/secp256k1/src/secp256k1.c
+++ b/src/secp256k1/src/secp256k1.c
@@ -5,6 +5,7 @@
**********************************************************************/
#include "include/secp256k1.h"
+#include "include/secp256k1_preallocated.h"
#include "util.h"
#include "num_impl.h"
@@ -19,6 +20,10 @@
#include "hash_impl.h"
#include "scratch_impl.h"
+#if defined(VALGRIND)
+# include <valgrind/memcheck.h>
+#endif
+
#define ARG_CHECK(cond) do { \
if (EXPECT(!(cond), 0)) { \
secp256k1_callback_call(&ctx->illegal_callback, #cond); \
@@ -26,53 +31,101 @@
} \
} while(0)
-static void default_illegal_callback_fn(const char* str, void* data) {
+#define ARG_CHECK_NO_RETURN(cond) do { \
+ if (EXPECT(!(cond), 0)) { \
+ secp256k1_callback_call(&ctx->illegal_callback, #cond); \
+ } \
+} while(0)
+
+#ifndef USE_EXTERNAL_DEFAULT_CALLBACKS
+#include <stdlib.h>
+#include <stdio.h>
+static void secp256k1_default_illegal_callback_fn(const char* str, void* data) {
(void)data;
fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str);
abort();
}
-
-static const secp256k1_callback default_illegal_callback = {
- default_illegal_callback_fn,
- NULL
-};
-
-static void default_error_callback_fn(const char* str, void* data) {
+static void secp256k1_default_error_callback_fn(const char* str, void* data) {
(void)data;
fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str);
abort();
}
+#else
+void secp256k1_default_illegal_callback_fn(const char* str, void* data);
+void secp256k1_default_error_callback_fn(const char* str, void* data);
+#endif
-static const secp256k1_callback default_error_callback = {
- default_error_callback_fn,
+static const secp256k1_callback default_illegal_callback = {
+ secp256k1_default_illegal_callback_fn,
NULL
};
+static const secp256k1_callback default_error_callback = {
+ secp256k1_default_error_callback_fn,
+ NULL
+};
struct secp256k1_context_struct {
secp256k1_ecmult_context ecmult_ctx;
secp256k1_ecmult_gen_context ecmult_gen_ctx;
secp256k1_callback illegal_callback;
secp256k1_callback error_callback;
+ int declassify;
};
static const secp256k1_context secp256k1_context_no_precomp_ = {
{ 0 },
{ 0 },
- { default_illegal_callback_fn, 0 },
- { default_error_callback_fn, 0 }
+ { secp256k1_default_illegal_callback_fn, 0 },
+ { secp256k1_default_error_callback_fn, 0 },
+ 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));
+size_t secp256k1_context_preallocated_size(unsigned int flags) {
+ size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context));
+
+ if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) {
+ secp256k1_callback_call(&default_illegal_callback,
+ "Invalid flags");
+ return 0;
+ }
+
+ if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) {
+ ret += SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
+ }
+ if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) {
+ ret += SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
+ }
+ return ret;
+}
+
+size_t secp256k1_context_preallocated_clone_size(const secp256k1_context* ctx) {
+ size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context));
+ VERIFY_CHECK(ctx != NULL);
+ if (secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)) {
+ ret += SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
+ }
+ if (secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)) {
+ ret += SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
+ }
+ return ret;
+}
+
+secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigned int flags) {
+ void* const base = prealloc;
+ size_t prealloc_size;
+ secp256k1_context* ret;
+
+ VERIFY_CHECK(prealloc != NULL);
+ prealloc_size = secp256k1_context_preallocated_size(flags);
+ ret = (secp256k1_context*)manual_alloc(&prealloc, sizeof(secp256k1_context), base, prealloc_size);
ret->illegal_callback = default_illegal_callback;
ret->error_callback = default_error_callback;
if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) {
secp256k1_callback_call(&ret->illegal_callback,
"Invalid flags");
- free(ret);
return NULL;
}
@@ -80,47 +133,80 @@ secp256k1_context* secp256k1_context_create(unsigned int flags) {
secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx);
if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) {
- secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback);
+ secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &prealloc);
}
if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) {
- secp256k1_ecmult_context_build(&ret->ecmult_ctx, &ret->error_callback);
+ secp256k1_ecmult_context_build(&ret->ecmult_ctx, &prealloc);
+ }
+ ret->declassify = !!(flags & SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY);
+
+ return (secp256k1_context*) ret;
+}
+
+secp256k1_context* secp256k1_context_create(unsigned int flags) {
+ size_t const prealloc_size = secp256k1_context_preallocated_size(flags);
+ secp256k1_context* ctx = (secp256k1_context*)checked_malloc(&default_error_callback, prealloc_size);
+ if (EXPECT(secp256k1_context_preallocated_create(ctx, flags) == NULL, 0)) {
+ free(ctx);
+ return NULL;
}
+ return ctx;
+}
+
+secp256k1_context* secp256k1_context_preallocated_clone(const secp256k1_context* ctx, void* prealloc) {
+ size_t prealloc_size;
+ secp256k1_context* ret;
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(prealloc != NULL);
+
+ prealloc_size = secp256k1_context_preallocated_clone_size(ctx);
+ ret = (secp256k1_context*)prealloc;
+ memcpy(ret, ctx, prealloc_size);
+ secp256k1_ecmult_gen_context_finalize_memcpy(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx);
+ secp256k1_ecmult_context_finalize_memcpy(&ret->ecmult_ctx, &ctx->ecmult_ctx);
return ret;
}
secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) {
- secp256k1_context* ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, sizeof(secp256k1_context));
- ret->illegal_callback = ctx->illegal_callback;
- ret->error_callback = ctx->error_callback;
- secp256k1_ecmult_context_clone(&ret->ecmult_ctx, &ctx->ecmult_ctx, &ctx->error_callback);
- secp256k1_ecmult_gen_context_clone(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx, &ctx->error_callback);
+ secp256k1_context* ret;
+ size_t prealloc_size;
+
+ VERIFY_CHECK(ctx != NULL);
+ prealloc_size = secp256k1_context_preallocated_clone_size(ctx);
+ ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, prealloc_size);
+ ret = secp256k1_context_preallocated_clone(ctx, ret);
return ret;
}
-void secp256k1_context_destroy(secp256k1_context* ctx) {
- CHECK(ctx != secp256k1_context_no_precomp);
+void secp256k1_context_preallocated_destroy(secp256k1_context* ctx) {
+ ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp);
if (ctx != NULL) {
secp256k1_ecmult_context_clear(&ctx->ecmult_ctx);
secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx);
+ }
+}
+void secp256k1_context_destroy(secp256k1_context* ctx) {
+ if (ctx != NULL) {
+ secp256k1_context_preallocated_destroy(ctx);
free(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);
+ ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp);
if (fun == NULL) {
- fun = default_illegal_callback_fn;
+ fun = secp256k1_default_illegal_callback_fn;
}
ctx->illegal_callback.fn = fun;
ctx->illegal_callback.data = data;
}
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);
+ ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp);
if (fun == NULL) {
- fun = default_error_callback_fn;
+ fun = secp256k1_default_error_callback_fn;
}
ctx->error_callback.fn = fun;
ctx->error_callback.data = data;
@@ -131,8 +217,23 @@ secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context*
return secp256k1_scratch_create(&ctx->error_callback, max_size);
}
-void secp256k1_scratch_space_destroy(secp256k1_scratch_space* scratch) {
- secp256k1_scratch_destroy(scratch);
+void secp256k1_scratch_space_destroy(const secp256k1_context *ctx, secp256k1_scratch_space* scratch) {
+ VERIFY_CHECK(ctx != NULL);
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
+}
+
+/* Mark memory as no-longer-secret for the purpose of analysing constant-time behaviour
+ * of the software. This is setup for use with valgrind but could be substituted with
+ * the appropriate instrumentation for other analysis tools.
+ */
+static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, void *p, size_t len) {
+#if defined(VALGRIND)
+ if (EXPECT(ctx->declassify,0)) VALGRIND_MAKE_MEM_DEFINED(p, len);
+#else
+ (void)ctx;
+ (void)p;
+ (void)len;
+#endif
}
static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) {
@@ -366,61 +467,83 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m
const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979;
const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979;
-int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
- secp256k1_scalar r, s;
+static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
secp256k1_scalar sec, non, msg;
int ret = 0;
- int overflow = 0;
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
- ARG_CHECK(msg32 != NULL);
- ARG_CHECK(signature != NULL);
- ARG_CHECK(seckey != NULL);
+ int is_sec_valid;
+ unsigned char nonce32[32];
+ unsigned int count = 0;
+ /* Default initialization here is important so we won't pass uninit values to the cmov in the end */
+ *r = secp256k1_scalar_zero;
+ *s = secp256k1_scalar_zero;
+ if (recid) {
+ *recid = 0;
+ }
if (noncefp == NULL) {
noncefp = secp256k1_nonce_function_default;
}
- secp256k1_scalar_set_b32(&sec, seckey, &overflow);
/* Fail if the secret key is invalid. */
- if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
- unsigned char nonce32[32];
- unsigned int count = 0;
- secp256k1_scalar_set_b32(&msg, msg32, NULL);
- while (1) {
- ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
- if (!ret) {
+ is_sec_valid = secp256k1_scalar_set_b32_seckey(&sec, seckey);
+ secp256k1_scalar_cmov(&sec, &secp256k1_scalar_one, !is_sec_valid);
+ secp256k1_scalar_set_b32(&msg, msg32, NULL);
+ while (1) {
+ int is_nonce_valid;
+ ret = !!noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
+ if (!ret) {
+ break;
+ }
+ is_nonce_valid = secp256k1_scalar_set_b32_seckey(&non, nonce32);
+ /* The nonce is still secret here, but it being invalid is is less likely than 1:2^255. */
+ secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid));
+ if (is_nonce_valid) {
+ ret = secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, r, s, &sec, &msg, &non, recid);
+ /* The final signature is no longer a secret, nor is the fact that we were successful or not. */
+ secp256k1_declassify(ctx, &ret, sizeof(ret));
+ if (ret) {
break;
}
- secp256k1_scalar_set_b32(&non, nonce32, &overflow);
- if (!overflow && !secp256k1_scalar_is_zero(&non)) {
- if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) {
- break;
- }
- }
- count++;
}
- memset(nonce32, 0, 32);
- secp256k1_scalar_clear(&msg);
- secp256k1_scalar_clear(&non);
- secp256k1_scalar_clear(&sec);
+ count++;
}
- if (ret) {
- secp256k1_ecdsa_signature_save(signature, &r, &s);
- } else {
- memset(signature, 0, sizeof(*signature));
+ /* We don't want to declassify is_sec_valid and therefore the range of
+ * seckey. As a result is_sec_valid is included in ret only after ret was
+ * used as a branching variable. */
+ ret &= is_sec_valid;
+ memset(nonce32, 0, 32);
+ secp256k1_scalar_clear(&msg);
+ secp256k1_scalar_clear(&non);
+ secp256k1_scalar_clear(&sec);
+ secp256k1_scalar_cmov(r, &secp256k1_scalar_zero, !ret);
+ secp256k1_scalar_cmov(s, &secp256k1_scalar_zero, !ret);
+ if (recid) {
+ const int zero = 0;
+ secp256k1_int_cmov(recid, &zero, !ret);
}
return ret;
}
+int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
+ secp256k1_scalar r, s;
+ int ret;
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
+ ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(signature != NULL);
+ ARG_CHECK(seckey != NULL);
+
+ ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, msg32, seckey, noncefp, noncedata);
+ secp256k1_ecdsa_signature_save(signature, &r, &s);
+ return ret;
+}
+
int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char *seckey) {
secp256k1_scalar sec;
int ret;
- int overflow;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
- secp256k1_scalar_set_b32(&sec, seckey, &overflow);
- ret = !overflow && !secp256k1_scalar_is_zero(&sec);
+ ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
secp256k1_scalar_clear(&sec);
return ret;
}
@@ -429,7 +552,6 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p
secp256k1_gej pj;
secp256k1_ge p;
secp256k1_scalar sec;
- int overflow;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubkey != NULL);
@@ -437,27 +559,35 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(seckey != NULL);
- secp256k1_scalar_set_b32(&sec, seckey, &overflow);
- ret = (!overflow) & (!secp256k1_scalar_is_zero(&sec));
- if (ret) {
- secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec);
- secp256k1_ge_set_gej(&p, &pj);
- secp256k1_pubkey_save(pubkey, &p);
- }
+ ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
+ secp256k1_scalar_cmov(&sec, &secp256k1_scalar_one, !ret);
+
+ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec);
+ secp256k1_ge_set_gej(&p, &pj);
+ secp256k1_pubkey_save(pubkey, &p);
+ memczero(pubkey, sizeof(*pubkey), !ret);
+
secp256k1_scalar_clear(&sec);
return ret;
}
-int secp256k1_ec_privkey_negate(const secp256k1_context* ctx, unsigned char *seckey) {
+int secp256k1_ec_seckey_negate(const secp256k1_context* ctx, unsigned char *seckey) {
secp256k1_scalar sec;
+ int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
- secp256k1_scalar_set_b32(&sec, seckey, NULL);
+ ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
+ secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret);
secp256k1_scalar_negate(&sec, &sec);
secp256k1_scalar_get_b32(seckey, &sec);
- return 1;
+ secp256k1_scalar_clear(&sec);
+ return ret;
+}
+
+int secp256k1_ec_privkey_negate(const secp256k1_context* ctx, unsigned char *seckey) {
+ return secp256k1_ec_seckey_negate(ctx, seckey);
}
int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *pubkey) {
@@ -475,7 +605,7 @@ int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *p
return ret;
}
-int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
+int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
secp256k1_scalar term;
secp256k1_scalar sec;
int ret = 0;
@@ -485,19 +615,21 @@ int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *
ARG_CHECK(tweak != NULL);
secp256k1_scalar_set_b32(&term, tweak, &overflow);
- secp256k1_scalar_set_b32(&sec, seckey, NULL);
+ ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
- ret = !overflow && secp256k1_eckey_privkey_tweak_add(&sec, &term);
- memset(seckey, 0, 32);
- if (ret) {
- secp256k1_scalar_get_b32(seckey, &sec);
- }
+ ret &= (!overflow) & secp256k1_eckey_privkey_tweak_add(&sec, &term);
+ secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret);
+ secp256k1_scalar_get_b32(seckey, &sec);
secp256k1_scalar_clear(&sec);
secp256k1_scalar_clear(&term);
return ret;
}
+int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
+ return secp256k1_ec_seckey_tweak_add(ctx, seckey, tweak);
+}
+
int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) {
secp256k1_ge p;
secp256k1_scalar term;
@@ -522,7 +654,7 @@ int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey
return ret;
}
-int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
+int secp256k1_ec_seckey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
secp256k1_scalar factor;
secp256k1_scalar sec;
int ret = 0;
@@ -532,18 +664,20 @@ int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *
ARG_CHECK(tweak != NULL);
secp256k1_scalar_set_b32(&factor, tweak, &overflow);
- secp256k1_scalar_set_b32(&sec, seckey, NULL);
- ret = !overflow && secp256k1_eckey_privkey_tweak_mul(&sec, &factor);
- memset(seckey, 0, 32);
- if (ret) {
- secp256k1_scalar_get_b32(seckey, &sec);
- }
+ ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
+ ret &= (!overflow) & secp256k1_eckey_privkey_tweak_mul(&sec, &factor);
+ secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret);
+ secp256k1_scalar_get_b32(seckey, &sec);
secp256k1_scalar_clear(&sec);
secp256k1_scalar_clear(&factor);
return ret;
}
+int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
+ return secp256k1_ec_seckey_tweak_mul(ctx, seckey, tweak);
+}
+
int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) {
secp256k1_ge p;
secp256k1_scalar factor;
diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c
index f1c4db929a..374ed7dc12 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -16,6 +16,7 @@
#include "secp256k1.c"
#include "include/secp256k1.h"
+#include "include/secp256k1_preallocated.h"
#include "testrand_impl.h"
#ifdef ENABLE_OPENSSL_TESTS
@@ -31,17 +32,6 @@ void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
#include "contrib/lax_der_parsing.c"
#include "contrib/lax_der_privatekey_parsing.c"
-#if !defined(VG_CHECK)
-# if defined(VALGRIND)
-# include <valgrind/memcheck.h>
-# define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y))
-# define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y))
-# else
-# define VG_UNDEF(x,y)
-# define VG_CHECK(x,y)
-# endif
-#endif
-
static int count = 64;
static secp256k1_context *ctx = NULL;
@@ -82,7 +72,9 @@ void random_field_element_magnitude(secp256k1_fe *fe) {
secp256k1_fe_negate(&zero, &zero, 0);
secp256k1_fe_mul_int(&zero, n - 1);
secp256k1_fe_add(fe, &zero);
- VERIFY_CHECK(fe->magnitude == n);
+#ifdef VERIFY
+ CHECK(fe->magnitude == n);
+#endif
}
void random_group_element_test(secp256k1_ge *ge) {
@@ -137,23 +129,53 @@ void random_scalar_order(secp256k1_scalar *num) {
} while(1);
}
-void run_context_tests(void) {
+void random_scalar_order_b32(unsigned char *b32) {
+ secp256k1_scalar num;
+ random_scalar_order(&num);
+ secp256k1_scalar_get_b32(b32, &num);
+}
+
+void run_context_tests(int use_prealloc) {
secp256k1_pubkey pubkey;
secp256k1_pubkey zero_pubkey;
secp256k1_ecdsa_signature sig;
unsigned char ctmp[32];
int32_t ecount;
int32_t ecount2;
- secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
- secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
- secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
- secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ secp256k1_context *none;
+ secp256k1_context *sign;
+ secp256k1_context *vrfy;
+ secp256k1_context *both;
+ void *none_prealloc = NULL;
+ void *sign_prealloc = NULL;
+ void *vrfy_prealloc = NULL;
+ void *both_prealloc = NULL;
secp256k1_gej pubj;
secp256k1_ge pub;
secp256k1_scalar msg, key, nonce;
secp256k1_scalar sigr, sigs;
+ if (use_prealloc) {
+ none_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE));
+ sign_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN));
+ vrfy_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY));
+ both_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY));
+ CHECK(none_prealloc != NULL);
+ CHECK(sign_prealloc != NULL);
+ CHECK(vrfy_prealloc != NULL);
+ CHECK(both_prealloc != NULL);
+ none = secp256k1_context_preallocated_create(none_prealloc, SECP256K1_CONTEXT_NONE);
+ sign = secp256k1_context_preallocated_create(sign_prealloc, SECP256K1_CONTEXT_SIGN);
+ vrfy = secp256k1_context_preallocated_create(vrfy_prealloc, SECP256K1_CONTEXT_VERIFY);
+ both = secp256k1_context_preallocated_create(both_prealloc, SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ } else {
+ none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
+ sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
+ vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
+ both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ }
+
memset(&zero_pubkey, 0, sizeof(zero_pubkey));
ecount = 0;
@@ -163,14 +185,57 @@ void run_context_tests(void) {
secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, NULL);
CHECK(vrfy->error_callback.fn != sign->error_callback.fn);
+ /* check if sizes for cloning are consistent */
+ CHECK(secp256k1_context_preallocated_clone_size(none) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE));
+ CHECK(secp256k1_context_preallocated_clone_size(sign) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN));
+ CHECK(secp256k1_context_preallocated_clone_size(vrfy) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY));
+ CHECK(secp256k1_context_preallocated_clone_size(both) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY));
+
/*** clone and destroy all of them to make sure cloning was complete ***/
{
secp256k1_context *ctx_tmp;
- ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_destroy(ctx_tmp);
- ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_destroy(ctx_tmp);
- ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_destroy(ctx_tmp);
- ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_destroy(ctx_tmp);
+ if (use_prealloc) {
+ /* clone into a non-preallocated context and then again into a new preallocated one. */
+ ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(none_prealloc); none_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); CHECK(none_prealloc != NULL);
+ ctx_tmp = none; none = secp256k1_context_preallocated_clone(none, none_prealloc); secp256k1_context_destroy(ctx_tmp);
+
+ ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(sign_prealloc); sign_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN)); CHECK(sign_prealloc != NULL);
+ ctx_tmp = sign; sign = secp256k1_context_preallocated_clone(sign, sign_prealloc); secp256k1_context_destroy(ctx_tmp);
+
+ ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(vrfy_prealloc); vrfy_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY)); CHECK(vrfy_prealloc != NULL);
+ ctx_tmp = vrfy; vrfy = secp256k1_context_preallocated_clone(vrfy, vrfy_prealloc); secp256k1_context_destroy(ctx_tmp);
+
+ ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(both_prealloc); both_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)); CHECK(both_prealloc != NULL);
+ ctx_tmp = both; both = secp256k1_context_preallocated_clone(both, both_prealloc); secp256k1_context_destroy(ctx_tmp);
+ } else {
+ /* clone into a preallocated context and then again into a new non-preallocated one. */
+ void *prealloc_tmp;
+
+ prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); CHECK(prealloc_tmp != NULL);
+ ctx_tmp = none; none = secp256k1_context_preallocated_clone(none, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
+ ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(prealloc_tmp);
+
+ prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN)); CHECK(prealloc_tmp != NULL);
+ ctx_tmp = sign; sign = secp256k1_context_preallocated_clone(sign, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
+ ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(prealloc_tmp);
+
+ prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY)); CHECK(prealloc_tmp != NULL);
+ ctx_tmp = vrfy; vrfy = secp256k1_context_preallocated_clone(vrfy, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
+ ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(prealloc_tmp);
+
+ prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)); CHECK(prealloc_tmp != NULL);
+ ctx_tmp = both; both = secp256k1_context_preallocated_clone(both, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
+ ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(prealloc_tmp);
+ }
}
/* Verify that the error callback makes it across the clone. */
@@ -229,10 +294,6 @@ void run_context_tests(void) {
secp256k1_context_set_illegal_callback(vrfy, NULL, NULL);
secp256k1_context_set_illegal_callback(sign, NULL, NULL);
- /* This shouldn't leak memory, due to already-set tests. */
- secp256k1_ecmult_gen_context_build(&sign->ecmult_gen_ctx, NULL);
- secp256k1_ecmult_context_build(&vrfy->ecmult_ctx, NULL);
-
/* obtain a working nonce */
do {
random_scalar_order_test(&nonce);
@@ -247,49 +308,95 @@ void run_context_tests(void) {
CHECK(secp256k1_ecdsa_sig_verify(&both->ecmult_ctx, &sigr, &sigs, &pub, &msg));
/* cleanup */
- secp256k1_context_destroy(none);
- secp256k1_context_destroy(sign);
- secp256k1_context_destroy(vrfy);
- secp256k1_context_destroy(both);
+ if (use_prealloc) {
+ secp256k1_context_preallocated_destroy(none);
+ secp256k1_context_preallocated_destroy(sign);
+ secp256k1_context_preallocated_destroy(vrfy);
+ secp256k1_context_preallocated_destroy(both);
+ free(none_prealloc);
+ free(sign_prealloc);
+ free(vrfy_prealloc);
+ free(both_prealloc);
+ } else {
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(vrfy);
+ secp256k1_context_destroy(both);
+ }
/* Defined as no-op. */
secp256k1_context_destroy(NULL);
+ secp256k1_context_preallocated_destroy(NULL);
+
}
void run_scratch_tests(void) {
+ const size_t adj_alloc = ((500 + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
+
int32_t ecount = 0;
+ size_t checkpoint;
+ size_t checkpoint_2;
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
secp256k1_scratch_space *scratch;
+ secp256k1_scratch_space local_scratch;
/* Test public API */
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_error_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);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - (ALIGNMENT - 1));
+ CHECK(scratch->alloc_size == 0);
+ CHECK(scratch->alloc_size % ALIGNMENT == 0);
+
+ /* Allocating 500 bytes succeeds */
+ checkpoint = secp256k1_scratch_checkpoint(&none->error_callback, scratch);
+ CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) != NULL);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1));
+ CHECK(scratch->alloc_size != 0);
+ CHECK(scratch->alloc_size % ALIGNMENT == 0);
+
+ /* Allocating another 500 bytes fails */
+ CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1));
+ CHECK(scratch->alloc_size != 0);
+ CHECK(scratch->alloc_size % ALIGNMENT == 0);
+
+ /* ...but it succeeds once we apply the checkpoint to undo it */
+ secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint);
+ CHECK(scratch->alloc_size == 0);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
+ CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) != NULL);
+ CHECK(scratch->alloc_size != 0);
+
+ /* try to apply a bad checkpoint */
+ checkpoint_2 = secp256k1_scratch_checkpoint(&none->error_callback, scratch);
+ secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint);
+ CHECK(ecount == 0);
+ secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint_2); /* checkpoint_2 is after checkpoint */
+ CHECK(ecount == 1);
+ secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, (size_t) -1); /* this is just wildly invalid */
+ CHECK(ecount == 2);
- /* ...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);
+ /* try to use badly initialized scratch space */
+ secp256k1_scratch_space_destroy(none, scratch);
+ memset(&local_scratch, 0, sizeof(local_scratch));
+ scratch = &local_scratch;
+ CHECK(!secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0));
+ CHECK(ecount == 3);
+ CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
+ CHECK(ecount == 4);
+ secp256k1_scratch_space_destroy(none, scratch);
+ CHECK(ecount == 5);
/* cleanup */
- secp256k1_scratch_space_destroy(scratch);
+ secp256k1_scratch_space_destroy(none, NULL); /* no-op */
secp256k1_context_destroy(none);
}
@@ -965,11 +1072,31 @@ void scalar_test(void) {
}
+void run_scalar_set_b32_seckey_tests(void) {
+ unsigned char b32[32];
+ secp256k1_scalar s1;
+ secp256k1_scalar s2;
+
+ /* Usually set_b32 and set_b32_seckey give the same result */
+ random_scalar_order_b32(b32);
+ secp256k1_scalar_set_b32(&s1, b32, NULL);
+ CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 1);
+ CHECK(secp256k1_scalar_eq(&s1, &s2) == 1);
+
+ memset(b32, 0, sizeof(b32));
+ CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 0);
+ memset(b32, 0xFF, sizeof(b32));
+ CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 0);
+}
+
void run_scalar_tests(void) {
int i;
for (i = 0; i < 128 * count; i++) {
scalar_test();
}
+ for (i = 0; i < count; i++) {
+ run_scalar_set_b32_seckey_tests();
+ }
{
/* (-1)+1 should be zero. */
@@ -985,16 +1112,43 @@ void run_scalar_tests(void) {
#ifndef USE_NUM_NONE
{
- /* A scalar with value of the curve order should be 0. */
+ /* Test secp256k1_scalar_set_b32 boundary conditions */
secp256k1_num order;
- secp256k1_scalar zero;
+ secp256k1_scalar scalar;
unsigned char bin[32];
+ unsigned char bin_tmp[32];
int overflow = 0;
+ /* 2^256-1 - order */
+ static const secp256k1_scalar all_ones_minus_order = SECP256K1_SCALAR_CONST(
+ 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000001UL,
+ 0x45512319UL, 0x50B75FC4UL, 0x402DA173UL, 0x2FC9BEBEUL
+ );
+
+ /* A scalar set to 0s should be 0. */
+ memset(bin, 0, 32);
+ secp256k1_scalar_set_b32(&scalar, bin, &overflow);
+ CHECK(overflow == 0);
+ CHECK(secp256k1_scalar_is_zero(&scalar));
+
+ /* A scalar with value of the curve order should be 0. */
secp256k1_scalar_order_get_num(&order);
secp256k1_num_get_bin(bin, 32, &order);
- secp256k1_scalar_set_b32(&zero, bin, &overflow);
+ secp256k1_scalar_set_b32(&scalar, bin, &overflow);
+ CHECK(overflow == 1);
+ CHECK(secp256k1_scalar_is_zero(&scalar));
+
+ /* A scalar with value of the curve order minus one should not overflow. */
+ bin[31] -= 1;
+ secp256k1_scalar_set_b32(&scalar, bin, &overflow);
+ CHECK(overflow == 0);
+ secp256k1_scalar_get_b32(bin_tmp, &scalar);
+ CHECK(memcmp(bin, bin_tmp, 32) == 0);
+
+ /* A scalar set to all 1s should overflow. */
+ memset(bin, 0xFF, 32);
+ secp256k1_scalar_set_b32(&scalar, bin, &overflow);
CHECK(overflow == 1);
- CHECK(secp256k1_scalar_is_zero(&zero));
+ CHECK(secp256k1_scalar_eq(&scalar, &all_ones_minus_order));
}
#endif
@@ -1709,24 +1863,32 @@ void run_field_misc(void) {
/* Test fe conditional move; z is not normalized here. */
q = x;
secp256k1_fe_cmov(&x, &z, 0);
- VERIFY_CHECK(!x.normalized && x.magnitude == z.magnitude);
+#ifdef VERIFY
+ CHECK(x.normalized && x.magnitude == 1);
+#endif
secp256k1_fe_cmov(&x, &x, 1);
CHECK(fe_memcmp(&x, &z) != 0);
CHECK(fe_memcmp(&x, &q) == 0);
secp256k1_fe_cmov(&q, &z, 1);
- VERIFY_CHECK(!q.normalized && q.magnitude == z.magnitude);
+#ifdef VERIFY
+ CHECK(!q.normalized && q.magnitude == z.magnitude);
+#endif
CHECK(fe_memcmp(&q, &z) == 0);
secp256k1_fe_normalize_var(&x);
secp256k1_fe_normalize_var(&z);
CHECK(!secp256k1_fe_equal_var(&x, &z));
secp256k1_fe_normalize_var(&q);
secp256k1_fe_cmov(&q, &z, (i&1));
- VERIFY_CHECK(q.normalized && q.magnitude == 1);
+#ifdef VERIFY
+ CHECK(q.normalized && q.magnitude == 1);
+#endif
for (j = 0; j < 6; j++) {
secp256k1_fe_negate(&z, &z, j+1);
secp256k1_fe_normalize_var(&q);
secp256k1_fe_cmov(&q, &z, (j&1));
- VERIFY_CHECK(!q.normalized && q.magnitude == (j+2));
+#ifdef VERIFY
+ CHECK((q.normalized != (j&1)) && q.magnitude == ((j&1) ? z.magnitude : 1));
+#endif
}
secp256k1_fe_normalize_var(&z);
/* Test storage conversion and conditional moves. */
@@ -2120,7 +2282,7 @@ void test_ge(void) {
/* 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 */
+ /* randomly set half the points to infinity */
if(secp256k1_fe_is_odd(&ge[i].x)) {
secp256k1_ge_set_infinity(&ge[i]);
}
@@ -2572,14 +2734,13 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
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(ecmult_multi(&ctx->error_callback, &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++) {
@@ -2595,36 +2756,31 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
/* 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));
+ CHECK(ecmult_multi(&ctx->error_callback, &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));
+ CHECK(ecmult_multi(&ctx->error_callback, &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));
+ CHECK(!ecmult_multi(&ctx->error_callback, &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));
+ CHECK(ecmult_multi(&ctx->error_callback, &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));
+ CHECK(ecmult_multi(&ctx->error_callback, &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));
@@ -2641,7 +2797,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
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(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r));
}
@@ -2651,7 +2807,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
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(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r));
}
@@ -2664,7 +2820,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
pt[2 * i + 1] = ptg;
}
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r));
random_scalar_order(&sc[0]);
@@ -2677,7 +2833,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
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(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r));
}
@@ -2692,7 +2848,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
secp256k1_scalar_negate(&sc[i], &sc[i]);
}
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32));
CHECK(secp256k1_gej_is_infinity(&r));
}
@@ -2711,7 +2867,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
}
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r, &sc[0], &szero);
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
+ CHECK(ecmult_multi(&ctx->error_callback, &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));
@@ -2734,7 +2890,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
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));
+ CHECK(ecmult_multi(&ctx->error_callback, &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));
@@ -2747,13 +2903,13 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
}
secp256k1_scalar_clear(&sc[0]);
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
+ CHECK(ecmult_multi(&ctx->error_callback, &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(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 6));
+ CHECK(ecmult_multi(&ctx->error_callback, &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 */
@@ -2798,7 +2954,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
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));
+ CHECK(ecmult_multi(&ctx->error_callback, &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));
@@ -2809,6 +2965,24 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
}
}
+void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) {
+ secp256k1_scalar szero;
+ secp256k1_scalar sc[32];
+ secp256k1_ge pt[32];
+ secp256k1_gej r;
+ ecmult_multi_data data;
+ secp256k1_scratch *scratch_empty;
+
+ data.sc = sc;
+ data.pt = pt;
+ secp256k1_scalar_set_int(&szero, 0);
+
+ /* Try to multiply 1 point, but scratch space is empty.*/
+ scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0);
+ CHECK(!ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1));
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch_empty);
+}
+
void test_secp256k1_pippenger_bucket_window_inv(void) {
int i;
@@ -2839,17 +3013,27 @@ void test_ecmult_multi_pippenger_max_points(void) {
int bucket_window = 0;
for(; scratch_size < max_size; scratch_size+=256) {
+ size_t i;
+ size_t total_alloc;
+ size_t checkpoint;
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
CHECK(scratch != NULL);
- n_points_supported = secp256k1_pippenger_max_points(scratch);
+ checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch);
+ n_points_supported = secp256k1_pippenger_max_points(&ctx->error_callback, scratch);
if (n_points_supported == 0) {
- secp256k1_scratch_destroy(scratch);
+ secp256k1_scratch_destroy(&ctx->error_callback, 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);
+ /* allocate `total_alloc` bytes over `PIPPENGER_SCRATCH_OBJECTS` many allocations */
+ total_alloc = secp256k1_pippenger_scratch_size(n_points_supported, bucket_window);
+ for (i = 0; i < PIPPENGER_SCRATCH_OBJECTS - 1; i++) {
+ CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, 1));
+ total_alloc--;
+ }
+ CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, total_alloc));
+ secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, checkpoint);
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
}
CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW);
}
@@ -2932,19 +3116,25 @@ void test_ecmult_multi_batching(void) {
}
data.sc = sc;
data.pt = pt;
+ secp256k1_gej_neg(&r2, &r2);
- /* Test with empty scratch space */
+ /* Test with empty scratch space. It should compute the correct result using
+ * ecmult_mult_simple algorithm which doesn't require a 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);
+ CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, &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(&ctx->error_callback, scratch);
/* Test with space for 1 point in pippenger. That's not enough because
- * ecmult_multi selects strauss which requires more memory. */
+ * ecmult_multi selects strauss which requires more memory. It should
+ * therefore select the simple algorithm. */
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);
+ CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, &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(&ctx->error_callback, 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);
@@ -2954,10 +3144,10 @@ void test_ecmult_multi_batching(void) {
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));
+ CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, &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);
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
}
free(sc);
free(pt);
@@ -2972,13 +3162,15 @@ void run_ecmult_multi_tests(void) {
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_batch_single(secp256k1_ecmult_pippenger_batch_single);
test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single);
- secp256k1_scratch_destroy(scratch);
+ test_ecmult_multi_batch_single(secp256k1_ecmult_strauss_batch_single);
+ secp256k1_scratch_destroy(&ctx->error_callback, 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);
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
test_ecmult_multi_batch_size_helper();
test_ecmult_multi_batching();
@@ -3050,7 +3242,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
}
bits = 128;
#endif
- skew = secp256k1_wnaf_const(wnaf, num, w, bits);
+ skew = secp256k1_wnaf_const(wnaf, &num, w, bits);
for (i = WNAF_SIZE_BITS(bits, w); i >= 0; --i) {
secp256k1_scalar t;
@@ -3786,37 +3978,57 @@ void run_eckey_edge_case_test(void) {
pubkey_negone = pubkey;
/* Tweak of zero leaves the value unchanged. */
memset(ctmp2, 0, 32);
- CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, ctmp2) == 1);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, ctmp2) == 1);
CHECK(memcmp(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40);
memcpy(&pubkey2, &pubkey, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1);
CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
/* Multiply tweak of zero zeroizes the output. */
- CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, ctmp2) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, ctmp2) == 0);
CHECK(memcmp(zeros, ctmp, 32) == 0);
CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, ctmp2) == 0);
CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
- /* Overflowing key tweak zeroizes. */
+ /* If seckey_tweak_add or seckey_tweak_mul are called with an overflowing
+ seckey, the seckey is zeroized. */
+ memcpy(ctmp, orderc, 32);
+ memset(ctmp2, 0, 32);
+ ctmp2[31] = 0x01;
+ CHECK(secp256k1_ec_seckey_verify(ctx, ctmp2) == 1);
+ CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, ctmp2) == 0);
+ CHECK(memcmp(zeros, ctmp, 32) == 0);
+ memcpy(ctmp, orderc, 32);
+ CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, ctmp2) == 0);
+ CHECK(memcmp(zeros, ctmp, 32) == 0);
+ /* If seckey_tweak_add or seckey_tweak_mul are called with an overflowing
+ tweak, the seckey is zeroized. */
memcpy(ctmp, orderc, 32);
ctmp[31] = 0x40;
- CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, orderc) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, orderc) == 0);
CHECK(memcmp(zeros, ctmp, 32) == 0);
memcpy(ctmp, orderc, 32);
ctmp[31] = 0x40;
- CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, orderc) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, orderc) == 0);
CHECK(memcmp(zeros, ctmp, 32) == 0);
memcpy(ctmp, orderc, 32);
ctmp[31] = 0x40;
+ /* If pubkey_tweak_add or pubkey_tweak_mul are called with an overflowing
+ tweak, the pubkey is zeroized. */
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, orderc) == 0);
CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, orderc) == 0);
CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
- /* Private key tweaks results in a key of zero. */
+ /* If the resulting key in secp256k1_ec_seckey_tweak_add and
+ * secp256k1_ec_pubkey_tweak_add is 0 the functions fail and in the latter
+ * case the pubkey is zeroized. */
+ memcpy(ctmp, orderc, 32);
+ ctmp[31] = 0x40;
+ memset(ctmp2, 0, 32);
ctmp2[31] = 1;
- CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp2, ctmp) == 0);
CHECK(memcmp(zeros, ctmp2, 32) == 0);
ctmp2[31] = 1;
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0);
@@ -3824,7 +4036,7 @@ void run_eckey_edge_case_test(void) {
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
/* Tweak computation wraps and results in a key of 1. */
ctmp2[31] = 2;
- CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 1);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp2, ctmp) == 1);
CHECK(memcmp(ctmp2, zeros, 31) == 0 && ctmp2[31] == 1);
ctmp2[31] = 2;
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1);
@@ -3872,16 +4084,16 @@ void run_eckey_edge_case_test(void) {
CHECK(ecount == 2);
ecount = 0;
memset(ctmp2, 0, 32);
- CHECK(secp256k1_ec_privkey_tweak_add(ctx, NULL, ctmp2) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, NULL, ctmp2) == 0);
CHECK(ecount == 1);
- CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, NULL) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, NULL) == 0);
CHECK(ecount == 2);
ecount = 0;
memset(ctmp2, 0, 32);
ctmp2[31] = 1;
- CHECK(secp256k1_ec_privkey_tweak_mul(ctx, NULL, ctmp2) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_mul(ctx, NULL, ctmp2) == 0);
CHECK(ecount == 1);
- CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, NULL) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, NULL) == 0);
CHECK(ecount == 2);
ecount = 0;
CHECK(secp256k1_ec_pubkey_create(ctx, NULL, ctmp) == 0);
@@ -3954,6 +4166,41 @@ void run_eckey_edge_case_test(void) {
secp256k1_context_set_illegal_callback(ctx, NULL, NULL);
}
+void run_eckey_negate_test(void) {
+ unsigned char seckey[32];
+ unsigned char seckey_tmp[32];
+
+ random_scalar_order_b32(seckey);
+ memcpy(seckey_tmp, seckey, 32);
+
+ /* Verify negation changes the key and changes it back */
+ CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 1);
+ CHECK(memcmp(seckey, seckey_tmp, 32) != 0);
+ CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 1);
+ CHECK(memcmp(seckey, seckey_tmp, 32) == 0);
+
+ /* Check that privkey alias gives same result */
+ CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 1);
+ CHECK(secp256k1_ec_privkey_negate(ctx, seckey_tmp) == 1);
+ CHECK(memcmp(seckey, seckey_tmp, 32) == 0);
+
+ /* Negating all 0s fails */
+ memset(seckey, 0, 32);
+ memset(seckey_tmp, 0, 32);
+ CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 0);
+ /* Check that seckey is not modified */
+ CHECK(memcmp(seckey, seckey_tmp, 32) == 0);
+
+ /* Negating an overflowing seckey fails and the seckey is zeroed. In this
+ * test, the seckey has 16 random bytes to ensure that ec_seckey_negate
+ * doesn't just set seckey to a constant value in case of failure. */
+ random_scalar_order_b32(seckey);
+ memset(seckey, 0xFF, 16);
+ memset(seckey_tmp, 0, 32);
+ CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 0);
+ CHECK(memcmp(seckey, seckey_tmp, 32) == 0);
+}
+
void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) {
secp256k1_scalar nonce;
do {
@@ -4093,15 +4340,22 @@ void test_ecdsa_end_to_end(void) {
if (secp256k1_rand_int(3) == 0) {
int ret1;
int ret2;
+ int ret3;
unsigned char rnd[32];
+ unsigned char privkey_tmp[32];
secp256k1_pubkey pubkey2;
secp256k1_rand256_test(rnd);
- ret1 = secp256k1_ec_privkey_tweak_add(ctx, privkey, rnd);
+ memcpy(privkey_tmp, privkey, 32);
+ ret1 = secp256k1_ec_seckey_tweak_add(ctx, privkey, rnd);
ret2 = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, rnd);
+ /* Check that privkey alias gives same result */
+ ret3 = secp256k1_ec_privkey_tweak_add(ctx, privkey_tmp, rnd);
CHECK(ret1 == ret2);
+ CHECK(ret2 == ret3);
if (ret1 == 0) {
return;
}
+ CHECK(memcmp(privkey, privkey_tmp, 32) == 0);
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1);
CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
}
@@ -4110,15 +4364,22 @@ void test_ecdsa_end_to_end(void) {
if (secp256k1_rand_int(3) == 0) {
int ret1;
int ret2;
+ int ret3;
unsigned char rnd[32];
+ unsigned char privkey_tmp[32];
secp256k1_pubkey pubkey2;
secp256k1_rand256_test(rnd);
- ret1 = secp256k1_ec_privkey_tweak_mul(ctx, privkey, rnd);
+ memcpy(privkey_tmp, privkey, 32);
+ ret1 = secp256k1_ec_seckey_tweak_mul(ctx, privkey, rnd);
ret2 = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, rnd);
+ /* Check that privkey alias gives same result */
+ ret3 = secp256k1_ec_privkey_tweak_mul(ctx, privkey_tmp, rnd);
CHECK(ret1 == ret2);
+ CHECK(ret2 == ret3);
if (ret1 == 0) {
return;
}
+ CHECK(memcmp(privkey, privkey_tmp, 32) == 0);
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1);
CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
}
@@ -4315,7 +4576,7 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_
if (valid_der) {
ret |= (!roundtrips_der_lax) << 12;
ret |= (len_der != len_der_lax) << 13;
- ret |= (memcmp(roundtrip_der_lax, roundtrip_der, len_der) != 0) << 14;
+ ret |= ((len_der != len_der_lax) || (memcmp(roundtrip_der_lax, roundtrip_der, len_der) != 0)) << 14;
}
ret |= (roundtrips_der != roundtrips_der_lax) << 15;
if (parsed_der) {
@@ -4356,7 +4617,7 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_
ret |= (roundtrips_der != roundtrips_openssl) << 7;
if (roundtrips_openssl) {
ret |= (len_der != (size_t)len_openssl) << 8;
- ret |= (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0) << 9;
+ ret |= ((len_der != (size_t)len_openssl) || (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0)) << 9;
}
#endif
return ret;
@@ -5016,9 +5277,188 @@ void run_ecdsa_openssl(void) {
# include "modules/recovery/tests_impl.h"
#endif
+void run_memczero_test(void) {
+ unsigned char buf1[6] = {1, 2, 3, 4, 5, 6};
+ unsigned char buf2[sizeof(buf1)];
+
+ /* memczero(..., ..., 0) is a noop. */
+ memcpy(buf2, buf1, sizeof(buf1));
+ memczero(buf1, sizeof(buf1), 0);
+ CHECK(memcmp(buf1, buf2, sizeof(buf1)) == 0);
+
+ /* memczero(..., ..., 1) zeros the buffer. */
+ memset(buf2, 0, sizeof(buf2));
+ memczero(buf1, sizeof(buf1) , 1);
+ CHECK(memcmp(buf1, buf2, sizeof(buf1)) == 0);
+}
+
+void int_cmov_test(void) {
+ int r = INT_MAX;
+ int a = 0;
+
+ secp256k1_int_cmov(&r, &a, 0);
+ CHECK(r == INT_MAX);
+
+ r = 0; a = INT_MAX;
+ secp256k1_int_cmov(&r, &a, 1);
+ CHECK(r == INT_MAX);
+
+ a = 0;
+ secp256k1_int_cmov(&r, &a, 1);
+ CHECK(r == 0);
+
+ a = 1;
+ secp256k1_int_cmov(&r, &a, 1);
+ CHECK(r == 1);
+
+ r = 1; a = 0;
+ secp256k1_int_cmov(&r, &a, 0);
+ CHECK(r == 1);
+
+}
+
+void fe_cmov_test(void) {
+ static const secp256k1_fe zero = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0);
+ static const secp256k1_fe one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
+ static const secp256k1_fe max = SECP256K1_FE_CONST(
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL
+ );
+ secp256k1_fe r = max;
+ secp256k1_fe a = zero;
+
+ secp256k1_fe_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ r = zero; a = max;
+ secp256k1_fe_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ a = zero;
+ secp256k1_fe_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &zero, sizeof(r)) == 0);
+
+ a = one;
+ secp256k1_fe_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+
+ r = one; a = zero;
+ secp256k1_fe_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+}
+
+void fe_storage_cmov_test(void) {
+ static const secp256k1_fe_storage zero = SECP256K1_FE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 0);
+ static const secp256k1_fe_storage one = SECP256K1_FE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
+ static const secp256k1_fe_storage max = SECP256K1_FE_STORAGE_CONST(
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL
+ );
+ secp256k1_fe_storage r = max;
+ secp256k1_fe_storage a = zero;
+
+ secp256k1_fe_storage_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ r = zero; a = max;
+ secp256k1_fe_storage_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ a = zero;
+ secp256k1_fe_storage_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &zero, sizeof(r)) == 0);
+
+ a = one;
+ secp256k1_fe_storage_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+
+ r = one; a = zero;
+ secp256k1_fe_storage_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+}
+
+void scalar_cmov_test(void) {
+ static const secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
+ static const secp256k1_scalar one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1);
+ static const secp256k1_scalar max = SECP256K1_SCALAR_CONST(
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL
+ );
+ secp256k1_scalar r = max;
+ secp256k1_scalar a = zero;
+
+ secp256k1_scalar_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ r = zero; a = max;
+ secp256k1_scalar_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ a = zero;
+ secp256k1_scalar_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &zero, sizeof(r)) == 0);
+
+ a = one;
+ secp256k1_scalar_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+
+ r = one; a = zero;
+ secp256k1_scalar_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+}
+
+void ge_storage_cmov_test(void) {
+ static const secp256k1_ge_storage zero = SECP256K1_GE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ static const secp256k1_ge_storage one = SECP256K1_GE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1);
+ static const secp256k1_ge_storage max = SECP256K1_GE_STORAGE_CONST(
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL
+ );
+ secp256k1_ge_storage r = max;
+ secp256k1_ge_storage a = zero;
+
+ secp256k1_ge_storage_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ r = zero; a = max;
+ secp256k1_ge_storage_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ a = zero;
+ secp256k1_ge_storage_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &zero, sizeof(r)) == 0);
+
+ a = one;
+ secp256k1_ge_storage_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+
+ r = one; a = zero;
+ secp256k1_ge_storage_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+}
+
+void run_cmov_tests(void) {
+ int_cmov_test();
+ fe_cmov_test();
+ fe_storage_cmov_test();
+ scalar_cmov_test();
+ ge_storage_cmov_test();
+}
+
int main(int argc, char **argv) {
unsigned char seed16[16] = {0};
unsigned char run32[32] = {0};
+
+ /* Disable buffering for stdout to improve reliability of getting
+ * diagnostic information. Happens right at the start of main because
+ * setbuf must be used before any other operation on the stream. */
+ setbuf(stdout, NULL);
+ /* Also disable buffering for stderr because it's not guaranteed that it's
+ * unbuffered on all systems. */
+ setbuf(stderr, NULL);
+
/* find iteration count */
if (argc > 1) {
count = strtol(argv[1], NULL, 0);
@@ -5030,7 +5470,7 @@ int main(int argc, char **argv) {
const char* ch = argv[2];
while (pos < 16 && ch[0] != 0 && ch[1] != 0) {
unsigned short sh;
- if (sscanf(ch, "%2hx", &sh)) {
+ if ((sscanf(ch, "%2hx", &sh)) == 1) {
seed16[pos] = sh;
} else {
break;
@@ -5062,7 +5502,8 @@ int main(int argc, char **argv) {
printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]);
/* initialize */
- run_context_tests();
+ run_context_tests(0);
+ run_context_tests(1);
run_scratch_tests();
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
if (secp256k1_rand_bits(1)) {
@@ -5119,6 +5560,9 @@ int main(int argc, char **argv) {
/* EC key edge cases */
run_eckey_edge_case_test();
+ /* EC key arithmetic test */
+ run_eckey_negate_test();
+
#ifdef ENABLE_MODULE_ECDH
/* ecdh tests */
run_ecdh_tests();
@@ -5139,6 +5583,11 @@ int main(int argc, char **argv) {
run_recovery_tests();
#endif
+ /* util tests */
+ run_memczero_test();
+
+ run_cmov_tests();
+
secp256k1_rand256(run32);
printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]);
diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c
index ab9779b02f..8cca1cef21 100644
--- a/src/secp256k1/src/tests_exhaustive.c
+++ b/src/secp256k1/src/tests_exhaustive.c
@@ -142,7 +142,7 @@ void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *gr
for (i = 0; i < order; i++) {
secp256k1_gej tmp;
if (i > 0) {
- secp256k1_gej_double_nonzero(&tmp, &groupj[i], NULL);
+ secp256k1_gej_double_nonzero(&tmp, &groupj[i]);
ge_equals_gej(&group[(2 * i) % order], &tmp);
}
secp256k1_gej_double_var(&tmp, &groupj[i], NULL);
@@ -212,14 +212,14 @@ void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_
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);
+ secp256k1_ecmult_multi_var(&ctx->error_callback, &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);
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
}
void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) {
diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h
index e1f5b76452..8289e23e0c 100644
--- a/src/secp256k1/src/util.h
+++ b/src/secp256k1/src/util.h
@@ -14,6 +14,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
+#include <limits.h>
typedef struct {
void (*fn)(const char *text, void* data);
@@ -68,6 +69,25 @@ static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback *
#define VERIFY_SETUP(stmt)
#endif
+/* Define `VG_UNDEF` and `VG_CHECK` when VALGRIND is defined */
+#if !defined(VG_CHECK)
+# if defined(VALGRIND)
+# include <valgrind/memcheck.h>
+# define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y))
+# define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y))
+# else
+# define VG_UNDEF(x,y)
+# define VG_CHECK(x,y)
+# endif
+#endif
+
+/* Like `VG_CHECK` but on VERIFY only */
+#if defined(VERIFY)
+#define VG_CHECK_VERIFY(x,y) VG_CHECK((x), (y))
+#else
+#define VG_CHECK_VERIFY(x,y)
+#endif
+
static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_t size) {
void *ret = malloc(size);
if (ret == NULL) {
@@ -84,6 +104,47 @@ static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void
return ret;
}
+#if defined(__BIGGEST_ALIGNMENT__)
+#define ALIGNMENT __BIGGEST_ALIGNMENT__
+#else
+/* 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. */
+#define ALIGNMENT 16
+#endif
+
+#define ROUND_TO_ALIGN(size) (((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT)
+
+/* Assume there is a contiguous memory object with bounds [base, base + max_size)
+ * of which the memory range [base, *prealloc_ptr) is already allocated for usage,
+ * where *prealloc_ptr is an aligned pointer. In that setting, this functions
+ * reserves the subobject [*prealloc_ptr, *prealloc_ptr + alloc_size) of
+ * alloc_size bytes by increasing *prealloc_ptr accordingly, taking into account
+ * alignment requirements.
+ *
+ * The function returns an aligned pointer to the newly allocated subobject.
+ *
+ * This is useful for manual memory management: if we're simply given a block
+ * [base, base + max_size), the caller can use this function to allocate memory
+ * in this block and keep track of the current allocation state with *prealloc_ptr.
+ *
+ * It is VERIFY_CHECKed that there is enough space left in the memory object and
+ * *prealloc_ptr is aligned relative to base.
+ */
+static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_size, void* base, size_t max_size) {
+ size_t aligned_alloc_size = ROUND_TO_ALIGN(alloc_size);
+ void* ret;
+ VERIFY_CHECK(prealloc_ptr != NULL);
+ VERIFY_CHECK(*prealloc_ptr != NULL);
+ VERIFY_CHECK(base != NULL);
+ VERIFY_CHECK((unsigned char*)*prealloc_ptr >= (unsigned char*)base);
+ VERIFY_CHECK(((unsigned char*)*prealloc_ptr - (unsigned char*)base) % ALIGNMENT == 0);
+ VERIFY_CHECK((unsigned char*)*prealloc_ptr - (unsigned char*)base + aligned_alloc_size <= max_size);
+ ret = *prealloc_ptr;
+ *((unsigned char**)prealloc_ptr) += aligned_alloc_size;
+ return ret;
+}
+
/* Macro for restrict, when available and not in a VERIFY build. */
#if defined(SECP256K1_BUILD) && defined(VERIFY)
# define SECP256K1_RESTRICT
@@ -118,4 +179,33 @@ static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void
SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t;
#endif
+/* Zero memory if flag == 1. Flag must be 0 or 1. Constant time. */
+static SECP256K1_INLINE void memczero(void *s, size_t len, int flag) {
+ unsigned char *p = (unsigned char *)s;
+ /* Access flag with a volatile-qualified lvalue.
+ This prevents clang from figuring out (after inlining) that flag can
+ take only be 0 or 1, which leads to variable time code. */
+ volatile int vflag = flag;
+ unsigned char mask = -(unsigned char) vflag;
+ while (len) {
+ *p &= ~mask;
+ p++;
+ len--;
+ }
+}
+
+/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized and non-negative.*/
+static SECP256K1_INLINE void secp256k1_int_cmov(int *r, const int *a, int flag) {
+ unsigned int mask0, mask1, r_masked, a_masked;
+ /* Casting a negative int to unsigned and back to int is implementation defined behavior */
+ VERIFY_CHECK(*r >= 0 && *a >= 0);
+
+ mask0 = (unsigned int)flag + ~0u;
+ mask1 = ~mask0;
+ r_masked = ((unsigned int)*r & mask0);
+ a_masked = ((unsigned int)*a & mask1);
+
+ *r = (int)(r_masked | a_masked);
+}
+
#endif /* SECP256K1_UTIL_H */
diff --git a/src/secp256k1/src/valgrind_ctime_test.c b/src/secp256k1/src/valgrind_ctime_test.c
new file mode 100644
index 0000000000..60a82d599e
--- /dev/null
+++ b/src/secp256k1/src/valgrind_ctime_test.c
@@ -0,0 +1,119 @@
+/**********************************************************************
+ * Copyright (c) 2020 Gregory Maxwell *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#include <valgrind/memcheck.h>
+#include "include/secp256k1.h"
+#include "util.h"
+
+#if ENABLE_MODULE_ECDH
+# include "include/secp256k1_ecdh.h"
+#endif
+
+#if ENABLE_MODULE_RECOVERY
+# include "include/secp256k1_recovery.h"
+#endif
+
+int main(void) {
+ secp256k1_context* ctx;
+ secp256k1_ecdsa_signature signature;
+ secp256k1_pubkey pubkey;
+ size_t siglen = 74;
+ size_t outputlen = 33;
+ int i;
+ int ret;
+ unsigned char msg[32];
+ unsigned char key[32];
+ unsigned char sig[74];
+ unsigned char spubkey[33];
+#if ENABLE_MODULE_RECOVERY
+ secp256k1_ecdsa_recoverable_signature recoverable_signature;
+ int recid;
+#endif
+
+ if (!RUNNING_ON_VALGRIND) {
+ fprintf(stderr, "This test can only usefully be run inside valgrind.\n");
+ fprintf(stderr, "Usage: libtool --mode=execute valgrind ./valgrind_ctime_test\n");
+ exit(1);
+ }
+
+ /** In theory, testing with a single secret input should be sufficient:
+ * If control flow depended on secrets the tool would generate an error.
+ */
+ for (i = 0; i < 32; i++) {
+ key[i] = i + 65;
+ }
+ for (i = 0; i < 32; i++) {
+ msg[i] = i + 1;
+ }
+
+ ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_DECLASSIFY);
+
+ /* Test keygen. */
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_ec_pubkey_create(ctx, &pubkey, key);
+ VALGRIND_MAKE_MEM_DEFINED(&pubkey, sizeof(secp256k1_pubkey));
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret);
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, spubkey, &outputlen, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
+
+ /* Test signing. */
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_ecdsa_sign(ctx, &signature, msg, key, NULL, NULL);
+ VALGRIND_MAKE_MEM_DEFINED(&signature, sizeof(secp256k1_ecdsa_signature));
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret);
+ CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature));
+
+#if ENABLE_MODULE_ECDH
+ /* Test ECDH. */
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_ecdh(ctx, msg, &pubkey, key, NULL, NULL);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+#endif
+
+#if ENABLE_MODULE_RECOVERY
+ /* Test signing a recoverable signature. */
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_ecdsa_sign_recoverable(ctx, &recoverable_signature, msg, key, NULL, NULL);
+ VALGRIND_MAKE_MEM_DEFINED(&recoverable_signature, sizeof(recoverable_signature));
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret);
+ CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &recoverable_signature));
+ CHECK(recid >= 0 && recid <= 3);
+#endif
+
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_ec_seckey_verify(ctx, key);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_ec_seckey_negate(ctx, key);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ VALGRIND_MAKE_MEM_UNDEFINED(msg, 32);
+ ret = secp256k1_ec_seckey_tweak_add(ctx, key, msg);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ VALGRIND_MAKE_MEM_UNDEFINED(msg, 32);
+ ret = secp256k1_ec_seckey_tweak_mul(ctx, key, msg);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+
+ /* Test context randomisation. Do this last because it leaves the context tainted. */
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_context_randomize(ctx, key);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret);
+
+ secp256k1_context_destroy(ctx);
+ return 0;
+}
diff --git a/src/serialize.h b/src/serialize.h
index fe53eeed31..7a94e704b2 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -9,13 +9,13 @@
#include <compat/endian.h>
#include <algorithm>
+#include <cstdint>
#include <cstring>
#include <ios>
#include <limits>
#include <map>
#include <memory>
#include <set>
-#include <stdint.h>
#include <string>
#include <string.h>
#include <utility>
@@ -43,26 +43,6 @@ static const unsigned int MAX_VECTOR_ALLOCATE = 5000000;
struct deserialize_type {};
constexpr deserialize_type deserialize {};
-/**
- * Used to bypass the rule against non-const reference to temporary
- * where it makes sense with wrappers.
- */
-template<typename T>
-inline T& REF(const T& val)
-{
- return const_cast<T&>(val);
-}
-
-/**
- * Used to acquire a non-const pointer "this" to generate bodies
- * of const serialization operations from a template
- */
-template<typename T>
-inline T* NCONST_PTR(const T* val)
-{
- return const_cast<T*>(val);
-}
-
//! Safely convert odd char pointer types to standard ones.
inline char* CharCast(char* c) { return c; }
inline char* CharCast(unsigned char* c) { return (char*)c; }
@@ -190,22 +170,8 @@ template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; }
#define READWRITE(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__))
#define READWRITEAS(type, obj) (::SerReadWriteMany(s, ser_action, ReadWriteAsHelper<type>(obj)))
-
-/**
- * Implement three methods for serializable objects. These are actually wrappers over
- * "SerializationOp" template, which implements the body of each class' serialization
- * code. Adding "ADD_SERIALIZE_METHODS" in the body of the class causes these wrappers to be
- * added as members.
- */
-#define ADD_SERIALIZE_METHODS \
- template<typename Stream> \
- void Serialize(Stream& s) const { \
- NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize()); \
- } \
- template<typename Stream> \
- void Unserialize(Stream& s) { \
- SerializationOp(s, CSerActionUnserialize()); \
- }
+#define SER_READ(obj, code) ::SerRead(s, ser_action, obj, [&](Stream& s, typename std::remove_const<Type>::type& obj) { code; })
+#define SER_WRITE(obj, code) ::SerWrite(s, ser_action, obj, [&](Stream& s, const Type& obj) { code; })
/**
* Implement the Ser and Unser methods needed for implementing a formatter (see Using below).
@@ -306,7 +272,7 @@ template<typename Stream> inline void Unserialize(Stream& s, bool& a) { char f=s
inline unsigned int GetSizeOfCompactSize(uint64_t nSize)
{
if (nSize < 253) return sizeof(unsigned char);
- else if (nSize <= std::numeric_limits<unsigned short>::max()) return sizeof(unsigned char) + sizeof(unsigned short);
+ else if (nSize <= std::numeric_limits<uint16_t>::max()) return sizeof(unsigned char) + sizeof(uint16_t);
else if (nSize <= std::numeric_limits<unsigned int>::max()) return sizeof(unsigned char) + sizeof(unsigned int);
else return sizeof(unsigned char) + sizeof(uint64_t);
}
@@ -320,7 +286,7 @@ void WriteCompactSize(Stream& os, uint64_t nSize)
{
ser_writedata8(os, nSize);
}
- else if (nSize <= std::numeric_limits<unsigned short>::max())
+ else if (nSize <= std::numeric_limits<uint16_t>::max())
{
ser_writedata8(os, 253);
ser_writedata16(os, nSize);
@@ -501,7 +467,7 @@ static inline Wrapper<Formatter, T&> Using(T&& t) { return Wrapper<Formatter, T&
#define VARINT_MODE(obj, mode) Using<VarIntFormatter<mode>>(obj)
#define VARINT(obj) Using<VarIntFormatter<VarIntMode::DEFAULT>>(obj)
#define COMPACTSIZE(obj) Using<CompactSizeFormatter>(obj)
-#define LIMITED_STRING(obj,n) LimitedString< n >(REF(obj))
+#define LIMITED_STRING(obj,n) Using<LimitedStringFormatter<n>>(obj)
/** Serialization wrapper class for integers in VarInt format. */
template<VarIntMode Mode>
@@ -518,7 +484,16 @@ struct VarIntFormatter
}
};
-template<int Bytes>
+/** Serialization wrapper class for custom integers and enums.
+ *
+ * It permits specifying the serialized size (1 to 8 bytes) and endianness.
+ *
+ * Use the big endian mode for values that are stored in memory in native
+ * byte order, but serialized in big endian notation. This is only intended
+ * to implement serializers that are compatible with existing formats, and
+ * its use is not recommended for new data structures.
+ */
+template<int Bytes, bool BigEndian = false>
struct CustomUintFormatter
{
static_assert(Bytes > 0 && Bytes <= 8, "CustomUintFormatter Bytes out of range");
@@ -527,52 +502,31 @@ struct CustomUintFormatter
template <typename Stream, typename I> void Ser(Stream& s, I v)
{
if (v < 0 || v > MAX) throw std::ios_base::failure("CustomUintFormatter value out of range");
- uint64_t raw = htole64(v);
- s.write((const char*)&raw, Bytes);
+ if (BigEndian) {
+ uint64_t raw = htobe64(v);
+ s.write(((const char*)&raw) + 8 - Bytes, Bytes);
+ } else {
+ uint64_t raw = htole64(v);
+ s.write((const char*)&raw, Bytes);
+ }
}
template <typename Stream, typename I> void Unser(Stream& s, I& v)
{
- static_assert(std::numeric_limits<I>::max() >= MAX && std::numeric_limits<I>::min() <= 0, "CustomUintFormatter type too small");
+ using U = typename std::conditional<std::is_enum<I>::value, std::underlying_type<I>, std::common_type<I>>::type::type;
+ static_assert(std::numeric_limits<U>::max() >= MAX && std::numeric_limits<U>::min() <= 0, "Assigned type too small");
uint64_t raw = 0;
- s.read((char*)&raw, Bytes);
- v = le64toh(raw);
+ if (BigEndian) {
+ s.read(((char*)&raw) + 8 - Bytes, Bytes);
+ v = static_cast<I>(be64toh(raw));
+ } else {
+ s.read((char*)&raw, Bytes);
+ v = static_cast<I>(le64toh(raw));
+ }
}
};
-/** Serialization wrapper class for big-endian integers.
- *
- * Use this wrapper around integer types that are stored in memory in native
- * byte order, but serialized in big endian notation. This is only intended
- * to implement serializers that are compatible with existing formats, and
- * its use is not recommended for new data structures.
- *
- * Only 16-bit types are supported for now.
- */
-template<typename I>
-class BigEndian
-{
-protected:
- I& m_val;
-public:
- explicit BigEndian(I& val) : m_val(val)
- {
- static_assert(std::is_unsigned<I>::value, "BigEndian type must be unsigned integer");
- static_assert(sizeof(I) == 2 && std::numeric_limits<I>::min() == 0 && std::numeric_limits<I>::max() == std::numeric_limits<uint16_t>::max(), "Unsupported BigEndian size");
- }
-
- template<typename Stream>
- void Serialize(Stream& s) const
- {
- ser_writedata16be(s, m_val);
- }
-
- template<typename Stream>
- void Unserialize(Stream& s)
- {
- m_val = ser_readdata16be(s);
- }
-};
+template<int Bytes> using BigEndianFormatter = CustomUintFormatter<Bytes, true>;
/** Formatter for integers in CompactSize format. */
struct CompactSizeFormatter
@@ -598,37 +552,26 @@ struct CompactSizeFormatter
};
template<size_t Limit>
-class LimitedString
+struct LimitedStringFormatter
{
-protected:
- std::string& string;
-public:
- explicit LimitedString(std::string& _string) : string(_string) {}
-
template<typename Stream>
- void Unserialize(Stream& s)
+ void Unser(Stream& s, std::string& v)
{
size_t size = ReadCompactSize(s);
if (size > Limit) {
throw std::ios_base::failure("String length limit exceeded");
}
- string.resize(size);
- if (size != 0)
- s.read((char*)string.data(), size);
+ v.resize(size);
+ if (size != 0) s.read((char*)v.data(), size);
}
template<typename Stream>
- void Serialize(Stream& s) const
+ void Ser(Stream& s, const std::string& v)
{
- WriteCompactSize(s, string.size());
- if (!string.empty())
- s.write((char*)string.data(), string.size());
+ s << v;
}
};
-template<typename I>
-BigEndian<I> WrapBigEndian(I& n) { return BigEndian<I>(n); }
-
/** Formatter to serialize/deserialize vector elements using another formatter
*
* Example:
@@ -1025,7 +968,7 @@ void Unserialize(Stream& is, std::shared_ptr<const T>& p)
/**
- * Support for ADD_SERIALIZE_METHODS and READWRITE macro
+ * Support for SERIALIZE_METHODS and READWRITE macro.
*/
struct CSerActionSerialize
{
@@ -1124,6 +1067,28 @@ inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action, Args&&
::UnserializeMany(s, args...);
}
+template<typename Stream, typename Type, typename Fn>
+inline void SerRead(Stream& s, CSerActionSerialize ser_action, Type&&, Fn&&)
+{
+}
+
+template<typename Stream, typename Type, typename Fn>
+inline void SerRead(Stream& s, CSerActionUnserialize ser_action, Type&& obj, Fn&& fn)
+{
+ fn(s, std::forward<Type>(obj));
+}
+
+template<typename Stream, typename Type, typename Fn>
+inline void SerWrite(Stream& s, CSerActionSerialize ser_action, Type&& obj, Fn&& fn)
+{
+ fn(s, std::forward<Type>(obj));
+}
+
+template<typename Stream, typename Type, typename Fn>
+inline void SerWrite(Stream& s, CSerActionUnserialize ser_action, Type&&, Fn&&)
+{
+}
+
template<typename I>
inline void WriteVarInt(CSizeComputer &s, I n)
{
diff --git a/src/span.h b/src/span.h
index 73b37e35f3..841f1eadf7 100644
--- a/src/span.h
+++ b/src/span.h
@@ -10,20 +10,101 @@
#include <algorithm>
#include <assert.h>
+#ifdef DEBUG
+#define CONSTEXPR_IF_NOT_DEBUG
+#define ASSERT_IF_DEBUG(x) assert((x))
+#else
+#define CONSTEXPR_IF_NOT_DEBUG constexpr
+#define ASSERT_IF_DEBUG(x)
+#endif
+
/** A Span is an object that can refer to a contiguous sequence of objects.
*
* It implements a subset of C++20's std::span.
+ *
+ * Things to be aware of when writing code that deals with Spans:
+ *
+ * - Similar to references themselves, Spans are subject to reference lifetime
+ * issues. The user is responsible for making sure the objects pointed to by
+ * a Span live as long as the Span is used. For example:
+ *
+ * std::vector<int> vec{1,2,3,4};
+ * Span<int> sp(vec);
+ * vec.push_back(5);
+ * printf("%i\n", sp.front()); // UB!
+ *
+ * may exhibit undefined behavior, as increasing the size of a vector may
+ * invalidate references.
+ *
+ * - One particular pitfall is that Spans can be constructed from temporaries,
+ * but this is unsafe when the Span is stored in a variable, outliving the
+ * temporary. For example, this will compile, but exhibits undefined behavior:
+ *
+ * Span<const int> sp(std::vector<int>{1, 2, 3});
+ * printf("%i\n", sp.front()); // UB!
+ *
+ * The lifetime of the vector ends when the statement it is created in ends.
+ * Thus the Span is left with a dangling reference, and using it is undefined.
+ *
+ * - Due to Span's automatic creation from range-like objects (arrays, and data
+ * types that expose a data() and size() member function), functions that
+ * accept a Span as input parameter can be called with any compatible
+ * range-like object. For example, this works:
+*
+ * void Foo(Span<const int> arg);
+ *
+ * Foo(std::vector<int>{1, 2, 3}); // Works
+ *
+ * This is very useful in cases where a function truly does not care about the
+ * container, and only about having exactly a range of elements. However it
+ * may also be surprising to see automatic conversions in this case.
+ *
+ * When a function accepts a Span with a mutable element type, it will not
+ * accept temporaries; only variables or other references. For example:
+ *
+ * void FooMut(Span<int> arg);
+ *
+ * FooMut(std::vector<int>{1, 2, 3}); // Does not compile
+ * std::vector<int> baz{1, 2, 3};
+ * FooMut(baz); // Works
+ *
+ * This is similar to how functions that take (non-const) lvalue references
+ * as input cannot accept temporaries. This does not work either:
+ *
+ * void FooVec(std::vector<int>& arg);
+ * FooVec(std::vector<int>{1, 2, 3}); // Does not compile
+ *
+ * The idea is that if a function accepts a mutable reference, a meaningful
+ * result will be present in that variable after the call. Passing a temporary
+ * is useless in that context.
*/
template<typename C>
class Span
{
C* m_data;
- std::ptrdiff_t m_size;
+ std::size_t m_size;
public:
constexpr Span() noexcept : m_data(nullptr), m_size(0) {}
- constexpr Span(C* data, std::ptrdiff_t size) noexcept : m_data(data), m_size(size) {}
- constexpr Span(C* data, C* end) noexcept : m_data(data), m_size(end - data) {}
+
+ /** Construct a span from a begin pointer and a size.
+ *
+ * This implements a subset of the iterator-based std::span constructor in C++20,
+ * which is hard to implement without std::address_of.
+ */
+ template <typename T, typename std::enable_if<std::is_convertible<T (*)[], C (*)[]>::value, int>::type = 0>
+ constexpr Span(T* begin, std::size_t size) noexcept : m_data(begin), m_size(size) {}
+
+ /** Construct a span from a begin and end pointer.
+ *
+ * This implements a subset of the iterator-based std::span constructor in C++20,
+ * which is hard to implement without std::address_of.
+ */
+ template <typename T, typename std::enable_if<std::is_convertible<T (*)[], C (*)[]>::value, int>::type = 0>
+ CONSTEXPR_IF_NOT_DEBUG Span(T* begin, T* end) noexcept : m_data(begin), m_size(end - begin)
+ {
+ ASSERT_IF_DEBUG(end >= begin);
+ }
/** Implicit conversion of spans between compatible types.
*
@@ -42,18 +123,59 @@ public:
/** Default assignment operator. */
Span& operator=(const Span& other) noexcept = default;
+ /** Construct a Span from an array. This matches the corresponding C++20 std::span constructor. */
+ template <int N>
+ constexpr Span(C (&a)[N]) noexcept : m_data(a), m_size(N) {}
+
+ /** Construct a Span for objects with .data() and .size() (std::string, std::array, std::vector, ...).
+ *
+ * This implements a subset of the functionality provided by the C++20 std::span range-based constructor.
+ *
+ * To prevent surprises, only Spans for constant value types are supported when passing in temporaries.
+ * Note that this restriction does not exist when converting arrays or other Spans (see above).
+ */
+ template <typename V, typename std::enable_if<(std::is_const<C>::value || std::is_lvalue_reference<V>::value) && std::is_convertible<typename std::remove_pointer<decltype(std::declval<V&>().data())>::type (*)[], C (*)[]>::value && std::is_convertible<decltype(std::declval<V&>().size()), std::size_t>::value, int>::type = 0>
+ constexpr Span(V&& v) noexcept : m_data(v.data()), m_size(v.size()) {}
+
constexpr C* data() const noexcept { return m_data; }
constexpr C* begin() const noexcept { return m_data; }
constexpr C* end() const noexcept { return m_data + m_size; }
- constexpr C& front() const noexcept { return m_data[0]; }
- constexpr C& back() const noexcept { return m_data[m_size - 1]; }
- constexpr std::ptrdiff_t size() const noexcept { return m_size; }
- constexpr C& operator[](std::ptrdiff_t pos) const noexcept { return m_data[pos]; }
-
- constexpr Span<C> subspan(std::ptrdiff_t offset) const noexcept { return Span<C>(m_data + offset, m_size - offset); }
- constexpr Span<C> subspan(std::ptrdiff_t offset, std::ptrdiff_t count) const noexcept { return Span<C>(m_data + offset, count); }
- constexpr Span<C> first(std::ptrdiff_t count) const noexcept { return Span<C>(m_data, count); }
- constexpr Span<C> last(std::ptrdiff_t count) const noexcept { return Span<C>(m_data + m_size - count, count); }
+ CONSTEXPR_IF_NOT_DEBUG C& front() const noexcept
+ {
+ ASSERT_IF_DEBUG(size() > 0);
+ return m_data[0];
+ }
+ CONSTEXPR_IF_NOT_DEBUG C& back() const noexcept
+ {
+ ASSERT_IF_DEBUG(size() > 0);
+ return m_data[m_size - 1];
+ }
+ constexpr std::size_t size() const noexcept { return m_size; }
+ CONSTEXPR_IF_NOT_DEBUG C& operator[](std::size_t pos) const noexcept
+ {
+ ASSERT_IF_DEBUG(size() > pos);
+ return m_data[pos];
+ }
+ CONSTEXPR_IF_NOT_DEBUG Span<C> subspan(std::size_t offset) const noexcept
+ {
+ ASSERT_IF_DEBUG(size() >= offset);
+ return Span<C>(m_data + offset, m_size - offset);
+ }
+ CONSTEXPR_IF_NOT_DEBUG Span<C> subspan(std::size_t offset, std::size_t count) const noexcept
+ {
+ ASSERT_IF_DEBUG(size() >= offset + count);
+ return Span<C>(m_data + offset, count);
+ }
+ CONSTEXPR_IF_NOT_DEBUG Span<C> first(std::size_t count) const noexcept
+ {
+ ASSERT_IF_DEBUG(size() >= count);
+ return Span<C>(m_data, count);
+ }
+ CONSTEXPR_IF_NOT_DEBUG Span<C> last(std::size_t count) const noexcept
+ {
+ ASSERT_IF_DEBUG(size() >= count);
+ return Span<C>(m_data + m_size - count, count);
+ }
friend constexpr bool operator==(const Span& a, const Span& b) noexcept { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); }
friend constexpr bool operator!=(const Span& a, const Span& b) noexcept { return !(a == b); }
@@ -65,26 +187,20 @@ public:
template <typename O> friend class Span;
};
-/** Create a span to a container exposing data() and size().
- *
- * This correctly deals with constness: the returned Span's element type will be
- * whatever data() returns a pointer to. If either the passed container is const,
- * or its element type is const, the resulting span will have a const element type.
- *
- * std::span will have a constructor that implements this functionality directly.
- */
-template<typename A, int N>
-constexpr Span<A> MakeSpan(A (&a)[N]) { return Span<A>(a, N); }
-
-template<typename V>
-constexpr Span<typename std::remove_pointer<decltype(std::declval<V>().data())>::type> MakeSpan(V& v) { return Span<typename std::remove_pointer<decltype(std::declval<V>().data())>::type>(v.data(), v.size()); }
+// MakeSpan helps constructing a Span of the right type automatically.
+/** MakeSpan for arrays: */
+template <typename A, int N> Span<A> constexpr MakeSpan(A (&a)[N]) { return Span<A>(a, N); }
+/** MakeSpan for temporaries / rvalue references, only supporting const output. */
+template <typename V> constexpr auto MakeSpan(V&& v) -> typename std::enable_if<!std::is_lvalue_reference<V>::value, Span<const typename std::remove_pointer<decltype(v.data())>::type>>::type { return std::forward<V>(v); }
+/** MakeSpan for (lvalue) references, supporting mutable output. */
+template <typename V> constexpr auto MakeSpan(V& v) -> Span<typename std::remove_pointer<decltype(v.data())>::type> { return v; }
/** Pop the last element off a span, and return a reference to that element. */
template <typename T>
T& SpanPopBack(Span<T>& span)
{
size_t size = span.size();
- assert(size > 0);
+ ASSERT_IF_DEBUG(size > 0);
T& back = span[size - 1];
span = Span<T>(span.data(), size - 1);
return back;
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index f17b539e09..b4f392116c 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -29,7 +29,6 @@
#endif
LockedPoolManager* LockedPoolManager::_instance = nullptr;
-std::once_flag LockedPoolManager::init_flag;
/*******************************************************************************/
// Utilities
diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h
index de668f0773..b9e2e99d1a 100644
--- a/src/support/lockedpool.h
+++ b/src/support/lockedpool.h
@@ -221,7 +221,8 @@ public:
/** Return the current instance, or create it once */
static LockedPoolManager& Instance()
{
- std::call_once(LockedPoolManager::init_flag, LockedPoolManager::CreateInstance);
+ static std::once_flag init_flag;
+ std::call_once(init_flag, LockedPoolManager::CreateInstance);
return *LockedPoolManager::_instance;
}
@@ -234,7 +235,6 @@ private:
static bool LockingFailed();
static LockedPoolManager* _instance;
- static std::once_flag init_flag;
};
#endif // BITCOIN_SUPPORT_LOCKEDPOOL_H
diff --git a/src/sync.cpp b/src/sync.cpp
index b86c57e498..10f0483189 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -7,15 +7,19 @@
#endif
#include <sync.h>
-#include <tinyformat.h>
#include <logging.h>
+#include <tinyformat.h>
#include <util/strencodings.h>
#include <util/threadnames.h>
#include <map>
#include <set>
#include <system_error>
+#include <thread>
+#include <unordered_map>
+#include <utility>
+#include <vector>
#ifdef DEBUG_LOCKCONTENTION
#if !defined(HAVE_THREAD_LOCAL)
@@ -56,7 +60,7 @@ struct CLockLocation {
std::string ToString() const
{
return strprintf(
- "%s %s:%s%s (in thread %s)",
+ "'%s' in %s:%s%s (in thread '%s')",
mutexName, sourceFile, sourceLine, (fTry ? " (TRY)" : ""), m_thread_name);
}
@@ -73,35 +77,35 @@ private:
int sourceLine;
};
-typedef std::vector<std::pair<void*, CLockLocation> > LockStack;
-typedef std::map<std::pair<void*, void*>, LockStack> LockOrders;
-typedef std::set<std::pair<void*, void*> > InvLockOrders;
+using LockStackItem = std::pair<void*, CLockLocation>;
+using LockStack = std::vector<LockStackItem>;
+using LockStacks = std::unordered_map<std::thread::id, LockStack>;
-struct LockData {
- // Very ugly hack: as the global constructs and destructors run single
- // threaded, we use this boolean to know whether LockData still exists,
- // as DeleteLock can get called by global RecursiveMutex destructors
- // after LockData disappears.
- bool available;
- LockData() : available(true) {}
- ~LockData() { available = false; }
+using LockPair = std::pair<void*, void*>;
+using LockOrders = std::map<LockPair, LockStack>;
+using InvLockOrders = std::set<LockPair>;
+struct LockData {
+ LockStacks m_lock_stacks;
LockOrders lockorders;
InvLockOrders invlockorders;
std::mutex dd_mutex;
};
+
LockData& GetLockData() {
- static LockData lockdata;
- return lockdata;
+ // This approach guarantees that the object is not destroyed until after its last use.
+ // The operating system automatically reclaims all the memory in a program's heap when that program exits.
+ // Since the ~LockData() destructor is never called, the LockData class and all
+ // its subclasses must have implicitly-defined destructors.
+ static LockData& lock_data = *new LockData();
+ return lock_data;
}
-static thread_local LockStack g_lockstack;
-
-static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2)
+static void potential_deadlock_detected(const LockPair& mismatch, const LockStack& s1, const LockStack& s2)
{
LogPrintf("POTENTIAL DEADLOCK DETECTED\n");
LogPrintf("Previous lock order was:\n");
- for (const std::pair<void*, CLockLocation> & i : s2) {
+ for (const LockStackItem& i : s1) {
if (i.first == mismatch.first) {
LogPrintf(" (1)"); /* Continued */
}
@@ -110,21 +114,25 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
}
LogPrintf(" %s\n", i.second.ToString());
}
+
+ std::string mutex_a, mutex_b;
LogPrintf("Current lock order is:\n");
- for (const std::pair<void*, CLockLocation> & i : s1) {
+ for (const LockStackItem& i : s2) {
if (i.first == mismatch.first) {
LogPrintf(" (1)"); /* Continued */
+ mutex_a = i.second.Name();
}
if (i.first == mismatch.second) {
LogPrintf(" (2)"); /* Continued */
+ mutex_b = i.second.Name();
}
LogPrintf(" %s\n", i.second.ToString());
}
if (g_debug_lockorder_abort) {
- tfm::format(std::cerr, "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 for %s, details in debug log.\n", s2.back().second.ToString());
abort();
}
- throw std::logic_error("potential deadlock detected");
+ throw std::logic_error(strprintf("potential deadlock detected: %s -> %s -> %s", mutex_b, mutex_a, mutex_b));
}
static void push_lock(void* c, const CLockLocation& locklocation)
@@ -132,18 +140,18 @@ static void push_lock(void* c, const CLockLocation& locklocation)
LockData& lockdata = GetLockData();
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
- g_lockstack.push_back(std::make_pair(c, locklocation));
-
- for (const std::pair<void*, CLockLocation>& i : g_lockstack) {
+ LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
+ lock_stack.emplace_back(c, locklocation);
+ for (const LockStackItem& i : lock_stack) {
if (i.first == c)
break;
- std::pair<void*, void*> p1 = std::make_pair(i.first, c);
+ const LockPair p1 = std::make_pair(i.first, c);
if (lockdata.lockorders.count(p1))
continue;
- lockdata.lockorders.emplace(p1, g_lockstack);
+ lockdata.lockorders.emplace(p1, lock_stack);
- std::pair<void*, void*> p2 = std::make_pair(c, i.first);
+ const LockPair p2 = std::make_pair(c, i.first);
lockdata.invlockorders.insert(p2);
if (lockdata.lockorders.count(p2))
potential_deadlock_detected(p1, lockdata.lockorders[p2], lockdata.lockorders[p1]);
@@ -152,7 +160,14 @@ static void push_lock(void* c, const CLockLocation& locklocation)
static void pop_lock()
{
- g_lockstack.pop_back();
+ LockData& lockdata = GetLockData();
+ std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
+
+ LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
+ lock_stack.pop_back();
+ if (lock_stack.empty()) {
+ lockdata.m_lock_stacks.erase(std::this_thread::get_id());
+ }
}
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
@@ -162,11 +177,17 @@ void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs
void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line)
{
- if (!g_lockstack.empty()) {
- const auto& lastlock = g_lockstack.back();
- if (lastlock.first == cs) {
- lockname = lastlock.second.Name();
- return;
+ {
+ LockData& lockdata = GetLockData();
+ std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
+
+ const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
+ if (!lock_stack.empty()) {
+ const auto& lastlock = lock_stack.back();
+ if (lastlock.first == cs) {
+ lockname = lastlock.second.Name();
+ return;
+ }
}
}
throw std::system_error(EPERM, std::generic_category(), strprintf("%s:%s %s was not most recent critical section locked", file, line, guardname));
@@ -179,49 +200,60 @@ void LeaveCritical()
std::string LocksHeld()
{
+ LockData& lockdata = GetLockData();
+ std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
+
+ const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
std::string result;
- for (const std::pair<void*, CLockLocation>& i : g_lockstack)
+ for (const LockStackItem& i : lock_stack)
result += i.second.ToString() + std::string("\n");
return result;
}
-void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
+static bool LockHeld(void* mutex)
+{
+ LockData& lockdata = GetLockData();
+ std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
+
+ const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
+ for (const LockStackItem& i : lock_stack) {
+ if (i.first == mutex) return true;
+ }
+
+ return false;
+}
+
+template <typename MutexType>
+void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs)
{
- for (const std::pair<void*, CLockLocation>& i : g_lockstack)
- if (i.first == cs)
- return;
+ if (LockHeld(cs)) return;
tfm::format(std::cerr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
abort();
}
+template void AssertLockHeldInternal(const char*, const char*, int, Mutex*);
+template void AssertLockHeldInternal(const char*, const char*, int, RecursiveMutex*);
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
{
- for (const std::pair<void*, CLockLocation>& i : g_lockstack) {
- if (i.first == cs) {
- tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
- abort();
- }
- }
+ if (!LockHeld(cs)) return;
+ tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
+ abort();
}
void DeleteLock(void* cs)
{
LockData& lockdata = GetLockData();
- if (!lockdata.available) {
- // We're already shutting down.
- return;
- }
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
- std::pair<void*, void*> item = std::make_pair(cs, nullptr);
+ const LockPair item = std::make_pair(cs, nullptr);
LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
while (it != lockdata.lockorders.end() && it->first.first == cs) {
- std::pair<void*, void*> invitem = std::make_pair(it->first.second, it->first.first);
+ const LockPair invitem = std::make_pair(it->first.second, it->first.first);
lockdata.invlockorders.erase(invitem);
lockdata.lockorders.erase(it++);
}
InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item);
while (invit != lockdata.invlockorders.end() && invit->first == cs) {
- std::pair<void*, void*> invinvitem = std::make_pair(invit->second, invit->first);
+ const LockPair invinvitem = std::make_pair(invit->second, invit->first);
lockdata.lockorders.erase(invinvitem);
lockdata.invlockorders.erase(invit++);
}
diff --git a/src/sync.h b/src/sync.h
index 0c6f0ef0a7..77327d8bfe 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -52,7 +52,8 @@ void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs
void LeaveCritical();
void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
std::string LocksHeld();
-void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs);
+template <typename MutexType>
+void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs);
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
void DeleteLock(void* cs);
@@ -66,7 +67,8 @@ extern bool g_debug_lockorder_abort;
void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
void static inline LeaveCritical() {}
void static inline CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
-void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
+template <typename MutexType>
+void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
void static inline DeleteLock(void* cs) {}
#endif
@@ -101,6 +103,12 @@ public:
}
using UniqueLock = std::unique_lock<PARENT>;
+#ifdef __clang__
+ //! For negative capabilities in the Clang Thread Safety Analysis.
+ //! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in conjunction
+ //! with the ! operator, to indicate that a mutex should not be held.
+ const AnnotatedMixin& operator!() const { return *this; }
+#endif // __clang__
};
/**
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index 8694891a51..14cf1a4a76 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -132,24 +132,7 @@ public:
return base.GetShortID(txhash);
}
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(header);
- READWRITE(nonce);
- size_t shorttxids_size = shorttxids.size();
- READWRITE(VARINT(shorttxids_size));
- shorttxids.resize(shorttxids_size);
- for (size_t i = 0; i < shorttxids.size(); i++) {
- uint32_t lsb = shorttxids[i] & 0xffffffff;
- uint16_t msb = (shorttxids[i] >> 32) & 0xffff;
- READWRITE(lsb);
- READWRITE(msb);
- shorttxids[i] = (uint64_t(msb) << 32) | uint64_t(lsb);
- }
- READWRITE(prefilledtxn);
- }
+ SERIALIZE_METHODS(TestHeaderAndShortIDs, obj) { READWRITE(obj.header, obj.nonce, Using<VectorFormatter<CustomUintFormatter<CBlockHeaderAndShortTxIDs::SHORTTXIDS_LENGTH>>>(obj.shorttxids), obj.prefilledtxn); }
};
BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index e5043f6816..00c4bdc14e 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -94,7 +94,7 @@ bool BuildChainTestingSetup::BuildChain(const CBlockIndex* pindex,
CBlockHeader header = block->GetBlockHeader();
BlockValidationState state;
- if (!ProcessNewBlockHeaders({header}, state, Params(), &pindex)) {
+ if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({header}, state, Params(), &pindex)) {
return false;
}
}
@@ -171,7 +171,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
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));
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
}
for (size_t i = 0; i < 2; i++) {
const auto& block = chainA[i];
@@ -189,7 +189,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
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));
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
}
for (size_t i = 0; i < 3; i++) {
const auto& block = chainB[i];
@@ -220,7 +220,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
// 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));
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
}
// Check that chain A and B blocks can be retrieved.
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 0565982215..8348810ac1 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -3,13 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <checkqueue.h>
+#include <sync.h>
#include <test/util/setup_common.h>
#include <util/memory.h>
#include <util/system.h>
#include <util/time.h>
#include <boost/test/unit_test.hpp>
-#include <boost/thread.hpp>
+#include <boost/thread/thread.hpp>
#include <atomic>
#include <condition_variable>
@@ -57,14 +58,14 @@ struct FailingCheck {
};
struct UniqueCheck {
- static std::mutex m;
- static std::unordered_multiset<size_t> results;
+ static Mutex m;
+ static std::unordered_multiset<size_t> results GUARDED_BY(m);
size_t check_id;
UniqueCheck(size_t check_id_in) : check_id(check_id_in){};
UniqueCheck() : check_id(0){};
bool operator()()
{
- std::lock_guard<std::mutex> l(m);
+ LOCK(m);
results.insert(check_id);
return true;
}
@@ -127,7 +128,7 @@ struct FrozenCleanupCheck {
std::mutex FrozenCleanupCheck::m{};
std::atomic<uint64_t> FrozenCleanupCheck::nFrozen{0};
std::condition_variable FrozenCleanupCheck::cv{};
-std::mutex UniqueCheck::m;
+Mutex UniqueCheck::m;
std::unordered_multiset<size_t> UniqueCheck::results;
std::atomic<size_t> FakeCheckCheckCompletion::n_calls{0};
std::atomic<size_t> MemoryCheck::fake_allocated_memory{0};
@@ -290,11 +291,15 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
control.Add(vChecks);
}
}
- bool r = true;
- BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
- for (size_t i = 0; i < COUNT; ++i)
- r = r && UniqueCheck::results.count(i) == 1;
- BOOST_REQUIRE(r);
+ {
+ LOCK(UniqueCheck::m);
+ bool r = true;
+ BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
+ for (size_t i = 0; i < COUNT; ++i) {
+ r = r && UniqueCheck::results.count(i) == 1;
+ }
+ BOOST_REQUIRE(r);
+ }
tg.interrupt_all();
tg.join_all();
}
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 60196c36a5..173ec5e3d9 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -538,7 +538,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
CDataStream tmp(SER_DISK, CLIENT_VERSION);
uint64_t x = 3000000000ULL;
tmp << VARINT(x);
- BOOST_CHECK_EQUAL(HexStr(tmp.begin(), tmp.end()), "8a95c0bb00");
+ BOOST_CHECK_EQUAL(HexStr(tmp), "8a95c0bb00");
CDataStream ss5(ParseHex("00008a95c0bb00"), SER_DISK, CLIENT_VERSION);
try {
Coin cc5;
diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json
index c01ef307b7..724789bbf9 100644
--- a/src/test/data/script_tests.json
+++ b/src/test/data/script_tests.json
@@ -678,7 +678,7 @@
["0 0x02 0x0000 0", "CHECKMULTISIGVERIFY 1", "", "OK"],
["While not really correctly DER encoded, the empty signature is allowed by"],
-["STRICTENC to provide a compact way to provide a delibrately invalid signature."],
+["STRICTENC to provide a compact way to provide a deliberately invalid signature."],
["0", "0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 CHECKSIG NOT", "STRICTENC", "OK"],
["0 0", "1 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 1 CHECKMULTISIG NOT", "STRICTENC", "OK"],
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index c378546e8b..3d802cbeb3 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -331,24 +331,26 @@ struct StringContentsSerializer {
}
StringContentsSerializer& operator+=(const StringContentsSerializer& s) { return *this += s.str; }
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- if (ser_action.ForRead()) {
- str.clear();
- char c = 0;
- while (true) {
- try {
- READWRITE(c);
- str.push_back(c);
- } catch (const std::ios_base::failure&) {
- break;
- }
+ template<typename Stream>
+ void Serialize(Stream& s) const
+ {
+ for (size_t i = 0; i < str.size(); i++) {
+ s << str[i];
+ }
+ }
+
+ template<typename Stream>
+ void Unserialize(Stream& s)
+ {
+ str.clear();
+ char c = 0;
+ while (true) {
+ try {
+ s >> c;
+ str.push_back(c);
+ } catch (const std::ios_base::failure&) {
+ break;
}
- } else {
- for (size_t i = 0; i < str.size(); i++)
- READWRITE(str[i]);
}
}
};
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 75b38670c9..b1a635d9da 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -4,10 +4,12 @@
// Unit tests for denial-of-service detection/prevention code
+#include <arith_uint256.h>
#include <banman.h>
#include <chainparams.h>
#include <net.h>
#include <net_processing.h>
+#include <pubkey.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
@@ -78,7 +80,7 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler, *m_node.mempool);
+ auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool);
// Mock an outbound peer
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
@@ -98,11 +100,11 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
// Test starts here
{
- LOCK2(cs_main, dummyNode1.cs_sendProcessing);
+ LOCK(dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in getheaders
}
{
- LOCK2(cs_main, dummyNode1.cs_vSend);
+ LOCK(dummyNode1.cs_vSend);
BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
dummyNode1.vSendMsg.clear();
}
@@ -111,17 +113,17 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
// Wait 21 minutes
SetMockTime(nStartTime+21*60);
{
- LOCK2(cs_main, dummyNode1.cs_sendProcessing);
+ LOCK(dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in getheaders
}
{
- LOCK2(cs_main, dummyNode1.cs_vSend);
+ LOCK(dummyNode1.cs_vSend);
BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
}
// Wait 3 more minutes
SetMockTime(nStartTime+24*60);
{
- LOCK2(cs_main, dummyNode1.cs_sendProcessing);
+ LOCK(dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in disconnect
}
BOOST_CHECK(dummyNode1.fDisconnect == true);
@@ -148,7 +150,7 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidat
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{
auto connman = MakeUnique<CConnmanTest>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler, *m_node.mempool);
+ auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool);
const Consensus::Params& consensusParams = Params().GetConsensus();
constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS;
@@ -217,11 +219,11 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
connman->ClearNodes();
}
-BOOST_AUTO_TEST_CASE(DoS_banning)
+BOOST_AUTO_TEST_CASE(peer_discouragement)
{
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler, *m_node.mempool);
+ auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
banman->ClearBanned();
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
@@ -232,14 +234,14 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
dummyNode1.fSuccessfullyConnected = true;
{
LOCK(cs_main);
- Misbehaving(dummyNode1.GetId(), 100); // Should get banned
+ Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD); // Should be discouraged
}
{
- LOCK2(cs_main, dummyNode1.cs_sendProcessing);
+ LOCK(dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
}
- BOOST_CHECK(banman->IsBanned(addr1));
- BOOST_CHECK(!banman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
+ BOOST_CHECK(banman->IsDiscouraged(addr1));
+ BOOST_CHECK(!banman->IsDiscouraged(ip(0xa0b0c001|0x0000ff00))); // Different IP, not discouraged
CAddress addr2(ip(0xa0b0c002), NODE_NONE);
CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", true);
@@ -249,81 +251,35 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
dummyNode2.fSuccessfullyConnected = true;
{
LOCK(cs_main);
- Misbehaving(dummyNode2.GetId(), 50);
+ Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1);
}
{
- LOCK2(cs_main, dummyNode2.cs_sendProcessing);
+ LOCK(dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
}
- BOOST_CHECK(!banman->IsBanned(addr2)); // 2 not banned yet...
- BOOST_CHECK(banman->IsBanned(addr1)); // ... but 1 still should be
+ BOOST_CHECK(!banman->IsDiscouraged(addr2)); // 2 not discouraged yet...
+ BOOST_CHECK(banman->IsDiscouraged(addr1)); // ... but 1 still should be
{
LOCK(cs_main);
- Misbehaving(dummyNode2.GetId(), 50);
+ Misbehaving(dummyNode2.GetId(), 1); // 2 reaches discouragement threshold
}
{
- LOCK2(cs_main, dummyNode2.cs_sendProcessing);
+ LOCK(dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
}
- BOOST_CHECK(banman->IsBanned(addr2));
+ BOOST_CHECK(banman->IsDiscouraged(addr1)); // Expect both 1 and 2
+ BOOST_CHECK(banman->IsDiscouraged(addr2)); // to be discouraged now
bool dummy;
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
peerLogic->FinalizeNode(dummyNode2.GetId(), dummy);
}
-BOOST_AUTO_TEST_CASE(DoS_banscore)
-{
- auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler, *m_node.mempool);
-
- banman->ClearBanned();
- gArgs.ForceSetArg("-banscore", "111"); // because 11 is my favorite number
- CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, CAddress(), "", true);
- dummyNode1.SetSendVersion(PROTOCOL_VERSION);
- peerLogic->InitializeNode(&dummyNode1);
- dummyNode1.nVersion = 1;
- dummyNode1.fSuccessfullyConnected = true;
- {
- LOCK(cs_main);
- Misbehaving(dummyNode1.GetId(), 100);
- }
- {
- LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
- }
- BOOST_CHECK(!banman->IsBanned(addr1));
- {
- LOCK(cs_main);
- Misbehaving(dummyNode1.GetId(), 10);
- }
- {
- LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
- }
- BOOST_CHECK(!banman->IsBanned(addr1));
- {
- LOCK(cs_main);
- Misbehaving(dummyNode1.GetId(), 1);
- }
- {
- LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
- }
- BOOST_CHECK(banman->IsBanned(addr1));
- gArgs.ForceSetArg("-banscore", ToString(DEFAULT_BANSCORE_THRESHOLD));
-
- bool dummy;
- peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
-}
-
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler, *m_node.mempool);
+ auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
banman->ClearBanned();
int64_t nStartTime = GetTime();
@@ -338,19 +294,13 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
{
LOCK(cs_main);
- Misbehaving(dummyNode.GetId(), 100);
+ Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD);
}
{
- LOCK2(cs_main, dummyNode.cs_sendProcessing);
+ LOCK(dummyNode.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode));
}
- BOOST_CHECK(banman->IsBanned(addr));
-
- SetMockTime(nStartTime+60*60);
- BOOST_CHECK(banman->IsBanned(addr));
-
- SetMockTime(nStartTime+60*60*24+1);
- BOOST_CHECK(!banman->IsBanned(addr));
+ BOOST_CHECK(banman->IsDiscouraged(addr));
bool dummy;
peerLogic->FinalizeNode(dummyNode.GetId(), dummy);
@@ -366,10 +316,26 @@ static CTransactionRef RandomOrphan()
return it->second.tx;
}
+static void MakeNewKeyWithFastRandomContext(CKey& key)
+{
+ std::vector<unsigned char> keydata;
+ keydata = g_insecure_rand_ctx.randbytes(32);
+ key.Set(keydata.data(), keydata.data() + keydata.size(), /*fCompressedIn*/ true);
+ assert(key.IsValid());
+}
+
BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
{
+ // This test had non-deterministic coverage due to
+ // randomly selected seeds.
+ // This seed is chosen so that all branches of the function
+ // ecdsa_signature_parse_der_lax are executed during this test.
+ // Specifically branches that run only when an ECDSA
+ // signature's R and S values have leading zeros.
+ g_insecure_rand_ctx = FastRandomContext(ArithToUint256(arith_uint256(33)));
+
CKey key;
- key.MakeNewKey(true);
+ MakeNewKeyWithFastRandomContext(key);
FillableSigningProvider keystore;
BOOST_CHECK(keystore.AddKey(key));
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index 5f9a78ceb2..20132d5782 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -135,7 +135,7 @@ void DoCheck(const std::string& prv, const std::string& pub, int flags, const st
// When the descriptor is hardened, evaluate with access to the private keys inside.
const FlatSigningProvider& key_provider = (flags & HARDENED) ? keys_priv : keys_pub;
- // Evaluate the descriptor selected by `t` in poisition `i`.
+ // Evaluate the descriptor selected by `t` in position `i`.
FlatSigningProvider script_provider, script_provider_cached;
std::vector<CScript> spks, spks_cached;
DescriptorCache desc_cache;
@@ -216,7 +216,7 @@ void DoCheck(const std::string& prv, const std::string& pub, int flags, const st
// For each of the produced scripts, verify solvability, and when possible, try to sign a transaction spending it.
for (size_t n = 0; n < spks.size(); ++n) {
- BOOST_CHECK_EQUAL(ref[n], HexStr(spks[n].begin(), spks[n].end()));
+ BOOST_CHECK_EQUAL(ref[n], HexStr(spks[n]));
BOOST_CHECK_EQUAL(IsSolvable(Merge(key_provider, script_provider), spks[n]), (flags & UNSOLVABLE) == 0);
if (flags & SIGNABLE) {
diff --git a/src/test/fuzz/addrdb.cpp b/src/test/fuzz/addrdb.cpp
index f21ff3fac3..16b1cb755a 100644
--- a/src/test/fuzz/addrdb.cpp
+++ b/src/test/fuzz/addrdb.cpp
@@ -3,13 +3,13 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrdb.h>
-#include <optional.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <cassert>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
@@ -17,20 +17,14 @@ void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ // The point of this code is to exercise all CBanEntry constructors.
const CBanEntry ban_entry = [&] {
- switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 3)) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 2)) {
case 0:
return CBanEntry{fuzzed_data_provider.ConsumeIntegral<int64_t>()};
break;
- case 1:
- return CBanEntry{fuzzed_data_provider.ConsumeIntegral<int64_t>(), fuzzed_data_provider.PickValueInArray<BanReason>({
- BanReason::BanReasonUnknown,
- BanReason::BanReasonNodeMisbehaving,
- BanReason::BanReasonManuallyAdded,
- })};
- break;
- case 2: {
- const Optional<CBanEntry> ban_entry = ConsumeDeserializable<CBanEntry>(fuzzed_data_provider);
+ case 1: {
+ const std::optional<CBanEntry> ban_entry = ConsumeDeserializable<CBanEntry>(fuzzed_data_provider);
if (ban_entry) {
return *ban_entry;
}
@@ -39,5 +33,5 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
return CBanEntry{};
}();
- assert(!ban_entry.banReasonToString().empty());
+ (void)ban_entry; // currently unused
}
diff --git a/src/test/fuzz/asmap.cpp b/src/test/fuzz/asmap.cpp
index ea56277eac..40ca01bd9f 100644
--- a/src/test/fuzz/asmap.cpp
+++ b/src/test/fuzz/asmap.cpp
@@ -23,8 +23,8 @@ static const std::vector<bool> IPV4_PREFIX_ASMAP = {
true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00
true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00
true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00
- true, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, // Match 0xFF
- true, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true // Match 0xFF
+ true, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, // Match 0xFF
+ true, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true // Match 0xFF
};
void test_one_input(const std::vector<uint8_t>& buffer)
diff --git a/src/test/fuzz/asmap_direct.cpp b/src/test/fuzz/asmap_direct.cpp
index 6d8a65f5ab..2d21eff9d6 100644
--- a/src/test/fuzz/asmap_direct.cpp
+++ b/src/test/fuzz/asmap_direct.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <util/asmap.h>
#include <test/fuzz/fuzz.h>
+#include <util/asmap.h>
#include <cstdint>
#include <optional>
@@ -34,7 +34,9 @@ void test_one_input(const std::vector<uint8_t>& buffer)
if (SanityCheckASMap(asmap, buffer.size() - 1 - sep_pos)) {
// Verify that for valid asmaps, no prefix (except up to 7 zero padding bits) is valid.
std::vector<bool> asmap_prefix = asmap;
- while (!asmap_prefix.empty() && asmap_prefix.size() + 7 > asmap.size() && asmap_prefix.back() == false) asmap_prefix.pop_back();
+ while (!asmap_prefix.empty() && asmap_prefix.size() + 7 > asmap.size() && asmap_prefix.back() == false) {
+ asmap_prefix.pop_back();
+ }
while (!asmap_prefix.empty()) {
asmap_prefix.pop_back();
assert(!SanityCheckASMap(asmap_prefix, buffer.size() - 1 - sep_pos));
diff --git a/src/test/fuzz/autofile.cpp b/src/test/fuzz/autofile.cpp
new file mode 100644
index 0000000000..7ea0bdd2a7
--- /dev/null
+++ b/src/test/fuzz/autofile.cpp
@@ -0,0 +1,72 @@
+// Copyright (c) 2020 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 <optional.h>
+#include <streams.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <array>
+#include <cstdint>
+#include <iostream>
+#include <optional>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider);
+ CAutoFile auto_file = fuzzed_auto_file_provider.open();
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) {
+ case 0: {
+ std::array<uint8_t, 4096> arr{};
+ try {
+ auto_file.read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ } catch (const std::ios_base::failure&) {
+ }
+ break;
+ }
+ case 1: {
+ const std::array<uint8_t, 4096> arr{};
+ try {
+ auto_file.write((const char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ } catch (const std::ios_base::failure&) {
+ }
+ break;
+ }
+ case 2: {
+ try {
+ auto_file.ignore(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ } catch (const std::ios_base::failure&) {
+ }
+ break;
+ }
+ case 3: {
+ auto_file.fclose();
+ break;
+ }
+ case 4: {
+ ReadFromStream(fuzzed_data_provider, auto_file);
+ break;
+ }
+ case 5: {
+ WriteToStream(fuzzed_data_provider, auto_file);
+ break;
+ }
+ }
+ }
+ (void)auto_file.Get();
+ (void)auto_file.GetType();
+ (void)auto_file.GetVersion();
+ (void)auto_file.IsNull();
+ if (fuzzed_data_provider.ConsumeBool()) {
+ FILE* f = auto_file.release();
+ if (f != nullptr) {
+ fclose(f);
+ }
+ }
+}
diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp
new file mode 100644
index 0000000000..fc4a1d9261
--- /dev/null
+++ b/src/test/fuzz/banman.cpp
@@ -0,0 +1,88 @@
+// Copyright (c) 2020 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 <banman.h>
+#include <fs.h>
+#include <netaddress.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <util/system.h>
+
+#include <cstdint>
+#include <limits>
+#include <string>
+#include <vector>
+
+namespace {
+int64_t ConsumeBanTimeOffset(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ // Avoid signed integer overflow by capping to int32_t max:
+ // banman.cpp:137:73: runtime error: signed integer overflow: 1591700817 + 9223372036854775807 cannot be represented in type 'long'
+ return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(std::numeric_limits<int64_t>::min(), std::numeric_limits<int32_t>::max());
+}
+} // namespace
+
+void initialize()
+{
+ InitializeFuzzingContext();
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const fs::path banlist_file = GetDataDir() / "fuzzed_banlist.dat";
+ fs::remove(banlist_file);
+ {
+ BanMan ban_man{banlist_file, nullptr, ConsumeBanTimeOffset(fuzzed_data_provider)};
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 11)) {
+ case 0: {
+ ban_man.Ban(ConsumeNetAddr(fuzzed_data_provider),
+ ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool());
+ break;
+ }
+ case 1: {
+ ban_man.Ban(ConsumeSubNet(fuzzed_data_provider),
+ ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool());
+ break;
+ }
+ case 2: {
+ ban_man.ClearBanned();
+ break;
+ }
+ case 4: {
+ ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider));
+ break;
+ }
+ case 5: {
+ ban_man.IsBanned(ConsumeSubNet(fuzzed_data_provider));
+ break;
+ }
+ case 6: {
+ ban_man.Unban(ConsumeNetAddr(fuzzed_data_provider));
+ break;
+ }
+ case 7: {
+ ban_man.Unban(ConsumeSubNet(fuzzed_data_provider));
+ break;
+ }
+ case 8: {
+ banmap_t banmap;
+ ban_man.GetBanned(banmap);
+ break;
+ }
+ case 9: {
+ ban_man.DumpBanlist();
+ break;
+ }
+ case 11: {
+ ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider));
+ break;
+ }
+ }
+ }
+ }
+ fs::remove(banlist_file);
+}
diff --git a/src/test/fuzz/block.cpp b/src/test/fuzz/block.cpp
index f30fa03e0b..91bd34a251 100644
--- a/src/test/fuzz/block.cpp
+++ b/src/test/fuzz/block.cpp
@@ -38,12 +38,17 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const Consensus::Params& consensus_params = Params().GetConsensus();
BlockValidationState validation_state_pow_and_merkle;
const bool valid_incl_pow_and_merkle = CheckBlock(block, validation_state_pow_and_merkle, consensus_params, /* fCheckPOW= */ true, /* fCheckMerkleRoot= */ true);
+ assert(validation_state_pow_and_merkle.IsValid() || validation_state_pow_and_merkle.IsInvalid() || validation_state_pow_and_merkle.IsError());
+ (void)validation_state_pow_and_merkle.Error("");
BlockValidationState validation_state_pow;
const bool valid_incl_pow = CheckBlock(block, validation_state_pow, consensus_params, /* fCheckPOW= */ true, /* fCheckMerkleRoot= */ false);
+ assert(validation_state_pow.IsValid() || validation_state_pow.IsInvalid() || validation_state_pow.IsError());
BlockValidationState validation_state_merkle;
const bool valid_incl_merkle = CheckBlock(block, validation_state_merkle, consensus_params, /* fCheckPOW= */ false, /* fCheckMerkleRoot= */ true);
+ assert(validation_state_merkle.IsValid() || validation_state_merkle.IsInvalid() || validation_state_merkle.IsError());
BlockValidationState validation_state_none;
const bool valid_incl_none = CheckBlock(block, validation_state_none, consensus_params, /* fCheckPOW= */ false, /* fCheckMerkleRoot= */ false);
+ assert(validation_state_none.IsValid() || validation_state_none.IsInvalid() || validation_state_none.IsError());
if (valid_incl_pow_and_merkle) {
assert(valid_incl_pow && valid_incl_merkle && valid_incl_none);
} else if (valid_incl_merkle || valid_incl_pow) {
diff --git a/src/test/fuzz/block_header.cpp b/src/test/fuzz/block_header.cpp
index 92dcccc0e1..09c2b4a951 100644
--- a/src/test/fuzz/block_header.cpp
+++ b/src/test/fuzz/block_header.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <optional.h>
#include <primitives/block.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
@@ -11,13 +10,14 @@
#include <cassert>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- const Optional<CBlockHeader> block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
+ const std::optional<CBlockHeader> block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
if (!block_header) {
return;
}
@@ -38,4 +38,12 @@ void test_one_input(const std::vector<uint8_t>& buffer)
block.SetNull();
assert(block.GetBlockHeader().GetHash() == mut_block_header.GetHash());
}
+ {
+ std::optional<CBlockLocator> block_locator = ConsumeDeserializable<CBlockLocator>(fuzzed_data_provider);
+ if (block_locator) {
+ (void)block_locator->IsNull();
+ block_locator->SetNull();
+ assert(block_locator->IsNull());
+ }
+ }
}
diff --git a/src/test/fuzz/blockfilter.cpp b/src/test/fuzz/blockfilter.cpp
index be9320dcbf..7232325a20 100644
--- a/src/test/fuzz/blockfilter.cpp
+++ b/src/test/fuzz/blockfilter.cpp
@@ -3,19 +3,19 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <blockfilter.h>
-#include <optional.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- const Optional<BlockFilter> block_filter = ConsumeDeserializable<BlockFilter>(fuzzed_data_provider);
+ const std::optional<BlockFilter> block_filter = ConsumeDeserializable<BlockFilter>(fuzzed_data_provider);
if (!block_filter) {
return;
}
diff --git a/src/test/fuzz/bloom_filter.cpp b/src/test/fuzz/bloom_filter.cpp
index 7039bf16c1..d955c71bc9 100644
--- a/src/test/fuzz/bloom_filter.cpp
+++ b/src/test/fuzz/bloom_filter.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bloom.h>
-#include <optional.h>
#include <primitives/transaction.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
@@ -12,6 +11,7 @@
#include <cassert>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
@@ -35,7 +35,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
break;
}
case 1: {
- const Optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
+ const std::optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
if (!out_point) {
break;
}
@@ -46,7 +46,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
break;
}
case 2: {
- const Optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
+ const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
if (!u256) {
break;
}
@@ -57,7 +57,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
break;
}
case 3: {
- const Optional<CMutableTransaction> mut_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ const std::optional<CMutableTransaction> mut_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!mut_tx) {
break;
}
diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp
new file mode 100644
index 0000000000..6bbd13eb5c
--- /dev/null
+++ b/src/test/fuzz/buffered_file.cpp
@@ -0,0 +1,78 @@
+// Copyright (c) 2020 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 <optional.h>
+#include <streams.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <array>
+#include <cstdint>
+#include <iostream>
+#include <optional>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider);
+ std::optional<CBufferedFile> opt_buffered_file;
+ FILE* fuzzed_file = fuzzed_file_provider.open();
+ try {
+ opt_buffered_file.emplace(fuzzed_file, fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096), fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096), fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeIntegral<int>());
+ } catch (const std::ios_base::failure&) {
+ if (fuzzed_file != nullptr) {
+ fclose(fuzzed_file);
+ }
+ }
+ if (opt_buffered_file && fuzzed_file != nullptr) {
+ bool setpos_fail = false;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) {
+ case 0: {
+ std::array<uint8_t, 4096> arr{};
+ try {
+ opt_buffered_file->read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ } catch (const std::ios_base::failure&) {
+ }
+ break;
+ }
+ case 1: {
+ opt_buffered_file->Seek(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096));
+ break;
+ }
+ case 2: {
+ opt_buffered_file->SetLimit(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096));
+ break;
+ }
+ case 3: {
+ if (!opt_buffered_file->SetPos(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096))) {
+ setpos_fail = true;
+ }
+ break;
+ }
+ case 4: {
+ if (setpos_fail) {
+ // Calling FindByte(...) after a failed SetPos(...) call may result in an infinite loop.
+ break;
+ }
+ try {
+ opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral<char>());
+ } catch (const std::ios_base::failure&) {
+ }
+ break;
+ }
+ case 5: {
+ ReadFromStream(fuzzed_data_provider, *opt_buffered_file);
+ break;
+ }
+ }
+ }
+ opt_buffered_file->GetPos();
+ opt_buffered_file->GetType();
+ opt_buffered_file->GetVersion();
+ }
+}
diff --git a/src/test/fuzz/chain.cpp b/src/test/fuzz/chain.cpp
index b322516cc7..47c71850ce 100644
--- a/src/test/fuzz/chain.cpp
+++ b/src/test/fuzz/chain.cpp
@@ -3,18 +3,18 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
-#include <optional.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <cstdint>
+#include <optional>
#include <vector>
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- Optional<CDiskBlockIndex> disk_block_index = ConsumeDeserializable<CDiskBlockIndex>(fuzzed_data_provider);
+ std::optional<CDiskBlockIndex> disk_block_index = ConsumeDeserializable<CDiskBlockIndex>(fuzzed_data_provider);
if (!disk_block_index) {
return;
}
diff --git a/src/test/fuzz/checkqueue.cpp b/src/test/fuzz/checkqueue.cpp
index 2ed097b827..c69043bb6b 100644
--- a/src/test/fuzz/checkqueue.cpp
+++ b/src/test/fuzz/checkqueue.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <checkqueue.h>
-#include <optional.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
new file mode 100644
index 0000000000..c186bef7ae
--- /dev/null
+++ b/src/test/fuzz/coins_view.cpp
@@ -0,0 +1,294 @@
+// Copyright (c) 2020 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 <chainparams.h>
+#include <chainparamsbase.h>
+#include <coins.h>
+#include <consensus/tx_verify.h>
+#include <consensus/validation.h>
+#include <key.h>
+#include <node/coinstats.h>
+#include <policy/policy.h>
+#include <primitives/transaction.h>
+#include <pubkey.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <validation.h>
+
+#include <cstdint>
+#include <limits>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace {
+const Coin EMPTY_COIN{};
+
+bool operator==(const Coin& a, const Coin& b)
+{
+ if (a.IsSpent() && b.IsSpent()) return true;
+ return a.fCoinBase == b.fCoinBase && a.nHeight == b.nHeight && a.out == b.out;
+}
+} // namespace
+
+void initialize()
+{
+ static const ECCVerifyHandle ecc_verify_handle;
+ ECC_Start();
+ SelectParams(CBaseChainParams::REGTEST);
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ CCoinsView backend_coins_view;
+ CCoinsViewCache coins_view_cache{&backend_coins_view};
+ COutPoint random_out_point;
+ Coin random_coin;
+ CMutableTransaction random_mutable_transaction;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 9)) {
+ case 0: {
+ if (random_coin.IsSpent()) {
+ break;
+ }
+ Coin coin = random_coin;
+ bool expected_code_path = false;
+ const bool possible_overwrite = fuzzed_data_provider.ConsumeBool();
+ try {
+ coins_view_cache.AddCoin(random_out_point, std::move(coin), possible_overwrite);
+ expected_code_path = true;
+ } catch (const std::logic_error& e) {
+ if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) {
+ assert(!possible_overwrite);
+ expected_code_path = true;
+ }
+ }
+ assert(expected_code_path);
+ break;
+ }
+ case 1: {
+ (void)coins_view_cache.Flush();
+ break;
+ }
+ case 2: {
+ coins_view_cache.SetBestBlock(ConsumeUInt256(fuzzed_data_provider));
+ break;
+ }
+ case 3: {
+ Coin move_to;
+ (void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr);
+ break;
+ }
+ case 4: {
+ coins_view_cache.Uncache(random_out_point);
+ break;
+ }
+ case 5: {
+ if (fuzzed_data_provider.ConsumeBool()) {
+ backend_coins_view = CCoinsView{};
+ }
+ coins_view_cache.SetBackend(backend_coins_view);
+ break;
+ }
+ case 6: {
+ const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
+ if (!opt_out_point) {
+ break;
+ }
+ random_out_point = *opt_out_point;
+ break;
+ }
+ case 7: {
+ const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
+ if (!opt_coin) {
+ break;
+ }
+ random_coin = *opt_coin;
+ break;
+ }
+ case 8: {
+ const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (!opt_mutable_transaction) {
+ break;
+ }
+ random_mutable_transaction = *opt_mutable_transaction;
+ break;
+ }
+ case 9: {
+ CCoinsMap coins_map;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ CCoinsCacheEntry coins_cache_entry;
+ coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>();
+ if (fuzzed_data_provider.ConsumeBool()) {
+ coins_cache_entry.coin = random_coin;
+ } else {
+ const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
+ if (!opt_coin) {
+ break;
+ }
+ coins_cache_entry.coin = *opt_coin;
+ }
+ coins_map.emplace(random_out_point, std::move(coins_cache_entry));
+ }
+ bool expected_code_path = false;
+ try {
+ coins_view_cache.BatchWrite(coins_map, fuzzed_data_provider.ConsumeBool() ? ConsumeUInt256(fuzzed_data_provider) : coins_view_cache.GetBestBlock());
+ expected_code_path = true;
+ } catch (const std::logic_error& e) {
+ if (e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"}) {
+ expected_code_path = true;
+ }
+ }
+ assert(expected_code_path);
+ break;
+ }
+ }
+ }
+
+ {
+ const Coin& coin_using_access_coin = coins_view_cache.AccessCoin(random_out_point);
+ const bool exists_using_access_coin = !(coin_using_access_coin == EMPTY_COIN);
+ const bool exists_using_have_coin = coins_view_cache.HaveCoin(random_out_point);
+ const bool exists_using_have_coin_in_cache = coins_view_cache.HaveCoinInCache(random_out_point);
+ Coin coin_using_get_coin;
+ const bool exists_using_get_coin = coins_view_cache.GetCoin(random_out_point, coin_using_get_coin);
+ if (exists_using_get_coin) {
+ assert(coin_using_get_coin == coin_using_access_coin);
+ }
+ assert((exists_using_access_coin && exists_using_have_coin_in_cache && exists_using_have_coin && exists_using_get_coin) ||
+ (!exists_using_access_coin && !exists_using_have_coin_in_cache && !exists_using_have_coin && !exists_using_get_coin));
+ const bool exists_using_have_coin_in_backend = backend_coins_view.HaveCoin(random_out_point);
+ if (exists_using_have_coin_in_backend) {
+ assert(exists_using_have_coin);
+ }
+ Coin coin_using_backend_get_coin;
+ if (backend_coins_view.GetCoin(random_out_point, coin_using_backend_get_coin)) {
+ assert(exists_using_have_coin_in_backend);
+ assert(coin_using_get_coin == coin_using_backend_get_coin);
+ } else {
+ assert(!exists_using_have_coin_in_backend);
+ }
+ }
+
+ {
+ bool expected_code_path = false;
+ try {
+ (void)coins_view_cache.Cursor();
+ } catch (const std::logic_error&) {
+ expected_code_path = true;
+ }
+ assert(expected_code_path);
+ (void)coins_view_cache.DynamicMemoryUsage();
+ (void)coins_view_cache.EstimateSize();
+ (void)coins_view_cache.GetBestBlock();
+ (void)coins_view_cache.GetCacheSize();
+ (void)coins_view_cache.GetHeadBlocks();
+ (void)coins_view_cache.HaveInputs(CTransaction{random_mutable_transaction});
+ }
+
+ {
+ const CCoinsViewCursor* coins_view_cursor = backend_coins_view.Cursor();
+ assert(coins_view_cursor == nullptr);
+ (void)backend_coins_view.EstimateSize();
+ (void)backend_coins_view.GetBestBlock();
+ (void)backend_coins_view.GetHeadBlocks();
+ }
+
+ if (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 6)) {
+ case 0: {
+ const CTransaction transaction{random_mutable_transaction};
+ bool is_spent = false;
+ for (const CTxOut& tx_out : transaction.vout) {
+ if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) {
+ is_spent = true;
+ }
+ }
+ if (is_spent) {
+ // Avoid:
+ // coins.cpp:69: void CCoinsViewCache::AddCoin(const COutPoint &, Coin &&, bool): Assertion `!coin.IsSpent()' failed.
+ break;
+ }
+ bool expected_code_path = false;
+ const int height = fuzzed_data_provider.ConsumeIntegral<int>();
+ const bool possible_overwrite = fuzzed_data_provider.ConsumeBool();
+ try {
+ AddCoins(coins_view_cache, transaction, height, possible_overwrite);
+ expected_code_path = true;
+ } catch (const std::logic_error& e) {
+ if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) {
+ assert(!possible_overwrite);
+ expected_code_path = true;
+ }
+ }
+ assert(expected_code_path);
+ break;
+ }
+ case 1: {
+ (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
+ break;
+ }
+ case 2: {
+ TxValidationState state;
+ CAmount tx_fee_out;
+ const CTransaction transaction{random_mutable_transaction};
+ if (ContainsSpentInput(transaction, coins_view_cache)) {
+ // Avoid:
+ // consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, TxValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed.
+ break;
+ }
+ try {
+ (void)Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out);
+ assert(MoneyRange(tx_fee_out));
+ } catch (const std::runtime_error&) {
+ }
+ break;
+ }
+ case 3: {
+ const CTransaction transaction{random_mutable_transaction};
+ if (ContainsSpentInput(transaction, coins_view_cache)) {
+ // Avoid:
+ // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
+ break;
+ }
+ (void)GetP2SHSigOpCount(transaction, coins_view_cache);
+ break;
+ }
+ case 4: {
+ const CTransaction transaction{random_mutable_transaction};
+ if (ContainsSpentInput(transaction, coins_view_cache)) {
+ // Avoid:
+ // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
+ break;
+ }
+ const int flags = fuzzed_data_provider.ConsumeIntegral<int>();
+ if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) {
+ // Avoid:
+ // script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed.
+ break;
+ }
+ (void)GetTransactionSigOpCost(transaction, coins_view_cache, flags);
+ break;
+ }
+ case 5: {
+ CCoinsStats stats;
+ bool expected_code_path = false;
+ try {
+ (void)GetUTXOStats(&coins_view_cache, stats, CoinStatsHashType::HASH_SERIALIZED);
+ } catch (const std::logic_error&) {
+ expected_code_path = true;
+ }
+ assert(expected_code_path);
+ break;
+ }
+ case 6: {
+ (void)IsWitnessStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
new file mode 100644
index 0000000000..595cdf9abb
--- /dev/null
+++ b/src/test/fuzz/crypto.cpp
@@ -0,0 +1,124 @@
+// Copyright (c) 2020 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/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 <hash.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ if (data.empty()) {
+ data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ }
+
+ CHash160 hash160;
+ CHash256 hash256;
+ CHMAC_SHA256 hmac_sha256{data.data(), data.size()};
+ CHMAC_SHA512 hmac_sha512{data.data(), data.size()};
+ CRIPEMD160 ripemd160;
+ CSHA1 sha1;
+ CSHA256 sha256;
+ CSHA512 sha512;
+ CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
+
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 2)) {
+ case 0: {
+ if (fuzzed_data_provider.ConsumeBool()) {
+ data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ if (data.empty()) {
+ data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ }
+ }
+
+ (void)hash160.Write(data.data(), data.size());
+ (void)hash256.Write(data.data(), data.size());
+ (void)hmac_sha256.Write(data.data(), data.size());
+ (void)hmac_sha512.Write(data.data(), data.size());
+ (void)ripemd160.Write(data.data(), data.size());
+ (void)sha1.Write(data.data(), data.size());
+ (void)sha256.Write(data.data(), data.size());
+ (void)sha512.Write(data.data(), data.size());
+ (void)sip_hasher.Write(data.data(), data.size());
+
+ (void)Hash(data.begin(), data.end());
+ (void)Hash160(data);
+ (void)Hash160(data.begin(), data.end());
+ (void)sha512.Size();
+ break;
+ }
+ case 1: {
+ (void)hash160.Reset();
+ (void)hash256.Reset();
+ (void)ripemd160.Reset();
+ (void)sha1.Reset();
+ (void)sha256.Reset();
+ (void)sha512.Reset();
+ break;
+ }
+ case 2: {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 8)) {
+ case 0: {
+ data.resize(CHash160::OUTPUT_SIZE);
+ hash160.Finalize(data.data());
+ break;
+ }
+ case 1: {
+ data.resize(CHash256::OUTPUT_SIZE);
+ hash256.Finalize(data.data());
+ break;
+ }
+ case 2: {
+ data.resize(CHMAC_SHA256::OUTPUT_SIZE);
+ hmac_sha256.Finalize(data.data());
+ break;
+ }
+ case 3: {
+ data.resize(CHMAC_SHA512::OUTPUT_SIZE);
+ hmac_sha512.Finalize(data.data());
+ break;
+ }
+ case 4: {
+ data.resize(CRIPEMD160::OUTPUT_SIZE);
+ ripemd160.Finalize(data.data());
+ break;
+ }
+ case 5: {
+ data.resize(CSHA1::OUTPUT_SIZE);
+ sha1.Finalize(data.data());
+ break;
+ }
+ case 6: {
+ data.resize(CSHA256::OUTPUT_SIZE);
+ sha256.Finalize(data.data());
+ break;
+ }
+ case 7: {
+ data.resize(CSHA512::OUTPUT_SIZE);
+ sha512.Finalize(data.data());
+ break;
+ }
+ case 8: {
+ data.resize(1);
+ data[0] = sip_hasher.Finalize() % 256;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/src/test/fuzz/crypto_aes256.cpp b/src/test/fuzz/crypto_aes256.cpp
new file mode 100644
index 0000000000..ae14073c96
--- /dev/null
+++ b/src/test/fuzz/crypto_aes256.cpp
@@ -0,0 +1,30 @@
+// Copyright (c) 2020 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 <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cassert>
+#include <cstdint>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const std::vector<uint8_t> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, AES256_KEYSIZE);
+
+ AES256Encrypt encrypt{key.data()};
+ AES256Decrypt decrypt{key.data()};
+
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const std::vector<uint8_t> plaintext = ConsumeFixedLengthByteVector(fuzzed_data_provider, AES_BLOCKSIZE);
+ std::vector<uint8_t> ciphertext(AES_BLOCKSIZE);
+ encrypt.Encrypt(ciphertext.data(), plaintext.data());
+ std::vector<uint8_t> decrypted_plaintext(AES_BLOCKSIZE);
+ decrypt.Decrypt(decrypted_plaintext.data(), ciphertext.data());
+ assert(decrypted_plaintext == plaintext);
+ }
+}
diff --git a/src/test/fuzz/crypto_aes256cbc.cpp b/src/test/fuzz/crypto_aes256cbc.cpp
new file mode 100644
index 0000000000..52983c7e79
--- /dev/null
+++ b/src/test/fuzz/crypto_aes256cbc.cpp
@@ -0,0 +1,34 @@
+// Copyright (c) 2020 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 <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cassert>
+#include <cstdint>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const std::vector<uint8_t> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, AES256_KEYSIZE);
+ const std::vector<uint8_t> iv = ConsumeFixedLengthByteVector(fuzzed_data_provider, AES_BLOCKSIZE);
+ const bool pad = fuzzed_data_provider.ConsumeBool();
+
+ AES256CBCEncrypt encrypt{key.data(), iv.data(), pad};
+ AES256CBCDecrypt decrypt{key.data(), iv.data(), pad};
+
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const std::vector<uint8_t> plaintext = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ std::vector<uint8_t> ciphertext(plaintext.size() + AES_BLOCKSIZE);
+ const int encrypt_ret = encrypt.Encrypt(plaintext.data(), plaintext.size(), ciphertext.data());
+ ciphertext.resize(encrypt_ret);
+ std::vector<uint8_t> decrypted_plaintext(ciphertext.size());
+ const int decrypt_ret = decrypt.Decrypt(ciphertext.data(), ciphertext.size(), decrypted_plaintext.data());
+ decrypted_plaintext.resize(decrypt_ret);
+ assert(decrypted_plaintext == plaintext || (!pad && plaintext.size() % AES_BLOCKSIZE != 0 && encrypt_ret == 0 && decrypt_ret == 0));
+ }
+}
diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp
new file mode 100644
index 0000000000..b7438d312d
--- /dev/null
+++ b/src/test/fuzz/crypto_chacha20.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) 2020 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/chacha20.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+
+ ChaCha20 chacha20;
+ if (fuzzed_data_provider.ConsumeBool()) {
+ const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(16, 32));
+ chacha20 = ChaCha20{key.data(), key.size()};
+ }
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 4)) {
+ case 0: {
+ const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(16, 32));
+ chacha20.SetKey(key.data(), key.size());
+ break;
+ }
+ case 1: {
+ chacha20.SetIV(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ break;
+ }
+ case 2: {
+ chacha20.Seek(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ break;
+ }
+ case 3: {
+ std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ chacha20.Keystream(output.data(), output.size());
+ break;
+ }
+ case 4: {
+ std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size());
+ chacha20.Crypt(input.data(), output.data(), input.size());
+ break;
+ }
+ }
+ }
+}
diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
new file mode 100644
index 0000000000..48e4263f27
--- /dev/null
+++ b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
@@ -0,0 +1,72 @@
+// Copyright (c) 2020 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/chacha_poly_aead.h>
+#include <crypto/poly1305.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cassert>
+#include <cstdint>
+#include <limits>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+
+ const std::vector<uint8_t> k1 = ConsumeFixedLengthByteVector(fuzzed_data_provider, CHACHA20_POLY1305_AEAD_KEY_LEN);
+ const std::vector<uint8_t> k2 = ConsumeFixedLengthByteVector(fuzzed_data_provider, CHACHA20_POLY1305_AEAD_KEY_LEN);
+
+ ChaCha20Poly1305AEAD aead(k1.data(), k1.size(), k2.data(), k2.size());
+ uint64_t seqnr_payload = 0;
+ uint64_t seqnr_aad = 0;
+ int aad_pos = 0;
+ size_t buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
+ std::vector<uint8_t> in(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+ std::vector<uint8_t> out(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+ bool is_encrypt = fuzzed_data_provider.ConsumeBool();
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 6)) {
+ case 0: {
+ buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(64, 4096);
+ in = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+ out = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+ break;
+ }
+ case 1: {
+ (void)aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffer_size, is_encrypt);
+ break;
+ }
+ case 2: {
+ uint32_t len = 0;
+ const bool ok = aead.GetLength(&len, seqnr_aad, aad_pos, in.data());
+ assert(ok);
+ break;
+ }
+ case 3: {
+ seqnr_payload += 1;
+ aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN;
+ if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) {
+ aad_pos = 0;
+ seqnr_aad += 1;
+ }
+ break;
+ }
+ case 4: {
+ seqnr_payload = fuzzed_data_provider.ConsumeIntegral<int>();
+ break;
+ }
+ case 5: {
+ seqnr_aad = fuzzed_data_provider.ConsumeIntegral<int>();
+ break;
+ }
+ case 6: {
+ is_encrypt = fuzzed_data_provider.ConsumeBool();
+ break;
+ }
+ }
+ }
+}
diff --git a/src/test/fuzz/crypto_common.cpp b/src/test/fuzz/crypto_common.cpp
new file mode 100644
index 0000000000..7ccb125216
--- /dev/null
+++ b/src/test/fuzz/crypto_common.cpp
@@ -0,0 +1,70 @@
+// Copyright (c) 2020 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/common.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <array>
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const uint16_t random_u16 = fuzzed_data_provider.ConsumeIntegral<uint16_t>();
+ const uint32_t random_u32 = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
+ const uint64_t random_u64 = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
+ const std::vector<uint8_t> random_bytes_2 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 2);
+ const std::vector<uint8_t> random_bytes_4 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 4);
+ const std::vector<uint8_t> random_bytes_8 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 8);
+
+ std::array<uint8_t, 2> writele16_arr;
+ WriteLE16(writele16_arr.data(), random_u16);
+ assert(ReadLE16(writele16_arr.data()) == random_u16);
+
+ std::array<uint8_t, 4> writele32_arr;
+ WriteLE32(writele32_arr.data(), random_u32);
+ assert(ReadLE32(writele32_arr.data()) == random_u32);
+
+ std::array<uint8_t, 8> writele64_arr;
+ WriteLE64(writele64_arr.data(), random_u64);
+ assert(ReadLE64(writele64_arr.data()) == random_u64);
+
+ std::array<uint8_t, 4> writebe32_arr;
+ WriteBE32(writebe32_arr.data(), random_u32);
+ assert(ReadBE32(writebe32_arr.data()) == random_u32);
+
+ std::array<uint8_t, 8> writebe64_arr;
+ WriteBE64(writebe64_arr.data(), random_u64);
+ assert(ReadBE64(writebe64_arr.data()) == random_u64);
+
+ const uint16_t readle16_result = ReadLE16(random_bytes_2.data());
+ std::array<uint8_t, 2> readle16_arr;
+ WriteLE16(readle16_arr.data(), readle16_result);
+ assert(std::memcmp(random_bytes_2.data(), readle16_arr.data(), 2) == 0);
+
+ const uint32_t readle32_result = ReadLE32(random_bytes_4.data());
+ std::array<uint8_t, 4> readle32_arr;
+ WriteLE32(readle32_arr.data(), readle32_result);
+ assert(std::memcmp(random_bytes_4.data(), readle32_arr.data(), 4) == 0);
+
+ const uint64_t readle64_result = ReadLE64(random_bytes_8.data());
+ std::array<uint8_t, 8> readle64_arr;
+ WriteLE64(readle64_arr.data(), readle64_result);
+ assert(std::memcmp(random_bytes_8.data(), readle64_arr.data(), 8) == 0);
+
+ const uint32_t readbe32_result = ReadBE32(random_bytes_4.data());
+ std::array<uint8_t, 4> readbe32_arr;
+ WriteBE32(readbe32_arr.data(), readbe32_result);
+ assert(std::memcmp(random_bytes_4.data(), readbe32_arr.data(), 4) == 0);
+
+ const uint64_t readbe64_result = ReadBE64(random_bytes_8.data());
+ std::array<uint8_t, 8> readbe64_arr;
+ WriteBE64(readbe64_arr.data(), readbe64_result);
+ assert(std::memcmp(random_bytes_8.data(), readbe64_arr.data(), 8) == 0);
+}
diff --git a/src/test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp b/src/test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp
new file mode 100644
index 0000000000..e0a4e90c10
--- /dev/null
+++ b/src/test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp
@@ -0,0 +1,25 @@
+// Copyright (c) 2020 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 <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+
+ const std::vector<uint8_t> initial_key_material = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+
+ CHKDF_HMAC_SHA256_L32 hkdf_hmac_sha256_l32(initial_key_material.data(), initial_key_material.size(), fuzzed_data_provider.ConsumeRandomLengthString(1024));
+ while (fuzzed_data_provider.ConsumeBool()) {
+ std::vector<uint8_t> out(32);
+ hkdf_hmac_sha256_l32.Expand32(fuzzed_data_provider.ConsumeRandomLengthString(128), out.data());
+ }
+}
diff --git a/src/test/fuzz/crypto_poly1305.cpp b/src/test/fuzz/crypto_poly1305.cpp
new file mode 100644
index 0000000000..5681e6a693
--- /dev/null
+++ b/src/test/fuzz/crypto_poly1305.cpp
@@ -0,0 +1,22 @@
+// Copyright (c) 2020 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/poly1305.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+
+ const std::vector<uint8_t> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, POLY1305_KEYLEN);
+ const std::vector<uint8_t> in = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+
+ std::vector<uint8_t> tag_out(POLY1305_TAGLEN);
+ poly1305_auth(tag_out.data(), in.data(), in.size(), key.data());
+}
diff --git a/src/test/fuzz/cuckoocache.cpp b/src/test/fuzz/cuckoocache.cpp
index f674efe1b1..5b45aa79d8 100644
--- a/src/test/fuzz/cuckoocache.cpp
+++ b/src/test/fuzz/cuckoocache.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <cuckoocache.h>
-#include <optional.h>
#include <script/sigcache.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
diff --git a/src/test/fuzz/decode_tx.cpp b/src/test/fuzz/decode_tx.cpp
index 09c4ff05df..0d89d4228a 100644
--- a/src/test/fuzz/decode_tx.cpp
+++ b/src/test/fuzz/decode_tx.cpp
@@ -14,7 +14,7 @@
void test_one_input(const std::vector<uint8_t>& buffer)
{
- const std::string tx_hex = HexStr(std::string{buffer.begin(), buffer.end()});
+ const std::string tx_hex = HexStr(buffer);
CMutableTransaction mtx;
const bool result_none = DecodeHexTx(mtx, tx_hex, false, false);
const bool result_try_witness = DecodeHexTx(mtx, tx_hex, false, true);
diff --git a/src/test/fuzz/fees.cpp b/src/test/fuzz/fees.cpp
index f29acace23..ce8700befa 100644
--- a/src/test/fuzz/fees.cpp
+++ b/src/test/fuzz/fees.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <amount.h>
-#include <optional.h>
#include <policy/fees.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
diff --git a/src/test/fuzz/flatfile.cpp b/src/test/fuzz/flatfile.cpp
index a55de77df7..95dabb8bab 100644
--- a/src/test/fuzz/flatfile.cpp
+++ b/src/test/fuzz/flatfile.cpp
@@ -3,24 +3,24 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <flatfile.h>
-#include <optional.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <cassert>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- Optional<FlatFilePos> flat_file_pos = ConsumeDeserializable<FlatFilePos>(fuzzed_data_provider);
+ std::optional<FlatFilePos> flat_file_pos = ConsumeDeserializable<FlatFilePos>(fuzzed_data_provider);
if (!flat_file_pos) {
return;
}
- Optional<FlatFilePos> another_flat_file_pos = ConsumeDeserializable<FlatFilePos>(fuzzed_data_provider);
+ std::optional<FlatFilePos> another_flat_file_pos = ConsumeDeserializable<FlatFilePos>(fuzzed_data_provider);
if (another_flat_file_pos) {
assert((*flat_file_pos == *another_flat_file_pos) != (*flat_file_pos != *another_flat_file_pos));
}
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index 6e2188fe86..1e1807d734 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -12,15 +12,22 @@
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
-#if defined(__AFL_COMPILER)
+// Decide if main(...) should be provided:
+// * AFL needs main(...) regardless of platform.
+// * macOS handles __attribute__((weak)) main(...) poorly when linking
+// against libFuzzer. See https://github.com/bitcoin/bitcoin/pull/18008
+// for details.
+#if defined(__AFL_COMPILER) || !defined(MAC_OSX)
+#define PROVIDE_MAIN_FUNCTION
+#endif
+
+#if defined(PROVIDE_MAIN_FUNCTION)
static bool read_stdin(std::vector<uint8_t>& data)
{
uint8_t buffer[1024];
ssize_t length = 0;
while ((length = read(STDIN_FILENO, buffer, 1024)) > 0) {
data.insert(data.end(), buffer, buffer + length);
-
- if (data.size() > (1 << 20)) return false;
}
return length == 0;
}
@@ -46,9 +53,8 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
return 0;
}
-// Generally, the fuzzer will provide main(), except for AFL
-#if defined(__AFL_COMPILER)
-int main(int argc, char** argv)
+#if defined(PROVIDE_MAIN_FUNCTION)
+__attribute__((weak)) int main(int argc, char** argv)
{
initialize();
#ifdef __AFL_INIT
diff --git a/src/test/fuzz/golomb_rice.cpp b/src/test/fuzz/golomb_rice.cpp
index 3e20416116..a9f450b0c4 100644
--- a/src/test/fuzz/golomb_rice.cpp
+++ b/src/test/fuzz/golomb_rice.cpp
@@ -5,8 +5,8 @@
#include <blockfilter.h>
#include <serialize.h>
#include <streams.h>
-#include <test/fuzz/fuzz.h>
#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <util/bytevectorhash.h>
#include <util/golombrice.h>
diff --git a/src/test/fuzz/hex.cpp b/src/test/fuzz/hex.cpp
index 5fed17c17c..6a8699fd0f 100644
--- a/src/test/fuzz/hex.cpp
+++ b/src/test/fuzz/hex.cpp
@@ -16,7 +16,8 @@
#include <string>
#include <vector>
-void initialize() {
+void initialize()
+{
static const ECCVerifyHandle verify_handle;
}
diff --git a/src/test/fuzz/http_request.cpp b/src/test/fuzz/http_request.cpp
index ebf89749e9..36d44e361f 100644
--- a/src/test/fuzz/http_request.cpp
+++ b/src/test/fuzz/http_request.cpp
@@ -7,6 +7,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <util/strencodings.h>
#include <event2/buffer.h>
#include <event2/event.h>
@@ -48,7 +49,14 @@ void test_one_input(const std::vector<uint8_t>& buffer)
assert(evbuf != nullptr);
const std::vector<uint8_t> http_buffer = ConsumeRandomLengthByteVector(fuzzed_data_provider, 4096);
evbuffer_add(evbuf, http_buffer.data(), http_buffer.size());
- if (evhttp_parse_firstline_(evreq, evbuf) != 1 || evhttp_parse_headers_(evreq, evbuf) != 1) {
+ // Avoid constructing requests that will be interpreted by libevent as PROXY requests to avoid triggering
+ // a nullptr dereference. The dereference (req->evcon->http_server) takes place in evhttp_parse_request_line
+ // and is a consequence of our hacky but necessary use of the internal function evhttp_parse_firstline_ in
+ // this fuzzing harness. The workaround is not aesthetically pleasing, but it successfully avoids the troublesome
+ // code path. " http:// HTTP/1.1\n" was a crashing input prior to this workaround.
+ const std::string http_buffer_str = ToLower({http_buffer.begin(), http_buffer.end()});
+ if (http_buffer_str.find(" http://") != std::string::npos || http_buffer_str.find(" https://") != std::string::npos ||
+ evhttp_parse_firstline_(evreq, evbuf) != 1 || evhttp_parse_headers_(evreq, evbuf) != 1) {
evbuffer_free(evbuf);
evhttp_request_free(evreq);
return;
diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp
index 1919a5f881..c746374c61 100644
--- a/src/test/fuzz/key.cpp
+++ b/src/test/fuzz/key.cpp
@@ -108,7 +108,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
assert(pubkey.IsCompressed());
assert(pubkey.IsValid());
assert(pubkey.IsFullyValid());
- assert(HexToPubKey(HexStr(pubkey.begin(), pubkey.end())) == pubkey);
+ assert(HexToPubKey(HexStr(pubkey)) == pubkey);
assert(GetAllDestinationsForKey(pubkey).size() == 3);
}
@@ -157,25 +157,25 @@ void test_one_input(const std::vector<uint8_t>& buffer)
assert(ok_add_key_pubkey);
assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
- txnouttype which_type_tx_pubkey;
+ TxoutType which_type_tx_pubkey;
const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, which_type_tx_pubkey);
assert(is_standard_tx_pubkey);
- assert(which_type_tx_pubkey == txnouttype::TX_PUBKEY);
+ assert(which_type_tx_pubkey == TxoutType::PUBKEY);
- txnouttype which_type_tx_multisig;
+ TxoutType which_type_tx_multisig;
const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, which_type_tx_multisig);
assert(is_standard_tx_multisig);
- assert(which_type_tx_multisig == txnouttype::TX_MULTISIG);
+ assert(which_type_tx_multisig == TxoutType::MULTISIG);
std::vector<std::vector<unsigned char>> v_solutions_ret_tx_pubkey;
- const txnouttype outtype_tx_pubkey = Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey);
- assert(outtype_tx_pubkey == txnouttype::TX_PUBKEY);
+ const TxoutType outtype_tx_pubkey = Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey);
+ assert(outtype_tx_pubkey == TxoutType::PUBKEY);
assert(v_solutions_ret_tx_pubkey.size() == 1);
assert(v_solutions_ret_tx_pubkey[0].size() == 33);
std::vector<std::vector<unsigned char>> v_solutions_ret_tx_multisig;
- const txnouttype outtype_tx_multisig = Solver(tx_multisig_script, v_solutions_ret_tx_multisig);
- assert(outtype_tx_multisig == txnouttype::TX_MULTISIG);
+ const TxoutType outtype_tx_multisig = Solver(tx_multisig_script, v_solutions_ret_tx_multisig);
+ assert(outtype_tx_multisig == TxoutType::MULTISIG);
assert(v_solutions_ret_tx_multisig.size() == 3);
assert(v_solutions_ret_tx_multisig[0].size() == 1);
assert(v_solutions_ret_tx_multisig[1].size() == 33);
diff --git a/src/test/fuzz/kitchen_sink.cpp b/src/test/fuzz/kitchen_sink.cpp
index af6dc71322..82cbc00a3a 100644
--- a/src/test/fuzz/kitchen_sink.cpp
+++ b/src/test/fuzz/kitchen_sink.cpp
@@ -7,6 +7,7 @@
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <util/error.h>
+#include <util/translation.h>
#include <cstdint>
#include <vector>
diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp
new file mode 100644
index 0000000000..d9de9d9866
--- /dev/null
+++ b/src/test/fuzz/load_external_block_file.cpp
@@ -0,0 +1,31 @@
+// Copyright (c) 2020 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 <flatfile.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+#include <validation.h>
+
+#include <cstdint>
+#include <vector>
+
+void initialize()
+{
+ InitializeFuzzingContext();
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider);
+ FILE* fuzzed_block_file = fuzzed_file_provider.open();
+ if (fuzzed_block_file == nullptr) {
+ return;
+ }
+ FlatFilePos flat_file_pos;
+ LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr);
+}
diff --git a/src/test/fuzz/merkleblock.cpp b/src/test/fuzz/merkleblock.cpp
index eb8fa1d421..c44e334272 100644
--- a/src/test/fuzz/merkleblock.cpp
+++ b/src/test/fuzz/merkleblock.cpp
@@ -3,20 +3,20 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <merkleblock.h>
-#include <optional.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <uint256.h>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- Optional<CPartialMerkleTree> partial_merkle_tree = ConsumeDeserializable<CPartialMerkleTree>(fuzzed_data_provider);
+ std::optional<CPartialMerkleTree> partial_merkle_tree = ConsumeDeserializable<CPartialMerkleTree>(fuzzed_data_provider);
if (!partial_merkle_tree) {
return;
}
diff --git a/src/test/fuzz/message.cpp b/src/test/fuzz/message.cpp
index dfa98a812b..fa0322a391 100644
--- a/src/test/fuzz/message.cpp
+++ b/src/test/fuzz/message.cpp
@@ -4,7 +4,6 @@
#include <chainparams.h>
#include <key_io.h>
-#include <optional.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
diff --git a/src/test/fuzz/net_permissions.cpp b/src/test/fuzz/net_permissions.cpp
index bfc5d21427..ae531f4462 100644
--- a/src/test/fuzz/net_permissions.cpp
+++ b/src/test/fuzz/net_permissions.cpp
@@ -3,10 +3,10 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <net_permissions.h>
-#include <optional.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <util/translation.h>
#include <cassert>
#include <cstdint>
@@ -30,7 +30,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
static_cast<NetPermissionFlags>(fuzzed_data_provider.ConsumeIntegral<uint32_t>());
NetWhitebindPermissions net_whitebind_permissions;
- std::string error_net_whitebind_permissions;
+ bilingual_str error_net_whitebind_permissions;
if (NetWhitebindPermissions::TryParse(s, net_whitebind_permissions, error_net_whitebind_permissions)) {
(void)NetPermissions::ToStrings(net_whitebind_permissions.m_flags);
(void)NetPermissions::AddFlag(net_whitebind_permissions.m_flags, net_permission_flags);
@@ -40,7 +40,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
NetWhitelistPermissions net_whitelist_permissions;
- std::string error_net_whitelist_permissions;
+ bilingual_str error_net_whitelist_permissions;
if (NetWhitelistPermissions::TryParse(s, net_whitelist_permissions, error_net_whitelist_permissions)) {
(void)NetPermissions::ToStrings(net_whitelist_permissions.m_flags);
(void)NetPermissions::AddFlag(net_whitelist_permissions.m_flags, net_permission_flags);
diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp
index d8d53566c7..2901c704f6 100644
--- a/src/test/fuzz/netaddress.cpp
+++ b/src/test/fuzz/netaddress.cpp
@@ -5,41 +5,13 @@
#include <netaddress.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
#include <cassert>
#include <cstdint>
#include <netinet/in.h>
#include <vector>
-namespace {
-CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
-{
- const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION});
- if (network == Network::NET_IPV4) {
- const in_addr v4_addr = {
- .s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
- return CNetAddr{v4_addr};
- } else if (network == Network::NET_IPV6) {
- if (fuzzed_data_provider.remaining_bytes() < 16) {
- return CNetAddr{};
- }
- in6_addr v6_addr = {};
- memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16);
- return CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
- } else if (network == Network::NET_INTERNAL) {
- CNetAddr net_addr;
- net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32));
- return net_addr;
- } else if (network == Network::NET_ONION) {
- CNetAddr net_addr;
- net_addr.SetSpecial(fuzzed_data_provider.ConsumeBytesAsString(32));
- return net_addr;
- } else {
- assert(false);
- }
-}
-}; // namespace
-
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
diff --git a/src/test/fuzz/p2p_transport_deserializer.cpp b/src/test/fuzz/p2p_transport_deserializer.cpp
index 57393fed45..6fba2bfaba 100644
--- a/src/test/fuzz/p2p_transport_deserializer.cpp
+++ b/src/test/fuzz/p2p_transport_deserializer.cpp
@@ -30,7 +30,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
pch += handled;
n_bytes -= handled;
if (deserializer.Complete()) {
- const int64_t m_time = std::numeric_limits<int64_t>::max();
+ const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()};
const CNetMessage msg = deserializer.GetMessage(Params().MessageStart(), m_time);
assert(msg.m_command.size() <= CMessageHeader::COMMAND_SIZE);
assert(msg.m_raw_message_size <= buffer.size());
diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp
index 201f49c87b..6c94a47f3c 100644
--- a/src/test/fuzz/policy_estimator.cpp
+++ b/src/test/fuzz/policy_estimator.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <optional.h>
#include <policy/fees.h>
#include <primitives/transaction.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -11,9 +10,15 @@
#include <txmempool.h>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
+void initialize()
+{
+ InitializeFuzzingContext();
+}
+
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
@@ -21,7 +26,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
while (fuzzed_data_provider.ConsumeBool()) {
switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 3)) {
case 0: {
- const Optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!mtx) {
break;
}
@@ -35,7 +40,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
case 1: {
std::vector<CTxMemPoolEntry> mempool_entries;
while (fuzzed_data_provider.ConsumeBool()) {
- const Optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!mtx) {
break;
}
@@ -66,4 +71,10 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)block_policy_estimator.estimateSmartFee(fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeBool() ? &fee_calculation : nullptr, fuzzed_data_provider.ConsumeBool());
(void)block_policy_estimator.HighestTargetTracked(fuzzed_data_provider.PickValueInArray({FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}));
}
+ {
+ FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider);
+ CAutoFile fuzzed_auto_file = fuzzed_auto_file_provider.open();
+ block_policy_estimator.Write(fuzzed_auto_file);
+ block_policy_estimator.Read(fuzzed_auto_file);
+ }
}
diff --git a/src/test/fuzz/policy_estimator_io.cpp b/src/test/fuzz/policy_estimator_io.cpp
new file mode 100644
index 0000000000..0edcf201c7
--- /dev/null
+++ b/src/test/fuzz/policy_estimator_io.cpp
@@ -0,0 +1,28 @@
+// Copyright (c) 2020 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 <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+void initialize()
+{
+ InitializeFuzzingContext();
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider);
+ CAutoFile fuzzed_auto_file = fuzzed_auto_file_provider.open();
+ // Re-using block_policy_estimator across runs to avoid costly creation of CBlockPolicyEstimator object.
+ static CBlockPolicyEstimator block_policy_estimator;
+ if (block_policy_estimator.Read(fuzzed_auto_file)) {
+ block_policy_estimator.Write(fuzzed_auto_file);
+ }
+}
diff --git a/src/test/fuzz/pow.cpp b/src/test/fuzz/pow.cpp
index 0343d33401..b7fc72373d 100644
--- a/src/test/fuzz/pow.cpp
+++ b/src/test/fuzz/pow.cpp
@@ -4,7 +4,6 @@
#include <chain.h>
#include <chainparams.h>
-#include <optional.h>
#include <pow.h>
#include <primitives/block.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -12,6 +11,7 @@
#include <test/fuzz/util.h>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
@@ -28,7 +28,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const uint32_t fixed_time = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
const uint32_t fixed_bits = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
while (fuzzed_data_provider.remaining_bytes() > 0) {
- const Optional<CBlockHeader> block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
+ const std::optional<CBlockHeader> block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
if (!block_header) {
continue;
}
@@ -72,7 +72,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
}
{
- const Optional<uint256> hash = ConsumeDeserializable<uint256>(fuzzed_data_provider);
+ const std::optional<uint256> hash = ConsumeDeserializable<uint256>(fuzzed_data_provider);
if (hash) {
(void)CheckProofOfWork(*hash, fuzzed_data_provider.ConsumeIntegral<unsigned int>(), consensus_params);
}
diff --git a/src/test/fuzz/prevector.cpp b/src/test/fuzz/prevector.cpp
index 64920f4af5..626e187cbd 100644
--- a/src/test/fuzz/prevector.cpp
+++ b/src/test/fuzz/prevector.cpp
@@ -14,8 +14,9 @@
namespace {
-template<unsigned int N, typename T>
-class prevector_tester {
+template <unsigned int N, typename T>
+class prevector_tester
+{
typedef std::vector<T> realtype;
realtype real_vector;
realtype real_vector_alt;
@@ -27,35 +28,36 @@ class prevector_tester {
typedef typename pretype::size_type Size;
public:
- void test() const {
+ void test() const
+ {
const pretype& const_pre_vector = pre_vector;
assert(real_vector.size() == pre_vector.size());
assert(real_vector.empty() == pre_vector.empty());
for (Size s = 0; s < real_vector.size(); s++) {
- assert(real_vector[s] == pre_vector[s]);
- assert(&(pre_vector[s]) == &(pre_vector.begin()[s]));
- assert(&(pre_vector[s]) == &*(pre_vector.begin() + s));
- assert(&(pre_vector[s]) == &*((pre_vector.end() + s) - real_vector.size()));
+ assert(real_vector[s] == pre_vector[s]);
+ assert(&(pre_vector[s]) == &(pre_vector.begin()[s]));
+ assert(&(pre_vector[s]) == &*(pre_vector.begin() + s));
+ assert(&(pre_vector[s]) == &*((pre_vector.end() + s) - real_vector.size()));
}
// assert(realtype(pre_vector) == real_vector);
assert(pretype(real_vector.begin(), real_vector.end()) == pre_vector);
assert(pretype(pre_vector.begin(), pre_vector.end()) == pre_vector);
size_t pos = 0;
for (const T& v : pre_vector) {
- assert(v == real_vector[pos]);
- ++pos;
+ assert(v == real_vector[pos]);
+ ++pos;
}
for (const T& v : reverse_iterate(pre_vector)) {
- --pos;
- assert(v == real_vector[pos]);
+ --pos;
+ assert(v == real_vector[pos]);
}
for (const T& v : const_pre_vector) {
- assert(v == real_vector[pos]);
- ++pos;
+ assert(v == real_vector[pos]);
+ ++pos;
}
for (const T& v : reverse_iterate(const_pre_vector)) {
- --pos;
- assert(v == real_vector[pos]);
+ --pos;
+ assert(v == real_vector[pos]);
}
CDataStream ss1(SER_DISK, 0);
CDataStream ss2(SER_DISK, 0);
@@ -67,101 +69,120 @@ public:
}
}
- void resize(Size s) {
+ void resize(Size s)
+ {
real_vector.resize(s);
assert(real_vector.size() == s);
pre_vector.resize(s);
assert(pre_vector.size() == s);
}
- void reserve(Size s) {
+ void reserve(Size s)
+ {
real_vector.reserve(s);
assert(real_vector.capacity() >= s);
pre_vector.reserve(s);
assert(pre_vector.capacity() >= s);
}
- void insert(Size position, const T& value) {
+ void insert(Size position, const T& value)
+ {
real_vector.insert(real_vector.begin() + position, value);
pre_vector.insert(pre_vector.begin() + position, value);
}
- void insert(Size position, Size count, const T& value) {
+ void insert(Size position, Size count, const T& value)
+ {
real_vector.insert(real_vector.begin() + position, count, value);
pre_vector.insert(pre_vector.begin() + position, count, value);
}
- template<typename I>
- void insert_range(Size position, I first, I last) {
+ template <typename I>
+ void insert_range(Size position, I first, I last)
+ {
real_vector.insert(real_vector.begin() + position, first, last);
pre_vector.insert(pre_vector.begin() + position, first, last);
}
- void erase(Size position) {
+ void erase(Size position)
+ {
real_vector.erase(real_vector.begin() + position);
pre_vector.erase(pre_vector.begin() + position);
}
- void erase(Size first, Size last) {
+ void erase(Size first, Size last)
+ {
real_vector.erase(real_vector.begin() + first, real_vector.begin() + last);
pre_vector.erase(pre_vector.begin() + first, pre_vector.begin() + last);
}
- void update(Size pos, const T& value) {
+ void update(Size pos, const T& value)
+ {
real_vector[pos] = value;
pre_vector[pos] = value;
}
- void push_back(const T& value) {
+ void push_back(const T& value)
+ {
real_vector.push_back(value);
pre_vector.push_back(value);
}
- void pop_back() {
+ void pop_back()
+ {
real_vector.pop_back();
pre_vector.pop_back();
}
- void clear() {
+ void clear()
+ {
real_vector.clear();
pre_vector.clear();
}
- void assign(Size n, const T& value) {
+ void assign(Size n, const T& value)
+ {
real_vector.assign(n, value);
pre_vector.assign(n, value);
}
- Size size() const {
+ Size size() const
+ {
return real_vector.size();
}
- Size capacity() const {
+ Size capacity() const
+ {
return pre_vector.capacity();
}
- void shrink_to_fit() {
+ void shrink_to_fit()
+ {
pre_vector.shrink_to_fit();
}
- void swap() {
+ void swap()
+ {
real_vector.swap(real_vector_alt);
pre_vector.swap(pre_vector_alt);
}
- void move() {
+ void move()
+ {
real_vector = std::move(real_vector_alt);
real_vector_alt.clear();
pre_vector = std::move(pre_vector_alt);
pre_vector_alt.clear();
}
- void copy() {
+ void copy()
+ {
real_vector = real_vector_alt;
pre_vector = pre_vector_alt;
}
- void resize_uninitialized(realtype values) {
+ void resize_uninitialized(realtype values)
+ {
size_t r = values.size();
size_t s = real_vector.size() / 2;
if (real_vector.capacity() < s + r) {
@@ -181,7 +202,7 @@ public:
}
};
-}
+} // namespace
void test_one_input(const std::vector<uint8_t>& buffer)
{
diff --git a/src/test/fuzz/primitives_transaction.cpp b/src/test/fuzz/primitives_transaction.cpp
index 2e5ba6bdb0..4a0f920f58 100644
--- a/src/test/fuzz/primitives_transaction.cpp
+++ b/src/test/fuzz/primitives_transaction.cpp
@@ -2,13 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <optional.h>
#include <primitives/transaction.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
@@ -16,7 +16,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const CScript script = ConsumeScript(fuzzed_data_provider);
- const Optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
+ const std::optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
if (out_point) {
const CTxIn tx_in{*out_point, script, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
(void)tx_in;
@@ -24,8 +24,8 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const CTxOut tx_out_1{ConsumeMoney(fuzzed_data_provider), script};
const CTxOut tx_out_2{ConsumeMoney(fuzzed_data_provider), ConsumeScript(fuzzed_data_provider)};
assert((tx_out_1 == tx_out_2) != (tx_out_1 != tx_out_2));
- const Optional<CMutableTransaction> mutable_tx_1 = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
- const Optional<CMutableTransaction> mutable_tx_2 = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ const std::optional<CMutableTransaction> mutable_tx_1 = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ const std::optional<CMutableTransaction> mutable_tx_2 = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (mutable_tx_1 && mutable_tx_2) {
const CTransaction tx_1{*mutable_tx_1};
const CTransaction tx_2{*mutable_tx_2};
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index c03365199a..9e40d5cd55 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -14,6 +14,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/util/mining.h>
+#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <util/memory.h>
#include <validationinterface.h>
@@ -29,7 +30,17 @@
#include <string>
#include <vector>
-bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc);
+void ProcessMessage(
+ CNode& pfrom,
+ const std::string& msg_type,
+ CDataStream& vRecv,
+ const std::chrono::microseconds time_received,
+ const CChainParams& chainparams,
+ ChainstateManager& chainman,
+ CTxMemPool& mempool,
+ CConnman& connman,
+ BanMan* banman,
+ const std::atomic<bool>& interruptMsgProc);
namespace {
@@ -63,19 +74,26 @@ void initialize()
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ ConnmanTestMsg& connman = *(ConnmanTestMsg*)g_setup->m_node.connman.get();
const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE).c_str()};
if (!LIMIT_TO_MESSAGE_TYPE.empty() && random_message_type != LIMIT_TO_MESSAGE_TYPE) {
return;
}
CDataStream random_bytes_data_stream{fuzzed_data_provider.ConsumeRemainingBytes<unsigned char>(), SER_NETWORK, PROTOCOL_VERSION};
- CNode p2p_node{0, ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_BLOOM), 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, false};
+ CNode& p2p_node = *MakeUnique<CNode>(0, ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_BLOOM), 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, false).release();
p2p_node.fSuccessfullyConnected = true;
p2p_node.nVersion = PROTOCOL_VERSION;
p2p_node.SetSendVersion(PROTOCOL_VERSION);
+ connman.AddTestNode(p2p_node);
g_setup->m_node.peer_logic->InitializeNode(&p2p_node);
try {
- (void)ProcessMessage(&p2p_node, random_message_type, random_bytes_data_stream, GetTimeMillis(), Params(), *g_setup->m_node.mempool, g_setup->m_node.connman.get(), g_setup->m_node.banman.get(), std::atomic<bool>{false});
+ ProcessMessage(p2p_node, random_message_type, random_bytes_data_stream, GetTime<std::chrono::microseconds>(),
+ Params(), *g_setup->m_node.chainman, *g_setup->m_node.mempool,
+ *g_setup->m_node.connman, g_setup->m_node.banman.get(),
+ std::atomic<bool>{false});
} catch (const std::ios_base::failure&) {
}
SyncWithValidationInterfaceQueue();
+ LOCK2(::cs_main, g_cs_orphans); // See init.cpp for rationale for implicit locking order requirement
+ g_setup->m_node.connman->StopNodes();
}
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index bcbf65bdca..91ebf9fb1b 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -62,7 +62,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE).c_str()};
CSerializedNetMsg net_msg;
- net_msg.command = random_message_type;
+ net_msg.m_type = random_message_type;
net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
CNode& random_node = *peers.at(fuzzed_data_provider.ConsumeIntegralInRange<int>(0, peers.size() - 1));
@@ -75,6 +75,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
} catch (const std::ios_base::failure&) {
}
}
- connman.ClearTestNodes();
SyncWithValidationInterfaceQueue();
+ LOCK2(::cs_main, g_cs_orphans); // See init.cpp for rationale for implicit locking order requirement
+ g_setup->m_node.connman->StopNodes();
}
diff --git a/src/test/fuzz/protocol.cpp b/src/test/fuzz/protocol.cpp
index 954471de6c..78df0f89e7 100644
--- a/src/test/fuzz/protocol.cpp
+++ b/src/test/fuzz/protocol.cpp
@@ -2,20 +2,20 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <optional.h>
#include <protocol.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <cstdint>
+#include <optional>
#include <stdexcept>
#include <vector>
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- const Optional<CInv> inv = ConsumeDeserializable<CInv>(fuzzed_data_provider);
+ const std::optional<CInv> inv = ConsumeDeserializable<CInv>(fuzzed_data_provider);
if (!inv) {
return;
}
@@ -24,7 +24,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
} catch (const std::out_of_range&) {
}
(void)inv->ToString();
- const Optional<CInv> another_inv = ConsumeDeserializable<CInv>(fuzzed_data_provider);
+ const std::optional<CInv> another_inv = ConsumeDeserializable<CInv>(fuzzed_data_provider);
if (!another_inv) {
return;
}
diff --git a/src/test/fuzz/psbt.cpp b/src/test/fuzz/psbt.cpp
index 64328fb66e..908e2b16f2 100644
--- a/src/test/fuzz/psbt.cpp
+++ b/src/test/fuzz/psbt.cpp
@@ -39,7 +39,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
(void)psbt.IsNull();
- (void)psbt.IsSane();
Optional<CMutableTransaction> tx = psbt.tx;
if (tx) {
@@ -50,7 +49,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
for (const PSBTInput& input : psbt.inputs) {
(void)PSBTInputSigned(input);
(void)input.IsNull();
- (void)input.IsSane();
}
for (const PSBTOutput& output : psbt.outputs) {
diff --git a/src/test/fuzz/rbf.cpp b/src/test/fuzz/rbf.cpp
index eb54b05df9..1fd88a5f7b 100644
--- a/src/test/fuzz/rbf.cpp
+++ b/src/test/fuzz/rbf.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <optional.h>
#include <policy/rbf.h>
#include <primitives/transaction.h>
#include <sync.h>
@@ -12,19 +11,20 @@
#include <txmempool.h>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- Optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!mtx) {
return;
}
CTxMemPool pool;
while (fuzzed_data_provider.ConsumeBool()) {
- const Optional<CMutableTransaction> another_mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ const std::optional<CMutableTransaction> another_mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!another_mtx) {
break;
}
diff --git a/src/test/fuzz/rolling_bloom_filter.cpp b/src/test/fuzz/rolling_bloom_filter.cpp
index 3b37321977..623b8cff3a 100644
--- a/src/test/fuzz/rolling_bloom_filter.cpp
+++ b/src/test/fuzz/rolling_bloom_filter.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bloom.h>
-#include <optional.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
@@ -11,6 +10,7 @@
#include <cassert>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
@@ -32,7 +32,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
break;
}
case 1: {
- const Optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
+ const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
if (!u256) {
break;
}
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index de82122dd6..cad548178d 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -11,6 +11,7 @@
#include <script/descriptor.h>
#include <script/interpreter.h>
#include <script/script.h>
+#include <script/script_error.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
@@ -21,6 +22,13 @@
#include <univalue.h>
#include <util/memory.h>
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <vector>
+
void initialize()
{
// Fuzzers using pubkey must hold an ECCVerifyHandle.
@@ -32,7 +40,7 @@ void initialize()
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- const Optional<CScript> script_opt = ConsumeDeserializable<CScript>(fuzzed_data_provider);
+ const std::optional<CScript> script_opt = ConsumeDeserializable<CScript>(fuzzed_data_provider);
if (!script_opt) return;
const CScript script{*script_opt};
@@ -50,7 +58,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
CTxDestination address;
(void)ExtractDestination(script, address);
- txnouttype type_ret;
+ TxoutType type_ret;
std::vector<CTxDestination> addresses;
int required_ret;
(void)ExtractDestinations(script, type_ret, addresses, required_ret);
@@ -64,7 +72,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)IsSolvable(signing_provider, script);
- txnouttype which_type;
+ TxoutType which_type;
(void)IsStandard(script, which_type);
(void)RecursiveDynamicUsage(script);
@@ -101,7 +109,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
}
- const Optional<CScript> other_script = ConsumeDeserializable<CScript>(fuzzed_data_provider);
+ const std::optional<CScript> other_script = ConsumeDeserializable<CScript>(fuzzed_data_provider);
if (other_script) {
{
CScript script_mut{script};
@@ -119,4 +127,40 @@ void test_one_input(const std::vector<uint8_t>& buffer)
wit.SetNull();
}
}
+
+ (void)GetOpName(ConsumeOpcodeType(fuzzed_data_provider));
+ (void)ScriptErrorString(static_cast<ScriptError>(fuzzed_data_provider.ConsumeIntegralInRange<int>(0, SCRIPT_ERR_ERROR_COUNT)));
+
+ {
+ const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ CScript append_script{bytes.begin(), bytes.end()};
+ append_script << fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ append_script << ConsumeOpcodeType(fuzzed_data_provider);
+ append_script << CScriptNum{fuzzed_data_provider.ConsumeIntegral<int64_t>()};
+ append_script << ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ }
+
+ {
+ WitnessUnknown witness_unknown_1{};
+ witness_unknown_1.version = fuzzed_data_provider.ConsumeIntegral<int>();
+ const std::vector<uint8_t> witness_unknown_program_1 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40);
+ witness_unknown_1.length = witness_unknown_program_1.size();
+ std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown_1.program);
+
+ WitnessUnknown witness_unknown_2{};
+ witness_unknown_2.version = fuzzed_data_provider.ConsumeIntegral<int>();
+ const std::vector<uint8_t> witness_unknown_program_2 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40);
+ witness_unknown_2.length = witness_unknown_program_2.size();
+ std::copy(witness_unknown_program_2.begin(), witness_unknown_program_2.end(), witness_unknown_2.program);
+
+ (void)(witness_unknown_1 == witness_unknown_2);
+ (void)(witness_unknown_1 < witness_unknown_2);
+ }
+
+ {
+ const CTxDestination tx_destination_1 = ConsumeTxDestination(fuzzed_data_provider);
+ const CTxDestination tx_destination_2 = ConsumeTxDestination(fuzzed_data_provider);
+ (void)(tx_destination_1 == tx_destination_2);
+ (void)(tx_destination_1 < tx_destination_2);
+ }
}
diff --git a/src/test/fuzz/script_bitcoin_consensus.cpp b/src/test/fuzz/script_bitcoin_consensus.cpp
new file mode 100644
index 0000000000..22f4b4f44a
--- /dev/null
+++ b/src/test/fuzz/script_bitcoin_consensus.cpp
@@ -0,0 +1,31 @@
+// Copyright (c) 2020 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/bitcoinconsensus.h>
+#include <script/interpreter.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const std::vector<uint8_t> random_bytes_1 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ const std::vector<uint8_t> random_bytes_2 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ const CAmount money = ConsumeMoney(fuzzed_data_provider);
+ bitcoinconsensus_error err;
+ bitcoinconsensus_error* err_p = fuzzed_data_provider.ConsumeBool() ? &err : nullptr;
+ const unsigned int n_in = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ const unsigned int flags = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ assert(bitcoinconsensus_version() == BITCOINCONSENSUS_API_VER);
+ if ((flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) {
+ return;
+ }
+ (void)bitcoinconsensus_verify_script(random_bytes_1.data(), random_bytes_1.size(), random_bytes_2.data(), random_bytes_2.size(), n_in, flags, err_p);
+ (void)bitcoinconsensus_verify_script_with_amount(random_bytes_1.data(), random_bytes_1.size(), money, random_bytes_2.data(), random_bytes_2.size(), n_in, flags, err_p);
+}
diff --git a/src/test/fuzz/script_descriptor_cache.cpp b/src/test/fuzz/script_descriptor_cache.cpp
new file mode 100644
index 0000000000..4bfe61cec7
--- /dev/null
+++ b/src/test/fuzz/script_descriptor_cache.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 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 <optional.h>
+#include <pubkey.h>
+#include <script/descriptor.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ DescriptorCache descriptor_cache;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const std::vector<uint8_t> code = fuzzed_data_provider.ConsumeBytes<uint8_t>(BIP32_EXTKEY_SIZE);
+ if (code.size() == BIP32_EXTKEY_SIZE) {
+ CExtPubKey xpub;
+ xpub.Decode(code.data());
+ const uint32_t key_exp_pos = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
+ CExtPubKey xpub_fetched;
+ if (fuzzed_data_provider.ConsumeBool()) {
+ (void)descriptor_cache.GetCachedParentExtPubKey(key_exp_pos, xpub_fetched);
+ descriptor_cache.CacheParentExtPubKey(key_exp_pos, xpub);
+ assert(descriptor_cache.GetCachedParentExtPubKey(key_exp_pos, xpub_fetched));
+ } else {
+ const uint32_t der_index = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
+ (void)descriptor_cache.GetCachedDerivedExtPubKey(key_exp_pos, der_index, xpub_fetched);
+ descriptor_cache.CacheDerivedExtPubKey(key_exp_pos, der_index, xpub);
+ assert(descriptor_cache.GetCachedDerivedExtPubKey(key_exp_pos, der_index, xpub_fetched));
+ }
+ assert(xpub == xpub_fetched);
+ }
+ (void)descriptor_cache.GetCachedParentExtPubKeys();
+ (void)descriptor_cache.GetCachedDerivedExtPubKeys();
+ }
+}
diff --git a/src/test/fuzz/script_interpreter.cpp b/src/test/fuzz/script_interpreter.cpp
new file mode 100644
index 0000000000..26d5732f24
--- /dev/null
+++ b/src/test/fuzz/script_interpreter.cpp
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 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 <primitives/transaction.h>
+#include <script/interpreter.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <vector>
+
+bool CastToBool(const std::vector<unsigned char>& vch);
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ {
+ const CScript script_code = ConsumeScript(fuzzed_data_provider);
+ const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (mtx) {
+ const CTransaction tx_to{*mtx};
+ const unsigned int in = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ if (in < tx_to.vin.size()) {
+ (void)SignatureHash(script_code, tx_to, in, fuzzed_data_provider.ConsumeIntegral<int>(), ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0}), nullptr);
+ const std::optional<CMutableTransaction> mtx_precomputed = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (mtx_precomputed) {
+ const CTransaction tx_precomputed{*mtx_precomputed};
+ const PrecomputedTransactionData precomputed_transaction_data{tx_precomputed};
+ (void)SignatureHash(script_code, tx_to, in, fuzzed_data_provider.ConsumeIntegral<int>(), ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0}), &precomputed_transaction_data);
+ }
+ }
+ }
+ }
+ {
+ (void)CastToBool(ConsumeRandomLengthByteVector(fuzzed_data_provider));
+ }
+}
diff --git a/src/test/fuzz/script_sigcache.cpp b/src/test/fuzz/script_sigcache.cpp
new file mode 100644
index 0000000000..434a47b702
--- /dev/null
+++ b/src/test/fuzz/script_sigcache.cpp
@@ -0,0 +1,45 @@
+// Copyright (c) 2020 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 <chainparamsbase.h>
+#include <key.h>
+#include <pubkey.h>
+#include <script/sigcache.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <vector>
+
+void initialize()
+{
+ static const ECCVerifyHandle ecc_verify_handle;
+ ECC_Start();
+ SelectParams(CBaseChainParams::REGTEST);
+ InitSignatureCache();
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ const std::optional<CMutableTransaction> mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ const CTransaction tx = mutable_transaction ? CTransaction{*mutable_transaction} : CTransaction{};
+ const unsigned int n_in = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ const CAmount amount = ConsumeMoney(fuzzed_data_provider);
+ const bool store = fuzzed_data_provider.ConsumeBool();
+ PrecomputedTransactionData tx_data;
+ CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, tx_data};
+ const std::optional<CPubKey> pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
+ if (pub_key) {
+ const std::vector<uint8_t> random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ if (!random_bytes.empty()) {
+ (void)caching_transaction_signature_checker.VerifySignature(random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider));
+ }
+ }
+}
diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp
new file mode 100644
index 0000000000..c626f950e7
--- /dev/null
+++ b/src/test/fuzz/script_sign.cpp
@@ -0,0 +1,149 @@
+// Copyright (c) 2020 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 <chainparamsbase.h>
+#include <key.h>
+#include <pubkey.h>
+#include <script/keyorigin.h>
+#include <script/sign.h>
+#include <script/signingprovider.h>
+#include <streams.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cassert>
+#include <cstdint>
+#include <iostream>
+#include <map>
+#include <optional>
+#include <string>
+#include <vector>
+
+void initialize()
+{
+ static const ECCVerifyHandle ecc_verify_handle;
+ ECC_Start();
+ SelectParams(CBaseChainParams::REGTEST);
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const std::vector<uint8_t> key = ConsumeRandomLengthByteVector(fuzzed_data_provider, 128);
+
+ {
+ CDataStream random_data_stream = ConsumeDataStream(fuzzed_data_provider);
+ std::map<CPubKey, KeyOriginInfo> hd_keypaths;
+ try {
+ DeserializeHDKeypaths(random_data_stream, key, hd_keypaths);
+ } catch (const std::ios_base::failure&) {
+ }
+ CDataStream serialized{SER_NETWORK, PROTOCOL_VERSION};
+ SerializeHDKeypaths(serialized, hd_keypaths, fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ }
+
+ {
+ std::map<CPubKey, KeyOriginInfo> hd_keypaths;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const std::optional<CPubKey> pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
+ if (!pub_key) {
+ break;
+ }
+ const std::optional<KeyOriginInfo> key_origin_info = ConsumeDeserializable<KeyOriginInfo>(fuzzed_data_provider);
+ if (!key_origin_info) {
+ break;
+ }
+ hd_keypaths[*pub_key] = *key_origin_info;
+ }
+ CDataStream serialized{SER_NETWORK, PROTOCOL_VERSION};
+ try {
+ SerializeHDKeypaths(serialized, hd_keypaths, fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ } catch (const std::ios_base::failure&) {
+ }
+ std::map<CPubKey, KeyOriginInfo> deserialized_hd_keypaths;
+ try {
+ DeserializeHDKeypaths(serialized, key, hd_keypaths);
+ } catch (const std::ios_base::failure&) {
+ }
+ assert(hd_keypaths.size() >= deserialized_hd_keypaths.size());
+ }
+
+ {
+ SignatureData signature_data_1{ConsumeScript(fuzzed_data_provider)};
+ SignatureData signature_data_2{ConsumeScript(fuzzed_data_provider)};
+ signature_data_1.MergeSignatureData(signature_data_2);
+ }
+
+ FillableSigningProvider provider;
+ CKey k;
+ const std::vector<uint8_t> key_data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ k.Set(key_data.begin(), key_data.end(), fuzzed_data_provider.ConsumeBool());
+ if (k.IsValid()) {
+ provider.AddKey(k);
+ }
+
+ {
+ const std::optional<CMutableTransaction> mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ const std::optional<CTxOut> tx_out = ConsumeDeserializable<CTxOut>(fuzzed_data_provider);
+ const unsigned int n_in = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ if (mutable_transaction && tx_out && mutable_transaction->vin.size() > n_in) {
+ SignatureData signature_data_1 = DataFromTransaction(*mutable_transaction, n_in, *tx_out);
+ CTxIn input;
+ UpdateInput(input, signature_data_1);
+ const CScript script = ConsumeScript(fuzzed_data_provider);
+ SignatureData signature_data_2{script};
+ signature_data_1.MergeSignatureData(signature_data_2);
+ }
+ if (mutable_transaction) {
+ CTransaction tx_from{*mutable_transaction};
+ CMutableTransaction tx_to;
+ const std::optional<CMutableTransaction> opt_tx_to = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (opt_tx_to) {
+ tx_to = *opt_tx_to;
+ }
+ CMutableTransaction script_tx_to = tx_to;
+ CMutableTransaction sign_transaction_tx_to = tx_to;
+ if (n_in < tx_to.vin.size() && tx_to.vin[n_in].prevout.n < tx_from.vout.size()) {
+ (void)SignSignature(provider, tx_from, tx_to, n_in, fuzzed_data_provider.ConsumeIntegral<int>());
+ }
+ if (n_in < script_tx_to.vin.size()) {
+ (void)SignSignature(provider, ConsumeScript(fuzzed_data_provider), script_tx_to, n_in, ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int>());
+ MutableTransactionSignatureCreator signature_creator{&tx_to, n_in, ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int>()};
+ std::vector<unsigned char> vch_sig;
+ CKeyID address;
+ if (fuzzed_data_provider.ConsumeBool()) {
+ if (k.IsValid()) {
+ address = k.GetPubKey().GetID();
+ }
+ } else {
+ address = CKeyID{ConsumeUInt160(fuzzed_data_provider)};
+ }
+ (void)signature_creator.CreateSig(provider, vch_sig, address, ConsumeScript(fuzzed_data_provider), fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0}));
+ }
+ std::map<COutPoint, Coin> coins;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const std::optional<COutPoint> outpoint = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
+ if (!outpoint) {
+ break;
+ }
+ const std::optional<Coin> coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
+ if (!coin) {
+ break;
+ }
+ coins[*outpoint] = *coin;
+ }
+ std::map<int, std::string> input_errors;
+ (void)SignTransaction(sign_transaction_tx_to, &provider, coins, fuzzed_data_provider.ConsumeIntegral<int>(), input_errors);
+ }
+ }
+
+ {
+ SignatureData signature_data_1;
+ (void)ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, ConsumeScript(fuzzed_data_provider), signature_data_1);
+ SignatureData signature_data_2;
+ (void)ProduceSignature(provider, DUMMY_MAXIMUM_SIGNATURE_CREATOR, ConsumeScript(fuzzed_data_provider), signature_data_2);
+ }
+}
diff --git a/src/test/fuzz/span.cpp b/src/test/fuzz/span.cpp
index 4aea530ef2..f6b6e8f6f0 100644
--- a/src/test/fuzz/span.cpp
+++ b/src/test/fuzz/span.cpp
@@ -18,7 +18,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
std::string str = fuzzed_data_provider.ConsumeBytesAsString(32);
- const Span<const char> span = MakeSpan(str);
+ const Span<const char> span{str};
(void)span.data();
(void)span.begin();
(void)span.end();
@@ -32,7 +32,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
std::string another_str = fuzzed_data_provider.ConsumeBytesAsString(32);
- const Span<const char> another_span = MakeSpan(another_str);
+ const Span<const char> another_span{another_str};
assert((span <= another_span) != (span > another_span));
assert((span == another_span) != (span != another_span));
assert((span >= another_span) != (span < another_span));
diff --git a/src/test/fuzz/spanparsing.cpp b/src/test/fuzz/spanparsing.cpp
index 8e5e7dad11..e5bf5dd608 100644
--- a/src/test/fuzz/spanparsing.cpp
+++ b/src/test/fuzz/spanparsing.cpp
@@ -12,7 +12,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const size_t query_size = fuzzed_data_provider.ConsumeIntegral<size_t>();
const std::string query = fuzzed_data_provider.ConsumeBytesAsString(std::min<size_t>(query_size, 1024 * 1024));
const std::string span_str = fuzzed_data_provider.ConsumeRemainingBytesAsString();
- const Span<const char> const_span = MakeSpan(span_str);
+ const Span<const char> const_span{span_str};
Span<const char> mut_span = const_span;
(void)spanparsing::Const(query, mut_span);
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index 49bee0e81f..271062dc95 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -12,6 +12,7 @@
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/descriptor.h>
+#include <script/script.h>
#include <serialize.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -89,11 +90,15 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)urlDecode(random_string_1);
(void)ValidAsCString(random_string_1);
(void)_(random_string_1.c_str());
+ try {
+ throw scriptnum_error{random_string_1};
+ } catch (const std::runtime_error&) {
+ }
{
CDataStream data_stream{SER_NETWORK, INIT_PROTO_VERSION};
std::string s;
- LimitedString<10> limited_string = LIMITED_STRING(s, 10);
+ auto limited_string = LIMITED_STRING(s, 10);
data_stream << random_string_1;
try {
data_stream >> limited_string;
@@ -108,7 +113,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
{
CDataStream data_stream{SER_NETWORK, INIT_PROTO_VERSION};
- const LimitedString<10> limited_string = LIMITED_STRING(random_string_1, 10);
+ const auto limited_string = LIMITED_STRING(random_string_1, 10);
data_stream << limited_string;
std::string deserialized_string;
data_stream >> deserialized_string;
@@ -119,4 +124,10 @@ void test_one_input(const std::vector<uint8_t>& buffer)
int64_t amount_out;
(void)ParseFixedPoint(random_string_1, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 1024), &amount_out);
}
+ {
+ (void)Untranslated(random_string_1);
+ const bilingual_str bs1{random_string_1, random_string_2};
+ const bilingual_str bs2{random_string_2, random_string_1};
+ (void)(bs1 + bs2);
+ }
}
diff --git a/src/test/fuzz/strprintf.cpp b/src/test/fuzz/strprintf.cpp
index d5be1070bd..29064bc45c 100644
--- a/src/test/fuzz/strprintf.cpp
+++ b/src/test/fuzz/strprintf.cpp
@@ -6,6 +6,7 @@
#include <test/fuzz/fuzz.h>
#include <tinyformat.h>
#include <util/strencodings.h>
+#include <util/translation.h>
#include <algorithm>
#include <cstdint>
@@ -16,6 +17,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const std::string format_string = fuzzed_data_provider.ConsumeRandomLengthString(64);
+ const bilingual_str bilingual_string{format_string, format_string};
const int digits_in_format_specifier = std::count_if(format_string.begin(), format_string.end(), IsDigit);
@@ -47,50 +49,62 @@ void test_one_input(const std::vector<uint8_t>& buffer)
try {
(void)strprintf(format_string, (signed char*)nullptr);
+ (void)tinyformat::format(bilingual_string, (signed char*)nullptr);
} catch (const tinyformat::format_error&) {
}
try {
(void)strprintf(format_string, (unsigned char*)nullptr);
+ (void)tinyformat::format(bilingual_string, (unsigned char*)nullptr);
} catch (const tinyformat::format_error&) {
}
try {
(void)strprintf(format_string, (void*)nullptr);
+ (void)tinyformat::format(bilingual_string, (void*)nullptr);
} catch (const tinyformat::format_error&) {
}
try {
(void)strprintf(format_string, (bool*)nullptr);
+ (void)tinyformat::format(bilingual_string, (bool*)nullptr);
} catch (const tinyformat::format_error&) {
}
try {
(void)strprintf(format_string, (float*)nullptr);
+ (void)tinyformat::format(bilingual_string, (float*)nullptr);
} catch (const tinyformat::format_error&) {
}
try {
(void)strprintf(format_string, (double*)nullptr);
+ (void)tinyformat::format(bilingual_string, (double*)nullptr);
} catch (const tinyformat::format_error&) {
}
try {
(void)strprintf(format_string, (int16_t*)nullptr);
+ (void)tinyformat::format(bilingual_string, (int16_t*)nullptr);
} catch (const tinyformat::format_error&) {
}
try {
(void)strprintf(format_string, (uint16_t*)nullptr);
+ (void)tinyformat::format(bilingual_string, (uint16_t*)nullptr);
} catch (const tinyformat::format_error&) {
}
try {
(void)strprintf(format_string, (int32_t*)nullptr);
+ (void)tinyformat::format(bilingual_string, (int32_t*)nullptr);
} catch (const tinyformat::format_error&) {
}
try {
(void)strprintf(format_string, (uint32_t*)nullptr);
+ (void)tinyformat::format(bilingual_string, (uint32_t*)nullptr);
} catch (const tinyformat::format_error&) {
}
try {
(void)strprintf(format_string, (int64_t*)nullptr);
+ (void)tinyformat::format(bilingual_string, (int64_t*)nullptr);
} catch (const tinyformat::format_error&) {
}
try {
(void)strprintf(format_string, (uint64_t*)nullptr);
+ (void)tinyformat::format(bilingual_string, (uint64_t*)nullptr);
} catch (const tinyformat::format_error&) {
}
@@ -98,21 +112,27 @@ void test_one_input(const std::vector<uint8_t>& buffer)
switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 5)) {
case 0:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
break;
case 1:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str());
break;
case 2:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<signed char>());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<signed char>());
break;
case 3:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>());
break;
case 4:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<char>());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<char>());
break;
case 5:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeBool());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeBool());
break;
}
} catch (const tinyformat::format_error&) {
@@ -138,27 +158,35 @@ void test_one_input(const std::vector<uint8_t>& buffer)
switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 7)) {
case 0:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
break;
case 1:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
break;
case 2:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
break;
case 3:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
break;
case 4:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
break;
case 5:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
break;
case 6:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
break;
case 7:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
break;
}
} catch (const tinyformat::format_error&) {
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 501bb1de5a..148610c04d 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -8,19 +8,27 @@
#include <amount.h>
#include <arith_uint256.h>
#include <attributes.h>
+#include <chainparamsbase.h>
+#include <coins.h>
#include <consensus/consensus.h>
-#include <optional.h>
+#include <netaddress.h>
+#include <netbase.h>
#include <primitives/transaction.h>
#include <script/script.h>
+#include <script/standard.h>
#include <serialize.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
+#include <test/util/setup_common.h>
#include <txmempool.h>
#include <uint256.h>
#include <version.h>
+#include <algorithm>
#include <cstdint>
+#include <cstdio>
+#include <optional>
#include <string>
#include <vector>
@@ -30,6 +38,11 @@ NODISCARD inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataPr
return {s.begin(), s.end()};
}
+NODISCARD inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+{
+ return {ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length), SER_NETWORK, INIT_PROTO_VERSION};
+}
+
NODISCARD inline std::vector<std::string> ConsumeRandomLengthStringVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_vector_size = 16, const size_t max_string_length = 16) noexcept
{
const size_t n_elements = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size);
@@ -52,7 +65,7 @@ NODISCARD inline std::vector<T> ConsumeRandomLengthIntegralVector(FuzzedDataProv
}
template <typename T>
-NODISCARD inline Optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+NODISCARD inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
{
const std::vector<uint8_t> buffer = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length);
CDataStream ds{buffer, SER_NETWORK, INIT_PROTO_VERSION};
@@ -60,7 +73,7 @@ NODISCARD inline Optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_da
try {
ds >> obj;
} catch (const std::ios_base::failure&) {
- return nullopt;
+ return std::nullopt;
}
return obj;
}
@@ -86,10 +99,19 @@ NODISCARD inline CScriptNum ConsumeScriptNum(FuzzedDataProvider& fuzzed_data_pro
return CScriptNum{fuzzed_data_provider.ConsumeIntegral<int64_t>()};
}
+NODISCARD inline uint160 ConsumeUInt160(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ const std::vector<uint8_t> v160 = fuzzed_data_provider.ConsumeBytes<uint8_t>(160 / 8);
+ if (v160.size() != 160 / 8) {
+ return {};
+ }
+ return uint160{v160};
+}
+
NODISCARD inline uint256 ConsumeUInt256(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
- const std::vector<unsigned char> v256 = fuzzed_data_provider.ConsumeBytes<unsigned char>(sizeof(uint256));
- if (v256.size() != sizeof(uint256)) {
+ const std::vector<uint8_t> v256 = fuzzed_data_provider.ConsumeBytes<uint8_t>(256 / 8);
+ if (v256.size() != 256 / 8) {
return {};
}
return uint256{v256};
@@ -115,6 +137,43 @@ NODISCARD inline CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzze
return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}};
}
+NODISCARD inline CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ CTxDestination tx_destination;
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) {
+ case 0: {
+ tx_destination = CNoDestination{};
+ break;
+ }
+ case 1: {
+ tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)};
+ break;
+ }
+ case 2: {
+ tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)};
+ break;
+ }
+ case 3: {
+ tx_destination = WitnessV0ScriptHash{ConsumeUInt256(fuzzed_data_provider)};
+ break;
+ }
+ case 4: {
+ tx_destination = WitnessV0KeyHash{ConsumeUInt160(fuzzed_data_provider)};
+ break;
+ }
+ case 5: {
+ WitnessUnknown witness_unknown{};
+ witness_unknown.version = fuzzed_data_provider.ConsumeIntegral<int>();
+ const std::vector<uint8_t> witness_unknown_program_1 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40);
+ witness_unknown.length = witness_unknown_program_1.size();
+ std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown.program);
+ tx_destination = witness_unknown;
+ break;
+ }
+ }
+ return tx_destination;
+}
+
template <typename T>
NODISCARD bool MultiplicationOverflow(const T i, const T j) noexcept
{
@@ -149,4 +208,266 @@ NODISCARD bool AdditionOverflow(const T i, const T j) noexcept
return std::numeric_limits<T>::max() - i < j;
}
+NODISCARD inline bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept
+{
+ for (const CTxIn& tx_in : tx.vin) {
+ const Coin& coin = inputs.AccessCoin(tx_in.prevout);
+ if (coin.IsSpent()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Returns a byte vector of specified size regardless of the number of remaining bytes available
+ * from the fuzzer. Pads with zero value bytes if needed to achieve the specified size.
+ */
+NODISCARD inline std::vector<uint8_t> ConsumeFixedLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t length) noexcept
+{
+ std::vector<uint8_t> result(length);
+ const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(length);
+ if (!random_bytes.empty()) {
+ std::memcpy(result.data(), random_bytes.data(), random_bytes.size());
+ }
+ return result;
+}
+
+CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION});
+ CNetAddr net_addr;
+ if (network == Network::NET_IPV4) {
+ const in_addr v4_addr = {
+ .s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+ net_addr = CNetAddr{v4_addr};
+ } else if (network == Network::NET_IPV6) {
+ if (fuzzed_data_provider.remaining_bytes() >= 16) {
+ in6_addr v6_addr = {};
+ memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16);
+ net_addr = CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+ }
+ } else if (network == Network::NET_INTERNAL) {
+ net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32));
+ } else if (network == Network::NET_ONION) {
+ net_addr.SetSpecial(fuzzed_data_provider.ConsumeBytesAsString(32));
+ }
+ return net_addr;
+}
+
+CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int32_t>()};
+}
+
+void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST)
+{
+ static const BasicTestingSetup basic_testing_setup{chain_name, {"-nodebuglogfile"}};
+}
+
+class FuzzedFileProvider
+{
+ FuzzedDataProvider& m_fuzzed_data_provider;
+ int64_t m_offset = 0;
+
+public:
+ FuzzedFileProvider(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider}
+ {
+ }
+
+ FILE* open()
+ {
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ return nullptr;
+ }
+ std::string mode;
+ switch (m_fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) {
+ case 0: {
+ mode = "r";
+ break;
+ }
+ case 1: {
+ mode = "r+";
+ break;
+ }
+ case 2: {
+ mode = "w";
+ break;
+ }
+ case 3: {
+ mode = "w+";
+ break;
+ }
+ case 4: {
+ mode = "a";
+ break;
+ }
+ case 5: {
+ mode = "a+";
+ break;
+ }
+ }
+#ifdef _GNU_SOURCE
+ const cookie_io_functions_t io_hooks = {
+ FuzzedFileProvider::read,
+ FuzzedFileProvider::write,
+ FuzzedFileProvider::seek,
+ FuzzedFileProvider::close,
+ };
+ return fopencookie(this, mode.c_str(), io_hooks);
+#else
+ (void)mode;
+ return nullptr;
+#endif
+ }
+
+ static ssize_t read(void* cookie, char* buf, size_t size)
+ {
+ FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
+ if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) {
+ return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
+ }
+ const std::vector<uint8_t> random_bytes = fuzzed_file->m_fuzzed_data_provider.ConsumeBytes<uint8_t>(size);
+ if (random_bytes.empty()) {
+ return 0;
+ }
+ std::memcpy(buf, random_bytes.data(), random_bytes.size());
+ if (AdditionOverflow((uint64_t)fuzzed_file->m_offset, random_bytes.size())) {
+ return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
+ }
+ fuzzed_file->m_offset += random_bytes.size();
+ return random_bytes.size();
+ }
+
+ static ssize_t write(void* cookie, const char* buf, size_t size)
+ {
+ FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
+ const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size);
+ if (AdditionOverflow(fuzzed_file->m_offset, n)) {
+ return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
+ }
+ fuzzed_file->m_offset += n;
+ return n;
+ }
+
+ static int seek(void* cookie, int64_t* offset, int whence)
+ {
+ assert(whence == SEEK_SET || whence == SEEK_CUR); // SEEK_END not implemented yet.
+ FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
+ int64_t new_offset = 0;
+ if (whence == SEEK_SET) {
+ new_offset = *offset;
+ } else if (whence == SEEK_CUR) {
+ if (AdditionOverflow(fuzzed_file->m_offset, *offset)) {
+ return -1;
+ }
+ new_offset = fuzzed_file->m_offset + *offset;
+ }
+ if (new_offset < 0) {
+ return -1;
+ }
+ fuzzed_file->m_offset = new_offset;
+ *offset = new_offset;
+ return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
+ }
+
+ static int close(void* cookie)
+ {
+ FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
+ return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
+ }
+};
+
+NODISCARD inline FuzzedFileProvider ConsumeFile(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ return {fuzzed_data_provider};
+}
+
+class FuzzedAutoFileProvider
+{
+ FuzzedDataProvider& m_fuzzed_data_provider;
+ FuzzedFileProvider m_fuzzed_file_provider;
+
+public:
+ FuzzedAutoFileProvider(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider}, m_fuzzed_file_provider{fuzzed_data_provider}
+ {
+ }
+
+ CAutoFile open()
+ {
+ return {m_fuzzed_file_provider.open(), m_fuzzed_data_provider.ConsumeIntegral<int>(), m_fuzzed_data_provider.ConsumeIntegral<int>()};
+ }
+};
+
+NODISCARD inline FuzzedAutoFileProvider ConsumeAutoFile(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ return {fuzzed_data_provider};
+}
+
+#define WRITE_TO_STREAM_CASE(id, type, consume) \
+ case id: { \
+ type o = consume; \
+ stream << o; \
+ break; \
+ }
+template <typename Stream>
+void WriteToStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noexcept
+{
+ while (fuzzed_data_provider.ConsumeBool()) {
+ try {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 13)) {
+ WRITE_TO_STREAM_CASE(0, bool, fuzzed_data_provider.ConsumeBool())
+ WRITE_TO_STREAM_CASE(1, char, fuzzed_data_provider.ConsumeIntegral<char>())
+ WRITE_TO_STREAM_CASE(2, int8_t, fuzzed_data_provider.ConsumeIntegral<int8_t>())
+ WRITE_TO_STREAM_CASE(3, uint8_t, fuzzed_data_provider.ConsumeIntegral<uint8_t>())
+ WRITE_TO_STREAM_CASE(4, int16_t, fuzzed_data_provider.ConsumeIntegral<int16_t>())
+ WRITE_TO_STREAM_CASE(5, uint16_t, fuzzed_data_provider.ConsumeIntegral<uint16_t>())
+ WRITE_TO_STREAM_CASE(6, int32_t, fuzzed_data_provider.ConsumeIntegral<int32_t>())
+ WRITE_TO_STREAM_CASE(7, uint32_t, fuzzed_data_provider.ConsumeIntegral<uint32_t>())
+ WRITE_TO_STREAM_CASE(8, int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>())
+ WRITE_TO_STREAM_CASE(9, uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>())
+ WRITE_TO_STREAM_CASE(10, float, fuzzed_data_provider.ConsumeFloatingPoint<float>())
+ WRITE_TO_STREAM_CASE(11, double, fuzzed_data_provider.ConsumeFloatingPoint<double>())
+ WRITE_TO_STREAM_CASE(12, std::string, fuzzed_data_provider.ConsumeRandomLengthString(32))
+ WRITE_TO_STREAM_CASE(13, std::vector<char>, ConsumeRandomLengthIntegralVector<char>(fuzzed_data_provider))
+ }
+ } catch (const std::ios_base::failure&) {
+ break;
+ }
+ }
+}
+
+#define READ_FROM_STREAM_CASE(id, type) \
+ case id: { \
+ type o; \
+ stream >> o; \
+ break; \
+ }
+template <typename Stream>
+void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noexcept
+{
+ while (fuzzed_data_provider.ConsumeBool()) {
+ try {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 13)) {
+ READ_FROM_STREAM_CASE(0, bool)
+ READ_FROM_STREAM_CASE(1, char)
+ READ_FROM_STREAM_CASE(2, int8_t)
+ READ_FROM_STREAM_CASE(3, uint8_t)
+ READ_FROM_STREAM_CASE(4, int16_t)
+ READ_FROM_STREAM_CASE(5, uint16_t)
+ READ_FROM_STREAM_CASE(6, int32_t)
+ READ_FROM_STREAM_CASE(7, uint32_t)
+ READ_FROM_STREAM_CASE(8, int64_t)
+ READ_FROM_STREAM_CASE(9, uint64_t)
+ READ_FROM_STREAM_CASE(10, float)
+ READ_FROM_STREAM_CASE(11, double)
+ READ_FROM_STREAM_CASE(12, std::string)
+ READ_FROM_STREAM_CASE(13, std::vector<char>)
+ }
+ } catch (const std::ios_base::failure&) {
+ break;
+ }
+ }
+}
+
#endif // BITCOIN_TEST_FUZZ_UTIL_H
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 512e48f8e5..45c9b90ee9 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -13,9 +13,18 @@
#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(getarg_tests, BasicTestingSetup)
+namespace getarg_tests{
+ class LocalTestingSetup : BasicTestingSetup {
+ protected:
+ void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args);
+ void ResetArgs(const std::string& strArg);
+ ArgsManager m_args;
+ };
+}
+
+BOOST_FIXTURE_TEST_SUITE(getarg_tests, LocalTestingSetup)
-static void ResetArgs(const std::string& strArg)
+void LocalTestingSetup :: ResetArgs(const std::string& strArg)
{
std::vector<std::string> vecArg;
if (strArg.size())
@@ -30,14 +39,14 @@ static void ResetArgs(const std::string& strArg)
vecChar.push_back(s.c_str());
std::string error;
- BOOST_CHECK(gArgs.ParseParameters(vecChar.size(), vecChar.data(), error));
+ BOOST_CHECK(m_args.ParseParameters(vecChar.size(), vecChar.data(), error));
}
-static void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args)
+void LocalTestingSetup :: SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args)
{
- gArgs.ClearArgs();
+ m_args.ClearArgs();
for (const auto& arg : args) {
- gArgs.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
+ m_args.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
}
}
@@ -46,52 +55,52 @@ BOOST_AUTO_TEST_CASE(boolarg)
const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
SetupArgs({foo});
ResetArgs("-foo");
- BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", true));
- BOOST_CHECK(!gArgs.GetBoolArg("-fo", false));
- BOOST_CHECK(gArgs.GetBoolArg("-fo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-fo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-fo", true));
- BOOST_CHECK(!gArgs.GetBoolArg("-fooo", false));
- BOOST_CHECK(gArgs.GetBoolArg("-fooo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-fooo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-fooo", true));
ResetArgs("-foo=0");
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
ResetArgs("-foo=1");
- BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", true));
// New 0.6 feature: auto-map -nosomething to !-something:
ResetArgs("-nofoo");
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
ResetArgs("-nofoo=1");
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
ResetArgs("-foo -nofoo"); // -nofoo should win
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
ResetArgs("-foo=1 -nofoo=1"); // -nofoo should win
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
ResetArgs("-foo=0 -nofoo=0"); // -nofoo=0 should win
- BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", true));
// New 0.6 feature: treat -- same as -:
ResetArgs("--foo=1");
- BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", true));
ResetArgs("--nofoo=1");
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
}
@@ -101,24 +110,24 @@ BOOST_AUTO_TEST_CASE(stringarg)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "eleven");
ResetArgs("-foo -bar");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "");
ResetArgs("-foo=");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "");
ResetArgs("-foo=11");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "11");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "11");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "11");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "11");
ResetArgs("-foo=eleven");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "eleven");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "eleven");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "eleven");
}
@@ -128,20 +137,20 @@ BOOST_AUTO_TEST_CASE(intarg)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 11);
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 0);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 11), 11);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 0), 0);
ResetArgs("-foo -bar");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 0);
- BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 0);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 11), 0);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 11), 0);
ResetArgs("-foo=11 -bar=12");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 11);
- BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 12);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 0), 11);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 11), 12);
ResetArgs("-foo=NaN -bar=NotANumber");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 1), 0);
- BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 0);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 1), 0);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 11), 0);
}
BOOST_AUTO_TEST_CASE(doubledash)
@@ -150,11 +159,11 @@ BOOST_AUTO_TEST_CASE(doubledash)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("--foo");
- BOOST_CHECK_EQUAL(gArgs.GetBoolArg("-foo", false), true);
+ BOOST_CHECK_EQUAL(m_args.GetBoolArg("-foo", false), true);
ResetArgs("--foo=verbose --bar=1");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "verbose");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 0), 1);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "verbose");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 0), 1);
}
BOOST_AUTO_TEST_CASE(boolargno)
@@ -163,24 +172,24 @@ BOOST_AUTO_TEST_CASE(boolargno)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("-nofoo");
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
ResetArgs("-nofoo=1");
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
ResetArgs("-nofoo=0");
- BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
- BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", false));
ResetArgs("-foo --nofoo"); // --nofoo should win
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
ResetArgs("-nofoo -foo"); // foo always wins:
- BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
- BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", false));
}
BOOST_AUTO_TEST_CASE(logargs)
@@ -200,7 +209,7 @@ BOOST_AUTO_TEST_CASE(logargs)
});
// Log the arguments
- gArgs.LogArgs();
+ m_args.LogArgs();
LogInstance().DeleteCallback(print_connection);
// Check that what should appear does, and what shouldn't doesn't.
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index cf2bd03698..fd35537c77 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -5,6 +5,7 @@
#include <key.h>
#include <key_io.h>
+#include <streams.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <util/strencodings.h>
@@ -220,4 +221,47 @@ BOOST_AUTO_TEST_CASE(key_key_negation)
BOOST_CHECK(key.GetPubKey().data()[0] == 0x03);
}
+static CPubKey UnserializePubkey(const std::vector<uint8_t>& data)
+{
+ CDataStream stream{SER_NETWORK, INIT_PROTO_VERSION};
+ stream << data;
+ CPubKey pubkey;
+ stream >> pubkey;
+ return pubkey;
+}
+
+static unsigned int GetLen(unsigned char chHeader)
+{
+ if (chHeader == 2 || chHeader == 3)
+ return CPubKey::COMPRESSED_SIZE;
+ if (chHeader == 4 || chHeader == 6 || chHeader == 7)
+ return CPubKey::SIZE;
+ return 0;
+}
+
+static void CmpSerializationPubkey(const CPubKey& pubkey)
+{
+ CDataStream stream{SER_NETWORK, INIT_PROTO_VERSION};
+ stream << pubkey;
+ CPubKey pubkey2;
+ stream >> pubkey2;
+ BOOST_CHECK(pubkey == pubkey2);
+}
+
+BOOST_AUTO_TEST_CASE(pubkey_unserialize)
+{
+ for (uint8_t i = 2; i <= 7; ++i) {
+ CPubKey key = UnserializePubkey({0x02});
+ BOOST_CHECK(!key.IsValid());
+ CmpSerializationPubkey(key);
+ key = UnserializePubkey(std::vector<uint8_t>(GetLen(i), i));
+ CmpSerializationPubkey(key);
+ if (i == 5) {
+ BOOST_CHECK(!key.IsValid());
+ } else {
+ BOOST_CHECK(key.IsValid());
+ }
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 9f3ca87206..62a0dc4241 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -253,7 +253,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
pblock->nNonce = blockinfo[i].nonce;
}
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
- BOOST_CHECK(ProcessNewBlock(chainparams, shared_pblock, true, nullptr));
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr));
pblock->hashPrevBlock = pblock->GetHash();
}
@@ -448,7 +448,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.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();
@@ -461,7 +461,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
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
+ 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
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index dd2890c134..e14d2dd72d 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
for (int i = 0; i < 4; i++)
key[i].MakeNewKey(true);
- txnouttype whichType;
+ TxoutType whichType;
CScript a_and_b;
a_and_b << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 84bf593497..ab42be21bd 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -6,6 +6,7 @@
#include <addrman.h>
#include <chainparams.h>
#include <clientversion.h>
+#include <cstdint>
#include <net.h>
#include <netbase.h>
#include <serialize.h>
@@ -83,10 +84,10 @@ BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(cnode_listen_port)
{
// test default
- unsigned short port = GetListenPort();
+ uint16_t port = GetListenPort();
BOOST_CHECK(port == Params().GetDefaultPort());
// test set port
- unsigned short altPort = 12345;
+ uint16_t altPort = 12345;
BOOST_CHECK(gArgs.SoftSetArg("-port", ToString(altPort)));
port = GetListenPort();
BOOST_CHECK(port == altPort);
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 2e1972cc3f..ea3e633cc2 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -6,6 +6,7 @@
#include <netbase.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
+#include <util/translation.h>
#include <string>
@@ -159,6 +160,9 @@ BOOST_AUTO_TEST_CASE(subnet_test)
BOOST_CHECK(ResolveSubNet("1.2.2.20/26").Match(ResolveIP("1.2.2.63")));
// All-Matching IPv6 Matches arbitrary IPv4 and IPv6
BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
+ // But not `::` or `0.0.0.0` because they are considered invalid addresses
+ BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("::")));
+ BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("0.0.0.0")));
BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1.2.3.4")));
// All-Matching IPv4 does not Match IPv6
BOOST_CHECK(!ResolveSubNet("0.0.0.0/0").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
@@ -325,15 +329,15 @@ BOOST_AUTO_TEST_CASE(netbase_parsenetwork)
BOOST_AUTO_TEST_CASE(netpermissions_test)
{
- std::string error;
+ bilingual_str error;
NetWhitebindPermissions whitebindPermissions;
NetWhitelistPermissions whitelistPermissions;
// Detect invalid white bind
BOOST_CHECK(!NetWhitebindPermissions::TryParse("", whitebindPermissions, error));
- BOOST_CHECK(error.find("Cannot resolve -whitebind address") != std::string::npos);
+ BOOST_CHECK(error.original.find("Cannot resolve -whitebind address") != std::string::npos);
BOOST_CHECK(!NetWhitebindPermissions::TryParse("127.0.0.1", whitebindPermissions, error));
- BOOST_CHECK(error.find("Need to specify a port with -whitebind") != std::string::npos);
+ BOOST_CHECK(error.original.find("Need to specify a port with -whitebind") != std::string::npos);
BOOST_CHECK(!NetWhitebindPermissions::TryParse("", whitebindPermissions, error));
// If no permission flags, assume backward compatibility
@@ -377,11 +381,11 @@ BOOST_AUTO_TEST_CASE(netpermissions_test)
// Detect invalid flag
BOOST_CHECK(!NetWhitebindPermissions::TryParse("bloom,forcerelay,oopsie@1.2.3.4:32", whitebindPermissions, error));
- BOOST_CHECK(error.find("Invalid P2P permission") != std::string::npos);
+ BOOST_CHECK(error.original.find("Invalid P2P permission") != std::string::npos);
- // Check whitelist error
+ // Check netmask error
BOOST_CHECK(!NetWhitelistPermissions::TryParse("bloom,forcerelay,noban@1.2.3.4:32", whitelistPermissions, error));
- BOOST_CHECK(error.find("Invalid netmask specified in -whitelist") != std::string::npos);
+ BOOST_CHECK(error.original.find("Invalid netmask specified in -whitelist") != std::string::npos);
// Happy path for whitelist parsing
BOOST_CHECK(NetWhitelistPermissions::TryParse("noban@1.2.3.4", whitelistPermissions, error));
@@ -393,12 +397,13 @@ BOOST_AUTO_TEST_CASE(netpermissions_test)
BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay,mempool@1.2.3.4/32", whitelistPermissions, error));
const auto strings = NetPermissions::ToStrings(PF_ALL);
- BOOST_CHECK_EQUAL(strings.size(), 5U);
+ BOOST_CHECK_EQUAL(strings.size(), 6U);
BOOST_CHECK(std::find(strings.begin(), strings.end(), "bloomfilter") != strings.end());
BOOST_CHECK(std::find(strings.begin(), strings.end(), "forcerelay") != strings.end());
BOOST_CHECK(std::find(strings.begin(), strings.end(), "relay") != strings.end());
BOOST_CHECK(std::find(strings.begin(), strings.end(), "noban") != strings.end());
BOOST_CHECK(std::find(strings.begin(), strings.end(), "mempool") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "download") != strings.end());
}
BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters)
diff --git a/src/test/policy_fee_tests.cpp b/src/test/policy_fee_tests.cpp
new file mode 100644
index 0000000000..6d8872b11e
--- /dev/null
+++ b/src/test/policy_fee_tests.cpp
@@ -0,0 +1,34 @@
+// Copyright (c) 2020 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/fees.h>
+
+#include <test/util/setup_common.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(policy_fee_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(FeeRounder)
+{
+ FeeFilterRounder fee_rounder{CFeeRate{1000}};
+
+ // check that 1000 rounds to 974 or 1071
+ std::set<CAmount> results;
+ while (results.size() < 2) {
+ results.emplace(fee_rounder.round(1000));
+ }
+ BOOST_CHECK_EQUAL(*results.begin(), 974);
+ BOOST_CHECK_EQUAL(*++results.begin(), 1071);
+
+ // check that negative amounts rounds to 0
+ BOOST_CHECK_EQUAL(fee_rounder.round(-0), 0);
+ BOOST_CHECK_EQUAL(fee_rounder.round(-1), 0);
+
+ // check that MAX_MONEY rounds to 9170997
+ BOOST_CHECK_EQUAL(fee_rounder.round(MAX_MONEY), 9170997);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp
index 04bf7c20c1..8c2712f764 100644
--- a/src/test/raii_event_tests.cpp
+++ b/src/test/raii_event_tests.cpp
@@ -4,9 +4,6 @@
#include <event2/event.h>
-#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
-// It would probably be ideal to define dummy test(s) that report skipped, but boost::test doesn't seem to make that practical (at least not in versions available with common distros)
-
#include <map>
#include <stdlib.h>
@@ -16,6 +13,10 @@
#include <boost/test/unit_test.hpp>
+BOOST_FIXTURE_TEST_SUITE(raii_event_tests, BasicTestingSetup)
+
+#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
+
static std::map<void*, short> tags;
static std::map<void*, uint16_t> orders;
static uint16_t tagSequence = 0;
@@ -34,8 +35,6 @@ static void tag_free(void* mem) {
free(mem);
}
-BOOST_FIXTURE_TEST_SUITE(raii_event_tests, BasicTestingSetup)
-
BOOST_AUTO_TEST_CASE(raii_event_creation)
{
event_set_mem_functions(tag_malloc, realloc, tag_free);
@@ -87,6 +86,14 @@ BOOST_AUTO_TEST_CASE(raii_event_order)
event_set_mem_functions(malloc, realloc, free);
}
-BOOST_AUTO_TEST_SUITE_END()
+#else
+
+BOOST_AUTO_TEST_CASE(raii_event_tests_SKIPPED)
+{
+ // It would probably be ideal to report skipped, but boost::test doesn't seem to make that practical (at least not in versions available with common distros)
+ BOOST_TEST_MESSAGE("Skipping raii_event_tess: libevent doesn't support event_set_mem_functions");
+}
#endif // EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/ref_tests.cpp b/src/test/ref_tests.cpp
new file mode 100644
index 0000000000..0ec0799fbc
--- /dev/null
+++ b/src/test/ref_tests.cpp
@@ -0,0 +1,33 @@
+// Copyright (c) 2020 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/ref.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(ref_tests)
+
+BOOST_AUTO_TEST_CASE(ref_test)
+{
+ util::Ref ref;
+ BOOST_CHECK(!ref.Has<int>());
+ BOOST_CHECK_THROW(ref.Get<int>(), NonFatalCheckError);
+ int value = 5;
+ ref.Set(value);
+ BOOST_CHECK(ref.Has<int>());
+ BOOST_CHECK_EQUAL(ref.Get<int>(), 5);
+ ++ref.Get<int>();
+ BOOST_CHECK_EQUAL(ref.Get<int>(), 6);
+ BOOST_CHECK_EQUAL(value, 6);
+ ++value;
+ BOOST_CHECK_EQUAL(value, 7);
+ BOOST_CHECK_EQUAL(ref.Get<int>(), 7);
+ BOOST_CHECK(!ref.Has<bool>());
+ BOOST_CHECK_THROW(ref.Get<bool>(), NonFatalCheckError);
+ ref.Clear();
+ BOOST_CHECK(!ref.Has<int>());
+ BOOST_CHECK_THROW(ref.Get<int>(), NonFatalCheckError);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index d9c66f1c19..b54cbb3f00 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -10,6 +10,7 @@
#include <interfaces/chain.h>
#include <node/context.h>
#include <test/util/setup_common.h>
+#include <util/ref.h>
#include <util/time.h>
#include <boost/algorithm/string.hpp>
@@ -19,13 +20,20 @@
#include <rpc/blockchain.h>
-UniValue CallRPC(std::string args)
+class RPCTestingSetup : public TestingSetup
+{
+public:
+ UniValue CallRPC(std::string args);
+};
+
+UniValue RPCTestingSetup::CallRPC(std::string args)
{
std::vector<std::string> vArgs;
boost::split(vArgs, args, boost::is_any_of(" \t"));
std::string strMethod = vArgs[0];
vArgs.erase(vArgs.begin());
- JSONRPCRequest request;
+ util::Ref context{m_node};
+ JSONRPCRequest request(context);
request.strMethod = strMethod;
request.params = RPCConvertValues(strMethod, vArgs);
request.fHelp = false;
@@ -40,7 +48,7 @@ UniValue CallRPC(std::string args)
}
-BOOST_FIXTURE_TEST_SUITE(rpc_tests, TestingSetup)
+BOOST_FIXTURE_TEST_SUITE(rpc_tests, RPCTestingSetup)
BOOST_AUTO_TEST_CASE(rpc_rawparams)
{
diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index 1395a7f38c..2e5a7549b7 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -7,7 +7,7 @@
#include <util/time.h>
#include <boost/test/unit_test.hpp>
-#include <boost/thread.hpp>
+#include <boost/thread/thread.hpp>
#include <mutex>
@@ -89,7 +89,7 @@ BOOST_AUTO_TEST_CASE(manythreads)
}
// Drain the task queue then exit threads
- microTasks.stop(true);
+ microTasks.StopWhenDrained();
microThreads.join_all(); // ... wait until all the threads are done
int counterSum = 0;
@@ -155,7 +155,7 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
}
// finish up
- scheduler.stop(true);
+ scheduler.StopWhenDrained();
threads.join_all();
BOOST_CHECK_EQUAL(counter1, 100);
@@ -186,7 +186,7 @@ BOOST_AUTO_TEST_CASE(mockforward)
scheduler.MockForward(std::chrono::minutes{5});
// ensure scheduler has chance to process all tasks queued for before 1 ms from now.
- scheduler.scheduleFromNow([&scheduler] { scheduler.stop(false); }, std::chrono::milliseconds{1});
+ scheduler.scheduleFromNow([&scheduler] { scheduler.stop(); }, std::chrono::milliseconds{1});
scheduler_thread.join();
// check that the queue only has one job remaining
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index b185d3b4ac..77d748241b 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -31,35 +31,35 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
CScript s;
std::vector<std::vector<unsigned char> > solutions;
- // TX_PUBKEY
+ // TxoutType::PUBKEY
s.clear();
s << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_PUBKEY);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::PUBKEY);
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0]));
- // TX_PUBKEYHASH
+ // TxoutType::PUBKEYHASH
s.clear();
s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_PUBKEYHASH);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::PUBKEYHASH);
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID()));
- // TX_SCRIPTHASH
+ // TxoutType::SCRIPTHASH
CScript redeemScript(s); // initialize with leftover P2PKH script
s.clear();
s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_SCRIPTHASH);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::SCRIPTHASH);
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(CScriptID(redeemScript)));
- // TX_MULTISIG
+ // TxoutType::MULTISIG
s.clear();
s << OP_1 <<
ToByteVector(pubkeys[0]) <<
ToByteVector(pubkeys[1]) <<
OP_2 << OP_CHECKMULTISIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_MULTISIG);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::MULTISIG);
BOOST_CHECK_EQUAL(solutions.size(), 4U);
BOOST_CHECK(solutions[0] == std::vector<unsigned char>({1}));
BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0]));
@@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
ToByteVector(pubkeys[1]) <<
ToByteVector(pubkeys[2]) <<
OP_3 << OP_CHECKMULTISIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_MULTISIG);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::MULTISIG);
BOOST_CHECK_EQUAL(solutions.size(), 5U);
BOOST_CHECK(solutions[0] == std::vector<unsigned char>({2}));
BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0]));
@@ -80,37 +80,37 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
BOOST_CHECK(solutions[3] == ToByteVector(pubkeys[2]));
BOOST_CHECK(solutions[4] == std::vector<unsigned char>({3}));
- // TX_NULL_DATA
+ // TxoutType::NULL_DATA
s.clear();
s << OP_RETURN <<
std::vector<unsigned char>({0}) <<
std::vector<unsigned char>({75}) <<
std::vector<unsigned char>({255});
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NULL_DATA);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NULL_DATA);
BOOST_CHECK_EQUAL(solutions.size(), 0U);
- // TX_WITNESS_V0_KEYHASH
+ // TxoutType::WITNESS_V0_KEYHASH
s.clear();
s << OP_0 << ToByteVector(pubkeys[0].GetID());
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_WITNESS_V0_KEYHASH);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_V0_KEYHASH);
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID()));
- // TX_WITNESS_V0_SCRIPTHASH
+ // TxoutType::WITNESS_V0_SCRIPTHASH
uint256 scriptHash;
CSHA256().Write(&redeemScript[0], redeemScript.size())
.Finalize(scriptHash.begin());
s.clear();
s << OP_0 << ToByteVector(scriptHash);
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_WITNESS_V0_SCRIPTHASH);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_V0_SCRIPTHASH);
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(scriptHash));
- // TX_NONSTANDARD
+ // TxoutType::NONSTANDARD
s.clear();
s << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
}
BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
@@ -123,50 +123,50 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
CScript s;
std::vector<std::vector<unsigned char> > solutions;
- // TX_PUBKEY with incorrectly sized pubkey
+ // TxoutType::PUBKEY with incorrectly sized pubkey
s.clear();
s << std::vector<unsigned char>(30, 0x01) << OP_CHECKSIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_PUBKEYHASH with incorrectly sized key hash
+ // TxoutType::PUBKEYHASH with incorrectly sized key hash
s.clear();
s << OP_DUP << OP_HASH160 << ToByteVector(pubkey) << OP_EQUALVERIFY << OP_CHECKSIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_SCRIPTHASH with incorrectly sized script hash
+ // TxoutType::SCRIPTHASH with incorrectly sized script hash
s.clear();
s << OP_HASH160 << std::vector<unsigned char>(21, 0x01) << OP_EQUAL;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_MULTISIG 0/2
+ // TxoutType::MULTISIG 0/2
s.clear();
s << OP_0 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_MULTISIG 2/1
+ // TxoutType::MULTISIG 2/1
s.clear();
s << OP_2 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_MULTISIG n = 2 with 1 pubkey
+ // TxoutType::MULTISIG n = 2 with 1 pubkey
s.clear();
s << OP_1 << ToByteVector(pubkey) << OP_2 << OP_CHECKMULTISIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_MULTISIG n = 1 with 0 pubkeys
+ // TxoutType::MULTISIG n = 1 with 0 pubkeys
s.clear();
s << OP_1 << OP_1 << OP_CHECKMULTISIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_NULL_DATA with other opcodes
+ // TxoutType::NULL_DATA with other opcodes
s.clear();
s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_WITNESS with incorrect program size
+ // TxoutType::WITNESS_UNKNOWN with incorrect program size
s.clear();
s << OP_0 << std::vector<unsigned char>(19, 0x01);
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
}
BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
@@ -179,21 +179,21 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
CScript s;
CTxDestination address;
- // TX_PUBKEY
+ // TxoutType::PUBKEY
s.clear();
s << ToByteVector(pubkey) << OP_CHECKSIG;
BOOST_CHECK(ExtractDestination(s, address));
BOOST_CHECK(boost::get<PKHash>(&address) &&
*boost::get<PKHash>(&address) == PKHash(pubkey));
- // TX_PUBKEYHASH
+ // TxoutType::PUBKEYHASH
s.clear();
s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
BOOST_CHECK(ExtractDestination(s, address));
BOOST_CHECK(boost::get<PKHash>(&address) &&
*boost::get<PKHash>(&address) == PKHash(pubkey));
- // TX_SCRIPTHASH
+ // TxoutType::SCRIPTHASH
CScript redeemScript(s); // initialize with leftover P2PKH script
s.clear();
s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
@@ -201,17 +201,17 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
BOOST_CHECK(boost::get<ScriptHash>(&address) &&
*boost::get<ScriptHash>(&address) == ScriptHash(redeemScript));
- // TX_MULTISIG
+ // TxoutType::MULTISIG
s.clear();
s << OP_1 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG;
BOOST_CHECK(!ExtractDestination(s, address));
- // TX_NULL_DATA
+ // TxoutType::NULL_DATA
s.clear();
s << OP_RETURN << std::vector<unsigned char>({75});
BOOST_CHECK(!ExtractDestination(s, address));
- // TX_WITNESS_V0_KEYHASH
+ // TxoutType::WITNESS_V0_KEYHASH
s.clear();
s << OP_0 << ToByteVector(pubkey.GetID());
BOOST_CHECK(ExtractDestination(s, address));
@@ -219,7 +219,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(keyhash.begin());
BOOST_CHECK(boost::get<WitnessV0KeyHash>(&address) && *boost::get<WitnessV0KeyHash>(&address) == keyhash);
- // TX_WITNESS_V0_SCRIPTHASH
+ // TxoutType::WITNESS_V0_SCRIPTHASH
s.clear();
WitnessV0ScriptHash scripthash;
CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin());
@@ -227,7 +227,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
BOOST_CHECK(ExtractDestination(s, address));
BOOST_CHECK(boost::get<WitnessV0ScriptHash>(&address) && *boost::get<WitnessV0ScriptHash>(&address) == scripthash);
- // TX_WITNESS with unknown version
+ // TxoutType::WITNESS_UNKNOWN with unknown version
s.clear();
s << OP_1 << ToByteVector(pubkey);
BOOST_CHECK(ExtractDestination(s, address));
@@ -248,49 +248,49 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
}
CScript s;
- txnouttype whichType;
+ TxoutType whichType;
std::vector<CTxDestination> addresses;
int nRequired;
- // TX_PUBKEY
+ // TxoutType::PUBKEY
s.clear();
s << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TX_PUBKEY);
+ BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEY);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
*boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
- // TX_PUBKEYHASH
+ // TxoutType::PUBKEYHASH
s.clear();
s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH);
+ BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEYHASH);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
*boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
- // TX_SCRIPTHASH
+ // TxoutType::SCRIPTHASH
CScript redeemScript(s); // initialize with leftover P2PKH script
s.clear();
s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH);
+ BOOST_CHECK_EQUAL(whichType, TxoutType::SCRIPTHASH);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
BOOST_CHECK(boost::get<ScriptHash>(&addresses[0]) &&
*boost::get<ScriptHash>(&addresses[0]) == ScriptHash(redeemScript));
- // TX_MULTISIG
+ // TxoutType::MULTISIG
s.clear();
s << OP_2 <<
ToByteVector(pubkeys[0]) <<
ToByteVector(pubkeys[1]) <<
OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TX_MULTISIG);
+ BOOST_CHECK_EQUAL(whichType, TxoutType::MULTISIG);
BOOST_CHECK_EQUAL(addresses.size(), 2U);
BOOST_CHECK_EQUAL(nRequired, 2);
BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
@@ -298,7 +298,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK(boost::get<PKHash>(&addresses[1]) &&
*boost::get<PKHash>(&addresses[1]) == PKHash(pubkeys[1]));
- // TX_NULL_DATA
+ // TxoutType::NULL_DATA
s.clear();
s << OP_RETURN << std::vector<unsigned char>({75});
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 56454f61f3..cb3ae290d1 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -102,7 +102,7 @@ static ScriptErrorDesc script_errors[]={
{SCRIPT_ERR_SIG_FINDANDDELETE, "SIG_FINDANDDELETE"},
};
-static const char *FormatScriptError(ScriptError_t err)
+static std::string FormatScriptError(ScriptError_t err)
{
for (unsigned int i=0; i<ARRAYLEN(script_errors); ++i)
if (script_errors[i].err == err)
@@ -134,7 +134,7 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
CMutableTransaction tx = BuildSpendingTransaction(scriptSig, scriptWitness, txCredit);
CMutableTransaction tx2 = tx;
BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue), &err) == expect, message);
- BOOST_CHECK_MESSAGE(err == scriptError, std::string(FormatScriptError(err)) + " where " + std::string(FormatScriptError((ScriptError_t)scriptError)) + " expected: " + message);
+ BOOST_CHECK_MESSAGE(err == scriptError, FormatScriptError(err) + " where " + FormatScriptError((ScriptError_t)scriptError) + " expected: " + message);
// Verify that removing flags from a passing test or adding flags to a failing test does not change the result.
for (int i = 0; i < 16; ++i) {
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index 9a6c721ab8..c2328f931c 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -29,15 +29,13 @@ public:
memcpy(charstrval, charstrvalin, sizeof(charstrval));
}
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(intval);
- READWRITE(boolval);
- READWRITE(stringval);
- READWRITE(charstrval);
- READWRITE(txval);
+ SERIALIZE_METHODS(CSerializeMethodsTestSingle, obj)
+ {
+ READWRITE(obj.intval);
+ READWRITE(obj.boolval);
+ READWRITE(obj.stringval);
+ READWRITE(obj.charstrval);
+ READWRITE(obj.txval);
}
bool operator==(const CSerializeMethodsTestSingle& rhs)
@@ -54,11 +52,10 @@ class CSerializeMethodsTestMany : public CSerializeMethodsTestSingle
{
public:
using CSerializeMethodsTestSingle::CSerializeMethodsTestSingle;
- ADD_SERIALIZE_METHODS;
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(intval, boolval, stringval, charstrval, txval);
+ SERIALIZE_METHODS(CSerializeMethodsTestMany, obj)
+ {
+ READWRITE(obj.intval, obj.boolval, obj.stringval, obj.charstrval, obj.txval);
}
};
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index 5ca136ea6e..c0bb92258b 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(sighash_test)
ss << txTo;
std::cout << "\t[\"" ;
- std::cout << HexStr(ss.begin(), ss.end()) << "\", \"";
+ std::cout << HexStr(ss) << "\", \"";
std::cout << HexStr(scriptCode) << "\", ";
std::cout << nIn << ", ";
std::cout << nHashType << ", \"";
diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp
index 5c6c2ee38e..3ea8714f3a 100644
--- a/src/test/sync_tests.cpp
+++ b/src/test/sync_tests.cpp
@@ -18,7 +18,7 @@ void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2)
try {
LOCK2(mutex2, mutex1);
} catch (const std::logic_error& e) {
- BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected");
+ BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected: mutex1 -> mutex2 -> mutex1");
error_thrown = true;
}
#ifdef DEBUG_LOCKORDER
diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp
index 8c880babd1..1dcee23bbb 100644
--- a/src/test/timedata_tests.cpp
+++ b/src/test/timedata_tests.cpp
@@ -9,6 +9,7 @@
#include <test/util/setup_common.h>
#include <timedata.h>
#include <util/string.h>
+#include <util/translation.h>
#include <warnings.h>
#include <string>
@@ -66,7 +67,7 @@ BOOST_AUTO_TEST_CASE(addtimedata)
MultiAddTimeData(1, DEFAULT_MAX_TIME_ADJUSTMENT + 1); //filter size 5
}
- BOOST_CHECK(GetWarnings(true).find("clock is wrong") != std::string::npos);
+ BOOST_CHECK(GetWarnings(true).original.find("clock is wrong") != std::string::npos);
// nTimeOffset is not changed if the median of offsets exceeds DEFAULT_MAX_TIME_ADJUSTMENT
BOOST_CHECK_EQUAL(GetTimeOffset(), 0);
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index ddbc68f8e2..4bf6e734ce 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -716,12 +716,12 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "scriptpubkey");
- // MAX_OP_RETURN_RELAY-byte TX_NULL_DATA (standard)
+ // MAX_OP_RETURN_RELAY-byte TxoutType::NULL_DATA (standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size());
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
- // MAX_OP_RETURN_RELAY+1-byte TX_NULL_DATA (non-standard)
+ // MAX_OP_RETURN_RELAY+1-byte TxoutType::NULL_DATA (non-standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800");
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size());
reason.clear();
@@ -745,12 +745,12 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "scriptpubkey");
- // TX_NULL_DATA w/o PUSHDATA
+ // TxoutType::NULL_DATA w/o PUSHDATA
t.vout.resize(1);
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
- // Only one TX_NULL_DATA permitted in all cases
+ // Only one TxoutType::NULL_DATA permitted in all cases
t.vout.resize(2);
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
t.vout[1].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 1df6844062..74536ae74c 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -11,6 +11,7 @@
#include <node/context.h>
#include <pow.h>
#include <script/standard.h>
+#include <util/check.h>
#include <validation.h>
CTxIn generatetoaddress(const NodeContext& node, const std::string& address)
@@ -31,7 +32,7 @@ CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
assert(block->nNonce);
}
- bool processed{ProcessNewBlock(Params(), block, true, nullptr)};
+ bool processed{Assert(node.chainman)->ProcessNewBlock(Params(), block, true, nullptr)};
assert(processed);
return CTxIn{block->vtx[0]->GetHash(), 0};
@@ -39,9 +40,8 @@ CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
{
- assert(node.mempool);
auto block = std::make_shared<CBlock>(
- BlockAssembler{*node.mempool, Params()}
+ BlockAssembler{*Assert(node.mempool), Params()}
.CreateNewBlock(coinbase_scriptPubKey)
->block);
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index bf0afc4171..24c0d6382b 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -19,6 +19,7 @@
#include <rpc/blockchain.h>
#include <rpc/register.h>
#include <rpc/server.h>
+#include <scheduler.h>
#include <script/sigcache.h>
#include <streams.h>
#include <txdb.h>
@@ -74,11 +75,13 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
"dummy",
"-printtoconsole=0",
"-logtimemicros",
+ "-logthreadnames",
"-debug",
"-debugexclude=libevent",
"-debugexclude=leveldb",
},
extra_args);
+ util::ThreadRename("test");
fs::create_directories(m_path_root);
gArgs.ForceSetArg("-datadir", m_path_root.string());
ClearDatadirCache();
@@ -123,19 +126,19 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
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.
- g_rpc_node = &m_node;
RegisterAllCoreRPCCommands(tableRPC);
m_node.scheduler = MakeUnique<CScheduler>();
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
- threadGroup.create_thread([&]{ m_node.scheduler->serviceQueue(); });
- GetMainSignals().RegisterBackgroundSignalScheduler(*g_rpc_node->scheduler);
+ threadGroup.create_thread([&] { TraceThread("scheduler", [&] { m_node.scheduler->serviceQueue(); }); });
+ GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler);
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
- g_chainman.InitializeChainstate();
+ m_node.chainman = &::g_chainman;
+ m_node.chainman->InitializeChainstate();
::ChainstateActive().InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
assert(!::ChainstateActive().CanFlushToDisk());
@@ -161,7 +164,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
m_node.mempool->setSanityCheck(1.0);
m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
- m_node.peer_logic = MakeUnique<PeerLogicValidation>(m_node.connman.get(), m_node.banman.get(), *m_node.scheduler, *m_node.mempool);
+ m_node.peer_logic = MakeUnique<PeerLogicValidation>(m_node.connman.get(), m_node.banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
{
CConnman::Options options;
options.m_msgproc = m_node.peer_logic.get();
@@ -176,14 +179,14 @@ TestingSetup::~TestingSetup()
threadGroup.join_all();
GetMainSignals().FlushBackgroundCallbacks();
GetMainSignals().UnregisterBackgroundSignalScheduler();
- g_rpc_node = nullptr;
m_node.connman.reset();
m_node.banman.reset();
m_node.args = nullptr;
m_node.mempool = nullptr;
m_node.scheduler.reset();
UnloadBlockIndex();
- g_chainman.Reset();
+ m_node.chainman->Reset();
+ m_node.chainman = nullptr;
pblocktree.reset();
}
@@ -228,7 +231,7 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
- ProcessNewBlock(chainparams, shared_pblock, true, nullptr);
+ Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr);
CBlock result = block;
return result;
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 2477f9ad06..78b279e42a 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -11,13 +11,13 @@
#include <node/context.h>
#include <pubkey.h>
#include <random.h>
-#include <scheduler.h>
#include <txmempool.h>
+#include <util/check.h>
#include <util/string.h>
#include <type_traits>
-#include <boost/thread.hpp>
+#include <boost/thread/thread.hpp>
/** This is connected to the logger. Can be used to redirect logs to any other log */
extern const std::function<void(const std::string&)> G_TEST_LOG_FUN;
diff --git a/src/test/util/transaction_utils.h b/src/test/util/transaction_utils.h
index 1beddd334b..6f2faeec6c 100644
--- a/src/test/util/transaction_utils.h
+++ b/src/test/util/transaction_utils.h
@@ -22,8 +22,8 @@ CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey, int n
CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CScriptWitness& scriptWitness, const CTransaction& txCredit);
// Helper: create two dummy transactions, each with two outputs.
-// The first has nValues[0] and nValues[1] outputs paid to a TX_PUBKEY,
-// the second nValues[2] and nValues[3] outputs paid to a TX_PUBKEYHASH.
+// The first has nValues[0] and nValues[1] outputs paid to a TxoutType::PUBKEY,
+// the second nValues[2] and nValues[3] outputs paid to a TxoutType::PUBKEYHASH.
std::vector<CMutableTransaction> SetupDummyInputs(FillableSigningProvider& keystoreRet, CCoinsViewCache& coinsRet, const std::array<CAmount,4>& nValues);
#endif // BITCOIN_TEST_UTIL_TRANSACTION_UTILS_H
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index cf26ca3adb..e247c09a97 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -41,6 +41,16 @@ namespace BCLog {
BOOST_FIXTURE_TEST_SUITE(util_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_CASE(util_check)
+{
+ // Check that Assert can forward
+ const std::unique_ptr<int> p_two = Assert(MakeUnique<int>(2));
+ // Check that Assert works on lvalues and rvalues
+ const int two = *Assert(p_two);
+ Assert(two == 2);
+ Assert(true);
+}
+
BOOST_AUTO_TEST_CASE(util_criticalsection)
{
RecursiveMutex cs;
@@ -1829,7 +1839,7 @@ BOOST_AUTO_TEST_CASE(test_spanparsing)
// Const(...): parse a constant, update span to skip it if successful
input = "MilkToastHoney";
- sp = MakeSpan(input);
+ sp = input;
success = Const("", sp); // empty
BOOST_CHECK(success);
BOOST_CHECK_EQUAL(SpanToStr(sp), "MilkToastHoney");
@@ -1854,7 +1864,7 @@ BOOST_AUTO_TEST_CASE(test_spanparsing)
// Func(...): parse a function call, update span to argument if successful
input = "Foo(Bar(xy,z()))";
- sp = MakeSpan(input);
+ sp = input;
success = Func("FooBar", sp);
BOOST_CHECK(!success);
@@ -1877,31 +1887,31 @@ BOOST_AUTO_TEST_CASE(test_spanparsing)
Span<const char> result;
input = "(n*(n-1))/2";
- sp = MakeSpan(input);
+ sp = input;
result = Expr(sp);
BOOST_CHECK_EQUAL(SpanToStr(result), "(n*(n-1))/2");
BOOST_CHECK_EQUAL(SpanToStr(sp), "");
input = "foo,bar";
- sp = MakeSpan(input);
+ sp = input;
result = Expr(sp);
BOOST_CHECK_EQUAL(SpanToStr(result), "foo");
BOOST_CHECK_EQUAL(SpanToStr(sp), ",bar");
input = "(aaaaa,bbbbb()),c";
- sp = MakeSpan(input);
+ sp = input;
result = Expr(sp);
BOOST_CHECK_EQUAL(SpanToStr(result), "(aaaaa,bbbbb())");
BOOST_CHECK_EQUAL(SpanToStr(sp), ",c");
input = "xyz)foo";
- sp = MakeSpan(input);
+ sp = input;
result = Expr(sp);
BOOST_CHECK_EQUAL(SpanToStr(result), "xyz");
BOOST_CHECK_EQUAL(SpanToStr(sp), ")foo");
input = "((a),(b),(c)),xxx";
- sp = MakeSpan(input);
+ sp = input;
result = Expr(sp);
BOOST_CHECK_EQUAL(SpanToStr(result), "((a),(b),(c))");
BOOST_CHECK_EQUAL(SpanToStr(sp), ",xxx");
@@ -1910,7 +1920,7 @@ BOOST_AUTO_TEST_CASE(test_spanparsing)
std::vector<Span<const char>> results;
input = "xxx";
- results = Split(MakeSpan(input), 'x');
+ results = Split(input, 'x');
BOOST_CHECK_EQUAL(results.size(), 4U);
BOOST_CHECK_EQUAL(SpanToStr(results[0]), "");
BOOST_CHECK_EQUAL(SpanToStr(results[1]), "");
@@ -1918,19 +1928,19 @@ BOOST_AUTO_TEST_CASE(test_spanparsing)
BOOST_CHECK_EQUAL(SpanToStr(results[3]), "");
input = "one#two#three";
- results = Split(MakeSpan(input), '-');
+ results = Split(input, '-');
BOOST_CHECK_EQUAL(results.size(), 1U);
BOOST_CHECK_EQUAL(SpanToStr(results[0]), "one#two#three");
input = "one#two#three";
- results = Split(MakeSpan(input), '#');
+ results = Split(input, '#');
BOOST_CHECK_EQUAL(results.size(), 3U);
BOOST_CHECK_EQUAL(SpanToStr(results[0]), "one");
BOOST_CHECK_EQUAL(SpanToStr(results[1]), "two");
BOOST_CHECK_EQUAL(SpanToStr(results[2]), "three");
input = "*foo*bar*";
- results = Split(MakeSpan(input), '*');
+ results = Split(input, '*');
BOOST_CHECK_EQUAL(results.size(), 4U);
BOOST_CHECK_EQUAL(SpanToStr(results[0]), "");
BOOST_CHECK_EQUAL(SpanToStr(results[1]), "foo");
diff --git a/src/test/util_threadnames_tests.cpp b/src/test/util_threadnames_tests.cpp
index 4dcc080b2d..f3f9fb2bff 100644
--- a/src/test/util_threadnames_tests.cpp
+++ b/src/test/util_threadnames_tests.cpp
@@ -53,8 +53,6 @@ std::set<std::string> RenameEnMasse(int num_threads)
*/
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;
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 899f054b83..8e85b7df3e 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -163,10 +163,10 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
std::transform(blocks.begin(), blocks.end(), std::back_inserter(headers), [](std::shared_ptr<const CBlock> b) { return b->GetBlockHeader(); });
// Process all the headers so we understand the toplogy of the chain
- BOOST_CHECK(ProcessNewBlockHeaders(headers, state, Params()));
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlockHeaders(headers, state, Params()));
// Connect the genesis block and drain any outstanding events
- BOOST_CHECK(ProcessNewBlock(Params(), std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored));
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(Params(), std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored));
SyncWithValidationInterfaceQueue();
// subscribe to events (this subscriber will validate event ordering)
@@ -183,18 +183,18 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
// will subscribe to events generated during block validation and assert on ordering invariance
std::vector<std::thread> threads;
for (int i = 0; i < 10; i++) {
- threads.emplace_back([&blocks]() {
+ threads.emplace_back([&]() {
bool ignored;
FastRandomContext insecure;
for (int i = 0; i < 1000; i++) {
auto block = blocks[insecure.randrange(blocks.size() - 1)];
- ProcessNewBlock(Params(), block, true, &ignored);
+ Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, &ignored);
}
// to make sure that eventually we process the full chain - do it here
for (auto block : blocks) {
if (block->vtx.size() == 1) {
- bool processed = ProcessNewBlock(Params(), block, true, &ignored);
+ bool processed = Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, &ignored);
assert(processed);
}
}
@@ -232,8 +232,8 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
{
bool ignored;
- auto ProcessBlock = [&ignored](std::shared_ptr<const CBlock> block) -> bool {
- return ProcessNewBlock(Params(), block, /* fForceProcessing */ true, /* fNewBlock */ &ignored);
+ auto ProcessBlock = [&](std::shared_ptr<const CBlock> block) -> bool {
+ return Assert(m_node.chainman)->ProcessNewBlock(Params(), block, /* fForceProcessing */ true, /* fNewBlock */ &ignored);
};
// Process all mined blocks
diff --git a/src/threadsafety.h b/src/threadsafety.h
index bb988dfdfd..5f2c40bac6 100644
--- a/src/threadsafety.h
+++ b/src/threadsafety.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_THREADSAFETY_H
#define BITCOIN_THREADSAFETY_H
+#include <mutex>
+
#ifdef __clang__
// TL;DR Add GUARDED_BY(mutex) to member variables. The others are
// rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo);
@@ -54,4 +56,26 @@
#define ASSERT_EXCLUSIVE_LOCK(...)
#endif // __GNUC__
+// StdMutex provides an annotated version of std::mutex for us,
+// and should only be used when sync.h Mutex/LOCK/etc are not usable.
+class LOCKABLE StdMutex : public std::mutex
+{
+public:
+#ifdef __clang__
+ //! For negative capabilities in the Clang Thread Safety Analysis.
+ //! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in conjunction
+ //! with the ! operator, to indicate that a mutex should not be held.
+ const StdMutex& operator!() const { return *this; }
+#endif // __clang__
+};
+
+// StdLockGuard provides an annotated version of std::lock_guard for us,
+// and should only be used when sync.h Mutex/LOCK/etc are not usable.
+class SCOPED_LOCKABLE StdLockGuard : public std::lock_guard<StdMutex>
+{
+public:
+ explicit StdLockGuard(StdMutex& cs) EXCLUSIVE_LOCK_FUNCTION(cs) : std::lock_guard<StdMutex>(cs) {}
+ ~StdLockGuard() UNLOCK_FUNCTION() {}
+};
+
#endif // BITCOIN_THREADSAFETY_H
diff --git a/src/timedata.cpp b/src/timedata.cpp
index dd56acf44f..6b3a79017b 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -9,15 +9,14 @@
#include <timedata.h>
#include <netaddress.h>
+#include <node/ui_interface.h>
#include <sync.h>
-#include <ui_interface.h>
#include <util/system.h>
#include <util/translation.h>
#include <warnings.h>
-
-static RecursiveMutex cs_nTimeOffset;
-static int64_t nTimeOffset GUARDED_BY(cs_nTimeOffset) = 0;
+static Mutex g_timeoffset_mutex;
+static int64_t nTimeOffset GUARDED_BY(g_timeoffset_mutex) = 0;
/**
* "Never go to sea with two chronometers; take one or three."
@@ -28,7 +27,7 @@ static int64_t nTimeOffset GUARDED_BY(cs_nTimeOffset) = 0;
*/
int64_t GetTimeOffset()
{
- LOCK(cs_nTimeOffset);
+ LOCK(g_timeoffset_mutex);
return nTimeOffset;
}
@@ -46,7 +45,7 @@ static int64_t abs64(int64_t n)
void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
{
- LOCK(cs_nTimeOffset);
+ LOCK(g_timeoffset_mutex);
// Ignore duplicates
static std::set<CNetAddr> setKnown;
if (setKnown.size() == BITCOIN_TIMEDATA_MAX_SAMPLES)
@@ -57,7 +56,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
// Add data
static CMedianFilter<int64_t> vTimeOffsets(BITCOIN_TIMEDATA_MAX_SAMPLES, 0);
vTimeOffsets.input(nOffsetSample);
- LogPrint(BCLog::NET,"added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60);
+ LogPrint(BCLog::NET, "added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample / 60);
// There is a known issue here (see issue #4521):
//
@@ -76,33 +75,27 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
// So we should hold off on fixing this and clean it up as part of
// a timing cleanup that strengthens it in a number of other ways.
//
- if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
- {
+ if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) {
int64_t nMedian = vTimeOffsets.median();
std::vector<int64_t> vSorted = vTimeOffsets.sorted();
// Only let other nodes change our time by so much
- if (abs64(nMedian) <= std::max<int64_t>(0, gArgs.GetArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT)))
- {
+ if (abs64(nMedian) <= std::max<int64_t>(0, gArgs.GetArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT))) {
nTimeOffset = nMedian;
- }
- else
- {
+ } else {
nTimeOffset = 0;
static bool fDone;
- if (!fDone)
- {
+ if (!fDone) {
// If nobody has a time different than ours but within 5 minutes of ours, give a warning
bool fMatch = false;
- for (const int64_t nOffset : vSorted)
- if (nOffset != 0 && abs64(nOffset) < 5 * 60)
- fMatch = true;
+ for (const int64_t nOffset : vSorted) {
+ if (nOffset != 0 && abs64(nOffset) < 5 * 60) fMatch = true;
+ }
- if (!fMatch)
- {
+ if (!fMatch) {
fDone = true;
bilingual_str strMessage = strprintf(_("Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly."), PACKAGE_NAME);
- SetMiscWarning(strMessage.translated);
+ SetMiscWarning(strMessage);
uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING);
}
}
@@ -113,8 +106,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
LogPrint(BCLog::NET, "%+d ", n); /* Continued */
}
LogPrint(BCLog::NET, "| "); /* Continued */
-
- LogPrint(BCLog::NET, "nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset/60);
+ LogPrint(BCLog::NET, "nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset / 60);
}
}
}
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 071aa1336b..047560f45d 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -5,10 +5,10 @@
#include <txdb.h>
+#include <node/ui_interface.h>
#include <pow.h>
#include <random.h>
#include <shutdown.h>
-#include <ui_interface.h>
#include <uint256.h>
#include <util/system.h>
#include <util/translation.h>
@@ -16,8 +16,6 @@
#include <stdint.h>
-#include <boost/thread.hpp>
-
static const char DB_COIN = 'C';
static const char DB_COINS = 'c';
static const char DB_BLOCK_FILES = 'f';
@@ -36,19 +34,7 @@ struct CoinEntry {
char key;
explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)), key(DB_COIN) {}
- template<typename Stream>
- void Serialize(Stream &s) const {
- s << key;
- s << outpoint->hash;
- s << VARINT(outpoint->n);
- }
-
- template<typename Stream>
- void Unserialize(Stream& s) {
- s >> key;
- s >> outpoint->hash;
- s >> VARINT(outpoint->n);
- }
+ SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
};
}
@@ -254,7 +240,6 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
// Load m_block_index
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) {
@@ -366,7 +351,6 @@ bool CCoinsViewDB::Upgrade() {
std::pair<unsigned char, uint256> key;
std::pair<unsigned char, uint256> prev_key = {DB_COINS, uint256()};
while (pcursor->Valid()) {
- boost::this_thread::interruption_point();
if (ShutdownRequested()) {
break;
}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index c5c0208d8f..7d8eb8a323 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -410,7 +410,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
// for any reason except being included in a block. Clients interested
// in transactions included in blocks can subscribe to the BlockConnected
// notification.
- GetMainSignals().TransactionRemovedFromMempool(it->GetSharedTx());
+ GetMainSignals().TransactionRemovedFromMempool(it->GetSharedTx(), reason);
}
const uint256 hash = it->GetTx().GetHash();
diff --git a/src/txmempool.h b/src/txmempool.h
index 4bee78b8d6..583f7614b7 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -704,18 +704,27 @@ public:
/** Adds a transaction to the unbroadcast set */
void AddUnbroadcastTx(const uint256& txid) {
LOCK(cs);
- m_unbroadcast_txids.insert(txid);
+ // Sanity Check: the transaction should also be in the mempool
+ if (exists(txid)) {
+ m_unbroadcast_txids.insert(txid);
+ }
}
/** Removes a transaction from the unbroadcast set */
void RemoveUnbroadcastTx(const uint256& txid, const bool unchecked = false);
/** Returns transactions in unbroadcast set */
- const std::set<uint256> GetUnbroadcastTxs() const {
+ std::set<uint256> GetUnbroadcastTxs() const {
LOCK(cs);
return m_unbroadcast_txids;
}
+ /** Returns whether a txid is in the unbroadcast set */
+ bool IsUnbroadcastTx(const uint256& txid) const {
+ LOCK(cs);
+ return (m_unbroadcast_txids.count(txid) != 0);
+ }
+
private:
/** UpdateForDescendants is used by UpdateTransactionsFromBlock to update
* the descendants for a single transaction that has been added to the
diff --git a/src/util/check.h b/src/util/check.h
index d18887ae95..9edf394492 100644
--- a/src/util/check.h
+++ b/src/util/check.h
@@ -5,6 +5,10 @@
#ifndef BITCOIN_UTIL_CHECK_H
#define BITCOIN_UTIL_CHECK_H
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <tinyformat.h>
#include <stdexcept>
@@ -21,7 +25,7 @@ class NonFatalCheckError : public std::runtime_error
* - where the condition is assumed to be true, not for error handling or validating user input
* - where a failure to fulfill the condition is recoverable and does not abort the program
*
- * For example in RPC code, where it is undersirable to crash the whole program, this can be generally used to replace
+ * For example in RPC code, where it is undesirable to crash the whole program, this can be generally used to replace
* asserts or recoverable logic errors. A NonFatalCheckError in RPC code is caught and passed as a string to the RPC
* caller, which can then report the issue to the developers.
*/
@@ -38,4 +42,18 @@ class NonFatalCheckError : public std::runtime_error
} \
} while (false)
+#if defined(NDEBUG)
+#error "Cannot compile without assertions!"
+#endif
+
+/** Helper for Assert(). TODO remove in C++14 and replace `decltype(get_pure_r_value(val))` with `T` (templated lambda) */
+template <typename T>
+T get_pure_r_value(T&& val)
+{
+ return std::forward<T>(val);
+}
+
+/** Identity function. Abort if the value compares equal to zero */
+#define Assert(val) [&]() -> decltype(get_pure_r_value(val)) { auto&& check = (val); assert(#val && check); return std::forward<decltype(get_pure_r_value(val))>(check); }()
+
#endif // BITCOIN_UTIL_CHECK_H
diff --git a/src/util/error.cpp b/src/util/error.cpp
index 72a6e87cde..3e29083712 100644
--- a/src/util/error.cpp
+++ b/src/util/error.cpp
@@ -8,37 +8,37 @@
#include <util/system.h>
#include <util/translation.h>
-std::string TransactionErrorString(const TransactionError err)
+bilingual_str TransactionErrorString(const TransactionError err)
{
switch (err) {
case TransactionError::OK:
- return "No error";
+ return Untranslated("No error");
case TransactionError::MISSING_INPUTS:
- return "Missing inputs";
+ return Untranslated("Inputs missing or spent");
case TransactionError::ALREADY_IN_CHAIN:
- return "Transaction already in block chain";
+ return Untranslated("Transaction already in block chain");
case TransactionError::P2P_DISABLED:
- return "Peer-to-peer functionality missing or disabled";
+ return Untranslated("Peer-to-peer functionality missing or disabled");
case TransactionError::MEMPOOL_REJECTED:
- return "Transaction rejected by AcceptToMemoryPool";
+ return Untranslated("Transaction rejected by AcceptToMemoryPool");
case TransactionError::MEMPOOL_ERROR:
- return "AcceptToMemoryPool failed";
+ return Untranslated("AcceptToMemoryPool failed");
case TransactionError::INVALID_PSBT:
- return "PSBT is not sane";
+ return Untranslated("PSBT is not well-formed");
case TransactionError::PSBT_MISMATCH:
- return "PSBTs not compatible (different transactions)";
+ return Untranslated("PSBTs not compatible (different transactions)");
case TransactionError::SIGHASH_MISMATCH:
- return "Specified sighash value does not match existing value";
+ return Untranslated("Specified sighash value does not match value stored in PSBT");
case TransactionError::MAX_FEE_EXCEEDED:
- return "Fee exceeds maximum configured by -maxtxfee";
+ return Untranslated("Fee exceeds maximum configured by -maxtxfee");
// no default case, so the compiler can warn about missing cases
}
assert(false);
}
-std::string ResolveErrMsg(const std::string& optname, const std::string& strBind)
+bilingual_str ResolveErrMsg(const std::string& optname, const std::string& strBind)
{
- return strprintf(_("Cannot resolve -%s address: '%s'").translated, optname, strBind);
+ return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind);
}
bilingual_str AmountHighWarn(const std::string& optname)
diff --git a/src/util/error.h b/src/util/error.h
index 61af88ddea..b9830c9eea 100644
--- a/src/util/error.h
+++ b/src/util/error.h
@@ -32,9 +32,9 @@ enum class TransactionError {
MAX_FEE_EXCEEDED,
};
-std::string TransactionErrorString(const TransactionError error);
+bilingual_str TransactionErrorString(const TransactionError error);
-std::string ResolveErrMsg(const std::string& optname, const std::string& strBind);
+bilingual_str ResolveErrMsg(const std::string& optname, const std::string& strBind);
bilingual_str AmountHighWarn(const std::string& optname);
diff --git a/src/util/fees.cpp b/src/util/fees.cpp
index b335bfa666..6208a20a97 100644
--- a/src/util/fees.cpp
+++ b/src/util/fees.cpp
@@ -6,11 +6,16 @@
#include <util/fees.h>
#include <policy/fees.h>
+#include <util/strencodings.h>
+#include <util/string.h>
#include <map>
#include <string>
+#include <vector>
+#include <utility>
-std::string StringForFeeReason(FeeReason reason) {
+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"},
@@ -29,16 +34,31 @@ std::string StringForFeeReason(FeeReason reason) {
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},
+const std::vector<std::pair<std::string, FeeEstimateMode>>& FeeModeMap()
+{
+ static const std::vector<std::pair<std::string, FeeEstimateMode>> FEE_MODES = {
+ {"unset", FeeEstimateMode::UNSET},
+ {"economical", FeeEstimateMode::ECONOMICAL},
+ {"conservative", FeeEstimateMode::CONSERVATIVE},
+ {(CURRENCY_UNIT + "/kB"), FeeEstimateMode::BTC_KB},
+ {(CURRENCY_ATOM + "/B"), FeeEstimateMode::SAT_B},
};
- auto mode = fee_modes.find(mode_string);
+ return FEE_MODES;
+}
- if (mode == fee_modes.end()) return false;
+std::string FeeModes(const std::string& delimiter)
+{
+ return Join(FeeModeMap(), delimiter, [&](const std::pair<std::string, FeeEstimateMode>& i) { return i.first; });
+}
- fee_estimate_mode = mode->second;
- return true;
+bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode)
+{
+ auto searchkey = ToUpper(mode_string);
+ for (const auto& pair : FeeModeMap()) {
+ if (ToUpper(pair.first) == searchkey) {
+ fee_estimate_mode = pair.second;
+ return true;
+ }
+ }
+ return false;
}
diff --git a/src/util/fees.h b/src/util/fees.h
index a930c8935a..d52046a44c 100644
--- a/src/util/fees.h
+++ b/src/util/fees.h
@@ -12,5 +12,6 @@ enum class FeeReason;
bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode);
std::string StringForFeeReason(FeeReason reason);
+std::string FeeModes(const std::string& delimiter);
#endif // BITCOIN_UTIL_FEES_H
diff --git a/src/util/ref.h b/src/util/ref.h
new file mode 100644
index 0000000000..9685ea9fec
--- /dev/null
+++ b/src/util/ref.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 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_REF_H
+#define BITCOIN_UTIL_REF_H
+
+#include <util/check.h>
+
+#include <typeindex>
+
+namespace util {
+
+/**
+ * Type-safe dynamic reference.
+ *
+ * This implements a small subset of the functionality in C++17's std::any
+ * class, and can be dropped when the project updates to C++17
+ * (https://github.com/bitcoin/bitcoin/issues/16684)
+ */
+class Ref
+{
+public:
+ Ref() = default;
+ template<typename T> Ref(T& value) { Set(value); }
+ template<typename T> T& Get() const { CHECK_NONFATAL(Has<T>()); return *static_cast<T*>(m_value); }
+ template<typename T> void Set(T& value) { m_value = &value; m_type = std::type_index(typeid(T)); }
+ template<typename T> bool Has() const { return m_value && m_type == std::type_index(typeid(T)); }
+ void Clear() { m_value = nullptr; m_type = std::type_index(typeid(void)); }
+
+private:
+ void* m_value = nullptr;
+ std::type_index m_type = std::type_index(typeid(void));
+};
+
+} // namespace util
+
+#endif // BITCOIN_UTIL_REF_H
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 2013b416db..7e7ba840cd 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -3,6 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <sync.h>
#include <util/system.h>
#include <chainparamsbase.h>
@@ -75,18 +76,18 @@ const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
ArgsManager gArgs;
+/** Mutex to protect dir_locks. */
+static Mutex cs_dir_locks;
/** A map that contains all the currently held directory locks. After
* successful locking, these will be held here until the global destructor
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
* is called.
*/
-static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks;
-/** Mutex to protect dir_locks. */
-static std::mutex cs_dir_locks;
+static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks GUARDED_BY(cs_dir_locks);
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only)
{
- std::lock_guard<std::mutex> ulock(cs_dir_locks);
+ LOCK(cs_dir_locks);
fs::path pathLockFile = directory / lockfile_name;
// If a lock for this directory already exists in the map, don't try to re-lock it
@@ -110,13 +111,13 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name)
{
- std::lock_guard<std::mutex> lock(cs_dir_locks);
+ LOCK(cs_dir_locks);
dir_locks.erase((directory / lockfile_name).string());
}
void ReleaseDirectoryLocks()
{
- std::lock_guard<std::mutex> ulock(cs_dir_locks);
+ LOCK(cs_dir_locks);
dir_locks.clear();
}
@@ -1162,8 +1163,9 @@ void ScheduleBatchPriority()
{
#ifdef SCHED_BATCH
const static sched_param param{};
- if (pthread_setschedparam(pthread_self(), SCHED_BATCH, &param) != 0) {
- LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(errno));
+ const int rc = pthread_setschedparam(pthread_self(), SCHED_BATCH, &param);
+ if (rc != 0) {
+ LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(rc));
}
#endif
}
diff --git a/src/util/time.h b/src/util/time.h
index b00c25f67c..af934e423b 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -15,10 +15,15 @@ void UninterruptibleSleep(const std::chrono::microseconds& n);
/**
* Helper to count the seconds of a duration.
*
- * All durations should be using std::chrono and calling this should generally be avoided in code. Though, it is still
- * preferred to an inline t.count() to protect against a reliance on the exact type of t.
+ * All durations should be using std::chrono and calling this should generally
+ * be avoided in code. Though, it is still preferred to an inline t.count() to
+ * protect against a reliance on the exact type of t.
+ *
+ * This helper is used to convert durations before passing them over an
+ * interface that doesn't support std::chrono (e.g. RPC, debug log, or the GUI)
*/
inline int64_t count_seconds(std::chrono::seconds t) { return t.count(); }
+inline int64_t count_microseconds(std::chrono::microseconds t) { return t.count(); }
/**
* DEPRECATED
diff --git a/src/util/translation.h b/src/util/translation.h
index 268bcf30a7..695d6dac96 100644
--- a/src/util/translation.h
+++ b/src/util/translation.h
@@ -23,6 +23,11 @@ struct bilingual_str {
translated += rhs.translated;
return *this;
}
+
+ bool empty() const
+ {
+ return original.empty();
+ }
};
inline bilingual_str operator+(bilingual_str lhs, const bilingual_str& rhs)
diff --git a/src/util/ui_change_type.h b/src/util/ui_change_type.h
new file mode 100644
index 0000000000..1db761a18d
--- /dev/null
+++ b/src/util/ui_change_type.h
@@ -0,0 +1,15 @@
+// Copyright (c) 2012-2020 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_UI_CHANGE_TYPE_H
+#define BITCOIN_UTIL_UI_CHANGE_TYPE_H
+
+/** General change type (added, updated, removed). */
+enum ChangeType {
+ CT_NEW,
+ CT_UPDATED,
+ CT_DELETED
+};
+
+#endif // BITCOIN_UTIL_UI_CHANGE_TYPE_H
diff --git a/src/validation.cpp b/src/validation.cpp
index 056a51cf65..4fe02ed244 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -20,6 +20,7 @@
#include <index/txindex.h>
#include <logging.h>
#include <logging/timer.h>
+#include <node/ui_interface.h>
#include <optional.h>
#include <policy/fees.h>
#include <policy/policy.h>
@@ -36,9 +37,9 @@
#include <tinyformat.h>
#include <txdb.h>
#include <txmempool.h>
-#include <ui_interface.h>
#include <uint256.h>
#include <undo.h>
+#include <util/check.h> // For NDEBUG compile time check
#include <util/moneystr.h>
#include <util/rbf.h>
#include <util/strencodings.h>
@@ -50,11 +51,6 @@
#include <string>
#include <boost/algorithm/string/replace.hpp>
-#include <boost/thread.hpp>
-
-#if defined(NDEBUG)
-# error "Bitcoin cannot be compiled without assertions."
-#endif
#define MICRO 0.000001
#define MILLI 0.001
@@ -71,12 +67,20 @@ static const unsigned int MAX_DISCONNECTED_TX_POOL_SIZE = 20000;
static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
-/** Time to wait (in seconds) between writing blocks/block index to disk. */
-static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
-/** Time to wait (in seconds) between flushing chainstate to disk. */
-static const unsigned int DATABASE_FLUSH_INTERVAL = 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;
+/** Time to wait between writing blocks/block index to disk. */
+static constexpr std::chrono::hours DATABASE_WRITE_INTERVAL{1};
+/** Time to wait between flushing chainstate to disk. */
+static constexpr std::chrono::hours DATABASE_FLUSH_INTERVAL{24};
+/** Maximum age of our tip for us to be considered current for fee estimation */
+static constexpr std::chrono::hours MAX_FEE_ESTIMATION_TIP_AGE{3};
+const std::vector<std::string> CHECKLEVEL_DOC {
+ "level 0 reads the blocks from disk",
+ "level 1 verifies block validity",
+ "level 2 verifies undo data",
+ "level 3 checks disconnection of tip blocks",
+ "level 4 tries to reconnect the blocks",
+ "each level includes the checks of the previous levels",
+};
bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
// First sort by most total work, ...
@@ -196,8 +200,8 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
std::unique_ptr<CBlockTreeDB> pblocktree;
// See definition for documentation
-static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
-static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
+static void FindFilesToPruneManual(ChainstateManager& chainman, std::set<int>& setFilesToPrune, int nManualPruneHeight);
+static void FindFilesToPrune(ChainstateManager& chainman, std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false);
static FlatFileSeq BlockFileSeq();
@@ -295,7 +299,7 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
prevheights[txinIndex] = coin.nHeight;
}
}
- lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index);
+ lockPair = CalculateSequenceLocks(tx, flags, prevheights, index);
if (lp) {
lp->height = lockPair.first;
lp->time = lockPair.second;
@@ -347,7 +351,7 @@ static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
AssertLockHeld(cs_main);
if (::ChainstateActive().IsInitialBlockDownload())
return false;
- if (::ChainActive().Tip()->GetBlockTime() < (GetTime() - MAX_FEE_ESTIMATION_TIP_AGE))
+ if (::ChainActive().Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
return false;
if (::ChainActive().Height() < pindexBestHeader->nHeight - 1)
return false;
@@ -569,8 +573,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
CAmount& nConflictingFees = ws.m_conflicting_fees;
size_t& nConflictingSize = ws.m_conflicting_size;
- if (!CheckTransaction(tx, state))
+ if (!CheckTransaction(tx, state)) {
return false; // state filled in by CheckTransaction
+ }
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
@@ -680,7 +685,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
CAmount nFees = 0;
if (!Consensus::CheckTxInputs(tx, state, m_view, GetSpendHeight(m_view), nFees)) {
- return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), state.ToString());
+ return false; // state filled in by CheckTxInputs
}
// Check for non-standard pay-to-script-hash in inputs
@@ -1314,12 +1319,6 @@ bool CChainState::IsInitialBlockDownload() const
static CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr;
-BlockMap& BlockIndex()
-{
- LOCK(::cs_main);
- return g_chainman.m_blockman.m_block_index;
-}
-
static void AlertNotify(const std::string& strMessage)
{
uiInterface.NotifyAlertChanged();
@@ -1423,12 +1422,12 @@ void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(c
pindexBestHeader = ::ChainActive().Tip();
}
- LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__,
+ LogPrintf("%s: invalid block=%s height=%d log2_work=%f date=%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime()));
CBlockIndex *tip = ::ChainActive().Tip();
assert (tip);
- LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__,
+ LogPrintf("%s: current best=%s height=%d log2_work=%f date=%s\n", __func__,
tip->GetBlockHash().ToString(), ::ChainActive().Height(), log(tip->nChainWork.getdouble())/log(2.0),
FormatISO8601DateTime(tip->GetBlockTime()));
CheckForkWarningConditions();
@@ -1481,14 +1480,21 @@ int GetSpendHeight(const CCoinsViewCache& inputs)
}
-static CuckooCache::cache<uint256, SignatureCacheHasher> scriptExecutionCache;
-static uint256 scriptExecutionCacheNonce(GetRandHash());
+static CuckooCache::cache<uint256, SignatureCacheHasher> g_scriptExecutionCache;
+static CSHA256 g_scriptExecutionCacheHasher;
void InitScriptExecutionCache() {
+ // Setup the salted hasher
+ uint256 nonce = GetRandHash();
+ // We want the nonce to be 64 bytes long to force the hasher to process
+ // this chunk, which makes later hash computations more efficient. We
+ // just write our 32-byte entropy twice to fill the 64 bytes.
+ g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
+ g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
// nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
// setup_bytes creates the minimum possible cache (2 elements).
size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
- size_t nElems = scriptExecutionCache.setup_bytes(nMaxCacheSize);
+ size_t nElems = g_scriptExecutionCache.setup_bytes(nMaxCacheSize);
LogPrintf("Using %zu MiB out of %zu/2 requested for script execution cache, able to store %zu elements\n",
(nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
}
@@ -1526,12 +1532,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
// properly commits to the scriptPubKey in the inputs view of that
// transaction).
uint256 hashCacheEntry;
- // We only use the first 19 bytes of nonce to avoid a second SHA
- // round - giving us 19 + 32 + 4 = 55 bytes (+ 8 + 1 = 64)
- static_assert(55 - sizeof(flags) - 32 >= 128/8, "Want at least 128 bits of nonce for script execution cache");
- CSHA256().Write(scriptExecutionCacheNonce.begin(), 55 - sizeof(flags) - 32).Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
+ CSHA256 hasher = g_scriptExecutionCacheHasher;
+ hasher.Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
- if (scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
+ if (g_scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
return true;
}
@@ -1586,7 +1590,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
if (cacheFullScriptStore && !pvChecks) {
// We executed all of the provided scripts, and were told to
// cache the result. Do so now.
- scriptExecutionCache.insert(hashCacheEntry);
+ g_scriptExecutionCache.insert(hashCacheEntry);
}
return true;
@@ -1651,23 +1655,21 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
}
/** Abort with a message */
-// TODO: AbortNode() should take bilingual_str userMessage parameter.
-static bool AbortNode(const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0)
+static bool AbortNode(const std::string& strMessage, bilingual_str user_message = bilingual_str())
{
- SetMiscWarning(strMessage);
+ SetMiscWarning(Untranslated(strMessage));
LogPrintf("*** %s\n", strMessage);
- if (!userMessage.empty()) {
- uiInterface.ThreadSafeMessageBox(Untranslated(userMessage), "", CClientUIInterface::MSG_ERROR | prefix);
- } else {
- uiInterface.ThreadSafeMessageBox(_("Error: A fatal internal error occurred, see debug.log for details"), "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MSG_NOPREFIX);
+ if (user_message.empty()) {
+ user_message = _("A fatal internal error occurred, see debug.log for details");
}
+ AbortError(user_message);
StartShutdown();
return false;
}
-static bool AbortNode(BlockValidationState& state, const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0)
+static bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str())
{
- AbortNode(strMessage, userMessage, prefix);
+ AbortNode(strMessage, userMessage);
return state.Error(strMessage);
}
@@ -1765,19 +1767,24 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
}
-void static FlushBlockFile(bool fFinalize = false)
+static void FlushUndoFile(int block_file, bool finalize = false)
{
- LOCK(cs_LastBlockFile);
+ FlatFilePos undo_pos_old(block_file, vinfoBlockFile[block_file].nUndoSize);
+ if (!UndoFileSeq().Flush(undo_pos_old, finalize)) {
+ AbortNode("Flushing undo file to disk failed. This is likely the result of an I/O error.");
+ }
+}
+static void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false)
+{
+ LOCK(cs_LastBlockFile);
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) {
+ if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) {
AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
}
+ // we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks,
+ // e.g. during IBD or a sync after a node going offline
+ if (!fFinalize || finalize_undo) FlushUndoFile(nLastBlockFile, finalize_undo);
}
static bool FindUndoPos(BlockValidationState &state, int nFile, FlatFilePos &pos, unsigned int nAddSize);
@@ -1791,6 +1798,14 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationSt
return error("ConnectBlock(): FindUndoPos failed");
if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart()))
return AbortNode(state, "Failed to write undo data");
+ // rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
+ // we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height
+ // in the block file info as below; note that this does not catch the case where the undo writes are keeping up
+ // with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
+ // the FindBlockPos function
+ if (_pos.nFile < nLastBlockFile && static_cast<uint32_t>(pindex->nHeight) == vinfoBlockFile[_pos.nFile].nHeightLast) {
+ FlushUndoFile(_pos.nFile, true);
+ }
// update nUndoPos in block index
pindex->nUndoPos = _pos.nPos;
@@ -2147,7 +2162,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
prevheights[j] = view.AccessCoin(tx.vin[j].prevout).nHeight;
}
- if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) {
+ if (!SequenceLocks(tx, nLockTimeFlags, prevheights, *pindex)) {
LogPrintf("ERROR: %s: contains a non-BIP68-final transaction\n", __func__);
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-txns-nonfinal");
}
@@ -2264,8 +2279,8 @@ bool CChainState::FlushStateToDisk(
{
LOCK(cs_main);
assert(this->CanFlushToDisk());
- static int64_t nLastWrite = 0;
- static int64_t nLastFlush = 0;
+ static std::chrono::microseconds nLastWrite{0};
+ static std::chrono::microseconds nLastFlush{0};
std::set<int> setFilesToPrune;
bool full_flush_completed = false;
@@ -2282,11 +2297,11 @@ bool CChainState::FlushStateToDisk(
if (nManualPruneHeight > 0) {
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune (manual)", BCLog::BENCH);
- FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight);
+ FindFilesToPruneManual(g_chainman, setFilesToPrune, nManualPruneHeight);
} else {
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH);
- FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight());
+ FindFilesToPrune(g_chainman, setFilesToPrune, chainparams.PruneAfterHeight());
fCheckForPruning = false;
}
if (!setFilesToPrune.empty()) {
@@ -2297,12 +2312,12 @@ bool CChainState::FlushStateToDisk(
}
}
}
- int64_t nNow = GetTimeMicros();
+ const auto nNow = GetTime<std::chrono::microseconds>();
// Avoid writing/flushing immediately after startup.
- if (nLastWrite == 0) {
+ if (nLastWrite.count() == 0) {
nLastWrite = nNow;
}
- if (nLastFlush == 0) {
+ if (nLastFlush.count() == 0) {
nLastFlush = nNow;
}
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
@@ -2310,16 +2325,16 @@ bool CChainState::FlushStateToDisk(
// The cache is over the limit, we have to write now.
bool fCacheCritical = mode == FlushStateMode::IF_NEEDED && cache_state >= CoinsCacheSizeState::CRITICAL;
// It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash.
- bool fPeriodicWrite = mode == FlushStateMode::PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000;
+ bool fPeriodicWrite = mode == FlushStateMode::PERIODIC && nNow > nLastWrite + DATABASE_WRITE_INTERVAL;
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
- bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000;
+ bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > nLastFlush + DATABASE_FLUSH_INTERVAL;
// Combine all conditions that result in a full cache flush.
fDoFullFlush = (mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune;
// Write blocks and block index to disk.
if (fDoFullFlush || fPeriodicWrite) {
// Depend on nMinDiskSpace to ensure we can write block index
if (!CheckDiskSpace(GetBlocksDir())) {
- return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
{
LOG_TIME_MILLIS_WITH_CATEGORY("write block and undo data to disk", BCLog::BENCH);
@@ -2367,7 +2382,7 @@ bool CChainState::FlushStateToDisk(
// an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2.
if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * CoinsTip().GetCacheSize())) {
- return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
// Flush the chainstate (which may refer to block index entries).
if (!CoinsTip().Flush())
@@ -2404,20 +2419,20 @@ void CChainState::PruneAndFlush() {
}
}
-static void DoWarning(const std::string& strWarning)
+static void DoWarning(const bilingual_str& warning)
{
static bool fWarned = false;
- SetMiscWarning(strWarning);
+ SetMiscWarning(warning);
if (!fWarned) {
- AlertNotify(strWarning);
+ AlertNotify(warning.original);
fWarned = true;
}
}
/** Private helper function that concatenates warning messages. */
-static void AppendWarning(std::string& res, const std::string& warn)
+static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
{
- if (!res.empty()) res += ", ";
+ if (!res.empty()) res += Untranslated(", ");
res += warn;
}
@@ -2434,7 +2449,7 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
g_best_block_cv.notify_all();
}
- std::string warningMessages;
+ bilingual_str warning_messages;
if (!::ChainstateActive().IsInitialBlockDownload())
{
int nUpgraded = 0;
@@ -2443,11 +2458,11 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
WarningBitsConditionChecker checker(bit);
ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
- const std::string strWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)").translated, bit);
+ const bilingual_str warning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
if (state == ThresholdState::ACTIVE) {
- DoWarning(strWarning);
+ DoWarning(warning);
} else {
- AppendWarning(warningMessages, strWarning);
+ AppendWarning(warning_messages, warning);
}
}
}
@@ -2460,14 +2475,14 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
pindex = pindex->pprev;
}
if (nUpgraded > 0)
- AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version").translated, nUpgraded));
+ AppendWarning(warning_messages, strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
}
- LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
+ LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
- !warningMessages.empty() ? strprintf(" warning='%s'", warningMessages) : "");
+ !warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
}
@@ -2800,6 +2815,13 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
return true;
}
+static SynchronizationState GetSynchronizationState(bool init)
+{
+ if (!init) return SynchronizationState::POST_INIT;
+ if (::fReindex) return SynchronizationState::INIT_REINDEX;
+ return SynchronizationState::INIT_DOWNLOAD;
+}
+
static bool NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
bool fNotify = false;
bool fInitialBlockDownload = false;
@@ -2817,7 +2839,7 @@ static bool NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
}
// Send block tip changed notifications without cs_main
if (fNotify) {
- uiInterface.NotifyHeaderTip(fInitialBlockDownload, pindexHeader);
+ uiInterface.NotifyHeaderTip(GetSynchronizationState(fInitialBlockDownload), pindexHeader);
}
return fNotify;
}
@@ -2847,8 +2869,6 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
CBlockIndex *pindexNewTip = nullptr;
int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
do {
- boost::this_thread::interruption_point();
-
// 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.
@@ -2906,7 +2926,7 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
// Always notify the UI if a new block tip was connected
- uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
+ uiInterface.NotifyBlockTip(GetSynchronizationState(fInitialDownload), pindexNewTip);
}
}
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
@@ -2917,8 +2937,7 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
// never shutdown before connecting the genesis block during LoadChainTip(). Previously this
// caused an assert() failure during shutdown in such cases as the UTXO DB flushing checks
// that the best block hash is non-null.
- if (ShutdownRequested())
- break;
+ if (ShutdownRequested()) break;
} while (pindexNewTip != pindexMostWork);
CheckBlockIndex(chainparams.GetConsensus());
@@ -3097,7 +3116,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
// Only notify about a new block tip if the active chain was modified.
if (pindex_was_in_chain) {
- uiInterface.NotifyBlockTip(IsInitialBlockDownload(), to_mark_failed->pprev);
+ uiInterface.NotifyBlockTip(GetSynchronizationState(IsInitialBlockDownload()), to_mark_failed->pprev);
}
return true;
}
@@ -3236,8 +3255,13 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
vinfoBlockFile.resize(nFile + 1);
}
+ bool finalize_undo = false;
if (!fKnown) {
while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
+ // when the undo file is keeping up with the block file, we want to flush it explicitly
+ // when it is lagging behind (more blocks arrive than are being connected), we let the
+ // undo block write case handle it
+ finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)ChainActive().Tip()->nHeight);
nFile++;
if (vinfoBlockFile.size() <= nFile) {
vinfoBlockFile.resize(nFile + 1);
@@ -3251,7 +3275,7 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
if (!fKnown) {
LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
}
- FlushBlockFile(!fKnown);
+ FlushBlockFile(!fKnown, finalize_undo);
nLastBlockFile = nFile;
}
@@ -3265,7 +3289,7 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
bool out_of_space;
size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space);
if (out_of_space) {
- return AbortNode("Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode("Disk space is too low!", _("Disk space is too low!"));
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
@@ -3289,7 +3313,7 @@ static bool FindUndoPos(BlockValidationState &state, int nFile, FlatFilePos &pos
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 too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
@@ -3684,13 +3708,14 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS
}
// Exposed wrapper for AcceptBlockHeader
-bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
+bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
{
+ AssertLockNotHeld(cs_main);
{
LOCK(cs_main);
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
- bool accepted = g_chainman.m_blockman.AcceptBlockHeader(
+ bool accepted = m_blockman.AcceptBlockHeader(
header, state, chainparams, &pindex);
::ChainstateActive().CheckBlockIndex(chainparams.GetConsensus());
@@ -3812,7 +3837,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
return true;
}
-bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool *fNewBlock)
+bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock)
{
AssertLockNotHeld(cs_main);
@@ -3888,12 +3913,12 @@ uint64_t CalculateCurrentUsage()
return retval;
}
-/* Prune a block file (modify associated database entries)*/
-void PruneOneBlockFile(const int fileNumber)
+void ChainstateManager::PruneOneBlockFile(const int fileNumber)
{
+ AssertLockHeld(cs_main);
LOCK(cs_LastBlockFile);
- for (const auto& entry : g_chainman.BlockIndex()) {
+ for (const auto& entry : m_blockman.m_block_index) {
CBlockIndex* pindex = entry.second;
if (pindex->nFile == fileNumber) {
pindex->nStatus &= ~BLOCK_HAVE_DATA;
@@ -3907,12 +3932,12 @@ void PruneOneBlockFile(const int fileNumber)
// to be downloaded again in order to consider its chain, at which
// point it would be considered as a candidate for
// m_blocks_unlinked or setBlockIndexCandidates.
- auto range = g_chainman.m_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
+ auto range = m_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
while (range.first != range.second) {
std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first;
range.first++;
if (_it->second == pindex) {
- g_chainman.m_blockman.m_blocks_unlinked.erase(_it);
+ m_blockman.m_blocks_unlinked.erase(_it);
}
}
}
@@ -3934,7 +3959,7 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
}
/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
-static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight)
+static void FindFilesToPruneManual(ChainstateManager& chainman, std::set<int>& setFilesToPrune, int nManualPruneHeight)
{
assert(fPruneMode && nManualPruneHeight > 0);
@@ -3948,7 +3973,7 @@ static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPr
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune)
continue;
- PruneOneBlockFile(fileNumber);
+ chainman.PruneOneBlockFile(fileNumber);
setFilesToPrune.insert(fileNumber);
count++;
}
@@ -3981,7 +4006,7 @@ void PruneBlockFilesManual(int nManualPruneHeight)
*
* @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
*/
-static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
+static void FindFilesToPrune(ChainstateManager& chainman, std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
{
LOCK2(cs_main, cs_LastBlockFile);
if (::ChainActive().Tip() == nullptr || nPruneTarget == 0) {
@@ -4023,7 +4048,7 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune)
continue;
- PruneOneBlockFile(fileNumber);
+ chainman.PruneOneBlockFile(fileNumber);
// Queue up the files for removal
setFilesToPrune.insert(fileNumber);
nCurrentUsage -= nBytesToPrune;
@@ -4147,9 +4172,9 @@ void BlockManager::Unload() {
m_block_index.clear();
}
-bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- if (!g_chainman.m_blockman.LoadBlockIndex(
+ if (!chainman.m_blockman.LoadBlockIndex(
chainparams.GetConsensus(), *pblocktree,
::ChainstateActive().setBlockIndexCandidates)) {
return false;
@@ -4175,8 +4200,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
// Check presence of blk files
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
- for (const std::pair<const uint256, CBlockIndex*>& item : g_chainman.BlockIndex())
- {
+ for (const std::pair<const uint256, CBlockIndex*>& item : chainman.BlockIndex()) {
CBlockIndex* pindex = item.second;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
setBlkDataFiles.insert(pindex->nFile);
@@ -4260,7 +4284,6 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
int reportDone = 0;
LogPrintf("[0%%]..."); /* Continued */
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))));
if (reportDone < percentageDone/10) {
// report every 10% step
@@ -4306,8 +4329,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
nGoodTransactions += block.vtx.size();
}
}
- if (ShutdownRequested())
- return true;
+ if (ShutdownRequested()) 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);
@@ -4318,7 +4340,6 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
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)));
if (reportDone < percentageDone/10) {
// report every 10% step
@@ -4332,6 +4353,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, chainparams))
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
+ if (ShutdownRequested()) return true;
}
}
@@ -4593,14 +4615,15 @@ void UnloadBlockIndex()
fHavePruned = false;
}
-bool LoadBlockIndex(const CChainParams& chainparams)
+bool ChainstateManager::LoadBlockIndex(const CChainParams& chainparams)
{
+ AssertLockHeld(cs_main);
// Load block index from databases
bool needs_init = fReindex;
if (!fReindex) {
- bool ret = LoadBlockIndexDB(chainparams);
+ bool ret = LoadBlockIndexDB(*this, chainparams);
if (!ret) return false;
- needs_init = g_chainman.m_blockman.m_block_index.empty();
+ needs_init = m_blockman.m_block_index.empty();
}
if (needs_init) {
@@ -5058,12 +5081,18 @@ bool LoadMempool(CTxMemPool& pool)
pool.PrioritiseTransaction(i.first, i.second);
}
- std::set<uint256> unbroadcast_txids;
- file >> unbroadcast_txids;
- unbroadcast = unbroadcast_txids.size();
+ // TODO: remove this try except in v0.22
+ try {
+ std::set<uint256> unbroadcast_txids;
+ file >> unbroadcast_txids;
+ unbroadcast = unbroadcast_txids.size();
- for (const auto& txid : unbroadcast_txids) {
+ for (const auto& txid : unbroadcast_txids) {
pool.AddUnbroadcastTx(txid);
+ }
+ } catch (const std::exception&) {
+ // mempool.dat files created prior to v0.21 will not have an
+ // unbroadcast set. No need to log a failure if parsing fails here.
}
} catch (const std::exception& e) {
@@ -5213,10 +5242,10 @@ CChainState& ChainstateManager::InitializeChainstate(const uint256& snapshot_blo
return *to_modify;
}
-CChain& ChainstateManager::ActiveChain() const
+CChainState& ChainstateManager::ActiveChainstate() const
{
assert(m_active_chainstate);
- return m_active_chainstate->m_chain;
+ return *m_active_chainstate;
}
bool ChainstateManager::IsSnapshotActive() const
diff --git a/src/validation.h b/src/validation.h
index c4a5cc4593..acadf151c5 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -29,6 +29,7 @@
#include <memory>
#include <set>
#include <stdint.h>
+#include <string>
#include <utility>
#include <vector>
@@ -43,6 +44,7 @@ class CConnman;
class CScriptCheck;
class CBlockPolicyEstimator;
class CTxMemPool;
+class ChainstateManager;
class TxValidationState;
struct ChainTxData;
@@ -72,7 +74,6 @@ static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
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 using fee filter */
@@ -103,6 +104,13 @@ struct BlockHasher
size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
};
+/** Current sync state passed to tip changed callbacks. */
+enum class SynchronizationState {
+ INIT_REINDEX,
+ INIT_DOWNLOAD,
+ POST_INIT
+};
+
extern RecursiveMutex cs_main;
extern CBlockPolicyEstimator feeEstimator;
extern CTxMemPool mempool;
@@ -141,41 +149,8 @@ extern bool fHavePruned;
extern bool fPruneMode;
/** Number of MiB of block files that we're trying to stay below. */
extern uint64_t nPruneTarget;
-
-/**
- * Process an incoming block. This only returns after the best known valid
- * block is made active. Note that it does not, however, guarantee that the
- * specific block passed to it has been checked for validity!
- *
- * If you want to *possibly* get feedback on whether pblock is valid, you must
- * install a CValidationInterface (see validationinterface.h) - this will have
- * its BlockChecked method called whenever *any* block completes validation.
- *
- * Note that we guarantee that either the proof-of-work is valid on pblock, or
- * (and possibly also) BlockChecked will have been called.
- *
- * May not be called in a
- * validationinterface callback.
- *
- * @param[in] pblock The block we want to process.
- * @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources and whitelisted peers.
- * @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call
- * @returns If the block was processed, independently of block validity
- */
-bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock) LOCKS_EXCLUDED(cs_main);
-
-/**
- * Process incoming block headers.
- *
- * May not be called in a
- * validationinterface callback.
- *
- * @param[in] block The block headers themselves
- * @param[out] state This may be set to an Error state if any error occurred processing them
- * @param[in] chainparams The params for the chain we want to connect to
- * @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers
- */
-bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
+/** Documentation for argument 'checklevel'. */
+extern const std::vector<std::string> CHECKLEVEL_DOC;
/** Open a block file (blk?????.dat) */
FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly = false);
@@ -185,9 +160,6 @@ fs::path GetBlockPosFilename(const FlatFilePos &pos);
void 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,
- * initializing state if we're running with -reindex. */
-bool LoadBlockIndex(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Unload database information */
void UnloadBlockIndex();
/** Run an instance of the script checking thread */
@@ -210,11 +182,6 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex* pin
uint64_t CalculateCurrentUsage();
/**
- * Mark one block file as pruned.
- */
-void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
-/**
* Actually unlink the specified files
*/
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
@@ -486,9 +453,6 @@ enum class CoinsCacheSizeState
OK = 0
};
-// Defined below, but needed for `friend` usage in CChainState.
-class ChainstateManager;
-
/**
* CChainState stores and provides an API to update our local knowledge of the
* current best chain.
@@ -834,7 +798,8 @@ public:
std::vector<CChainState*> GetAll();
//! The most-work chain.
- CChain& ActiveChain() const;
+ CChainState& ActiveChainstate() const;
+ CChain& ActiveChain() const { return ActiveChainstate().m_chain; }
int ActiveHeight() const { return ActiveChain().Height(); }
CBlockIndex* ActiveTip() const { return ActiveChain().Tip(); }
@@ -863,6 +828,47 @@ public:
CChain& ValidatedChain() const { return ValidatedChainstate().m_chain; }
CBlockIndex* ValidatedTip() const { return ValidatedChain().Tip(); }
+ /**
+ * Process an incoming block. This only returns after the best known valid
+ * block is made active. Note that it does not, however, guarantee that the
+ * specific block passed to it has been checked for validity!
+ *
+ * If you want to *possibly* get feedback on whether pblock is valid, you must
+ * install a CValidationInterface (see validationinterface.h) - this will have
+ * its BlockChecked method called whenever *any* block completes validation.
+ *
+ * Note that we guarantee that either the proof-of-work is valid on pblock, or
+ * (and possibly also) BlockChecked will have been called.
+ *
+ * May not be called in a
+ * validationinterface callback.
+ *
+ * @param[in] pblock The block we want to process.
+ * @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources.
+ * @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call
+ * @returns If the block was processed, independently of block validity
+ */
+ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock) LOCKS_EXCLUDED(cs_main);
+
+ /**
+ * Process incoming block headers.
+ *
+ * May not be called in a
+ * validationinterface callback.
+ *
+ * @param[in] block The block headers themselves
+ * @param[out] state This may be set to an Error state if any error occurred processing them
+ * @param[in] chainparams The params for the chain we want to connect to
+ * @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers
+ */
+ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
+
+ //! Mark one block file as pruned (modify associated database entries)
+ void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ //! Load the block tree and coins database from disk, initializing state if we're running with -reindex
+ bool LoadBlockIndex(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
//! Unload block index and chain data before shutdown.
void Unload() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
@@ -870,17 +876,15 @@ public:
void Reset();
};
+/** DEPRECATED! Please use node.chainman instead. May only be used in validation.cpp internally */
extern ChainstateManager g_chainman GUARDED_BY(::cs_main);
-/** @returns the most-work valid chainstate. */
+/** Please prefer the identical ChainstateManager::ActiveChainstate */
CChainState& ChainstateActive();
-/** @returns the most-work chain. */
+/** Please prefer the identical ChainstateManager::ActiveChain */
CChain& ChainActive();
-/** @returns the global block index map. */
-BlockMap& BlockIndex();
-
/** Global variable that points to the active block tree (protected by cs_main) */
extern std::unique_ptr<CBlockTreeDB> pblocktree;
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 9437f9c817..3dfbcc581c 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -199,22 +199,22 @@ void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockInd
fInitialDownload);
}
-void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) {
- auto event = [ptx, this] {
- m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(ptx); });
+void CMainSignals::TransactionAddedToMempool(const CTransactionRef& tx) {
+ auto event = [tx, this] {
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
- ptx->GetHash().ToString(),
- ptx->GetWitnessHash().ToString());
+ tx->GetHash().ToString(),
+ tx->GetWitnessHash().ToString());
}
-void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
- auto event = [ptx, this] {
- m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(ptx); });
+void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {
+ auto event = [tx, reason, this] {
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
- ptx->GetHash().ToString(),
- ptx->GetWitnessHash().ToString());
+ tx->GetHash().ToString(),
+ tx->GetWitnessHash().ToString());
}
void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex) {
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 9c23965bc1..e96f2883fc 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -21,6 +21,7 @@ class CConnman;
class CValidationInterface;
class uint256;
class CScheduler;
+enum class MemPoolRemovalReason;
/** Register subscriber */
void RegisterValidationInterface(CValidationInterface* callbacks);
@@ -96,7 +97,7 @@ protected:
*
* Called on a background thread.
*/
- virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {}
+ virtual void TransactionAddedToMempool(const CTransactionRef& tx) {}
/**
* Notifies listeners of a transaction leaving mempool.
*
@@ -129,7 +130,7 @@ protected:
*
* Called on a background thread.
*/
- virtual void TransactionRemovedFromMempool(const CTransactionRef &ptx) {}
+ virtual void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {}
/**
* Notifies listeners of a block being connected.
* Provides a vector of transactions evicted from the mempool as a result.
@@ -196,8 +197,8 @@ public:
void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
- void TransactionAddedToMempool(const CTransactionRef &);
- void TransactionRemovedFromMempool(const CTransactionRef &);
+ void TransactionAddedToMempool(const CTransactionRef&);
+ void TransactionRemovedFromMempool(const CTransactionRef&, MemPoolRemovalReason);
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex);
void BlockDisconnected(const std::shared_ptr<const CBlock> &, const CBlockIndex* pindex);
void ChainStateFlushed(const CBlockLocator &);
diff --git a/src/version.h b/src/version.h
index d932b512d4..e5d1f5a7f9 100644
--- a/src/version.h
+++ b/src/version.h
@@ -14,15 +14,8 @@ static const int PROTOCOL_VERSION = 70015;
//! initial proto version, to be increased after version/verack negotiation
static const int INIT_PROTO_VERSION = 209;
-//! In this version, 'getheaders' was introduced.
-static const int GETHEADERS_VERSION = 31800;
-
//! disconnect from peers older than this proto version
-static const int MIN_PEER_PROTO_VERSION = GETHEADERS_VERSION;
-
-//! nTime field added to CAddress, starting with this version;
-//! if possible, avoid requesting addresses nodes older than this
-static const int CADDR_TIME_VERSION = 31402;
+static const int MIN_PEER_PROTO_VERSION = 31800;
//! BIP 0031, pong message, is enabled for all versions AFTER this one
static const int BIP0031_VERSION = 60000;
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
new file mode 100644
index 0000000000..44d1bafaf6
--- /dev/null
+++ b/src/wallet/bdb.cpp
@@ -0,0 +1,848 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2020 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/bdb.h>
+#include <wallet/db.h>
+
+#include <util/strencodings.h>
+#include <util/translation.h>
+
+#include <stdint.h>
+
+#ifndef WIN32
+#include <sys/stat.h>
+#endif
+
+namespace {
+
+//! Make sure database has a unique fileid within the environment. If it
+//! doesn't, throw an error. BDB caches do not work properly when more than one
+//! open database has the same fileid (values written to one database may show
+//! up in reads to other databases).
+//!
+//! BerkeleyDB generates unique fileids by default
+//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
+//! so bitcoin should never create different databases with the same fileid, but
+//! this error can be triggered if users manually copy database files.
+void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
+{
+ if (env.IsMock()) return;
+
+ int ret = db.get_mpf()->get_fileid(fileid.value);
+ if (ret != 0) {
+ throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
+ }
+
+ for (const auto& item : env.m_fileids) {
+ if (fileid == item.second && &fileid != &item.second) {
+ throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
+ HexStr(std::begin(item.second.value), std::end(item.second.value)), item.first));
+ }
+ }
+}
+
+RecursiveMutex cs_db;
+std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to db environment.
+} // namespace
+
+bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
+{
+ return memcmp(value, &rhs.value, sizeof(value)) == 0;
+}
+
+bool IsBDBWalletLoaded(const fs::path& wallet_path)
+{
+ fs::path env_directory;
+ std::string database_filename;
+ SplitWalletPath(wallet_path, env_directory, database_filename);
+ LOCK(cs_db);
+ auto env = g_dbenvs.find(env_directory.string());
+ if (env == g_dbenvs.end()) return false;
+ auto database = env->second.lock();
+ return database && database->IsDatabaseLoaded(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.
+ * @return A shared pointer to the BerkeleyEnvironment object for the wallet directory, never empty because ~BerkeleyEnvironment
+ * erases the weak pointer from the g_dbenvs map.
+ * @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the directory path key was not already in the map.
+ */
+std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
+{
+ fs::path env_directory;
+ SplitWalletPath(wallet_path, env_directory, database_filename);
+ LOCK(cs_db);
+ auto inserted = g_dbenvs.emplace(env_directory.string(), std::weak_ptr<BerkeleyEnvironment>());
+ if (inserted.second) {
+ auto env = std::make_shared<BerkeleyEnvironment>(env_directory.string());
+ inserted.first->second = env;
+ return env;
+ }
+ return inserted.first->second.lock();
+}
+
+//
+// BerkeleyBatch
+//
+
+void BerkeleyEnvironment::Close()
+{
+ if (!fDbEnvInit)
+ return;
+
+ fDbEnvInit = false;
+
+ for (auto& db : m_databases) {
+ auto count = mapFileUseCount.find(db.first);
+ assert(count == mapFileUseCount.end() || count->second == 0);
+ BerkeleyDatabase& database = db.second.get();
+ if (database.m_db) {
+ database.m_db->close(0);
+ database.m_db.reset();
+ }
+ }
+
+ 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()
+{
+ dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
+ fDbEnvInit = false;
+ fMockDb = false;
+}
+
+BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(dir_path.string())
+{
+ Reset();
+}
+
+BerkeleyEnvironment::~BerkeleyEnvironment()
+{
+ LOCK(cs_db);
+ g_dbenvs.erase(strPath);
+ Close();
+}
+
+bool BerkeleyEnvironment::Open(bool retry)
+{
+ if (fDbEnvInit) {
+ return true;
+ }
+
+ fs::path pathIn = strPath;
+ TryCreateDirectories(pathIn);
+ if (!LockDirectory(pathIn, ".walletlock")) {
+ LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
+ return false;
+ }
+
+ fs::path pathLogDir = pathIn / "database";
+ TryCreateDirectories(pathLogDir);
+ fs::path pathErrorFile = pathIn / "db.log";
+ LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
+
+ unsigned int nEnvFlags = 0;
+ if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB))
+ nEnvFlags |= DB_PRIVATE;
+
+ dbenv->set_lg_dir(pathLogDir.string().c_str());
+ dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
+ dbenv->set_lg_bsize(0x10000);
+ dbenv->set_lg_max(1048576);
+ dbenv->set_lk_max_locks(40000);
+ dbenv->set_lk_max_objects(40000);
+ dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a")); /// debug
+ dbenv->set_flags(DB_AUTO_COMMIT, 1);
+ dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
+ dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
+ int ret = dbenv->open(strPath.c_str(),
+ DB_CREATE |
+ DB_INIT_LOCK |
+ DB_INIT_LOG |
+ DB_INIT_MPOOL |
+ DB_INIT_TXN |
+ DB_THREAD |
+ DB_RECOVER |
+ nEnvFlags,
+ S_IRUSR | S_IWUSR);
+ if (ret != 0) {
+ LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
+ int ret2 = dbenv->close(0);
+ if (ret2 != 0) {
+ LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
+ }
+ Reset();
+ if (retry) {
+ // try moving the database env out of the way
+ fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime());
+ try {
+ fs::rename(pathLogDir, pathDatabaseBak);
+ LogPrintf("Moved old %s to %s. Retrying.\n", pathLogDir.string(), pathDatabaseBak.string());
+ } catch (const fs::filesystem_error&) {
+ // failure is ok (well, not really, but it's not worse than what we started with)
+ }
+ // try opening it again one more time
+ if (!Open(false /* retry */)) {
+ // if it still fails, it probably means we can't even create the database env
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ fDbEnvInit = true;
+ fMockDb = false;
+ return true;
+}
+
+//! Construct an in-memory mock Berkeley environment for testing
+BerkeleyEnvironment::BerkeleyEnvironment()
+{
+ Reset();
+
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::MakeMock\n");
+
+ dbenv->set_cachesize(1, 0, 1);
+ dbenv->set_lg_bsize(10485760 * 4);
+ dbenv->set_lg_max(10485760);
+ dbenv->set_lk_max_locks(10000);
+ dbenv->set_lk_max_objects(10000);
+ dbenv->set_flags(DB_AUTO_COMMIT, 1);
+ dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
+ int ret = dbenv->open(nullptr,
+ DB_CREATE |
+ DB_INIT_LOCK |
+ DB_INIT_LOG |
+ DB_INIT_MPOOL |
+ DB_INIT_TXN |
+ DB_THREAD |
+ DB_PRIVATE,
+ S_IRUSR | S_IWUSR);
+ if (ret > 0) {
+ throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
+ }
+
+ fDbEnvInit = true;
+ fMockDb = true;
+}
+
+bool BerkeleyEnvironment::Verify(const std::string& strFile)
+{
+ LOCK(cs_db);
+ assert(mapFileUseCount.count(strFile) == 0);
+
+ Db db(dbenv.get(), 0);
+ int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
+ return result == 0;
+}
+
+BerkeleyBatch::SafeDbt::SafeDbt()
+{
+ m_dbt.set_flags(DB_DBT_MALLOC);
+}
+
+BerkeleyBatch::SafeDbt::SafeDbt(void* data, size_t size)
+ : m_dbt(data, size)
+{
+}
+
+BerkeleyBatch::SafeDbt::~SafeDbt()
+{
+ if (m_dbt.get_data() != nullptr) {
+ // Clear memory, e.g. in case it was a private key
+ memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
+ // under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
+ // freed by the caller.
+ // https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
+ if (m_dbt.get_flags() & DB_DBT_MALLOC) {
+ free(m_dbt.get_data());
+ }
+ }
+}
+
+const void* BerkeleyBatch::SafeDbt::get_data() const
+{
+ return m_dbt.get_data();
+}
+
+u_int32_t BerkeleyBatch::SafeDbt::get_size() const
+{
+ return m_dbt.get_size();
+}
+
+BerkeleyBatch::SafeDbt::operator Dbt*()
+{
+ return &m_dbt;
+}
+
+bool BerkeleyDatabase::Verify(bilingual_str& errorStr)
+{
+ fs::path walletDir = env->Directory();
+ fs::path file_path = walletDir / strFile;
+
+ LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
+ LogPrintf("Using wallet %s\n", file_path.string());
+
+ if (!env->Open(true /* retry */)) {
+ errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
+ return false;
+ }
+
+ if (fs::exists(file_path))
+ {
+ if (!env->Verify(strFile)) {
+ errorStr = strprintf(_("%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), file_path);
+ return false;
+ }
+ }
+ // also return true if files does not exists
+ return true;
+}
+
+void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile)
+{
+ dbenv->txn_checkpoint(0, 0, 0);
+ if (fMockDb)
+ return;
+ dbenv->lsn_reset(strFile.c_str(), 0);
+}
+
+
+BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr), m_cursor(nullptr)
+{
+ fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
+ fFlushOnClose = fFlushOnCloseIn;
+ env = database.env.get();
+ if (database.IsDummy()) {
+ return;
+ }
+ const std::string &strFilename = database.strFile;
+
+ bool fCreate = strchr(pszMode, 'c') != nullptr;
+ unsigned int nFlags = DB_THREAD;
+ if (fCreate)
+ nFlags |= DB_CREATE;
+
+ {
+ LOCK(cs_db);
+ if (!env->Open(false /* retry */))
+ throw std::runtime_error("BerkeleyBatch: Failed to open database environment.");
+
+ pdb = database.m_db.get();
+ if (pdb == nullptr) {
+ int ret;
+ std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0);
+
+ bool fMockDb = env->IsMock();
+ if (fMockDb) {
+ DbMpoolFile* mpf = pdb_temp->get_mpf();
+ ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
+ if (ret != 0) {
+ throw std::runtime_error(strprintf("BerkeleyBatch: Failed to configure for no temp file backing for database %s", strFilename));
+ }
+ }
+
+ ret = pdb_temp->open(nullptr, // Txn pointer
+ fMockDb ? nullptr : strFilename.c_str(), // Filename
+ fMockDb ? strFilename.c_str() : "main", // Logical db name
+ DB_BTREE, // Database type
+ nFlags, // Flags
+ 0);
+
+ if (ret != 0) {
+ throw std::runtime_error(strprintf("BerkeleyBatch: Error %d, can't open database %s", ret, strFilename));
+ }
+
+ // Call CheckUniqueFileid on the containing BDB environment to
+ // avoid BDB data consistency bugs that happen when different data
+ // files in the same environment have the same fileid.
+ //
+ // Also call CheckUniqueFileid on all the other g_dbenvs to prevent
+ // bitcoin from opening the same data file through another
+ // environment when the file is referenced through equivalent but
+ // not obviously identical symlinked or hard linked or bind mounted
+ // paths. In the future a more relaxed check for equal inode and
+ // device ids could be done instead, which would allow opening
+ // different backup copies of a wallet at the same time. Maybe even
+ // more ideally, an exclusive lock for accessing the database could
+ // be implemented, so no equality checks are needed at all. (Newer
+ // versions of BDB have an set_lk_exclusive method for this
+ // purpose, but the older version we use does not.)
+ for (const auto& env : g_dbenvs) {
+ CheckUniqueFileid(*env.second.lock().get(), strFilename, *pdb_temp, this->env->m_fileids[strFilename]);
+ }
+
+ pdb = pdb_temp.release();
+ database.m_db.reset(pdb);
+
+ if (fCreate && !Exists(std::string("version"))) {
+ bool fTmp = fReadOnly;
+ fReadOnly = false;
+ Write(std::string("version"), CLIENT_VERSION);
+ fReadOnly = fTmp;
+ }
+ }
+ ++env->mapFileUseCount[strFilename];
+ strFile = strFilename;
+ }
+}
+
+void BerkeleyBatch::Flush()
+{
+ if (activeTxn)
+ return;
+
+ // Flush database activity from memory pool to disk log
+ unsigned int nMinutes = 0;
+ if (fReadOnly)
+ nMinutes = 1;
+
+ 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()
+{
+ ++nUpdateCounter;
+}
+
+void BerkeleyBatch::Close()
+{
+ if (!pdb)
+ return;
+ if (activeTxn)
+ activeTxn->abort();
+ activeTxn = nullptr;
+ pdb = nullptr;
+ CloseCursor();
+
+ if (fFlushOnClose)
+ Flush();
+
+ {
+ LOCK(cs_db);
+ --env->mapFileUseCount[strFile];
+ }
+ env->m_db_in_use.notify_all();
+}
+
+void BerkeleyEnvironment::CloseDb(const std::string& strFile)
+{
+ {
+ LOCK(cs_db);
+ auto it = m_databases.find(strFile);
+ assert(it != m_databases.end());
+ BerkeleyDatabase& database = it->second.get();
+ if (database.m_db) {
+ // Close the database handle
+ database.m_db->close(0);
+ database.m_db.reset();
+ }
+ }
+}
+
+void BerkeleyEnvironment::ReloadDbEnv()
+{
+ // Make sure that no Db's are in use
+ AssertLockNotHeld(cs_db);
+ std::unique_lock<RecursiveMutex> lock(cs_db);
+ m_db_in_use.wait(lock, [this](){
+ for (auto& count : mapFileUseCount) {
+ if (count.second > 0) return false;
+ }
+ return true;
+ });
+
+ std::vector<std::string> filenames;
+ for (auto it : m_databases) {
+ filenames.push_back(it.first);
+ }
+ // Close the individual Db's
+ for (const std::string& filename : filenames) {
+ CloseDb(filename);
+ }
+ // Reset the environment
+ Flush(true); // This will flush and close the environment
+ Reset();
+ Open(true);
+}
+
+bool BerkeleyDatabase::Rewrite(const char* pszSkip)
+{
+ if (IsDummy()) {
+ return true;
+ }
+ while (true) {
+ {
+ LOCK(cs_db);
+ if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) {
+ // Flush log data to the dat file
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
+ env->mapFileUseCount.erase(strFile);
+
+ bool fSuccess = true;
+ LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
+ std::string strFileRes = strFile + ".rewrite";
+ { // surround usage of db with extra {}
+ BerkeleyBatch db(*this, "r");
+ std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
+
+ int ret = pdbCopy->open(nullptr, // Txn pointer
+ strFileRes.c_str(), // Filename
+ "main", // Logical db name
+ DB_BTREE, // Database type
+ DB_CREATE, // Flags
+ 0);
+ if (ret > 0) {
+ LogPrintf("BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
+ fSuccess = false;
+ }
+
+ if (db.StartCursor()) {
+ while (fSuccess) {
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ CDataStream ssValue(SER_DISK, CLIENT_VERSION);
+ bool complete;
+ bool ret1 = db.ReadAtCursor(ssKey, ssValue, complete);
+ if (complete) {
+ break;
+ } else if (!ret1) {
+ fSuccess = false;
+ break;
+ }
+ if (pszSkip &&
+ strncmp(ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
+ continue;
+ if (strncmp(ssKey.data(), "\x07version", 8) == 0) {
+ // Update version:
+ ssValue.clear();
+ ssValue << CLIENT_VERSION;
+ }
+ Dbt datKey(ssKey.data(), ssKey.size());
+ Dbt datValue(ssValue.data(), ssValue.size());
+ int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE);
+ if (ret2 > 0)
+ fSuccess = false;
+ }
+ db.CloseCursor();
+ }
+ if (fSuccess) {
+ db.Close();
+ env->CloseDb(strFile);
+ if (pdbCopy->close(0))
+ fSuccess = false;
+ } else {
+ pdbCopy->close(0);
+ }
+ }
+ if (fSuccess) {
+ Db dbA(env->dbenv.get(), 0);
+ if (dbA.remove(strFile.c_str(), nullptr, 0))
+ fSuccess = false;
+ Db dbB(env->dbenv.get(), 0);
+ if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0))
+ fSuccess = false;
+ }
+ if (!fSuccess)
+ LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
+ return fSuccess;
+ }
+ }
+ UninterruptibleSleep(std::chrono::milliseconds{100});
+ }
+}
+
+
+void BerkeleyEnvironment::Flush(bool fShutdown)
+{
+ int64_t nStart = GetTimeMillis();
+ // Flush log data to the actual data file on all files that are not in use
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
+ if (!fDbEnvInit)
+ return;
+ {
+ LOCK(cs_db);
+ std::map<std::string, int>::iterator mi = mapFileUseCount.begin();
+ while (mi != mapFileUseCount.end()) {
+ std::string strFile = (*mi).first;
+ int nRefCount = (*mi).second;
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
+ if (nRefCount == 0) {
+ // Move log data to the dat file
+ CloseDb(strFile);
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
+ dbenv->txn_checkpoint(0, 0, 0);
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
+ if (!fMockDb)
+ dbenv->lsn_reset(strFile.c_str(), 0);
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s closed\n", strFile);
+ mapFileUseCount.erase(mi++);
+ } else
+ mi++;
+ }
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart);
+ if (fShutdown) {
+ char** listp;
+ if (mapFileUseCount.empty()) {
+ dbenv->log_archive(&listp, DB_ARCH_REMOVE);
+ Close();
+ if (!fMockDb) {
+ fs::remove_all(fs::path(strPath) / "database");
+ }
+ }
+ }
+ }
+}
+
+bool BerkeleyDatabase::PeriodicFlush()
+{
+ // There's nothing to do for dummy databases. Return true.
+ if (IsDummy()) return true;
+
+ // Don't flush if we can't acquire the lock.
+ TRY_LOCK(cs_db, lockDb);
+ if (!lockDb) return false;
+
+ // Don't flush if any databases are in use
+ for (const auto& use_count : env->mapFileUseCount) {
+ if (use_count.second > 0) return false;
+ }
+
+ // Don't flush if there haven't been any batch writes for this database.
+ auto it = env->mapFileUseCount.find(strFile);
+ if (it == env->mapFileUseCount.end()) return false;
+
+ LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
+ int64_t nStart = GetTimeMillis();
+
+ // Flush wallet file so it's self contained
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
+ env->mapFileUseCount.erase(it);
+
+ LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
+
+ return true;
+}
+
+bool BerkeleyDatabase::Backup(const std::string& strDest) const
+{
+ if (IsDummy()) {
+ return false;
+ }
+ while (true)
+ {
+ {
+ LOCK(cs_db);
+ if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
+ {
+ // Flush log data to the dat file
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
+ env->mapFileUseCount.erase(strFile);
+
+ // Copy wallet file
+ fs::path pathSrc = env->Directory() / strFile;
+ fs::path pathDest(strDest);
+ if (fs::is_directory(pathDest))
+ pathDest /= strFile;
+
+ try {
+ if (fs::equivalent(pathSrc, pathDest)) {
+ LogPrintf("cannot backup to wallet source file %s\n", pathDest.string());
+ return false;
+ }
+
+ fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
+ LogPrintf("copied %s to %s\n", strFile, pathDest.string());
+ return true;
+ } catch (const fs::filesystem_error& e) {
+ LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), fsbridge::get_filesystem_error_message(e));
+ return false;
+ }
+ }
+ }
+ UninterruptibleSleep(std::chrono::milliseconds{100});
+ }
+}
+
+void BerkeleyDatabase::Flush(bool shutdown)
+{
+ if (!IsDummy()) {
+ env->Flush(shutdown);
+ if (shutdown) {
+ LOCK(cs_db);
+ g_dbenvs.erase(env->Directory().string());
+ env = nullptr;
+ } else {
+ // TODO: To avoid g_dbenvs.erase erasing the environment prematurely after the
+ // first database shutdown when multiple databases are open in the same
+ // environment, should replace raw database `env` pointers with shared or weak
+ // pointers, or else separate the database and environment shutdowns so
+ // environments can be shut down after databases.
+ env->m_fileids.erase(strFile);
+ }
+ }
+}
+
+void BerkeleyDatabase::ReloadDbEnv()
+{
+ if (!IsDummy()) {
+ env->ReloadDbEnv();
+ }
+}
+
+bool BerkeleyBatch::StartCursor()
+{
+ assert(!m_cursor);
+ if (!pdb)
+ return false;
+ int ret = pdb->cursor(nullptr, &m_cursor, 0);
+ return ret == 0;
+}
+
+bool BerkeleyBatch::ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete)
+{
+ complete = false;
+ if (m_cursor == nullptr) return false;
+ // Read at cursor
+ SafeDbt datKey;
+ SafeDbt datValue;
+ int ret = m_cursor->get(datKey, datValue, DB_NEXT);
+ if (ret == DB_NOTFOUND) {
+ complete = true;
+ }
+ if (ret != 0)
+ return false;
+ else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
+ return false;
+
+ // Convert to streams
+ ssKey.SetType(SER_DISK);
+ ssKey.clear();
+ ssKey.write((char*)datKey.get_data(), datKey.get_size());
+ ssValue.SetType(SER_DISK);
+ ssValue.clear();
+ ssValue.write((char*)datValue.get_data(), datValue.get_size());
+ return true;
+}
+
+void BerkeleyBatch::CloseCursor()
+{
+ if (!m_cursor) return;
+ m_cursor->close();
+ m_cursor = nullptr;
+}
+
+bool BerkeleyBatch::TxnBegin()
+{
+ if (!pdb || activeTxn)
+ return false;
+ DbTxn* ptxn = env->TxnBegin();
+ if (!ptxn)
+ return false;
+ activeTxn = ptxn;
+ return true;
+}
+
+bool BerkeleyBatch::TxnCommit()
+{
+ if (!pdb || !activeTxn)
+ return false;
+ int ret = activeTxn->commit(0);
+ activeTxn = nullptr;
+ return (ret == 0);
+}
+
+bool BerkeleyBatch::TxnAbort()
+{
+ if (!pdb || !activeTxn)
+ return false;
+ int ret = activeTxn->abort();
+ activeTxn = nullptr;
+ return (ret == 0);
+}
+
+std::string BerkeleyDatabaseVersion()
+{
+ return DbEnv::version(nullptr, nullptr, nullptr);
+}
+
+bool BerkeleyBatch::ReadKey(CDataStream&& key, CDataStream& value)
+{
+ if (!pdb)
+ return false;
+
+ SafeDbt datKey(key.data(), key.size());
+
+ SafeDbt datValue;
+ int ret = pdb->get(activeTxn, datKey, datValue, 0);
+ if (ret == 0 && datValue.get_data() != nullptr) {
+ value.write((char*)datValue.get_data(), datValue.get_size());
+ return true;
+ }
+ return false;
+}
+
+bool BerkeleyBatch::WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite)
+{
+ if (!pdb)
+ return true;
+ if (fReadOnly)
+ assert(!"Write called on database in read-only mode");
+
+ SafeDbt datKey(key.data(), key.size());
+
+ SafeDbt datValue(value.data(), value.size());
+
+ int ret = pdb->put(activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
+ return (ret == 0);
+}
+
+bool BerkeleyBatch::EraseKey(CDataStream&& key)
+{
+ if (!pdb)
+ return false;
+ if (fReadOnly)
+ assert(!"Erase called on database in read-only mode");
+
+ SafeDbt datKey(key.data(), key.size());
+
+ int ret = pdb->del(activeTxn, datKey, 0);
+ return (ret == 0 || ret == DB_NOTFOUND);
+}
+
+bool BerkeleyBatch::HasKey(CDataStream&& key)
+{
+ if (!pdb)
+ return false;
+
+ SafeDbt datKey(key.data(), key.size());
+
+ int ret = pdb->exists(activeTxn, datKey, 0);
+ return ret == 0;
+}
+
+std::unique_ptr<BerkeleyBatch> BerkeleyDatabase::MakeBatch(const char* mode, bool flush_on_close)
+{
+ return MakeUnique<BerkeleyBatch>(*this, mode, flush_on_close);
+}
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
new file mode 100644
index 0000000000..e54776fc0d
--- /dev/null
+++ b/src/wallet/bdb.h
@@ -0,0 +1,237 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2020 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_BDB_H
+#define BITCOIN_WALLET_BDB_H
+
+#include <clientversion.h>
+#include <fs.h>
+#include <serialize.h>
+#include <streams.h>
+#include <util/system.h>
+#include <wallet/db.h>
+
+#include <atomic>
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsuggest-override"
+#endif
+#include <db_cxx.h>
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+struct bilingual_str;
+
+static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
+static const bool DEFAULT_WALLET_PRIVDB = true;
+
+struct WalletDatabaseFileId {
+ u_int8_t value[DB_FILE_ID_LEN];
+ bool operator==(const WalletDatabaseFileId& rhs) const;
+};
+
+class BerkeleyDatabase;
+
+class BerkeleyEnvironment
+{
+private:
+ bool fDbEnvInit;
+ bool fMockDb;
+ // Don't change into fs::path, as that can result in
+ // shutdown problems/crashes caused by a static initialized internal pointer.
+ std::string strPath;
+
+public:
+ std::unique_ptr<DbEnv> dbenv;
+ std::map<std::string, int> mapFileUseCount;
+ std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases;
+ std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
+ std::condition_variable_any m_db_in_use;
+
+ BerkeleyEnvironment(const fs::path& env_directory);
+ BerkeleyEnvironment();
+ ~BerkeleyEnvironment();
+ void Reset();
+
+ bool IsMock() const { return fMockDb; }
+ bool IsInitialized() const { return fDbEnvInit; }
+ bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
+ fs::path Directory() const { return strPath; }
+
+ bool Verify(const std::string& strFile);
+
+ bool Open(bool retry);
+ void Close();
+ void Flush(bool fShutdown);
+ void CheckpointLSN(const std::string& strFile);
+
+ void CloseDb(const std::string& strFile);
+ void ReloadDbEnv();
+
+ DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
+ {
+ DbTxn* ptxn = nullptr;
+ int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
+ if (!ptxn || ret != 0)
+ return nullptr;
+ return ptxn;
+ }
+};
+
+/** Get BerkeleyEnvironment and database filename given a wallet path. */
+std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
+
+/** Return whether a BDB wallet database is currently loaded. */
+bool IsBDBWalletLoaded(const fs::path& wallet_path);
+
+class BerkeleyBatch;
+
+/** An instance of this class represents one database.
+ * For BerkeleyDB this is just a (env, strFile) tuple.
+ **/
+class BerkeleyDatabase
+{
+ friend class BerkeleyBatch;
+public:
+ /** Create dummy DB handle */
+ BerkeleyDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr)
+ {
+ }
+
+ /** Create DB handle to real database */
+ BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename) :
+ nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(std::move(env)), strFile(std::move(filename))
+ {
+ auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this));
+ assert(inserted.second);
+ }
+
+ ~BerkeleyDatabase() {
+ if (env) {
+ size_t erased = env->m_databases.erase(strFile);
+ assert(erased == 1);
+ }
+ }
+
+ /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
+ */
+ bool Rewrite(const char* pszSkip=nullptr);
+
+ /** Back up the entire database to a file.
+ */
+ bool Backup(const std::string& strDest) const;
+
+ /** Make sure all changes are flushed to disk.
+ */
+ void Flush(bool shutdown);
+ /* flush the wallet passively (TRY_LOCK)
+ ideal to be called periodically */
+ bool PeriodicFlush();
+
+ void IncrementUpdateCounter();
+
+ void ReloadDbEnv();
+
+ std::atomic<unsigned int> nUpdateCounter;
+ unsigned int nLastSeen;
+ unsigned int nLastFlushed;
+ int64_t nLastWalletUpdate;
+
+ /** Verifies the environment and database file */
+ bool Verify(bilingual_str& error);
+
+ /**
+ * Pointer to shared database environment.
+ *
+ * Normally there is only one BerkeleyDatabase object per
+ * BerkeleyEnvivonment, but in the special, backwards compatible case where
+ * multiple wallet BDB data files are loaded from the same directory, this
+ * will point to a shared instance that gets freed when the last data file
+ * is closed.
+ */
+ std::shared_ptr<BerkeleyEnvironment> env;
+
+ /** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
+ std::unique_ptr<Db> m_db;
+
+ /** Make a BerkeleyBatch connected to this database */
+ std::unique_ptr<BerkeleyBatch> MakeBatch(const char* mode, bool flush_on_close);
+
+private:
+ std::string strFile;
+
+ /** Return whether this database handle is a dummy for testing.
+ * Only to be used at a low level, application should ideally not care
+ * about this.
+ */
+ bool IsDummy() const { return env == nullptr; }
+};
+
+/** RAII class that provides access to a Berkeley database */
+class BerkeleyBatch : public DatabaseBatch
+{
+ /** RAII class that automatically cleanses its data on destruction */
+ class SafeDbt final
+ {
+ Dbt m_dbt;
+
+ public:
+ // construct Dbt with internally-managed data
+ SafeDbt();
+ // construct Dbt with provided data
+ SafeDbt(void* data, size_t size);
+ ~SafeDbt();
+
+ // delegate to Dbt
+ const void* get_data() const;
+ u_int32_t get_size() const;
+
+ // conversion operator to access the underlying Dbt
+ operator Dbt*();
+ };
+
+private:
+ bool ReadKey(CDataStream&& key, CDataStream& value) override;
+ bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true) override;
+ bool EraseKey(CDataStream&& key) override;
+ bool HasKey(CDataStream&& key) override;
+
+protected:
+ Db* pdb;
+ std::string strFile;
+ DbTxn* activeTxn;
+ Dbc* m_cursor;
+ bool fReadOnly;
+ bool fFlushOnClose;
+ BerkeleyEnvironment *env;
+
+public:
+ explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
+ ~BerkeleyBatch() override { Close(); }
+
+ BerkeleyBatch(const BerkeleyBatch&) = delete;
+ BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;
+
+ void Flush() override;
+ void Close() override;
+
+ bool StartCursor() override;
+ bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) override;
+ void CloseCursor() override;
+ bool TxnBegin() override;
+ bool TxnCommit() override;
+ bool TxnAbort() override;
+};
+
+std::string BerkeleyDatabaseVersion();
+
+#endif // BITCOIN_WALLET_BDB_H
diff --git a/src/wallet/coincontrol.cpp b/src/wallet/coincontrol.cpp
index c83e598825..720877ead0 100644
--- a/src/wallet/coincontrol.cpp
+++ b/src/wallet/coincontrol.cpp
@@ -10,6 +10,7 @@ void CCoinControl::SetNull()
{
destChange = CNoDestination();
m_change_type.reset();
+ m_add_inputs = true;
fAllowOtherInputs = false;
fAllowWatchOnly = false;
m_avoid_partial_spends = gArgs.GetBoolArg("-avoidpartialspends", DEFAULT_AVOIDPARTIALSPENDS);
@@ -23,4 +24,3 @@ void CCoinControl::SetNull()
m_min_depth = DEFAULT_MIN_DEPTH;
m_max_depth = DEFAULT_MAX_DEPTH;
}
-
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 2893d0ab3d..c499b0ff25 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -26,6 +26,8 @@ public:
CTxDestination destChange;
//! Override the default change type if set, ignored if destChange is set
Optional<OutputType> m_change_type;
+ //! If false, only selected inputs are used
+ bool m_add_inputs;
//! If false, allows unselected inputs, but requires all selected inputs be used
bool fAllowOtherInputs;
//! Includes watch only addresses which are solvable
diff --git a/src/wallet/context.cpp b/src/wallet/context.cpp
new file mode 100644
index 0000000000..09b2f30467
--- /dev/null
+++ b/src/wallet/context.cpp
@@ -0,0 +1,8 @@
+// Copyright (c) 2020 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/context.h>
+
+WalletContext::WalletContext() {}
+WalletContext::~WalletContext() {}
diff --git a/src/wallet/context.h b/src/wallet/context.h
new file mode 100644
index 0000000000..a83591154f
--- /dev/null
+++ b/src/wallet/context.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2020 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_CONTEXT_H
+#define BITCOIN_WALLET_CONTEXT_H
+
+class ArgsManager;
+namespace interfaces {
+class Chain;
+} // namespace interfaces
+
+//! WalletContext struct containing references to state shared between CWallet
+//! instances, like the reference to the chain interface, and the list of opened
+//! wallets.
+//!
+//! Future shared state can be added here as an alternative to adding global
+//! variables.
+//!
+//! The struct isn't intended to have any member functions. It should just be a
+//! collection of state pointers that doesn't pull in dependencies or implement
+//! behavior.
+struct WalletContext {
+ interfaces::Chain* chain{nullptr};
+ ArgsManager* args{nullptr};
+
+ //! Declare default constructor and destructor that are not inline, so code
+ //! instantiating the WalletContext struct doesn't need to #include class
+ //! definitions for smart pointer and container members.
+ WalletContext();
+ ~WalletContext();
+};
+
+#endif // BITCOIN_WALLET_CONTEXT_H
diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h
index f59c63260e..f2df786e2e 100644
--- a/src/wallet/crypter.h
+++ b/src/wallet/crypter.h
@@ -43,15 +43,9 @@ public:
//! such as the various parameters to scrypt
std::vector<unsigned char> vchOtherDerivationParameters;
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(vchCryptedKey);
- READWRITE(vchSalt);
- READWRITE(nDerivationMethod);
- READWRITE(nDeriveIterations);
- READWRITE(vchOtherDerivationParameters);
+ SERIALIZE_METHODS(CMasterKey, obj)
+ {
+ READWRITE(obj.vchCryptedKey, obj.vchSalt, obj.nDerivationMethod, obj.nDeriveIterations, obj.vchOtherDerivationParameters);
}
CMasterKey()
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 1b2bd83a4c..1eb82a03c7 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -3,57 +3,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <fs.h>
#include <wallet/db.h>
-#include <util/strencodings.h>
-#include <util/translation.h>
+#include <string>
-#include <stdint.h>
-
-#ifndef WIN32
-#include <sys/stat.h>
-#endif
-
-#include <boost/thread.hpp>
-
-namespace {
-
-//! Make sure database has a unique fileid within the environment. If it
-//! doesn't, throw an error. BDB caches do not work properly when more than one
-//! open database has the same fileid (values written to one database may show
-//! up in reads to other databases).
-//!
-//! BerkeleyDB generates unique fileids by default
-//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
-//! so bitcoin should never create different databases with the same fileid, but
-//! this error can be triggered if users manually copy database files.
-void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
-{
- if (env.IsMock()) return;
-
- int ret = db.get_mpf()->get_fileid(fileid.value);
- if (ret != 0) {
- throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
- }
-
- for (const auto& item : env.m_fileids) {
- if (fileid == item.second && &fileid != &item.second) {
- throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
- HexStr(std::begin(item.second.value), std::end(item.second.value)), item.first));
- }
- }
-}
-
-RecursiveMutex cs_db;
-std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to db environment.
-} // namespace
-
-bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
-{
- return memcmp(value, &rhs.value, sizeof(value)) == 0;
-}
-
-static void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename)
+void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename)
{
if (fs::is_regular_file(wallet_path)) {
// Special case for backwards compatibility: if wallet path points to an
@@ -69,18 +24,6 @@ static void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory
}
}
-bool IsWalletLoaded(const fs::path& wallet_path)
-{
- fs::path env_directory;
- std::string database_filename;
- SplitWalletPath(wallet_path, env_directory, database_filename);
- LOCK(cs_db);
- auto env = g_dbenvs.find(env_directory.string());
- if (env == g_dbenvs.end()) return false;
- auto database = env->second.lock();
- return database && database->IsDatabaseLoaded(database_filename);
-}
-
fs::path WalletDataFilePath(const fs::path& wallet_path)
{
fs::path env_directory;
@@ -88,836 +31,3 @@ fs::path WalletDataFilePath(const fs::path& wallet_path)
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.
- * @return A shared pointer to the BerkeleyEnvironment object for the wallet directory, never empty because ~BerkeleyEnvironment
- * erases the weak pointer from the g_dbenvs map.
- * @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the directory path key was not already in the map.
- */
-std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
-{
- fs::path env_directory;
- SplitWalletPath(wallet_path, env_directory, database_filename);
- LOCK(cs_db);
- auto inserted = g_dbenvs.emplace(env_directory.string(), std::weak_ptr<BerkeleyEnvironment>());
- if (inserted.second) {
- auto env = std::make_shared<BerkeleyEnvironment>(env_directory.string());
- inserted.first->second = env;
- return env;
- }
- return inserted.first->second.lock();
-}
-
-//
-// BerkeleyBatch
-//
-
-void BerkeleyEnvironment::Close()
-{
- if (!fDbEnvInit)
- return;
-
- fDbEnvInit = false;
-
- for (auto& db : m_databases) {
- auto count = mapFileUseCount.find(db.first);
- assert(count == mapFileUseCount.end() || count->second == 0);
- BerkeleyDatabase& database = db.second.get();
- if (database.m_db) {
- database.m_db->close(0);
- database.m_db.reset();
- }
- }
-
- 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()
-{
- dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
- fDbEnvInit = false;
- fMockDb = false;
-}
-
-BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(dir_path.string())
-{
- Reset();
-}
-
-BerkeleyEnvironment::~BerkeleyEnvironment()
-{
- LOCK(cs_db);
- g_dbenvs.erase(strPath);
- Close();
-}
-
-bool BerkeleyEnvironment::Open(bool retry)
-{
- if (fDbEnvInit) {
- return true;
- }
-
- fs::path pathIn = strPath;
- TryCreateDirectories(pathIn);
- if (!LockDirectory(pathIn, ".walletlock")) {
- LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
- return false;
- }
-
- fs::path pathLogDir = pathIn / "database";
- TryCreateDirectories(pathLogDir);
- fs::path pathErrorFile = pathIn / "db.log";
- LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
-
- unsigned int nEnvFlags = 0;
- if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB))
- nEnvFlags |= DB_PRIVATE;
-
- dbenv->set_lg_dir(pathLogDir.string().c_str());
- dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
- dbenv->set_lg_bsize(0x10000);
- dbenv->set_lg_max(1048576);
- dbenv->set_lk_max_locks(40000);
- dbenv->set_lk_max_objects(40000);
- dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a")); /// debug
- dbenv->set_flags(DB_AUTO_COMMIT, 1);
- dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
- dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
- int ret = dbenv->open(strPath.c_str(),
- DB_CREATE |
- DB_INIT_LOCK |
- DB_INIT_LOG |
- DB_INIT_MPOOL |
- DB_INIT_TXN |
- DB_THREAD |
- DB_RECOVER |
- nEnvFlags,
- S_IRUSR | S_IWUSR);
- if (ret != 0) {
- LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
- int ret2 = dbenv->close(0);
- if (ret2 != 0) {
- LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
- }
- Reset();
- if (retry) {
- // try moving the database env out of the way
- fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime());
- try {
- fs::rename(pathLogDir, pathDatabaseBak);
- LogPrintf("Moved old %s to %s. Retrying.\n", pathLogDir.string(), pathDatabaseBak.string());
- } catch (const fs::filesystem_error&) {
- // failure is ok (well, not really, but it's not worse than what we started with)
- }
- // try opening it again one more time
- if (!Open(false /* retry */)) {
- // if it still fails, it probably means we can't even create the database env
- return false;
- }
- } else {
- return false;
- }
- }
-
- fDbEnvInit = true;
- fMockDb = false;
- return true;
-}
-
-//! Construct an in-memory mock Berkeley environment for testing
-BerkeleyEnvironment::BerkeleyEnvironment()
-{
- Reset();
-
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::MakeMock\n");
-
- dbenv->set_cachesize(1, 0, 1);
- dbenv->set_lg_bsize(10485760 * 4);
- dbenv->set_lg_max(10485760);
- dbenv->set_lk_max_locks(10000);
- dbenv->set_lk_max_objects(10000);
- dbenv->set_flags(DB_AUTO_COMMIT, 1);
- dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
- int ret = dbenv->open(nullptr,
- DB_CREATE |
- DB_INIT_LOCK |
- DB_INIT_LOG |
- DB_INIT_MPOOL |
- DB_INIT_TXN |
- DB_THREAD |
- DB_PRIVATE,
- S_IRUSR | S_IWUSR);
- if (ret > 0) {
- throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
- }
-
- fDbEnvInit = true;
- fMockDb = true;
-}
-
-BerkeleyEnvironment::VerifyResult BerkeleyEnvironment::Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename)
-{
- LOCK(cs_db);
- assert(mapFileUseCount.count(strFile) == 0);
-
- Db db(dbenv.get(), 0);
- int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
- if (result == 0)
- return VerifyResult::VERIFY_OK;
- else if (recoverFunc == nullptr)
- return VerifyResult::RECOVER_FAIL;
-
- // Try to recover:
- bool fRecovered = (*recoverFunc)(fs::path(strPath) / strFile, out_backup_filename);
- return (fRecovered ? VerifyResult::RECOVER_OK : VerifyResult::RECOVER_FAIL);
-}
-
-BerkeleyBatch::SafeDbt::SafeDbt()
-{
- m_dbt.set_flags(DB_DBT_MALLOC);
-}
-
-BerkeleyBatch::SafeDbt::SafeDbt(void* data, size_t size)
- : m_dbt(data, size)
-{
-}
-
-BerkeleyBatch::SafeDbt::~SafeDbt()
-{
- if (m_dbt.get_data() != nullptr) {
- // Clear memory, e.g. in case it was a private key
- memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
- // under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
- // freed by the caller.
- // https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
- if (m_dbt.get_flags() & DB_DBT_MALLOC) {
- free(m_dbt.get_data());
- }
- }
-}
-
-const void* BerkeleyBatch::SafeDbt::get_data() const
-{
- return m_dbt.get_data();
-}
-
-u_int32_t BerkeleyBatch::SafeDbt::get_size() const
-{
- return m_dbt.get_size();
-}
-
-BerkeleyBatch::SafeDbt::operator Dbt*()
-{
- return &m_dbt;
-}
-
-bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
-{
- std::string filename;
- std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
-
- // Recovery procedure:
- // move wallet file to walletfilename.timestamp.bak
- // Call Salvage with fAggressive=true to
- // get as much data as possible.
- // Rewrite salvaged data to fresh wallet file
- // Set -rescan so any missing transactions will be
- // found.
- int64_t now = GetTime();
- newFilename = strprintf("%s.%d.bak", filename, now);
-
- int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
- newFilename.c_str(), DB_AUTO_COMMIT);
- if (result == 0)
- LogPrintf("Renamed %s to %s\n", filename, newFilename);
- else
- {
- LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
- return false;
- }
-
- std::vector<BerkeleyEnvironment::KeyValPair> salvagedData;
- bool fSuccess = env->Salvage(newFilename, true, salvagedData);
- if (salvagedData.empty())
- {
- LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
- return false;
- }
- LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
-
- std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
- int ret = pdbCopy->open(nullptr, // Txn pointer
- filename.c_str(), // Filename
- "main", // Logical db name
- DB_BTREE, // Database type
- DB_CREATE, // Flags
- 0);
- if (ret > 0) {
- LogPrintf("Cannot create database file %s\n", filename);
- pdbCopy->close(0);
- return false;
- }
-
- DbTxn* ptxn = env->TxnBegin();
- for (BerkeleyEnvironment::KeyValPair& row : salvagedData)
- {
- if (recoverKVcallback)
- {
- CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
- CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
- if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue))
- continue;
- }
- Dbt datKey(&row.first[0], row.first.size());
- Dbt datValue(&row.second[0], row.second.size());
- int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
- if (ret2 > 0)
- fSuccess = false;
- }
- ptxn->commit(0);
- pdbCopy->close(0);
-
- return fSuccess;
-}
-
-bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr)
-{
- std::string walletFile;
- std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, walletFile);
- fs::path walletDir = env->Directory();
-
- LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
- LogPrintf("Using wallet %s\n", file_path.string());
-
- if (!env->Open(true /* retry */)) {
- errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
- return false;
- }
-
- return true;
-}
-
-bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::vector<bilingual_str>& warnings, bilingual_str& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc)
-{
- std::string walletFile;
- std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, walletFile);
- fs::path walletDir = env->Directory();
-
- if (fs::exists(walletDir / walletFile))
- {
- std::string backup_filename;
- BerkeleyEnvironment::VerifyResult r = env->Verify(walletFile, recoverFunc, backup_filename);
- if (r == BerkeleyEnvironment::VerifyResult::RECOVER_OK)
- {
- warnings.push_back(strprintf(_("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."),
- walletFile, backup_filename, walletDir));
- }
- if (r == BerkeleyEnvironment::VerifyResult::RECOVER_FAIL)
- {
- errorStr = strprintf(_("%s corrupt, salvage failed"), walletFile);
- return false;
- }
- }
- // also return true if files does not exists
- return true;
-}
-
-/* End of headers, beginning of key/value data */
-static const char *HEADER_END = "HEADER=END";
-/* End of key/value data */
-static const char *DATA_END = "DATA=END";
-
-bool BerkeleyEnvironment::Salvage(const std::string& strFile, bool fAggressive, std::vector<BerkeleyEnvironment::KeyValPair>& vResult)
-{
- LOCK(cs_db);
- assert(mapFileUseCount.count(strFile) == 0);
-
- u_int32_t flags = DB_SALVAGE;
- if (fAggressive)
- flags |= DB_AGGRESSIVE;
-
- std::stringstream strDump;
-
- Db db(dbenv.get(), 0);
- int result = db.verify(strFile.c_str(), nullptr, &strDump, flags);
- if (result == DB_VERIFY_BAD) {
- LogPrintf("BerkeleyEnvironment::Salvage: Database salvage found errors, all data may not be recoverable.\n");
- if (!fAggressive) {
- LogPrintf("BerkeleyEnvironment::Salvage: Rerun with aggressive mode to ignore errors and continue.\n");
- return false;
- }
- }
- if (result != 0 && result != DB_VERIFY_BAD) {
- LogPrintf("BerkeleyEnvironment::Salvage: Database salvage failed with result %d.\n", result);
- return false;
- }
-
- // Format of bdb dump is ascii lines:
- // header lines...
- // HEADER=END
- // hexadecimal key
- // hexadecimal value
- // ... repeated
- // DATA=END
-
- std::string strLine;
- while (!strDump.eof() && strLine != HEADER_END)
- getline(strDump, strLine); // Skip past header
-
- std::string keyHex, valueHex;
- while (!strDump.eof() && keyHex != DATA_END) {
- getline(strDump, keyHex);
- if (keyHex != DATA_END) {
- if (strDump.eof())
- break;
- getline(strDump, valueHex);
- if (valueHex == DATA_END) {
- LogPrintf("BerkeleyEnvironment::Salvage: WARNING: Number of keys in data does not match number of values.\n");
- break;
- }
- vResult.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex)));
- }
- }
-
- if (keyHex != DATA_END) {
- LogPrintf("BerkeleyEnvironment::Salvage: WARNING: Unexpected end of file while reading salvage output.\n");
- return false;
- }
-
- return (result == 0);
-}
-
-
-void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile)
-{
- dbenv->txn_checkpoint(0, 0, 0);
- if (fMockDb)
- return;
- dbenv->lsn_reset(strFile.c_str(), 0);
-}
-
-
-BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr)
-{
- fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
- fFlushOnClose = fFlushOnCloseIn;
- env = database.env.get();
- if (database.IsDummy()) {
- return;
- }
- const std::string &strFilename = database.strFile;
-
- bool fCreate = strchr(pszMode, 'c') != nullptr;
- unsigned int nFlags = DB_THREAD;
- if (fCreate)
- nFlags |= DB_CREATE;
-
- {
- LOCK(cs_db);
- if (!env->Open(false /* retry */))
- throw std::runtime_error("BerkeleyBatch: Failed to open database environment.");
-
- pdb = database.m_db.get();
- if (pdb == nullptr) {
- int ret;
- std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0);
-
- bool fMockDb = env->IsMock();
- if (fMockDb) {
- DbMpoolFile* mpf = pdb_temp->get_mpf();
- ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
- if (ret != 0) {
- throw std::runtime_error(strprintf("BerkeleyBatch: Failed to configure for no temp file backing for database %s", strFilename));
- }
- }
-
- ret = pdb_temp->open(nullptr, // Txn pointer
- fMockDb ? nullptr : strFilename.c_str(), // Filename
- fMockDb ? strFilename.c_str() : "main", // Logical db name
- DB_BTREE, // Database type
- nFlags, // Flags
- 0);
-
- if (ret != 0) {
- throw std::runtime_error(strprintf("BerkeleyBatch: Error %d, can't open database %s", ret, strFilename));
- }
-
- // Call CheckUniqueFileid on the containing BDB environment to
- // avoid BDB data consistency bugs that happen when different data
- // files in the same environment have the same fileid.
- //
- // Also call CheckUniqueFileid on all the other g_dbenvs to prevent
- // bitcoin from opening the same data file through another
- // environment when the file is referenced through equivalent but
- // not obviously identical symlinked or hard linked or bind mounted
- // paths. In the future a more relaxed check for equal inode and
- // device ids could be done instead, which would allow opening
- // different backup copies of a wallet at the same time. Maybe even
- // more ideally, an exclusive lock for accessing the database could
- // be implemented, so no equality checks are needed at all. (Newer
- // versions of BDB have an set_lk_exclusive method for this
- // purpose, but the older version we use does not.)
- for (const auto& env : g_dbenvs) {
- CheckUniqueFileid(*env.second.lock().get(), strFilename, *pdb_temp, this->env->m_fileids[strFilename]);
- }
-
- pdb = pdb_temp.release();
- database.m_db.reset(pdb);
-
- if (fCreate && !Exists(std::string("version"))) {
- bool fTmp = fReadOnly;
- fReadOnly = false;
- Write(std::string("version"), CLIENT_VERSION);
- fReadOnly = fTmp;
- }
- }
- ++env->mapFileUseCount[strFilename];
- strFile = strFilename;
- }
-}
-
-void BerkeleyBatch::Flush()
-{
- if (activeTxn)
- return;
-
- // Flush database activity from memory pool to disk log
- unsigned int nMinutes = 0;
- if (fReadOnly)
- nMinutes = 1;
-
- 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()
-{
- ++nUpdateCounter;
-}
-
-void BerkeleyBatch::Close()
-{
- if (!pdb)
- return;
- if (activeTxn)
- activeTxn->abort();
- activeTxn = nullptr;
- pdb = nullptr;
-
- if (fFlushOnClose)
- Flush();
-
- {
- LOCK(cs_db);
- --env->mapFileUseCount[strFile];
- }
- env->m_db_in_use.notify_all();
-}
-
-void BerkeleyEnvironment::CloseDb(const std::string& strFile)
-{
- {
- LOCK(cs_db);
- auto it = m_databases.find(strFile);
- assert(it != m_databases.end());
- BerkeleyDatabase& database = it->second.get();
- if (database.m_db) {
- // Close the database handle
- database.m_db->close(0);
- database.m_db.reset();
- }
- }
-}
-
-void BerkeleyEnvironment::ReloadDbEnv()
-{
- // Make sure that no Db's are in use
- AssertLockNotHeld(cs_db);
- std::unique_lock<RecursiveMutex> lock(cs_db);
- m_db_in_use.wait(lock, [this](){
- for (auto& count : mapFileUseCount) {
- if (count.second > 0) return false;
- }
- return true;
- });
-
- std::vector<std::string> filenames;
- for (auto it : m_databases) {
- filenames.push_back(it.first);
- }
- // Close the individual Db's
- for (const std::string& filename : filenames) {
- CloseDb(filename);
- }
- // Reset the environment
- Flush(true); // This will flush and close the environment
- Reset();
- Open(true);
-}
-
-bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip)
-{
- if (database.IsDummy()) {
- return true;
- }
- BerkeleyEnvironment *env = database.env.get();
- const std::string& strFile = database.strFile;
- while (true) {
- {
- LOCK(cs_db);
- if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) {
- // Flush log data to the dat file
- env->CloseDb(strFile);
- env->CheckpointLSN(strFile);
- env->mapFileUseCount.erase(strFile);
-
- bool fSuccess = true;
- LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
- std::string strFileRes = strFile + ".rewrite";
- { // surround usage of db with extra {}
- BerkeleyBatch db(database, "r");
- std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
-
- int ret = pdbCopy->open(nullptr, // Txn pointer
- strFileRes.c_str(), // Filename
- "main", // Logical db name
- DB_BTREE, // Database type
- DB_CREATE, // Flags
- 0);
- if (ret > 0) {
- LogPrintf("BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
- fSuccess = false;
- }
-
- Dbc* pcursor = db.GetCursor();
- if (pcursor)
- while (fSuccess) {
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- int ret1 = db.ReadAtCursor(pcursor, ssKey, ssValue);
- if (ret1 == DB_NOTFOUND) {
- pcursor->close();
- break;
- } else if (ret1 != 0) {
- pcursor->close();
- fSuccess = false;
- break;
- }
- if (pszSkip &&
- strncmp(ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
- continue;
- if (strncmp(ssKey.data(), "\x07version", 8) == 0) {
- // Update version:
- ssValue.clear();
- ssValue << CLIENT_VERSION;
- }
- Dbt datKey(ssKey.data(), ssKey.size());
- Dbt datValue(ssValue.data(), ssValue.size());
- int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE);
- if (ret2 > 0)
- fSuccess = false;
- }
- if (fSuccess) {
- db.Close();
- env->CloseDb(strFile);
- if (pdbCopy->close(0))
- fSuccess = false;
- } else {
- pdbCopy->close(0);
- }
- }
- if (fSuccess) {
- Db dbA(env->dbenv.get(), 0);
- if (dbA.remove(strFile.c_str(), nullptr, 0))
- fSuccess = false;
- Db dbB(env->dbenv.get(), 0);
- if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0))
- fSuccess = false;
- }
- if (!fSuccess)
- LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
- return fSuccess;
- }
- }
- UninterruptibleSleep(std::chrono::milliseconds{100});
- }
-}
-
-
-void BerkeleyEnvironment::Flush(bool fShutdown)
-{
- int64_t nStart = GetTimeMillis();
- // Flush log data to the actual data file on all files that are not in use
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
- if (!fDbEnvInit)
- return;
- {
- LOCK(cs_db);
- std::map<std::string, int>::iterator mi = mapFileUseCount.begin();
- while (mi != mapFileUseCount.end()) {
- std::string strFile = (*mi).first;
- int nRefCount = (*mi).second;
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
- if (nRefCount == 0) {
- // Move log data to the dat file
- CloseDb(strFile);
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
- dbenv->txn_checkpoint(0, 0, 0);
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
- if (!fMockDb)
- dbenv->lsn_reset(strFile.c_str(), 0);
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s closed\n", strFile);
- mapFileUseCount.erase(mi++);
- } else
- mi++;
- }
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart);
- if (fShutdown) {
- char** listp;
- if (mapFileUseCount.empty()) {
- dbenv->log_archive(&listp, DB_ARCH_REMOVE);
- Close();
- if (!fMockDb) {
- fs::remove_all(fs::path(strPath) / "database");
- }
- }
- }
- }
-}
-
-bool BerkeleyBatch::PeriodicFlush(BerkeleyDatabase& database)
-{
- if (database.IsDummy()) {
- return true;
- }
- bool ret = false;
- BerkeleyEnvironment *env = database.env.get();
- const std::string& strFile = database.strFile;
- TRY_LOCK(cs_db, lockDb);
- if (lockDb)
- {
- // Don't do this if any databases are in use
- int nRefCount = 0;
- std::map<std::string, int>::iterator mit = env->mapFileUseCount.begin();
- while (mit != env->mapFileUseCount.end())
- {
- nRefCount += (*mit).second;
- mit++;
- }
-
- if (nRefCount == 0)
- {
- boost::this_thread::interruption_point();
- std::map<std::string, int>::iterator mi = env->mapFileUseCount.find(strFile);
- if (mi != env->mapFileUseCount.end())
- {
- LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
- int64_t nStart = GetTimeMillis();
-
- // Flush wallet file so it's self contained
- env->CloseDb(strFile);
- env->CheckpointLSN(strFile);
-
- env->mapFileUseCount.erase(mi++);
- LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
- ret = true;
- }
- }
- }
-
- return ret;
-}
-
-bool BerkeleyDatabase::Rewrite(const char* pszSkip)
-{
- return BerkeleyBatch::Rewrite(*this, pszSkip);
-}
-
-bool BerkeleyDatabase::Backup(const std::string& strDest) const
-{
- if (IsDummy()) {
- return false;
- }
- while (true)
- {
- {
- LOCK(cs_db);
- if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
- {
- // Flush log data to the dat file
- env->CloseDb(strFile);
- env->CheckpointLSN(strFile);
- env->mapFileUseCount.erase(strFile);
-
- // Copy wallet file
- fs::path pathSrc = env->Directory() / strFile;
- fs::path pathDest(strDest);
- if (fs::is_directory(pathDest))
- pathDest /= strFile;
-
- try {
- if (fs::equivalent(pathSrc, pathDest)) {
- LogPrintf("cannot backup to wallet source file %s\n", pathDest.string());
- return false;
- }
-
- fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
- LogPrintf("copied %s to %s\n", strFile, pathDest.string());
- return true;
- } catch (const fs::filesystem_error& e) {
- LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), fsbridge::get_filesystem_error_message(e));
- return false;
- }
- }
- }
- UninterruptibleSleep(std::chrono::milliseconds{100});
- }
-}
-
-void BerkeleyDatabase::Flush(bool shutdown)
-{
- if (!IsDummy()) {
- env->Flush(shutdown);
- if (shutdown) {
- LOCK(cs_db);
- g_dbenvs.erase(env->Directory().string());
- env = nullptr;
- } else {
- // TODO: To avoid g_dbenvs.erase erasing the environment prematurely after the
- // first database shutdown when multiple databases are open in the same
- // environment, should replace raw database `env` pointers with shared or weak
- // pointers, or else separate the database and environment shutdowns so
- // environments can be shut down after databases.
- env->m_fileids.erase(strFile);
- }
- }
-}
-
-void BerkeleyDatabase::ReloadDbEnv()
-{
- if (!IsDummy()) {
- env->ReloadDbEnv();
- }
-}
-
-std::string BerkeleyDatabaseVersion()
-{
- return DbEnv::version(nullptr, nullptr, nullptr);
-}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 37f96a1a96..76668f8dc2 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -8,407 +8,90 @@
#include <clientversion.h>
#include <fs.h>
-#include <serialize.h>
#include <streams.h>
-#include <util/system.h>
-#include <atomic>
-#include <map>
-#include <memory>
#include <string>
-#include <unordered_map>
-#include <vector>
-
-#if defined(__GNUC__) && !defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wsuggest-override"
-#endif
-#include <db_cxx.h>
-#if defined(__GNUC__) && !defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-
-struct bilingual_str;
-
-static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
-static const bool DEFAULT_WALLET_PRIVDB = true;
-
-struct WalletDatabaseFileId {
- u_int8_t value[DB_FILE_ID_LEN];
- bool operator==(const WalletDatabaseFileId& rhs) const;
-};
-
-class BerkeleyDatabase;
-
-class BerkeleyEnvironment
-{
-private:
- bool fDbEnvInit;
- bool fMockDb;
- // Don't change into fs::path, as that can result in
- // shutdown problems/crashes caused by a static initialized internal pointer.
- std::string strPath;
-
-public:
- std::unique_ptr<DbEnv> dbenv;
- std::map<std::string, int> mapFileUseCount;
- std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases;
- std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
- std::condition_variable_any m_db_in_use;
-
- BerkeleyEnvironment(const fs::path& env_directory);
- BerkeleyEnvironment();
- ~BerkeleyEnvironment();
- void Reset();
-
- bool IsMock() const { return fMockDb; }
- bool IsInitialized() const { return fDbEnvInit; }
- bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
- fs::path Directory() const { return strPath; }
-
- /**
- * Verify that database file strFile is OK. If it is not,
- * call the callback to try to recover.
- * This must be called BEFORE strFile is opened.
- * Returns true if strFile is OK.
- */
- enum class VerifyResult { VERIFY_OK,
- RECOVER_OK,
- RECOVER_FAIL };
- typedef bool (*recoverFunc_type)(const fs::path& file_path, std::string& out_backup_filename);
- VerifyResult Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename);
- /**
- * Salvage data from a file that Verify says is bad.
- * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation).
- * Appends binary key/value pairs to vResult, returns true if successful.
- * NOTE: reads the entire database into memory, so cannot be used
- * for huge databases.
- */
- typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
- bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
-
- bool Open(bool retry);
- void Close();
- void Flush(bool fShutdown);
- void CheckpointLSN(const std::string& strFile);
-
- void CloseDb(const std::string& strFile);
- void ReloadDbEnv();
-
- DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
- {
- DbTxn* ptxn = nullptr;
- int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
- if (!ptxn || ret != 0)
- return nullptr;
- return ptxn;
- }
-};
-
-/** 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);
+void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename);
-/** Get BerkeleyEnvironment and database filename given a wallet path. */
-std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
-
-/** An instance of this class represents one database.
- * For BerkeleyDB this is just a (env, strFile) tuple.
- **/
-class BerkeleyDatabase
+/** RAII class that provides access to a WalletDatabase */
+class DatabaseBatch
{
- friend class BerkeleyBatch;
-public:
- /** Create dummy DB handle */
- BerkeleyDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr)
- {
- }
-
- /** Create DB handle to real database */
- BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename) :
- nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(std::move(env)), strFile(std::move(filename))
- {
- auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this));
- assert(inserted.second);
- }
-
- ~BerkeleyDatabase() {
- if (env) {
- size_t erased = env->m_databases.erase(strFile);
- assert(erased == 1);
- }
- }
-
- /** Return object for accessing database at specified path. */
- static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path)
- {
- std::string filename;
- return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename));
- }
-
- /** Return object for accessing dummy database with no read/write capabilities. */
- static std::unique_ptr<BerkeleyDatabase> CreateDummy()
- {
- return MakeUnique<BerkeleyDatabase>();
- }
-
- /** Return object for accessing temporary in-memory database. */
- static std::unique_ptr<BerkeleyDatabase> CreateMock()
- {
- return MakeUnique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
- }
-
- /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
- */
- bool Rewrite(const char* pszSkip=nullptr);
-
- /** Back up the entire database to a file.
- */
- bool Backup(const std::string& strDest) const;
-
- /** Make sure all changes are flushed to disk.
- */
- void Flush(bool shutdown);
-
- void IncrementUpdateCounter();
-
- void ReloadDbEnv();
-
- std::atomic<unsigned int> nUpdateCounter;
- unsigned int nLastSeen;
- unsigned int nLastFlushed;
- int64_t nLastWalletUpdate;
-
- /**
- * Pointer to shared database environment.
- *
- * Normally there is only one BerkeleyDatabase object per
- * BerkeleyEnvivonment, but in the special, backwards compatible case where
- * multiple wallet BDB data files are loaded from the same directory, this
- * will point to a shared instance that gets freed when the last data file
- * is closed.
- */
- std::shared_ptr<BerkeleyEnvironment> env;
-
- /** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
- std::unique_ptr<Db> m_db;
-
private:
- std::string strFile;
-
- /** Return whether this database handle is a dummy for testing.
- * Only to be used at a low level, application should ideally not care
- * about this.
- */
- bool IsDummy() const { return env == nullptr; }
-};
-
-/** RAII class that provides access to a Berkeley database */
-class BerkeleyBatch
-{
- /** RAII class that automatically cleanses its data on destruction */
- class SafeDbt final
- {
- Dbt m_dbt;
-
- public:
- // construct Dbt with internally-managed data
- SafeDbt();
- // construct Dbt with provided data
- SafeDbt(void* data, size_t size);
- ~SafeDbt();
-
- // delegate to Dbt
- const void* get_data() const;
- u_int32_t get_size() const;
-
- // conversion operator to access the underlying Dbt
- operator Dbt*();
- };
-
-protected:
- Db* pdb;
- std::string strFile;
- DbTxn* activeTxn;
- bool fReadOnly;
- bool fFlushOnClose;
- BerkeleyEnvironment *env;
+ virtual bool ReadKey(CDataStream&& key, CDataStream& value) = 0;
+ virtual bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite=true) = 0;
+ virtual bool EraseKey(CDataStream&& key) = 0;
+ virtual bool HasKey(CDataStream&& key) = 0;
public:
- explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
- ~BerkeleyBatch() { Close(); }
+ explicit DatabaseBatch() {}
+ virtual ~DatabaseBatch() {}
- BerkeleyBatch(const BerkeleyBatch&) = delete;
- BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;
+ DatabaseBatch(const DatabaseBatch&) = delete;
+ DatabaseBatch& operator=(const DatabaseBatch&) = delete;
- void Flush();
- void Close();
- static bool Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
-
- /* flush the wallet passively (TRY_LOCK)
- ideal to be called periodically */
- static bool PeriodicFlush(BerkeleyDatabase& database);
- /* verifies the database environment */
- static bool VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr);
- /* verifies the database file */
- static bool VerifyDatabaseFile(const fs::path& file_path, std::vector<bilingual_str>& warnings, bilingual_str& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc);
+ virtual void Flush() = 0;
+ virtual void Close() = 0;
template <typename K, typename T>
bool Read(const K& key, T& value)
{
- if (!pdb)
- return false;
-
- // Key
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;
- SafeDbt datKey(ssKey.data(), ssKey.size());
- // Read
- SafeDbt datValue;
- int ret = pdb->get(activeTxn, datKey, datValue, 0);
- bool success = false;
- if (datValue.get_data() != nullptr) {
- // Unserialize value
- try {
- CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
- ssValue >> value;
- success = true;
- } catch (const std::exception&) {
- // In this case success remains 'false'
- }
+ CDataStream ssValue(SER_DISK, CLIENT_VERSION);
+ if (!ReadKey(std::move(ssKey), ssValue)) return false;
+ try {
+ ssValue >> value;
+ return true;
+ } catch (const std::exception&) {
+ return false;
}
- return ret == 0 && success;
}
template <typename K, typename T>
bool Write(const K& key, const T& value, bool fOverwrite = true)
{
- if (!pdb)
- return true;
- if (fReadOnly)
- assert(!"Write called on database in read-only mode");
-
- // Key
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;
- SafeDbt datKey(ssKey.data(), ssKey.size());
- // Value
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
ssValue.reserve(10000);
ssValue << value;
- SafeDbt datValue(ssValue.data(), ssValue.size());
- // Write
- int ret = pdb->put(activeTxn, datKey, datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
- return (ret == 0);
+ return WriteKey(std::move(ssKey), std::move(ssValue), fOverwrite);
}
template <typename K>
bool Erase(const K& key)
{
- if (!pdb)
- return false;
- if (fReadOnly)
- assert(!"Erase called on database in read-only mode");
-
- // Key
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;
- SafeDbt datKey(ssKey.data(), ssKey.size());
- // Erase
- int ret = pdb->del(activeTxn, datKey, 0);
- return (ret == 0 || ret == DB_NOTFOUND);
+ return EraseKey(std::move(ssKey));
}
template <typename K>
bool Exists(const K& key)
{
- if (!pdb)
- return false;
-
- // Key
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;
- SafeDbt datKey(ssKey.data(), ssKey.size());
-
- // Exists
- int ret = pdb->exists(activeTxn, datKey, 0);
- return (ret == 0);
- }
-
- Dbc* GetCursor()
- {
- if (!pdb)
- return nullptr;
- Dbc* pcursor = nullptr;
- int ret = pdb->cursor(nullptr, &pcursor, 0);
- if (ret != 0)
- return nullptr;
- return pcursor;
- }
-
- int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue)
- {
- // Read at cursor
- SafeDbt datKey;
- SafeDbt datValue;
- int ret = pcursor->get(datKey, datValue, DB_NEXT);
- if (ret != 0)
- return ret;
- else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
- return 99999;
-
- // Convert to streams
- ssKey.SetType(SER_DISK);
- ssKey.clear();
- ssKey.write((char*)datKey.get_data(), datKey.get_size());
- ssValue.SetType(SER_DISK);
- ssValue.clear();
- ssValue.write((char*)datValue.get_data(), datValue.get_size());
- return 0;
- }
-
- bool TxnBegin()
- {
- if (!pdb || activeTxn)
- return false;
- DbTxn* ptxn = env->TxnBegin();
- if (!ptxn)
- return false;
- activeTxn = ptxn;
- return true;
- }
- bool TxnCommit()
- {
- if (!pdb || !activeTxn)
- return false;
- int ret = activeTxn->commit(0);
- activeTxn = nullptr;
- return (ret == 0);
+ return HasKey(std::move(ssKey));
}
- bool TxnAbort()
- {
- if (!pdb || !activeTxn)
- return false;
- int ret = activeTxn->abort();
- activeTxn = nullptr;
- return (ret == 0);
- }
-
- bool static Rewrite(BerkeleyDatabase& database, const char* pszSkip = nullptr);
+ virtual bool StartCursor() = 0;
+ virtual bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) = 0;
+ virtual void CloseCursor() = 0;
+ virtual bool TxnBegin() = 0;
+ virtual bool TxnCommit() = 0;
+ virtual bool TxnAbort() = 0;
};
-std::string BerkeleyDatabaseVersion();
-
#endif // BITCOIN_WALLET_DB_H
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 6f973aab1c..781920755c 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -7,8 +7,9 @@
#include <interfaces/chain.h>
#include <net.h>
#include <node/context.h>
+#include <node/ui_interface.h>
#include <outputtype.h>
-#include <ui_interface.h>
+#include <util/check.h>
#include <util/moneystr.h>
#include <util/system.h>
#include <util/translation.h>
@@ -16,9 +17,9 @@
#include <wallet/wallet.h>
#include <walletinitinterface.h>
-class WalletInit : public WalletInitInterface {
+class WalletInit : public WalletInitInterface
+{
public:
-
//! Was the wallet component compiled in.
bool HasWalletSupport() const override {return true;}
@@ -54,7 +55,6 @@ void WalletInit::AddWalletOptions() const
gArgs.AddArg("-paytxfee=<amt>", strprintf("Fee (in %s/kB) to add to transactions you send (default: %s)",
CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-rescan", "Rescan the block chain for missing wallet transactions on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
- gArgs.AddArg("-salvagewallet", "Attempt to recover private keys from a corrupt wallet on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-txconfirmtarget=<n>", strprintf("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)", DEFAULT_TX_CONFIRM_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-wallet=<path>", "Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
@@ -89,16 +89,6 @@ bool WalletInit::ParameterInteraction() const
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
}
- if (gArgs.GetBoolArg("-salvagewallet", false)) {
- if (is_multiwallet) {
- return InitError(strprintf(Untranslated("%s is only allowed with a single wallet file"), "-salvagewallet"));
- }
- // Rewrite just private keys: rescan to find transactions
- if (gArgs.SoftSetBoolArg("-rescan", true)) {
- LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__);
- }
- }
-
bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false);
// -zapwallettxes implies dropping the mempool on startup
if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) {
@@ -123,10 +113,11 @@ bool WalletInit::ParameterInteraction() const
void WalletInit::Construct(NodeContext& node) const
{
- if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
+ ArgsManager& args = *Assert(node.args);
+ if (args.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
LogPrintf("Wallet disabled!\n");
return;
}
- gArgs.SoftSetArg("-wallet", "");
- node.chain_clients.emplace_back(interfaces::MakeWalletClient(*node.chain, gArgs.GetArgs("-wallet")));
+ args.SoftSetArg("-wallet", "");
+ node.chain_clients.emplace_back(interfaces::MakeWalletClient(*node.chain, args, args.GetArgs("-wallet")));
}
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 16f3699d37..c2818a41e7 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -11,6 +11,7 @@
#include <util/system.h>
#include <util/translation.h>
#include <wallet/wallet.h>
+#include <wallet/walletdb.h>
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
{
@@ -37,11 +38,6 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
chain.initMessage(_("Verifying wallet(s)...").translated);
- // 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;
@@ -55,7 +51,7 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
bilingual_str error_string;
std::vector<bilingual_str> warnings;
- bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warnings);
+ bool verify_success = CWallet::Verify(chain, location, error_string, warnings);
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
if (!verify_success) {
chain.initError(error_string);
@@ -87,14 +83,16 @@ bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& walle
}
}
-void StartWallets(CScheduler& scheduler)
+void StartWallets(CScheduler& scheduler, const ArgsManager& args)
{
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
pwallet->postInitProcess();
}
// Schedule periodic wallet flushes and tx rebroadcasts
- scheduler.scheduleEvery(MaybeCompactWalletDB, std::chrono::milliseconds{500});
+ if (args.GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
+ scheduler.scheduleEvery(MaybeCompactWalletDB, std::chrono::milliseconds{500});
+ }
scheduler.scheduleEvery(MaybeResendWalletTxs, std::chrono::milliseconds{1000});
}
diff --git a/src/wallet/load.h b/src/wallet/load.h
index 5a62e29303..ff4f5b4b23 100644
--- a/src/wallet/load.h
+++ b/src/wallet/load.h
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
+class ArgsManager;
class CScheduler;
namespace interfaces {
@@ -16,15 +17,13 @@ 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 (WalletInit::ParameterInteraction() 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);
+void StartWallets(CScheduler& scheduler, const ArgsManager& args);
//! Flush all wallets in preparation for shutdown.
void FlushWallets();
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 7bf3d169c3..3b752ca936 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -92,12 +92,6 @@ static void RescanWallet(CWallet& wallet, const WalletRescanReserver& reserver,
UniValue importprivkey(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"importprivkey",
"\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"
@@ -124,6 +118,10 @@ UniValue importprivkey(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
@@ -195,12 +193,6 @@ UniValue importprivkey(const JSONRPCRequest& request)
UniValue abortrescan(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"abortrescan",
"\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",
@@ -216,6 +208,10 @@ UniValue abortrescan(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
pwallet->AbortRescan();
return true;
@@ -223,12 +219,6 @@ UniValue abortrescan(const JSONRPCRequest& request)
UniValue importaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"importaddress",
"\nAdds an address or script (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"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
@@ -255,6 +245,10 @@ UniValue importaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
EnsureLegacyScriptPubKeyMan(*pwallet, true);
std::string strLabel;
@@ -303,7 +297,7 @@ UniValue importaddress(const JSONRPCRequest& request)
pwallet->ImportScripts(scripts, 0 /* timestamp */);
if (fP2SH) {
- scripts.insert(GetScriptForDestination(ScriptHash(CScriptID(redeem_script))));
+ scripts.insert(GetScriptForDestination(ScriptHash(redeem_script)));
}
pwallet->ImportScriptPubKeys(strLabel, scripts, false /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
@@ -325,12 +319,6 @@ UniValue importaddress(const JSONRPCRequest& request)
UniValue importprunedfunds(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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",
{
@@ -341,6 +329,10 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
RPCExamples{""},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
@@ -383,12 +375,6 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
UniValue removeprunedfunds(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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",
{
@@ -402,6 +388,10 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -422,12 +412,6 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
UniValue importpubkey(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -450,6 +434,10 @@ UniValue importpubkey(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
EnsureLegacyScriptPubKeyMan(*wallet, true);
std::string strLabel;
@@ -509,12 +497,6 @@ UniValue importpubkey(const JSONRPCRequest& request)
UniValue importwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"importwallet",
"\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",
@@ -532,6 +514,10 @@ UniValue importwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
EnsureLegacyScriptPubKeyMan(*wallet, true);
if (pwallet->chain().havePruned()) {
@@ -667,12 +653,6 @@ UniValue importwallet(const JSONRPCRequest& request)
UniValue dumpprivkey(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"dumpprivkey",
"\nReveals the private key corresponding to 'address'.\n"
"Then the importprivkey can be used with this output\n",
@@ -689,6 +669,10 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet);
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
@@ -714,11 +698,6 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
UniValue dumpwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
- if (!EnsureWalletIsAvailable(pwallet.get(), request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"dumpwallet",
"\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
"Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet.\n"
@@ -739,6 +718,9 @@ UniValue dumpwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
CWallet& wallet = *pwallet;
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(wallet);
@@ -746,7 +728,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
wallet.BlockUntilSyncedToCurrentChain();
- LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
+ LOCK2(wallet.cs_wallet, spk_man.cs_KeyStore);
EnsureWalletIsUnlocked(&wallet);
@@ -769,7 +751,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
std::map<CKeyID, int64_t> mapKeyBirth;
const std::map<CKeyID, int64_t>& mapKeyPool = spk_man.GetAllReserveKeys();
- pwallet->GetKeyBirthTimes(mapKeyBirth);
+ wallet.GetKeyBirthTimes(mapKeyBirth);
std::set<CScriptID> scripts = spk_man.GetCScripts();
@@ -835,7 +817,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
create_time = FormatISO8601DateTime(it->second.nCreateTime);
}
if(spk_man.GetCScript(scriptid, script)) {
- file << strprintf("%s %s script=1", HexStr(script.begin(), script.end()), create_time);
+ file << strprintf("%s %s script=1", HexStr(script), create_time);
file << strprintf(" # addr=%s\n", address);
}
}
@@ -874,20 +856,20 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
{
// Use Solver to obtain script type and parsed pubkeys or hashes:
std::vector<std::vector<unsigned char>> solverdata;
- txnouttype script_type = Solver(script, solverdata);
+ TxoutType script_type = Solver(script, solverdata);
switch (script_type) {
- case TX_PUBKEY: {
+ case TxoutType::PUBKEY: {
CPubKey pubkey(solverdata[0].begin(), solverdata[0].end());
import_data.used_keys.emplace(pubkey.GetID(), false);
return "";
}
- case TX_PUBKEYHASH: {
+ case TxoutType::PUBKEYHASH: {
CKeyID id = CKeyID(uint160(solverdata[0]));
import_data.used_keys[id] = true;
return "";
}
- case TX_SCRIPTHASH: {
+ case TxoutType::SCRIPTHASH: {
if (script_ctx == ScriptContext::P2SH) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside another P2SH");
if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside a P2WSH");
CHECK_NONFATAL(script_ctx == ScriptContext::TOP);
@@ -898,14 +880,14 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
import_data.import_scripts.emplace(*subscript);
return RecurseImportData(*subscript, import_data, ScriptContext::P2SH);
}
- case TX_MULTISIG: {
+ case TxoutType::MULTISIG: {
for (size_t i = 1; i + 1< solverdata.size(); ++i) {
CPubKey pubkey(solverdata[i].begin(), solverdata[i].end());
import_data.used_keys.emplace(pubkey.GetID(), false);
}
return "";
}
- case TX_WITNESS_V0_SCRIPTHASH: {
+ case TxoutType::WITNESS_V0_SCRIPTHASH: {
if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WSH inside another P2WSH");
uint256 fullid(solverdata[0]);
CScriptID id;
@@ -919,7 +901,7 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
import_data.import_scripts.emplace(*subscript);
return RecurseImportData(*subscript, import_data, ScriptContext::WITNESS_V0);
}
- case TX_WITNESS_V0_KEYHASH: {
+ case TxoutType::WITNESS_V0_KEYHASH: {
if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WPKH inside P2WSH");
CKeyID id = CKeyID(uint160(solverdata[0]));
import_data.used_keys[id] = true;
@@ -928,10 +910,10 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
}
return "";
}
- case TX_NULL_DATA:
+ case TxoutType::NULL_DATA:
return "unspendable script";
- case TX_NONSTANDARD:
- case TX_WITNESS_UNKNOWN:
+ case TxoutType::NONSTANDARD:
+ case TxoutType::WITNESS_UNKNOWN:
default:
return "unrecognized script";
}
@@ -1211,7 +1193,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
// Check whether we have any work to do
for (const CScript& script : script_pub_keys) {
if (pwallet->IsMine(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()) + "\")");
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script (\"" + HexStr(script) + "\")");
}
}
@@ -1259,12 +1241,6 @@ static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
UniValue importmulti(const JSONRPCRequest& mainRequest)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"importmulti",
"\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), optionally rescanning the blockchain from the earliest creation time of the imported scripts. Requires a new wallet backup.\n"
"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"
@@ -1340,6 +1316,9 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
},
}.Check(mainRequest);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
@@ -1568,7 +1547,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
if (!w_desc.descriptor->GetOutputType()) {
warnings.push_back("Unknown output type, cannot set descriptor to active.");
} else {
- pwallet->SetActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
+ pwallet->AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
}
}
@@ -1585,14 +1564,8 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
return result;
}
-UniValue importdescriptors(const JSONRPCRequest& main_request) {
- // Acquire the wallet
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(main_request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, main_request.fHelp)) {
- return NullUniValue;
- }
-
+UniValue importdescriptors(const JSONRPCRequest& main_request)
+{
RPCHelpMan{"importdescriptors",
"\nImport descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n"
"\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
@@ -1644,6 +1617,10 @@ UniValue importdescriptors(const JSONRPCRequest& main_request) {
},
}.Check(main_request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(main_request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// Make sure wallet is a descriptor wallet
if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "importdescriptors is not available for non-descriptor wallets");
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 2a57248705..9d334063c4 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -21,12 +21,14 @@
#include <util/fees.h>
#include <util/message.h> // For MessageSign()
#include <util/moneystr.h>
+#include <util/ref.h>
#include <util/string.h>
#include <util/system.h>
#include <util/translation.h>
#include <util/url.h>
#include <util/vector.h>
#include <wallet/coincontrol.h>
+#include <wallet/context.h>
#include <wallet/feebumper.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
@@ -43,6 +45,8 @@ using interfaces::FoundBlock;
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
static const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
+static const uint32_t WALLET_BTC_KB_TO_SAT_B = COIN / 1000; // 1 sat / B = 0.00001 BTC / kB
+
static inline bool GetAvoidReuseFlag(const CWallet* const pwallet, const UniValue& param) {
bool can_avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
@@ -91,6 +95,7 @@ bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string&
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
{
+ CHECK_NONFATAL(!request.fHelp);
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
std::shared_ptr<CWallet> pwallet = GetWallet(wallet_name);
@@ -99,14 +104,11 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
}
std::vector<std::shared_ptr<CWallet>> wallets = GetWallets();
- return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr;
-}
+ if (wallets.size() == 1) {
+ return wallets[0];
+ }
-bool EnsureWalletIsAvailable(const CWallet* pwallet, bool avoidException)
-{
- if (pwallet) return true;
- if (avoidException) return false;
- if (!HasWallets()) {
+ if (wallets.empty()) {
throw JSONRPCError(
RPC_METHOD_NOT_FOUND, "Method not found (wallet method is disabled because no wallet is loaded)");
}
@@ -121,6 +123,14 @@ void EnsureWalletIsUnlocked(const CWallet* pwallet)
}
}
+WalletContext& EnsureWalletContext(const util::Ref& context)
+{
+ if (!context.Has<WalletContext>()) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found");
+ }
+ return context.Get<WalletContext>();
+}
+
// also_create should only be set to true only when the RPC is expected to add things to a blank wallet and make it no longer blank
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create)
{
@@ -183,15 +193,44 @@ static std::string LabelFromValue(const UniValue& value)
return label;
}
-static UniValue getnewaddress(const JSONRPCRequest& request)
+/**
+ * Update coin control with fee estimation based on the given parameters
+ *
+ * @param[in] pwallet Wallet pointer
+ * @param[in,out] cc Coin control which is to be updated
+ * @param[in] estimate_mode String value (e.g. "ECONOMICAL")
+ * @param[in] estimate_param Parameter (blocks to confirm, explicit fee rate, etc)
+ * @throws a JSONRPCError if estimate_mode is unknown, or if estimate_param is missing when required
+ */
+static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const UniValue& estimate_mode, const UniValue& estimate_param)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ if (!estimate_mode.isNull()) {
+ if (!FeeModeFromString(estimate_mode.get_str(), cc.m_fee_mode)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
+ }
+ }
+
+ if (cc.m_fee_mode == FeeEstimateMode::BTC_KB || cc.m_fee_mode == FeeEstimateMode::SAT_B) {
+ if (estimate_param.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Selected estimate_mode requires a fee rate");
+ }
+
+ CAmount fee_rate = AmountFromValue(estimate_param);
+ if (cc.m_fee_mode == FeeEstimateMode::SAT_B) {
+ fee_rate /= WALLET_BTC_KB_TO_SAT_B;
+ }
+
+ cc.m_feerate = CFeeRate(fee_rate);
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
+ // default RBF to true for explicit fee rate modes
+ if (cc.m_signal_bip125_rbf == nullopt) cc.m_signal_bip125_rbf = true;
+ } else if (!estimate_param.isNull()) {
+ cc.m_confirm_target = ParseConfirmTarget(estimate_param, pwallet->chain().estimateMaxBlocks());
}
+}
+static UniValue getnewaddress(const JSONRPCRequest& request)
+{
RPCHelpMan{"getnewaddress",
"\nReturns a new Bitcoin address for receiving payments.\n"
"If 'label' is specified, it is added to the address book \n"
@@ -209,6 +248,10 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (!pwallet->CanGetAddresses()) {
@@ -238,13 +281,6 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
static UniValue getrawchangeaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getrawchangeaddress",
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n",
@@ -260,13 +296,17 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (!pwallet->CanGetAddresses(true)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
}
- OutputType output_type = pwallet->m_default_change_type != OutputType::CHANGE_AUTO ? pwallet->m_default_change_type : pwallet->m_default_address_type;
+ OutputType output_type = pwallet->m_default_change_type.get_value_or(pwallet->m_default_address_type);
if (!request.params[0].isNull()) {
if (!ParseOutputType(request.params[0].get_str(), output_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
@@ -284,13 +324,6 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
static UniValue setlabel(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"setlabel",
"\nSets the label associated with the given address.\n",
{
@@ -304,6 +337,10 @@ static UniValue setlabel(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
CTxDestination dest = DecodeDestination(request.params[0].get_str());
@@ -322,47 +359,58 @@ static UniValue setlabel(const JSONRPCRequest& request)
return NullUniValue;
}
+void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_fee_outputs, std::vector<CRecipient> &recipients) {
+ std::set<CTxDestination> destinations;
+ int i = 0;
+ for (const std::string& address: address_amounts.getKeys()) {
+ CTxDestination dest = DecodeDestination(address);
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + address);
+ }
-static CTransactionRef SendMoney(CWallet* const pwallet, const CTxDestination& address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
-{
- CAmount curBalance = pwallet->GetBalance(0, coin_control.m_avoid_address_reuse).m_mine_trusted;
+ if (destinations.count(dest)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + address);
+ }
+ destinations.insert(dest);
- // Check amount
- if (nValue <= 0)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
+ CScript script_pub_key = GetScriptForDestination(dest);
+ CAmount amount = AmountFromValue(address_amounts[i++]);
+
+ bool subtract_fee = false;
+ for (unsigned int idx = 0; idx < subtract_fee_outputs.size(); idx++) {
+ const UniValue& addr = subtract_fee_outputs[idx];
+ if (addr.get_str() == address) {
+ subtract_fee = true;
+ }
+ }
+
+ CRecipient recipient = {script_pub_key, amount, subtract_fee};
+ recipients.push_back(recipient);
+ }
+}
- if (nValue > curBalance)
- throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
+UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value)
+{
+ EnsureWalletIsUnlocked(pwallet);
- // Parse Bitcoin address
- CScript scriptPubKey = GetScriptForDestination(address);
+ // Shuffle recipient list
+ std::shuffle(recipients.begin(), recipients.end(), FastRandomContext());
- // Create and send the transaction
+ // Send
CAmount nFeeRequired = 0;
- bilingual_str error;
- std::vector<CRecipient> vecSend;
int nChangePosRet = -1;
- CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
- vecSend.push_back(recipient);
+ bilingual_str error;
CTransactionRef tx;
- if (!pwallet->CreateTransaction(vecSend, tx, nFeeRequired, nChangePosRet, error, coin_control)) {
- if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
- error = strprintf(Untranslated("Error: This transaction requires a transaction fee of at least %s"), FormatMoney(nFeeRequired));
- throw JSONRPCError(RPC_WALLET_ERROR, error.original);
+ bool fCreated = pwallet->CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
+ if (!fCreated) {
+ throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
}
- pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */);
- return tx;
+ pwallet->CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
+ return tx->GetHash().GetHex();
}
static UniValue sendtoaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"sendtoaddress",
"\nSend an amount to a given address." +
HELP_REQUIRING_PASSPHRASE,
@@ -377,11 +425,9 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
{"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, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet 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\""},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered\n"
" dirty if they have previously been used in a transaction."},
},
@@ -392,26 +438,22 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1")
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"donation\" \"seans outpost\"")
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" true")
+ + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" false true 0.00002 " + (CURRENCY_UNIT + "/kB"))
+ + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" false true 2 " + (CURRENCY_ATOM + "/B"))
+ HelpExampleRpc("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 0.1, \"donation\", \"seans outpost\"")
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK(pwallet->cs_wallet);
- CTxDestination dest = DecodeDestination(request.params[0].get_str());
- if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
- }
-
- // Amount
- CAmount nAmount = AmountFromValue(request.params[1]);
- if (nAmount <= 0)
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
-
// Wallet comments
mapValue_t mapValue;
if (!request.params[2].isNull() && !request.params[2].get_str().empty())
@@ -429,35 +471,30 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
}
- if (!request.params[6].isNull()) {
- coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
- }
-
- if (!request.params[7].isNull()) {
- if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
- }
- }
-
coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(pwallet, request.params[8]);
// We also enable partial spend avoidance if reuse avoidance is set.
coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse;
+ SetFeeEstimateMode(pwallet, coin_control, request.params[7], request.params[6]);
+
EnsureWalletIsUnlocked(pwallet);
- CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue));
- return tx->GetHash().GetHex();
+ UniValue address_amounts(UniValue::VOBJ);
+ const std::string address = request.params[0].get_str();
+ address_amounts.pushKV(address, request.params[1]);
+ UniValue subtractFeeFromAmount(UniValue::VARR);
+ if (fSubtractFeeFromAmount) {
+ subtractFeeFromAmount.push_back(address);
+ }
+
+ std::vector<CRecipient> recipients;
+ ParseRecipients(address_amounts, subtractFeeFromAmount, recipients);
+
+ return SendMoney(pwallet, coin_control, recipients, mapValue);
}
static UniValue listaddressgroupings(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listaddressgroupings",
"\nLists groups of addresses which have had their common ownership\n"
"made public by common use as inputs or as the resulting change\n"
@@ -483,6 +520,10 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -513,13 +554,6 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
static UniValue signmessage(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"signmessage",
"\nSign a message with the private key of an address" +
HELP_REQUIRING_PASSPHRASE,
@@ -542,6 +576,10 @@ static UniValue signmessage(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -618,13 +656,6 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b
static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getreceivedbyaddress",
"\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
{
@@ -646,6 +677,10 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -658,13 +693,6 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
static UniValue getreceivedbylabel(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getreceivedbylabel",
"\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
{
@@ -686,6 +714,10 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -698,13 +730,6 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
static UniValue getbalance(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getbalance",
"\nReturns the total available balance.\n"
"The available balance is what the wallet considers currently spendable, and is\n"
@@ -728,6 +753,10 @@ static UniValue getbalance(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -755,13 +784,6 @@ static UniValue getbalance(const JSONRPCRequest& request)
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getunconfirmedbalance",
"DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
{},
@@ -769,6 +791,10 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
RPCExamples{""},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -781,13 +807,6 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
static UniValue sendmany(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"sendmany",
"\nSend multiple times. Amounts are double-precision floating point numbers." +
HELP_REQUIRING_PASSPHRASE,
@@ -809,11 +828,9 @@ static UniValue sendmany(const JSONRPCRequest& request)
},
},
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet 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\""},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
},
RPCResult{
RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
@@ -831,6 +848,10 @@ static UniValue sendmany(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -855,73 +876,16 @@ static UniValue sendmany(const JSONRPCRequest& request)
coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
}
- if (!request.params[6].isNull()) {
- coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
- }
+ SetFeeEstimateMode(pwallet, coin_control, request.params[7], request.params[6]);
- if (!request.params[7].isNull()) {
- if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
- }
- }
+ std::vector<CRecipient> recipients;
+ ParseRecipients(sendTo, subtractFeeFromAmount, recipients);
- std::set<CTxDestination> destinations;
- std::vector<CRecipient> vecSend;
-
- std::vector<std::string> keys = sendTo.getKeys();
- for (const std::string& name_ : keys) {
- CTxDestination dest = DecodeDestination(name_);
- if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
- }
-
- if (destinations.count(dest)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
- }
- destinations.insert(dest);
-
- CScript scriptPubKey = GetScriptForDestination(dest);
- CAmount nAmount = AmountFromValue(sendTo[name_]);
- if (nAmount <= 0)
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
-
- bool fSubtractFeeFromAmount = false;
- for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
- const UniValue& addr = subtractFeeFromAmount[idx];
- if (addr.get_str() == name_)
- fSubtractFeeFromAmount = true;
- }
-
- CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount};
- vecSend.push_back(recipient);
- }
-
- EnsureWalletIsUnlocked(pwallet);
-
- // Shuffle recipient list
- std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
-
- // Send
- CAmount nFeeRequired = 0;
- int nChangePosRet = -1;
- bilingual_str error;
- CTransactionRef tx;
- bool fCreated = pwallet->CreateTransaction(vecSend, tx, nFeeRequired, nChangePosRet, error, coin_control);
- if (!fCreated)
- throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
- pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */);
- return tx->GetHash().GetHex();
+ return SendMoney(pwallet, coin_control, recipients, std::move(mapValue));
}
static UniValue addmultisigaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"addmultisigaddress",
"\nAdd an nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
@@ -954,6 +918,10 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
@@ -992,7 +960,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(dest));
- result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
+ result.pushKV("redeemScript", HexStr(inner));
result.pushKV("descriptor", descriptor->ToString());
return result;
}
@@ -1157,13 +1125,6 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listreceivedbyaddress",
"\nList balances by receiving address.\n",
{
@@ -1197,6 +1158,10 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1208,13 +1173,6 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
static UniValue listreceivedbylabel(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listreceivedbylabel",
"\nList received transactions by label.\n",
{
@@ -1241,6 +1199,10 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1369,13 +1331,6 @@ static const std::vector<RPCResult> TransactionDescriptionString()
UniValue listtransactions(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listtransactions",
"\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",
@@ -1423,6 +1378,10 @@ UniValue listtransactions(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1482,12 +1441,6 @@ UniValue listtransactions(const JSONRPCRequest& request)
static UniValue listsinceblock(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
-
- if (!EnsureWalletIsAvailable(pwallet.get(), request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listsinceblock",
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
"If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
@@ -1541,6 +1494,9 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
const CWallet& wallet = *pwallet;
// 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
@@ -1623,13 +1579,6 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
static UniValue gettransaction(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"gettransaction",
"\nGet detailed information about in-wallet transaction <txid>\n",
{
@@ -1684,6 +1633,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1736,13 +1689,6 @@ static UniValue gettransaction(const JSONRPCRequest& request)
static UniValue abandontransaction(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"abandontransaction",
"\nMark in-wallet transaction <txid> as abandoned\n"
"This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
@@ -1759,6 +1705,10 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1780,13 +1730,6 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
static UniValue backupwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"backupwallet",
"\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n",
{
@@ -1799,6 +1742,10 @@ static UniValue backupwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1816,13 +1763,6 @@ static UniValue backupwallet(const JSONRPCRequest& request)
static UniValue keypoolrefill(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"keypoolrefill",
"\nFills the keypool."+
HELP_REQUIRING_PASSPHRASE,
@@ -1836,6 +1776,10 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
if (pwallet->IsLegacy() && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
}
@@ -1863,13 +1807,6 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
static UniValue walletpassphrase(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"walletpassphrase",
"\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
"This is needed prior to performing transactions related to private keys such as sending bitcoins\n"
@@ -1891,6 +1828,10 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
int64_t nSleepTime;
int64_t relock_time;
// Prevent concurrent calls to walletpassphrase with the same wallet.
@@ -1960,13 +1901,6 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
static UniValue walletpassphrasechange(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"walletpassphrasechange",
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
{
@@ -1980,6 +1914,10 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
@@ -2010,13 +1948,6 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
static UniValue walletlock(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"walletlock",
"\nRemoves the wallet encryption key from memory, locking the wallet.\n"
"After calling this method, you will need to call walletpassphrase again\n"
@@ -2035,6 +1966,10 @@ static UniValue walletlock(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
@@ -2050,13 +1985,6 @@ static UniValue walletlock(const JSONRPCRequest& request)
static UniValue encryptwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"encryptwallet",
"\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
"After this, any calls that interact with private keys such as sending or signing \n"
@@ -2081,6 +2009,10 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
@@ -2110,13 +2042,6 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
static UniValue lockunspent(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"lockunspent",
"\nUpdates list of temporarily unspendable outputs.\n"
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
@@ -2155,6 +2080,10 @@ static UniValue lockunspent(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -2236,13 +2165,6 @@ static UniValue lockunspent(const JSONRPCRequest& request)
static UniValue listlockunspent(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listlockunspent",
"\nReturns list of temporarily unspendable outputs.\n"
"See the lockunspent call to lock and unlock transactions for spending.\n",
@@ -2271,6 +2193,10 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
std::vector<COutPoint> vOutpts;
@@ -2291,13 +2217,6 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
static UniValue settxfee(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"settxfee",
"\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n"
"Can be deactivated by passing 0 as the fee. In that case automatic fee selection will be used by default.\n",
@@ -2313,6 +2232,10 @@ static UniValue settxfee(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
CAmount nAmount = AmountFromValue(request.params[0]);
@@ -2334,12 +2257,6 @@ static UniValue settxfee(const JSONRPCRequest& request)
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;
-
RPCHelpMan{
"getbalances",
"Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
@@ -2367,6 +2284,10 @@ static UniValue getbalances(const JSONRPCRequest& request)
HelpExampleRpc("getbalances", "")},
}.Check(request);
+ std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
+ if (!rpc_wallet) return NullUniValue;
+ CWallet& wallet = *rpc_wallet;
+
// 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();
@@ -2401,13 +2322,6 @@ static UniValue getbalances(const JSONRPCRequest& request)
static UniValue getwalletinfo(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getwalletinfo",
"Returns an object containing various wallet state info.\n",
{},
@@ -2443,6 +2357,10 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -2550,12 +2468,7 @@ static UniValue listwallets(const JSONRPCRequest& request)
UniValue obj(UniValue::VARR);
for (const std::shared_ptr<CWallet>& wallet : GetWallets()) {
- if (!EnsureWalletIsAvailable(wallet.get(), request.fHelp)) {
- return NullUniValue;
- }
-
LOCK(wallet->cs_wallet);
-
obj.push_back(wallet->GetName());
}
@@ -2584,6 +2497,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ WalletContext& context = EnsureWalletContext(request.context);
WalletLocation location(request.params[0].get_str());
if (!location.Exists()) {
@@ -2598,7 +2512,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
bilingual_str error;
std::vector<bilingual_str> warnings;
- std::shared_ptr<CWallet> const wallet = LoadWallet(*g_rpc_chain, location, error, warnings);
+ std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, location, error, warnings);
if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error.original);
UniValue obj(UniValue::VOBJ);
@@ -2610,13 +2524,6 @@ static UniValue loadwallet(const JSONRPCRequest& request)
static UniValue setwalletflag(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
std::string flags = "";
for (auto& it : WALLET_FLAG_MAP)
if (it.second & MUTABLE_WALLET_FLAGS)
@@ -2641,6 +2548,10 @@ static UniValue setwalletflag(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
std::string flag_str = request.params[0].get_str();
bool value = request.params[1].isNull() || request.params[1].get_bool();
@@ -2702,6 +2613,7 @@ static UniValue createwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ WalletContext& context = EnsureWalletContext(request.context);
uint64_t flags = 0;
if (!request.params[1].isNull() && request.params[1].get_bool()) {
flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
@@ -2726,11 +2638,12 @@ static UniValue createwallet(const JSONRPCRequest& request)
}
if (!request.params[5].isNull() && request.params[5].get_bool()) {
flags |= WALLET_FLAG_DESCRIPTORS;
+ warnings.emplace_back(Untranslated("Wallet is an experimental descriptor wallet"));
}
bilingual_str error;
std::shared_ptr<CWallet> wallet;
- WalletCreationStatus status = CreateWallet(*g_rpc_chain, passphrase, flags, request.params[0].get_str(), error, warnings, wallet);
+ WalletCreationStatus status = CreateWallet(*context.chain, passphrase, flags, request.params[0].get_str(), error, warnings, wallet);
switch (status) {
case WalletCreationStatus::CREATION_FAILED:
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
@@ -2791,13 +2704,6 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
static UniValue listunspent(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{
"listunspent",
"\nReturns array of unspent transaction outputs\n"
@@ -2855,6 +2761,10 @@ static UniValue listunspent(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
int nMinDepth = 1;
if (!request.params[0].isNull()) {
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
@@ -2956,7 +2866,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address));
CScript redeemScript;
if (provider->GetCScript(hash, redeemScript)) {
- entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()));
+ entry.pushKV("redeemScript", HexStr(redeemScript));
// Now check if the redeemScript is actually a P2WSH script
CTxDestination witness_destination;
if (redeemScript.IsPayToWitnessScriptHash()) {
@@ -2968,7 +2878,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
CScript witnessScript;
if (provider->GetCScript(id, witnessScript)) {
- entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ entry.pushKV("witnessScript", HexStr(witnessScript));
}
}
}
@@ -2978,13 +2888,13 @@ static UniValue listunspent(const JSONRPCRequest& request)
CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
CScript witnessScript;
if (provider->GetCScript(id, witnessScript)) {
- entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ entry.pushKV("witnessScript", HexStr(witnessScript));
}
}
}
}
- entry.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
+ entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
entry.pushKV("confirmations", out.nDepth);
entry.pushKV("spendable", out.fSpendable);
@@ -3004,13 +2914,12 @@ static UniValue listunspent(const JSONRPCRequest& request)
return results;
}
-void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, UniValue options)
+void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, UniValue options, CCoinControl& coinControl)
{
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- CCoinControl coinControl;
change_position = -1;
bool lockUnspents = false;
UniValue subtractFeeFromOutputs;
@@ -3025,6 +2934,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
RPCTypeCheckArgument(options, UniValue::VOBJ);
RPCTypeCheckObj(options,
{
+ {"add_inputs", UniValueType(UniValue::VBOOL)},
{"changeAddress", UniValueType(UniValue::VSTR)},
{"changePosition", UniValueType(UniValue::VNUM)},
{"change_type", UniValueType(UniValue::VSTR)},
@@ -3038,6 +2948,10 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
},
true, true);
+ if (options.exists("add_inputs") ) {
+ coinControl.m_add_inputs = options["add_inputs"].get_bool();
+ }
+
if (options.exists("changeAddress")) {
CTxDestination dest = DecodeDestination(options["changeAddress"].get_str());
@@ -3055,10 +2969,11 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("changeAddress")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both changeAddress and address_type options");
}
- coinControl.m_change_type = pwallet->m_default_change_type;
- if (!ParseOutputType(options["change_type"].get_str(), *coinControl.m_change_type)) {
+ OutputType out_type;
+ if (!ParseOutputType(options["change_type"].get_str(), out_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown change type '%s'", options["change_type"].get_str()));
}
+ coinControl.m_change_type.emplace(out_type);
}
coinControl.fAllowWatchOnly = ParseIncludeWatchonly(options["includeWatching"], *pwallet);
@@ -3068,6 +2983,12 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("feeRate"))
{
+ if (options.exists("conf_target")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate");
+ }
+ if (options.exists("estimate_mode")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
+ }
coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
coinControl.fOverrideFeeRate = true;
}
@@ -3078,20 +2999,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("replaceable")) {
coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
}
- if (options.exists("conf_target")) {
- if (options.exists("feeRate")) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate");
- }
- coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"], pwallet->chain().estimateMaxBlocks());
- }
- if (options.exists("estimate_mode")) {
- if (options.exists("feeRate")) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
- }
- if (!FeeModeFromString(options["estimate_mode"].get_str(), coinControl.m_fee_mode)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
- }
- }
+ SetFeeEstimateMode(pwallet, coinControl, options["estimate_mode"], options["conf_target"]);
}
} else {
// if options is null and not a bool
@@ -3124,16 +3032,9 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
static UniValue fundrawtransaction(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"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"
+ "\nIf the transaction has no inputs, they will be automatically selected to meet its out value.\n"
+ "It 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 signrawtransactionwithkey\n"
@@ -3147,6 +3048,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
{"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}",
{
+ {"add_inputs", RPCArg::Type::BOOL, /* default */ "true", "For a transaction with existing inputs, automatically include more if they are not enough."},
{"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\"."},
@@ -3165,11 +3067,9 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
},
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet 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, /* default */ "wallet 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\""},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
},
"options"},
{"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
@@ -3200,6 +3100,10 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
// parse hex string from parameter
@@ -3212,7 +3116,10 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
CAmount fee;
int change_position;
- FundTransaction(pwallet, tx, fee, change_position, request.params[1]);
+ CCoinControl coin_control;
+ // Automatically select (additional) coins. Can be overridden by options.add_inputs.
+ coin_control.m_add_inputs = true;
+ FundTransaction(pwallet, tx, fee, change_position, request.params[1], coin_control);
UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(CTransaction(tx)));
@@ -3224,13 +3131,6 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"signrawtransactionwithwallet",
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second optional argument (may be null) is an array of previous transaction outputs that\n"
@@ -3284,6 +3184,10 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
CMutableTransaction mtx;
@@ -3318,13 +3222,6 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
static UniValue bumpfee(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
- return NullUniValue;
-
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"
@@ -3340,8 +3237,8 @@ static UniValue bumpfee(const JSONRPCRequest& request)
{"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, /* default */ "wallet default", "Confirmation target (in blocks)"},
- {"fee_rate", RPCArg::Type::NUM, /* default */ "fall back to 'confTarget'", "fee rate (NOT total fee) to pay, in " + CURRENCY_UNIT + " per kB\n"
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
+ {"fee_rate", RPCArg::Type::NUM, /* default */ "fall back to 'conf_target'", "fee rate (NOT total fee) to pay, in " + CURRENCY_UNIT + " per kB\n"
" Specify a fee rate instead of relying on the built-in fee estimator.\n"
"Must be at least 0.0001 " + CURRENCY_UNIT + " per kB higher than the current transaction fee rate.\n"},
{"replaceable", RPCArg::Type::BOOL, /* default */ "true", "Whether the new transaction should still be\n"
@@ -3351,10 +3248,8 @@ static UniValue bumpfee(const JSONRPCRequest& request)
" 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, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
- " \"UNSET\"\n"
- " \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\""},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
},
"options"},
},
@@ -3376,6 +3271,10 @@ static UniValue bumpfee(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -3389,15 +3288,24 @@ static UniValue bumpfee(const JSONRPCRequest& request)
RPCTypeCheckObj(options,
{
{"confTarget", UniValueType(UniValue::VNUM)},
+ {"conf_target", UniValueType(UniValue::VNUM)},
{"fee_rate", UniValueType(UniValue::VNUM)},
{"replaceable", UniValueType(UniValue::VBOOL)},
{"estimate_mode", UniValueType(UniValue::VSTR)},
},
true, true);
- if (options.exists("confTarget") && options.exists("fee_rate")) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget can't be set with fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
- } else if (options.exists("confTarget")) { // TODO: alias this to conf_target
- coin_control.m_confirm_target = ParseConfirmTarget(options["confTarget"], pwallet->chain().estimateMaxBlocks());
+
+ if (options.exists("confTarget") && options.exists("conf_target")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and conf_target options should not both be set. Use conf_target (confTarget is deprecated).");
+ }
+
+ auto conf_target = options.exists("confTarget") ? options["confTarget"] : options["conf_target"];
+
+ if (!conf_target.isNull()) {
+ if (options.exists("fee_rate")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "conf_target can't be set with fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
+ }
+ coin_control.m_confirm_target = ParseConfirmTarget(conf_target, pwallet->chain().estimateMaxBlocks());
} else if (options.exists("fee_rate")) {
CFeeRate fee_rate(AmountFromValue(options["fee_rate"]));
if (fee_rate <= CFeeRate(0)) {
@@ -3409,11 +3317,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
if (options.exists("replaceable")) {
coin_control.m_signal_bip125_rbf = options["replaceable"].get_bool();
}
- if (options.exists("estimate_mode")) {
- if (!FeeModeFromString(options["estimate_mode"].get_str(), coin_control.m_fee_mode)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
- }
- }
+ SetFeeEstimateMode(pwallet, coin_control, options["estimate_mode"], conf_target);
}
// Make sure the results are valid at least up to the most recent block
@@ -3490,13 +3394,6 @@ static UniValue bumpfee(const JSONRPCRequest& request)
UniValue rescanblockchain(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"rescanblockchain",
"\nRescan the local blockchain for wallet related transactions.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
@@ -3517,6 +3414,10 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
WalletRescanReserver reserver(*pwallet);
if (!reserver.reserve()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
@@ -3579,9 +3480,9 @@ public:
{
// Always present: script type and redeemscript
std::vector<std::vector<unsigned char>> solutions_data;
- txnouttype which_type = Solver(subscript, solutions_data);
+ TxoutType which_type = Solver(subscript, solutions_data);
obj.pushKV("script", GetTxnOutputType(which_type));
- obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));
+ obj.pushKV("hex", HexStr(subscript));
CTxDestination embedded;
if (ExtractDestination(subscript, embedded)) {
@@ -3592,18 +3493,18 @@ public:
UniValue wallet_detail = boost::apply_visitor(*this, embedded);
subobj.pushKVs(wallet_detail);
subobj.pushKV("address", EncodeDestination(embedded));
- subobj.pushKV("scriptPubKey", HexStr(subscript.begin(), subscript.end()));
+ subobj.pushKV("scriptPubKey", HexStr(subscript));
// 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));
- } else if (which_type == TX_MULTISIG) {
+ } else if (which_type == TxoutType::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.
obj.pushKV("sigsrequired", solutions_data[0][0]);
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());
- pubkeys.push_back(HexStr(key.begin(), key.end()));
+ pubkeys.push_back(HexStr(key));
}
obj.pushKV("pubkeys", std::move(pubkeys));
}
@@ -3615,7 +3516,7 @@ public:
UniValue operator()(const PKHash& pkhash) const
{
- CKeyID keyID(pkhash);
+ CKeyID keyID{ToKeyID(pkhash)};
UniValue obj(UniValue::VOBJ);
CPubKey vchPubKey;
if (provider && provider->GetPubKey(keyID, vchPubKey)) {
@@ -3640,7 +3541,7 @@ public:
{
UniValue obj(UniValue::VOBJ);
CPubKey pubkey;
- if (provider && provider->GetPubKey(CKeyID(id), pubkey)) {
+ if (provider && provider->GetPubKey(ToKeyID(id), pubkey)) {
obj.pushKV("pubkey", HexStr(pubkey));
}
return obj;
@@ -3689,13 +3590,6 @@ static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool v
UniValue getaddressinfo(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getaddressinfo",
"\nReturn information about the given bitcoin address.\n"
"Some of the information will only be present if the address is in the active wallet.\n",
@@ -3728,12 +3622,10 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
{RPCResult::Type::STR_HEX, "pubkey", /* optional */ true, "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
{RPCResult::Type::OBJ, "embedded", /* optional */ true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
{
- {RPCResult::Type::ELISION, "", "Includes all\n"
- " getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath,\n"
- "hdseedid) and relation to the wallet (ismine, iswatchonly)."},
+ {RPCResult::Type::ELISION, "", "Includes all getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath, hdseedid)\n"
+ "and relation to the wallet (ismine, iswatchonly)."},
}},
{RPCResult::Type::BOOL, "iscompressed", /* optional */ true, "If the pubkey is compressed."},
- {RPCResult::Type::STR, "label", "DEPRECATED. The label associated with the address. Defaults to \"\". Replaced by the labels array below."},
{RPCResult::Type::NUM_TIME, "timestamp", /* optional */ true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::STR, "hdkeypath", /* optional */ true, "The HD keypath, if the key is HD and available."},
{RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "The Hash160 of the HD seed."},
@@ -3741,12 +3633,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
{RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n"
"as an array to keep the API stable if multiple labels are enabled in the future.",
{
- {RPCResult::Type::STR, "label name", "The label name. Defaults to \"\"."},
- {RPCResult::Type::OBJ, "", "label data, DEPRECATED, will be removed in 0.21. To re-enable, launch bitcoind with `-deprecatedrpc=labelspurpose`",
- {
- {RPCResult::Type::STR, "name", "The label name. Defaults to \"\"."},
- {RPCResult::Type::STR, "purpose", "The purpose of the associated address (send or receive)."},
- }},
+ {RPCResult::Type::STR, "label name", "Label name (defaults to \"\")."},
}},
}
},
@@ -3756,6 +3643,10 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
UniValue ret(UniValue::VOBJ);
@@ -3769,7 +3660,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
ret.pushKV("address", currentAddress);
CScript scriptPubKey = GetScriptForDestination(dest);
- ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
+ ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
@@ -3788,14 +3679,6 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
UniValue detail = DescribeWalletAddress(pwallet, dest);
ret.pushKVs(detail);
- // DEPRECATED: Return label field if existing. Currently only one label can
- // be associated with an address, so the label should be equivalent to the
- // value of the name key/value pair in the labels array below.
- const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
- if (pwallet->chain().rpcEnableDeprecated("label") && address_book_entry) {
- ret.pushKV("label", address_book_entry->GetLabel());
- }
-
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey);
@@ -3816,14 +3699,9 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
// stable if we allow multiple labels to be associated with an address in
// the future.
UniValue labels(UniValue::VARR);
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
if (address_book_entry) {
- // DEPRECATED: The previous behavior of returning an array containing a
- // JSON object of `name` and `purpose` key/value pairs is deprecated.
- if (pwallet->chain().rpcEnableDeprecated("labelspurpose")) {
- labels.push_back(AddressBookDataToJSON(*address_book_entry, true));
- } else {
- labels.push_back(address_book_entry->GetLabel());
- }
+ labels.push_back(address_book_entry->GetLabel());
}
ret.pushKV("labels", std::move(labels));
@@ -3832,13 +3710,6 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
static UniValue getaddressesbylabel(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getaddressesbylabel",
"\nReturns the list of addresses assigned the specified label.\n",
{
@@ -3859,6 +3730,10 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
std::string label = LabelFromValue(request.params[0]);
@@ -3892,13 +3767,6 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
static UniValue listlabels(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listlabels",
"\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
{
@@ -3922,6 +3790,10 @@ static UniValue listlabels(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
std::string purpose;
@@ -3948,13 +3820,6 @@ static UniValue listlabels(const JSONRPCRequest& request)
UniValue sethdseed(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"sethdseed",
"\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
"HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
@@ -3977,11 +3842,11 @@ UniValue sethdseed(const JSONRPCRequest& request)
},
}.Check(request);
- LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
- if (pwallet->chain().isInitialBlockDownload()) {
- throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
- }
+ LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed to a wallet with private keys disabled");
@@ -4025,13 +3890,6 @@ UniValue sethdseed(const JSONRPCRequest& request)
UniValue walletprocesspsbt(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"walletprocesspsbt",
"\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
"that we can sign for." +
@@ -4060,6 +3918,10 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
// Unserialize the transaction
@@ -4092,24 +3954,17 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"walletcreatefundedpsbt",
- "\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
+ "\nCreates and funds a transaction in the Partially Signed Transaction format.\n"
"Implements the Creator and Updater roles.\n",
{
- {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The inputs",
+ {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The inputs. Leave empty to add inputs automatically. See add_inputs option.",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
{"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"},
+ {"sequence", RPCArg::Type::NUM, /* default */ "depends on the value of the 'locktime' and 'options.replaceable' arguments", "The sequence number"},
},
},
},
@@ -4134,6 +3989,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
+ {"add_inputs", RPCArg::Type::BOOL, /* default */ "false", "If inputs are specified, automatically include more if they are not enough."},
{"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\"."},
@@ -4151,10 +4007,8 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet 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, /* default */ "fall back to wallet's confirmation target (txconfirmtarget)", "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\""},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
},
"options"},
{"bip32derivs", RPCArg::Type::BOOL, /* default */ "true", "Include BIP 32 derivation paths for public keys if we know them"},
@@ -4173,6 +4027,10 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {
UniValue::VARR,
UniValueType(), // ARR or OBJ, checked later
@@ -4191,7 +4049,11 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
rbf = replaceable_arg.isTrue();
}
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
- FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]);
+ CCoinControl coin_control;
+ // Automatically select coins, unless at least one is manually selected. Can
+ // be overridden by options.add_inputs.
+ coin_control.m_add_inputs = rawTx.vin.size() == 0;
+ FundTransaction(pwallet, rawTx, fee, change_position, request.params[3], coin_control);
// Make a blank psbt
PartiallySignedTransaction psbtx(rawTx);
@@ -4217,13 +4079,6 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
static UniValue upgradewallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"upgradewallet",
"\nUpgrade the wallet. Upgrades to the latest version if no version number is specified\n"
"New keys may be generated and a new wallet backup will need to be made.",
@@ -4237,6 +4092,10 @@ static UniValue upgradewallet(const JSONRPCRequest& request)
}
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VNUM}, true);
EnsureWalletIsUnlocked(pwallet);
@@ -4266,7 +4125,7 @@ UniValue removeprunedfunds(const JSONRPCRequest& request);
UniValue importmulti(const JSONRPCRequest& request);
UniValue importdescriptors(const JSONRPCRequest& request);
-void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers)
+Span<const CRPCCommand> GetWalletRPCCommands()
{
// clang-format off
static const CRPCCommand commands[] =
@@ -4332,9 +4191,5 @@ static const CRPCCommand commands[] =
{ "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} },
};
// clang-format on
-
- for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
- handlers.emplace_back(chain.handleRpc(commands[vcidx]));
+ return MakeSpan(commands);
}
-
-interfaces::Chain* g_rpc_chain = nullptr;
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index 8c149d455b..fb1e91282b 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -5,30 +5,22 @@
#ifndef BITCOIN_WALLET_RPCWALLET_H
#define BITCOIN_WALLET_RPCWALLET_H
+#include <span.h>
+
#include <memory>
#include <string>
#include <vector>
-class CRPCTable;
+class CRPCCommand;
class CWallet;
class JSONRPCRequest;
class LegacyScriptPubKeyMan;
class UniValue;
-struct PartiallySignedTransaction;
class CTransaction;
+struct PartiallySignedTransaction;
+struct WalletContext;
-namespace interfaces {
-class Chain;
-class Handler;
-}
-
-//! Pointer to chain interface that needs to be declared as a global to be
-//! accessible loadwallet and createwallet methods. Due to limitations of the
-//! RPC framework, there's currently no direct way to pass in state to RPC
-//! methods without globals.
-extern interfaces::Chain* g_rpc_chain;
-
-void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers);
+Span<const CRPCCommand> GetWalletRPCCommands();
/**
* Figures out what wallet, if any, to use for a JSONRPCRequest.
@@ -39,7 +31,7 @@ void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
void EnsureWalletIsUnlocked(const CWallet*);
-bool EnsureWalletIsAvailable(const CWallet*, bool avoidException);
+WalletContext& EnsureWalletContext(const util::Ref& context);
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false);
UniValue getaddressinfo(const JSONRPCRequest& request);
diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp
new file mode 100644
index 0000000000..e6e62332c0
--- /dev/null
+++ b/src/wallet/salvage.cpp
@@ -0,0 +1,155 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2020 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 <streams.h>
+#include <wallet/salvage.h>
+#include <wallet/wallet.h>
+#include <wallet/walletdb.h>
+
+/* End of headers, beginning of key/value data */
+static const char *HEADER_END = "HEADER=END";
+/* End of key/value data */
+static const char *DATA_END = "DATA=END";
+typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
+
+bool RecoverDatabaseFile(const fs::path& file_path)
+{
+ std::string filename;
+ std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
+
+ if (!env->Open(true /* retry */)) {
+ tfm::format(std::cerr, "Error initializing wallet database environment %s!", env->Directory());
+ return false;
+ }
+
+ // Recovery procedure:
+ // move wallet file to walletfilename.timestamp.bak
+ // Call Salvage with fAggressive=true to
+ // get as much data as possible.
+ // Rewrite salvaged data to fresh wallet file
+ // Set -rescan so any missing transactions will be
+ // found.
+ int64_t now = GetTime();
+ std::string newFilename = strprintf("%s.%d.bak", filename, now);
+
+ int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
+ newFilename.c_str(), DB_AUTO_COMMIT);
+ if (result == 0)
+ LogPrintf("Renamed %s to %s\n", filename, newFilename);
+ else
+ {
+ LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
+ return false;
+ }
+
+ /**
+ * Salvage data from a file. The DB_AGGRESSIVE flag is being used (see berkeley DB->verify() method documentation).
+ * key/value pairs are appended to salvagedData which are then written out to a new wallet file.
+ * NOTE: reads the entire database into memory, so cannot be used
+ * for huge databases.
+ */
+ std::vector<KeyValPair> salvagedData;
+
+ std::stringstream strDump;
+
+ Db db(env->dbenv.get(), 0);
+ result = db.verify(newFilename.c_str(), nullptr, &strDump, DB_SALVAGE | DB_AGGRESSIVE);
+ if (result == DB_VERIFY_BAD) {
+ LogPrintf("Salvage: Database salvage found errors, all data may not be recoverable.\n");
+ }
+ if (result != 0 && result != DB_VERIFY_BAD) {
+ LogPrintf("Salvage: Database salvage failed with result %d.\n", result);
+ return false;
+ }
+
+ // Format of bdb dump is ascii lines:
+ // header lines...
+ // HEADER=END
+ // hexadecimal key
+ // hexadecimal value
+ // ... repeated
+ // DATA=END
+
+ std::string strLine;
+ while (!strDump.eof() && strLine != HEADER_END)
+ getline(strDump, strLine); // Skip past header
+
+ std::string keyHex, valueHex;
+ while (!strDump.eof() && keyHex != DATA_END) {
+ getline(strDump, keyHex);
+ if (keyHex != DATA_END) {
+ if (strDump.eof())
+ break;
+ getline(strDump, valueHex);
+ if (valueHex == DATA_END) {
+ LogPrintf("Salvage: WARNING: Number of keys in data does not match number of values.\n");
+ break;
+ }
+ salvagedData.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex)));
+ }
+ }
+
+ bool fSuccess;
+ if (keyHex != DATA_END) {
+ LogPrintf("Salvage: WARNING: Unexpected end of file while reading salvage output.\n");
+ fSuccess = false;
+ } else {
+ fSuccess = (result == 0);
+ }
+
+ if (salvagedData.empty())
+ {
+ LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
+ return false;
+ }
+ LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
+
+ std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
+ int ret = pdbCopy->open(nullptr, // Txn pointer
+ filename.c_str(), // Filename
+ "main", // Logical db name
+ DB_BTREE, // Database type
+ DB_CREATE, // Flags
+ 0);
+ if (ret > 0) {
+ LogPrintf("Cannot create database file %s\n", filename);
+ pdbCopy->close(0);
+ return false;
+ }
+
+ DbTxn* ptxn = env->TxnBegin();
+ CWallet dummyWallet(nullptr, WalletLocation(), CreateDummyWalletDatabase());
+ for (KeyValPair& row : salvagedData)
+ {
+ /* Filter for only private key type KV pairs to be added to the salvaged wallet */
+ CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
+ CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
+ std::string strType, strErr;
+ bool fReadOK;
+ {
+ // Required in LoadKeyMetadata():
+ LOCK(dummyWallet.cs_wallet);
+ fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, strType, strErr);
+ }
+ if (!WalletBatch::IsKeyType(strType) && strType != DBKeys::HDCHAIN) {
+ continue;
+ }
+ if (!fReadOK)
+ {
+ LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, strErr);
+ continue;
+ }
+ Dbt datKey(&row.first[0], row.first.size());
+ Dbt datValue(&row.second[0], row.second.size());
+ int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
+ if (ret2 > 0)
+ fSuccess = false;
+ }
+ ptxn->commit(0);
+ pdbCopy->close(0);
+
+ return fSuccess;
+}
diff --git a/src/wallet/salvage.h b/src/wallet/salvage.h
new file mode 100644
index 0000000000..e361930f5e
--- /dev/null
+++ b/src/wallet/salvage.h
@@ -0,0 +1,14 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2020 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_SALVAGE_H
+#define BITCOIN_WALLET_SALVAGE_H
+
+#include <fs.h>
+#include <streams.h>
+
+bool RecoverDatabaseFile(const fs::path& file_path);
+
+#endif // BITCOIN_WALLET_SALVAGE_H
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index e4be5045e1..51715462c5 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -12,6 +12,9 @@
#include <util/translation.h>
#include <wallet/scriptpubkeyman.h>
+//! Value for the first BIP 32 hardened derivation. Can be used as a bit mask and as a value. See BIP 32 for more details.
+const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
+
bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error)
{
LOCK(cs_KeyStore);
@@ -85,16 +88,16 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
IsMineResult ret = IsMineResult::NO;
std::vector<valtype> vSolutions;
- txnouttype whichType = Solver(scriptPubKey, vSolutions);
+ TxoutType whichType = Solver(scriptPubKey, vSolutions);
CKeyID keyID;
switch (whichType)
{
- case TX_NONSTANDARD:
- case TX_NULL_DATA:
- case TX_WITNESS_UNKNOWN:
+ case TxoutType::NONSTANDARD:
+ case TxoutType::NULL_DATA:
+ case TxoutType::WITNESS_UNKNOWN:
break;
- case TX_PUBKEY:
+ case TxoutType::PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
if (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) {
return IsMineResult::INVALID;
@@ -103,7 +106,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
ret = std::max(ret, IsMineResult::SPENDABLE);
}
break;
- case TX_WITNESS_V0_KEYHASH:
+ case TxoutType::WITNESS_V0_KEYHASH:
{
if (sigversion == IsMineSigVersion::WITNESS_V0) {
// P2WPKH inside P2WSH is invalid.
@@ -118,7 +121,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(PKHash(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
break;
}
- case TX_PUBKEYHASH:
+ case TxoutType::PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0]));
if (!PermitsUncompressed(sigversion)) {
CPubKey pubkey;
@@ -130,7 +133,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
ret = std::max(ret, IsMineResult::SPENDABLE);
}
break;
- case TX_SCRIPTHASH:
+ case TxoutType::SCRIPTHASH:
{
if (sigversion != IsMineSigVersion::TOP) {
// P2SH inside P2WSH or P2SH is invalid.
@@ -143,7 +146,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
}
break;
}
- case TX_WITNESS_V0_SCRIPTHASH:
+ case TxoutType::WITNESS_V0_SCRIPTHASH:
{
if (sigversion == IsMineSigVersion::WITNESS_V0) {
// P2WSH inside P2WSH is invalid.
@@ -162,7 +165,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
break;
}
- case TX_MULTISIG:
+ case TxoutType::MULTISIG:
{
// Never treat bare multisig outputs as ours (they can still be made watchonly-though)
if (sigversion == IsMineSigVersion::TOP) {
@@ -220,6 +223,7 @@ bool LegacyScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master_key
bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys
bool keyFail = false;
CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
+ WalletBatch batch(m_storage.GetDatabase());
for (; mi != mapCryptedKeys.end(); ++mi)
{
const CPubKey &vchPubKey = (*mi).second.first;
@@ -233,6 +237,10 @@ bool LegacyScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master_key
keyPass = true;
if (fDecryptionThoroughlyChecked)
break;
+ else {
+ // Rewrite these encrypted keys with checksums
+ batch.WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]);
+ }
}
if (keyPass && keyFail)
{
@@ -290,6 +298,43 @@ bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool i
return true;
}
+bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal)
+{
+ LOCK(cs_KeyStore);
+
+ if (m_storage.IsLocked()) return false;
+
+ auto it = m_inactive_hd_chains.find(seed_id);
+ if (it == m_inactive_hd_chains.end()) {
+ return false;
+ }
+
+ CHDChain& chain = it->second;
+
+ // Top up key pool
+ int64_t target_size = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
+
+ // "size" of the keypools. Not really the size, actually the difference between index and the chain counter
+ // Since chain counter is 1 based and index is 0 based, one of them needs to be offset by 1.
+ int64_t kp_size = (internal ? chain.nInternalChainCounter : chain.nExternalChainCounter) - (index + 1);
+
+ // make sure the keypool fits the user-selected target (-keypool)
+ int64_t missing = std::max(target_size - kp_size, (int64_t) 0);
+
+ if (missing > 0) {
+ WalletBatch batch(m_storage.GetDatabase());
+ for (int64_t i = missing; i > 0; --i) {
+ GenerateNewKey(batch, chain, internal);
+ }
+ if (internal) {
+ WalletLogPrintf("inactive seed with id %s added %d internal keys\n", HexStr(seed_id), missing);
+ } else {
+ WalletLogPrintf("inactive seed with id %s added %d keys\n", HexStr(seed_id), missing);
+ }
+ }
+ return true;
+}
+
void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
{
LOCK(cs_KeyStore);
@@ -297,13 +342,28 @@ void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
for (const auto& keyid : GetAffectedKeys(script, *this)) {
std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid);
if (mi != m_pool_key_to_index.end()) {
- WalletLogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__);
+ WalletLogPrintf("%s: Detected a used keypool key, mark all keypool keys up to this key as used\n", __func__);
MarkReserveKeysAsUsed(mi->second);
if (!TopUp()) {
WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
}
}
+
+ // Find the key's metadata and check if it's seed id (if it has one) is inactive, i.e. it is not the current m_hd_chain seed id.
+ // If so, TopUp the inactive hd chain
+ auto it = mapKeyMetadata.find(keyid);
+ if (it != mapKeyMetadata.end()){
+ CKeyMetadata meta = it->second;
+ if (!meta.hd_seed_id.IsNull() && meta.hd_seed_id != m_hd_chain.seed_id) {
+ bool internal = (meta.key_origin.path[1] & ~BIP32_HARDENED_KEY_LIMIT) != 0;
+ int64_t index = meta.key_origin.path[2] & ~BIP32_HARDENED_KEY_LIMIT;
+
+ if (!TopUpInactiveHDChain(meta.hd_seed_id, index, internal)) {
+ WalletLogPrintf("%s: Adding inactive seed keys failed\n", __func__);
+ }
+ }
+ }
}
}
@@ -357,7 +417,7 @@ bool LegacyScriptPubKeyMan::SetupGeneration(bool force)
bool LegacyScriptPubKeyMan::IsHDEnabled() const
{
- return !hdChain.seed_id.IsNull();
+ return !m_hd_chain.seed_id.IsNull();
}
bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal) const
@@ -513,9 +573,8 @@ bool LegacyScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::
SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const
{
- CKeyID key_id(pkhash);
CKey key;
- if (!GetKey(key_id, key)) {
+ if (!GetKey(ToKeyID(pkhash), key)) {
return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
}
@@ -525,8 +584,11 @@ SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, con
return SigningResult::SIGNING_FAILED;
}
-TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs) const
+TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
+ if (n_signed) {
+ *n_signed = 0;
+ }
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
const CTxIn& txin = psbtx.tx->vin[i];
PSBTInput& input = psbtx.inputs.at(i);
@@ -535,11 +597,6 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
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;
- }
-
// Get the Sighash type
if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
return TransactionError::SIGHASH_MISMATCH;
@@ -557,6 +614,14 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
SignatureData sigdata;
input.FillSignatureData(sigdata);
SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, sighash_type);
+
+ bool signed_one = PSBTInputSigned(input);
+ if (n_signed && (signed_one || !sign)) {
+ // If sign is false, we assume that we _could_ sign if we get here. This
+ // will never have false negatives; it is hard to tell under what i
+ // circumstances it could have false positives.
+ (*n_signed)++;
+ }
}
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
@@ -713,8 +778,13 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pu
return true;
}
-bool LegacyScriptPubKeyMan::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
+bool LegacyScriptPubKeyMan::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid)
{
+ // Set fDecryptionThoroughlyChecked to false when the checksum is invalid
+ if (!checksum_valid) {
+ fDecryptionThoroughlyChecked = false;
+ }
+
return AddCryptedKeyInner(vchPubKey, vchCryptedSecret);
}
@@ -761,7 +831,7 @@ bool LegacyScriptPubKeyMan::HaveWatchOnly() const
static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut)
{
std::vector<std::vector<unsigned char>> solutions;
- return Solver(dest, solutions) == TX_PUBKEY &&
+ return Solver(dest, solutions) == TxoutType::PUBKEY &&
(pubKeyOut = CPubKey(solutions[0])).IsFullyValid();
}
@@ -835,13 +905,32 @@ bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript& dest, int64_t nCreateTim
return AddWatchOnly(dest);
}
-void LegacyScriptPubKeyMan::SetHDChain(const CHDChain& chain, bool memonly)
+void LegacyScriptPubKeyMan::LoadHDChain(const CHDChain& chain)
{
LOCK(cs_KeyStore);
- if (!memonly && !WalletBatch(m_storage.GetDatabase()).WriteHDChain(chain))
+ m_hd_chain = chain;
+}
+
+void LegacyScriptPubKeyMan::AddHDChain(const CHDChain& chain)
+{
+ LOCK(cs_KeyStore);
+ // Store the new chain
+ if (!WalletBatch(m_storage.GetDatabase()).WriteHDChain(chain)) {
throw std::runtime_error(std::string(__func__) + ": writing chain failed");
+ }
+ // When there's an old chain, add it as an inactive chain as we are now rotating hd chains
+ if (!m_hd_chain.seed_id.IsNull()) {
+ AddInactiveHDChain(m_hd_chain);
+ }
- hdChain = chain;
+ m_hd_chain = chain;
+}
+
+void LegacyScriptPubKeyMan::AddInactiveHDChain(const CHDChain& chain)
+{
+ LOCK(cs_KeyStore);
+ assert(!chain.seed_id.IsNull());
+ m_inactive_hd_chains[chain.seed_id] = chain;
}
bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const
@@ -920,7 +1009,7 @@ bool LegacyScriptPubKeyMan::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyO
return GetWatchPubKey(address, vchPubKeyOut);
}
-CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, bool internal)
+CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, CHDChain& hd_chain, bool internal)
{
assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET));
@@ -935,7 +1024,7 @@ CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, bool internal)
// use HD key derivation if HD was enabled during wallet creation and a seed is present
if (IsHDEnabled()) {
- DeriveNewChildKey(batch, metadata, secret, (m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false));
+ DeriveNewChildKey(batch, metadata, secret, hd_chain, (m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false));
} else {
secret.MakeNewKey(fCompressed);
}
@@ -957,9 +1046,7 @@ CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, bool internal)
return pubkey;
}
-const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
-
-void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal)
+void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal)
{
// for now we use a fixed keypath scheme of m/0'/0'/k
CKey seed; //seed (256bit)
@@ -969,7 +1056,7 @@ void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata&
CExtKey childKey; //key at m/0'/0'/<n>'
// try to get the seed
- if (!GetKey(hdChain.seed_id, seed))
+ if (!GetKey(hd_chain.seed_id, seed))
throw std::runtime_error(std::string(__func__) + ": seed not found");
masterKey.SetSeed(seed.begin(), seed.size());
@@ -988,30 +1075,30 @@ void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata&
// childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range
// example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
if (internal) {
- chainChildKey.Derive(childKey, hdChain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
- metadata.hdKeypath = "m/0'/1'/" + ToString(hdChain.nInternalChainCounter) + "'";
+ chainChildKey.Derive(childKey, hd_chain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
+ metadata.hdKeypath = "m/0'/1'/" + ToString(hd_chain.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++;
+ metadata.key_origin.path.push_back(hd_chain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
+ hd_chain.nInternalChainCounter++;
}
else {
- chainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
- metadata.hdKeypath = "m/0'/0'/" + ToString(hdChain.nExternalChainCounter) + "'";
+ chainChildKey.Derive(childKey, hd_chain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
+ metadata.hdKeypath = "m/0'/0'/" + ToString(hd_chain.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++;
+ metadata.key_origin.path.push_back(hd_chain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
+ hd_chain.nExternalChainCounter++;
}
} while (HaveKey(childKey.key.GetPubKey().GetID()));
secret = childKey.key;
- metadata.hd_seed_id = hdChain.seed_id;
+ metadata.hd_seed_id = hd_chain.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");
+ if (hd_chain.seed_id == m_hd_chain.seed_id && !batch.WriteHDChain(hd_chain))
+ throw std::runtime_error(std::string(__func__) + ": writing HD chain model failed");
}
void LegacyScriptPubKeyMan::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
@@ -1087,7 +1174,7 @@ void LegacyScriptPubKeyMan::SetHDSeed(const CPubKey& seed)
CHDChain newHdChain;
newHdChain.nVersion = m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE;
newHdChain.seed_id = seed.GetID();
- SetHDChain(newHdChain, false);
+ AddHDChain(newHdChain);
NotifyCanGetAddressesChanged();
WalletBatch batch(m_storage.GetDatabase());
m_storage.UnsetBlankWalletFlag(batch);
@@ -1166,7 +1253,7 @@ bool LegacyScriptPubKeyMan::TopUp(unsigned int kpSize)
internal = true;
}
- CPubKey pubkey(GenerateNewKey(batch, internal));
+ CPubKey pubkey(GenerateNewKey(batch, m_hd_chain, internal));
AddKeypoolPubkeyWithDB(pubkey, internal, batch);
}
if (missingInternal + missingExternal > 0) {
@@ -1239,7 +1326,7 @@ bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey& result, const OutputType typ
if (!ReserveKeyFromKeyPool(nIndex, keypool, internal) && !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (m_storage.IsLocked()) return false;
WalletBatch batch(m_storage.GetDatabase());
- result = GenerateNewKey(batch, internal);
+ result = GenerateNewKey(batch, m_hd_chain, internal);
return true;
}
KeepDestination(nIndex, type);
@@ -1497,7 +1584,7 @@ std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const
return set_address;
}
-void LegacyScriptPubKeyMan::SetType(OutputType type, bool internal) {}
+void LegacyScriptPubKeyMan::SetInternal(bool internal) {}
bool DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error)
{
@@ -1509,7 +1596,9 @@ bool DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDest
{
LOCK(cs_desc_man);
assert(m_wallet_descriptor.descriptor->IsSingleType()); // This is a combo descriptor which should not be an active descriptor
- if (type != m_address_type) {
+ Optional<OutputType> desc_addr_type = m_wallet_descriptor.descriptor->GetOutputType();
+ assert(desc_addr_type);
+ if (type != *desc_addr_type) {
throw std::runtime_error(std::string(__func__) + ": Types are inconsistent");
}
@@ -1777,7 +1866,7 @@ bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const
}
}
-bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_key)
+bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_key, OutputType addr_type)
{
LOCK(cs_desc_man);
assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
@@ -1794,7 +1883,7 @@ bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_
// Build descriptor string
std::string desc_prefix;
std::string desc_suffix = "/*)";
- switch (m_address_type) {
+ switch (addr_type) {
case OutputType::LEGACY: {
desc_prefix = "pkh(" + xpub + "/44'";
break;
@@ -1808,8 +1897,8 @@ bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_
desc_prefix = "wpkh(" + xpub + "/84'";
break;
}
- default: assert(false);
- }
+ } // no default case, so the compiler can warn about missing cases
+ assert(!desc_prefix.empty());
// Mainnet derives at 0', testnet and regtest derive at 1'
if (Params().IsTestChain()) {
@@ -1970,9 +2059,8 @@ SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message,
return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
}
- CKeyID key_id(pkhash);
CKey key;
- if (!keys->GetKey(key_id, key)) {
+ if (!keys->GetKey(ToKeyID(pkhash), key)) {
return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
}
@@ -1982,8 +2070,11 @@ SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message,
return SigningResult::OK;
}
-TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs) const
+TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
+ if (n_signed) {
+ *n_signed = 0;
+ }
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
const CTxIn& txin = psbtx.tx->vin[i];
PSBTInput& input = psbtx.inputs.at(i);
@@ -1992,11 +2083,6 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
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;
- }
-
// Get the Sighash type
if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
return TransactionError::SIGHASH_MISMATCH;
@@ -2035,6 +2121,14 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
}
SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, sighash_type);
+
+ bool signed_one = PSBTInputSigned(input);
+ if (n_signed && (signed_one || !sign)) {
+ // If sign is false, we assume that we _could_ sign if we get here. This
+ // will never have false negatives; it is hard to tell under what i
+ // circumstances it could have false positives.
+ (*n_signed)++;
+ }
}
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
@@ -2076,9 +2170,8 @@ uint256 DescriptorScriptPubKeyMan::GetID() const
return id;
}
-void DescriptorScriptPubKeyMan::SetType(OutputType type, bool internal)
+void DescriptorScriptPubKeyMan::SetInternal(bool internal)
{
- this->m_address_type = type;
this->m_internal = internal;
}
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 4c002edf2d..a96d971734 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -18,6 +18,8 @@
#include <boost/signals2/signal.hpp>
+#include <unordered_map>
+
enum class OutputType;
struct bilingual_str;
@@ -110,40 +112,52 @@ public:
CKeyPool();
CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
- ADD_SERIALIZE_METHODS;
+ template<typename Stream>
+ void Serialize(Stream& s) const
+ {
+ int nVersion = s.GetVersion();
+ if (!(s.GetType() & SER_GETHASH)) {
+ s << nVersion;
+ }
+ s << nTime << vchPubKey << fInternal << m_pre_split;
+ }
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
+ template<typename Stream>
+ void Unserialize(Stream& s)
+ {
int nVersion = s.GetVersion();
- if (!(s.GetType() & SER_GETHASH))
- READWRITE(nVersion);
- READWRITE(nTime);
- READWRITE(vchPubKey);
- if (ser_action.ForRead()) {
- try {
- READWRITE(fInternal);
- }
- catch (std::ios_base::failure&) {
- /* flag as external address if we can't read the internal boolean
- (this will be the case for any wallet before the HD chain split version) */
- fInternal = false;
- }
- try {
- READWRITE(m_pre_split);
- }
- catch (std::ios_base::failure&) {
- /* flag as postsplit address if we can't read the m_pre_split boolean
- (this will be the case for any wallet that upgrades to HD chain split)*/
- m_pre_split = false;
- }
+ if (!(s.GetType() & SER_GETHASH)) {
+ s >> nVersion;
+ }
+ s >> nTime >> vchPubKey;
+ try {
+ s >> fInternal;
+ } catch (std::ios_base::failure&) {
+ /* flag as external address if we can't read the internal boolean
+ (this will be the case for any wallet before the HD chain split version) */
+ fInternal = false;
}
- else {
- READWRITE(fInternal);
- READWRITE(m_pre_split);
+ try {
+ s >> m_pre_split;
+ } catch (std::ios_base::failure&) {
+ /* flag as postsplit address if we can't read the m_pre_split boolean
+ (this will be the case for any wallet that upgrades to HD chain split) */
+ m_pre_split = false;
}
}
};
+class KeyIDHasher
+{
+public:
+ KeyIDHasher() {}
+
+ size_t operator()(const CKeyID& id) const
+ {
+ return id.GetUint64(0);
+ }
+};
+
/*
* A class implementing ScriptPubKeyMan manages some (or all) scriptPubKeys used in a wallet.
* It contains the scripts and keys related to the scriptPubKeys it manages.
@@ -220,11 +234,11 @@ public:
/** Sign a message with the given script */
virtual SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { return SigningResult::SIGNING_FAILED; };
/** Adds script and derivation path information to a PSBT, and optionally signs it. */
- virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const { return TransactionError::INVALID_PSBT; }
+ virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const { return TransactionError::INVALID_PSBT; }
virtual uint256 GetID() const { return uint256(); }
- virtual void SetType(OutputType type, bool internal) {}
+ virtual void SetInternal(bool internal) {}
/** Prepends the wallet name in logging output to ease debugging in multi-wallet use cases */
template<typename... Params>
@@ -243,7 +257,7 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
{
private:
//! keeps track of whether Unlock has run a thorough check before
- bool fDecryptionThoroughlyChecked = false;
+ bool fDecryptionThoroughlyChecked = true;
using WatchOnlySet = std::set<CScript>;
using WatchKeyMap = std::map<CKeyID, CPubKey>;
@@ -288,10 +302,11 @@ private:
bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info);
/* the HD chain data model (external chain counters) */
- CHDChain hdChain;
+ CHDChain m_hd_chain;
+ std::unordered_map<CKeyID, CHDChain, KeyIDHasher> m_inactive_hd_chains;
/* 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_KeyStore);
+ void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
std::set<int64_t> setInternalKeyPool GUARDED_BY(cs_KeyStore);
std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_KeyStore);
@@ -320,6 +335,18 @@ private:
*/
bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
+ /**
+ * Like TopUp() but adds keys for inactive HD chains.
+ * Ensures that there are at least -keypool number of keys derived after the given index.
+ *
+ * @param seed_id the CKeyID for the HD seed.
+ * @param index the index to start generating keys from
+ * @param internal whether the internal chain should be used. true for internal chain, false for external chain.
+ *
+ * @return true if seed was found and keys were derived. false if unable to derive seeds
+ */
+ bool TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal);
+
public:
using ScriptPubKeyMan::ScriptPubKeyMan;
@@ -366,11 +393,11 @@ public:
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
uint256 GetID() const override;
- void SetType(OutputType type, bool internal) override;
+ void SetInternal(bool internal) override;
// Map from Key ID to key metadata.
std::map<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_KeyStore);
@@ -385,7 +412,7 @@ public:
//! Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
//! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
- bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
+ bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid);
void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
//! Adds a CScript to the store
bool LoadCScript(const CScript& redeemScript);
@@ -393,11 +420,14 @@ public:
void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata);
void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata);
//! Generate a new key
- CPubKey GenerateNewKey(WalletBatch& batch, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
+ CPubKey GenerateNewKey(WalletBatch& batch, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
- /* Set the HD chain model (chain child index counters) */
- void SetHDChain(const CHDChain& chain, bool memonly);
- const CHDChain& GetHDChain() const { return hdChain; }
+ /* Set the HD chain model (chain child index counters) and writes it to the database */
+ void AddHDChain(const CHDChain& chain);
+ //! Load a HD chain model (used by LoadWallet)
+ void LoadHDChain(const CHDChain& chain);
+ const CHDChain& GetHDChain() const { return m_hd_chain; }
+ void AddInactiveHDChain(const CHDChain& chain);
//! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
bool LoadWatchOnly(const CScript &dest);
@@ -497,14 +527,11 @@ private:
PubKeyMap m_map_pubkeys GUARDED_BY(cs_desc_man);
int32_t m_max_cached_index = -1;
- OutputType m_address_type;
bool m_internal = false;
KeyMap m_map_keys GUARDED_BY(cs_desc_man);
CryptedKeyMap m_map_crypted_keys GUARDED_BY(cs_desc_man);
- bool SetCrypted();
-
//! keeps track of whether Unlock has run a thorough check before
bool m_decryption_thoroughly_checked = false;
@@ -524,9 +551,9 @@ public:
: ScriptPubKeyMan(storage),
m_wallet_descriptor(descriptor)
{}
- DescriptorScriptPubKeyMan(WalletStorage& storage, OutputType address_type, bool internal)
+ DescriptorScriptPubKeyMan(WalletStorage& storage, bool internal)
: ScriptPubKeyMan(storage),
- m_address_type(address_type), m_internal(internal)
+ m_internal(internal)
{}
mutable RecursiveMutex cs_desc_man;
@@ -551,7 +578,7 @@ public:
bool IsHDEnabled() const override;
//! Setup descriptors based on the given CExtkey
- bool SetupDescriptorGeneration(const CExtKey& master_key);
+ bool SetupDescriptorGeneration(const CExtKey& master_key, OutputType addr_type);
bool HavePrivateKeys() const override;
@@ -571,11 +598,11 @@ public:
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
uint256 GetID() const override;
- void SetType(OutputType type, bool internal) override;
+ void SetInternal(bool internal) override;
void SetCache(const DescriptorCache& cache);
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 657d0828f2..1deedede4c 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -29,7 +29,7 @@ typedef std::set<CInputCoin> CoinSet;
static std::vector<COutput> vCoins;
static NodeContext testNode;
static auto testChain = interfaces::MakeChain(testNode);
-static CWallet testWallet(testChain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+static CWallet testWallet(testChain.get(), WalletLocation(), CreateDummyWalletDatabase());
static CAmount balance = 0;
CoinEligibilityFilter filter_standard(1, 6, 0);
@@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// Make sure that can use BnB when there are preset inputs
empty_wallet();
{
- std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock());
+ std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), CreateMockWalletDatabase());
bool firstRun;
wallet->LoadWallet(firstRun);
wallet->SetupLegacyScriptPubKeyMan();
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index f4a4c9fa7c..8f0083cd2e 100644
--- a/src/wallet/test/db_tests.cpp
+++ b/src/wallet/test/db_tests.cpp
@@ -8,7 +8,7 @@
#include <fs.h>
#include <test/util/setup_common.h>
-#include <wallet/db.h>
+#include <wallet/bdb.h>
BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
index 797a0d634f..35bd965673 100644
--- a/src/wallet/test/init_test_fixture.cpp
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -3,13 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <fs.h>
+#include <util/check.h>
#include <util/system.h>
#include <wallet/test/init_test_fixture.h>
-InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName): BasicTestingSetup(chainName)
+InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
- m_chain_client = MakeWalletClient(*m_chain, {});
+ m_chain_client = MakeWalletClient(*m_chain, *Assert(m_node.args), {});
std::string sep;
sep += fs::path::preferred_separator;
diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h
index 6ba7d66b7c..c95b4f1f6e 100644
--- a/src/wallet/test/init_test_fixture.h
+++ b/src/wallet/test/init_test_fixture.h
@@ -18,7 +18,6 @@ struct InitWalletDirTestingSetup: public BasicTestingSetup {
fs::path m_datadir;
fs::path m_cwd;
std::map<std::string, fs::path> m_walletdir_path_cases;
- NodeContext m_node;
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
std::unique_ptr<interfaces::ChainClient> m_chain_client;
};
diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp
index 4c0e4dc653..e416f16044 100644
--- a/src/wallet/test/ismine_tests.cpp
+++ b/src/wallet/test/ismine_tests.cpp
@@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK compressed
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
@@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK uncompressed
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
@@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH compressed
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0]));
@@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH uncompressed
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey));
@@ -103,7 +103,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2SH (invalid)
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2WSH (invalid)
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -163,11 +163,11 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH inside P2WSH (invalid)
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
- CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(PKHash(pubkeys[0])));
+ CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0]));
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript));
@@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2WSH inside P2WSH (invalid)
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -197,12 +197,12 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH compressed
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
- scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(PKHash(pubkeys[0])));
+ scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0]));
// Keystore implicitly has key and P2SH redeemScript
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey));
@@ -212,12 +212,12 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH uncompressed
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
- scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(PKHash(uncompressedPubkey)));
+ scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(uncompressedPubkey));
// Keystore has key, but no P2SH redeemScript
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
@@ -231,7 +231,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// scriptPubKey multisig
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -262,7 +262,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH multisig
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with compressed keys
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -309,7 +309,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with uncompressed key
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -335,7 +335,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig wrapped in P2SH
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -362,7 +362,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// OP_RETURN
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -376,7 +376,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unspendable
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -390,7 +390,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unknown
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// Nonstandard
{
- CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index b4c65a8665..ce7e661b67 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -63,8 +63,8 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
// Get the final tx
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- std::string final_hex = HexStr(ssTx.begin(), ssTx.end());
- BOOST_CHECK_EQUAL(final_hex, "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000");
+ std::string final_hex = HexStr(ssTx);
+ BOOST_CHECK_EQUAL(final_hex, "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001008a020000000158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8876500000001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000");
// Mutate the transaction so that one of the inputs is invalid
psbtx.tx->vin[0].prevout.n = 2;
diff --git a/src/wallet/test/scriptpubkeyman_tests.cpp b/src/wallet/test/scriptpubkeyman_tests.cpp
index 757865ea37..4f12079768 100644
--- a/src/wallet/test/scriptpubkeyman_tests.cpp
+++ b/src/wallet/test/scriptpubkeyman_tests.cpp
@@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(CanProvide)
// Set up wallet and keyman variables.
NodeContext node;
std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node);
- CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan();
// Make a 1 of 2 multisig script
diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp
index 97f8c94fa6..10ddfa22ef 100644
--- a/src/wallet/test/wallet_crypto_tests.cpp
+++ b/src/wallet/test/wallet_crypto_tests.cpp
@@ -24,10 +24,10 @@ static void TestPassphraseSingle(const std::vector<unsigned char>& vchSalt, cons
if(!correctKey.empty())
BOOST_CHECK_MESSAGE(memcmp(crypt.vchKey.data(), correctKey.data(), crypt.vchKey.size()) == 0, \
- HexStr(crypt.vchKey.begin(), crypt.vchKey.end()) + std::string(" != ") + HexStr(correctKey.begin(), correctKey.end()));
+ HexStr(crypt.vchKey) + std::string(" != ") + HexStr(correctKey));
if(!correctIV.empty())
BOOST_CHECK_MESSAGE(memcmp(crypt.vchIV.data(), correctIV.data(), crypt.vchIV.size()) == 0,
- HexStr(crypt.vchIV.begin(), crypt.vchIV.end()) + std::string(" != ") + HexStr(correctIV.begin(), correctIV.end()));
+ HexStr(crypt.vchIV) + std::string(" != ") + HexStr(correctIV));
}
static void TestPassphrase(const std::vector<unsigned char>& vchSalt, const SecureString& passphrase, uint32_t rounds,
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index 7ba3148ff3..44f9eb5ddd 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -6,7 +6,7 @@
WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
: TestingSetup(chainName),
- m_wallet(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock())
+ m_wallet(m_chain.get(), WalletLocation(), CreateMockWalletDatabase())
{
bool fFirstRun;
m_wallet.LoadWallet(fFirstRun);
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index a294935b64..99d7cfe921 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -10,18 +10,18 @@
#include <interfaces/chain.h>
#include <interfaces/wallet.h>
#include <node/context.h>
+#include <util/check.h>
#include <wallet/wallet.h>
#include <memory>
/** Testing setup and teardown for wallet.
*/
-struct WalletTestingSetup: public TestingSetup {
+struct WalletTestingSetup : public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
- NodeContext m_node;
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
- std::unique_ptr<interfaces::ChainClient> m_chain_client = interfaces::MakeWalletClient(*m_chain, {});
+ std::unique_ptr<interfaces::ChainClient> m_chain_client = interfaces::MakeWalletClient(*m_chain, *Assert(m_node.args), {});
CWallet m_wallet;
std::unique_ptr<interfaces::Handler> m_chain_notifications_handler;
};
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index d888b8f842..9cc847b2d0 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -15,6 +15,7 @@
#include <rpc/server.h>
#include <test/util/logging.h>
#include <test/util/setup_common.h>
+#include <util/ref.h>
#include <util/translation.h>
#include <validation.h>
#include <wallet/coincontrol.h>
@@ -79,7 +80,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions fails to read an unknown start block.
{
- CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -98,7 +99,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
- CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -117,14 +118,14 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Prune the older block file.
{
LOCK(cs_main);
- PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ Assert(m_node.chainman)->PruneOneBlockFile(oldTip->GetBlockPos().nFile);
}
UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
- CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -143,13 +144,13 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Prune the remaining block file.
{
LOCK(cs_main);
- PruneOneBlockFile(newTip->GetBlockPos().nFile);
+ Assert(m_node.chainman)->PruneOneBlockFile(newTip->GetBlockPos().nFile);
}
UnlinkPrunedFiles({newTip->GetBlockPos().nFile});
// Verify ScanForWalletTransactions scans no blocks.
{
- CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -180,7 +181,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// Prune the older block file.
{
LOCK(cs_main);
- PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ Assert(m_node.chainman)->PruneOneBlockFile(oldTip->GetBlockPos().nFile);
}
UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
@@ -188,7 +189,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.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
AddWallet(wallet);
@@ -208,7 +209,8 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1);
key.pushKV("internal", UniValue(true));
keys.push_back(key);
- JSONRPCRequest request;
+ util::Ref context;
+ JSONRPCRequest request(context);
request.params.setArray();
request.params.push_back(keys);
@@ -252,7 +254,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Import key into wallet and call dumpwallet to create backup file.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
{
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
@@ -262,7 +264,8 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
AddWallet(wallet);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
}
- JSONRPCRequest request;
+ util::Ref context;
+ JSONRPCRequest request(context);
request.params.setArray();
request.params.push_back(backup_file);
@@ -273,11 +276,12 @@ 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.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
LOCK(wallet->cs_wallet);
wallet->SetupLegacyScriptPubKeyMan();
- JSONRPCRequest request;
+ util::Ref context;
+ JSONRPCRequest request(context);
request.params.setArray();
request.params.push_back(backup_file);
AddWallet(wallet);
@@ -308,7 +312,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
NodeContext node;
auto chain = interfaces::MakeChain(node);
- CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
CWalletTx wtx(&wallet, m_coinbase_txns.back());
@@ -329,7 +333,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 50*COIN);
}
-static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
+static int64_t AddTx(ChainstateManager& chainman, CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
{
CMutableTransaction tx;
CWalletTx::Confirmation confirm;
@@ -337,7 +341,8 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
SetMockTime(mockTime);
CBlockIndex* block = nullptr;
if (blockTime > 0) {
- auto inserted = ::BlockIndex().emplace(GetRandHash(), new CBlockIndex);
+ LOCK(cs_main);
+ auto inserted = chainman.BlockIndex().emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second);
const uint256& hash = inserted.first->first;
block = inserted.first->second;
@@ -359,24 +364,24 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
{
// New transaction should use clock time if lower than block time.
- BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 100, 120), 100);
+ BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 1, 100, 120), 100);
// Test that updating existing transaction does not change smart time.
- BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 200, 220), 100);
+ BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 1, 200, 220), 100);
// New transaction should use clock time if there's no block time.
- BOOST_CHECK_EQUAL(AddTx(m_wallet, 2, 300, 0), 300);
+ BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 2, 300, 0), 300);
// New transaction should use block time if lower than clock time.
- BOOST_CHECK_EQUAL(AddTx(m_wallet, 3, 420, 400), 400);
+ BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 3, 420, 400), 400);
// New transaction should use latest entry time if higher than
// min(block time, clock time).
- BOOST_CHECK_EQUAL(AddTx(m_wallet, 4, 500, 390), 400);
+ BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 4, 500, 390), 400);
// If there are future entries, new transaction should use time of the
// newest entry that is no more than 300 seconds ahead of the clock time.
- BOOST_CHECK_EQUAL(AddTx(m_wallet, 5, 50, 600), 300);
+ BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 5, 50, 600), 300);
// Reset mock time for other tests.
SetMockTime(0);
@@ -482,7 +487,7 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock());
+ wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), CreateMockWalletDatabase());
{
LOCK2(wallet->cs_wallet, ::cs_main);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -531,7 +536,6 @@ public:
return it->second;
}
- NodeContext m_node;
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
std::unique_ptr<CWallet> wallet;
};
@@ -601,7 +605,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{
NodeContext node;
auto chain = interfaces::MakeChain(node);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
wallet->SetMinVersion(FEATURE_LATEST);
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
@@ -787,4 +791,37 @@ BOOST_FIXTURE_TEST_CASE(CreateWalletFromFile, TestChain100Setup)
TestUnloadWallet(std::move(wallet));
}
+BOOST_FIXTURE_TEST_CASE(ZapSelectTx, TestChain100Setup)
+{
+ auto chain = interfaces::MakeChain(m_node);
+ auto wallet = TestLoadWallet(*chain);
+ CKey key;
+ key.MakeNewKey(true);
+ AddKey(*wallet, key);
+
+ std::string error;
+ m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
+ auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
+ CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
+
+ SyncWithValidationInterfaceQueue();
+
+ {
+ auto block_hash = block_tx.GetHash();
+ auto prev_hash = m_coinbase_txns[0]->GetHash();
+
+ LOCK(wallet->cs_wallet);
+ BOOST_CHECK(wallet->HasWalletSpend(prev_hash));
+ BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 1u);
+
+ std::vector<uint256> vHashIn{ block_hash }, vHashOut;
+ BOOST_CHECK_EQUAL(wallet->ZapSelectTx(vHashIn, vHashOut), DBErrors::LOAD_OK);
+
+ BOOST_CHECK(!wallet->HasWalletSpend(prev_hash));
+ BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 0u);
+ }
+
+ TestUnloadWallet(std::move(wallet));
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 2b45c6a536..8eec00993f 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -21,6 +21,7 @@
#include <script/descriptor.h>
#include <script/script.h>
#include <script/signingprovider.h>
+#include <txmempool.h>
#include <util/bip32.h>
#include <util/check.h>
#include <util/error.h>
@@ -76,12 +77,6 @@ bool RemoveWallet(const std::shared_ptr<CWallet>& wallet)
return true;
}
-bool HasWallets()
-{
- LOCK(cs_wallets);
- return !vpwallets.empty();
-}
-
std::vector<std::shared_ptr<CWallet>> GetWallets()
{
LOCK(cs_wallets);
@@ -104,9 +99,11 @@ std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet)
return interfaces::MakeHandler([it] { LOCK(cs_wallets); g_load_wallet_fns.erase(it); });
}
+static Mutex g_loading_wallet_mutex;
static Mutex g_wallet_release_mutex;
static std::condition_variable g_wallet_release_cv;
-static std::set<std::string> g_unloading_wallet_set;
+static std::set<std::string> g_loading_wallet_set GUARDED_BY(g_loading_wallet_mutex);
+static std::set<std::string> g_unloading_wallet_set GUARDED_BY(g_wallet_release_mutex);
// Custom deleter for shared_ptr<CWallet>.
static void ReleaseWallet(CWallet* wallet)
@@ -150,10 +147,11 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
}
}
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings)
+namespace {
+std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
try {
- if (!CWallet::Verify(chain, location, false, error, warnings)) {
+ if (!CWallet::Verify(chain, location, error, warnings)) {
error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
return nullptr;
}
@@ -171,6 +169,19 @@ std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocati
return nullptr;
}
}
+} // namespace
+
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings)
+{
+ auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(location.GetName()));
+ if (!result.second) {
+ error = Untranslated("Wallet already being loading.");
+ return nullptr;
+ }
+ auto wallet = LoadWalletInternal(chain, location, error, warnings);
+ WITH_LOCK(g_loading_wallet_mutex, g_loading_wallet_set.erase(result.first));
+ return wallet;
+}
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
@@ -195,7 +206,7 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
}
// Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
- if (!CWallet::Verify(chain, location, false, error, warnings)) {
+ if (!CWallet::Verify(chain, location, error, warnings)) {
error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
return WalletCreationStatus::CREATION_FAILED;
}
@@ -751,7 +762,6 @@ void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned
bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
{
AssertLockHeld(cs_wallet);
- CTxDestination dst;
const CWalletTx* srctx = GetWalletTx(hash);
if (srctx) {
assert(srctx->tx->vout.size() > n);
@@ -1100,23 +1110,52 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmatio
MarkInputsDirty(ptx);
}
-void CWallet::transactionAddedToMempool(const CTransactionRef& ptx) {
+void CWallet::transactionAddedToMempool(const CTransactionRef& tx) {
LOCK(cs_wallet);
- CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, /* block_height */ 0, {}, /* nIndex */ 0);
- SyncTransaction(ptx, confirm);
+ SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
- auto it = mapWallet.find(ptx->GetHash());
+ auto it = mapWallet.find(tx->GetHash());
if (it != mapWallet.end()) {
it->second.fInMempool = true;
}
}
-void CWallet::transactionRemovedFromMempool(const CTransactionRef &ptx) {
+void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {
LOCK(cs_wallet);
- auto it = mapWallet.find(ptx->GetHash());
+ auto it = mapWallet.find(tx->GetHash());
if (it != mapWallet.end()) {
it->second.fInMempool = false;
}
+ // Handle transactions that were removed from the mempool because they
+ // conflict with transactions in a newly connected block.
+ if (reason == MemPoolRemovalReason::CONFLICT) {
+ // Call SyncNotifications, so external -walletnotify notifications will
+ // be triggered for these transactions. Set Status::UNCONFIRMED instead
+ // of Status::CONFLICTED for a few reasons:
+ //
+ // 1. The transactionRemovedFromMempool callback does not currently
+ // provide the conflicting block's hash and height, and for backwards
+ // compatibility reasons it may not be not safe to store conflicted
+ // wallet transactions with a null block hash. See
+ // https://github.com/bitcoin/bitcoin/pull/18600#discussion_r420195993.
+ // 2. For most of these transactions, the wallet's internal conflict
+ // detection in the blockConnected handler will subsequently call
+ // MarkConflicted and update them with CONFLICTED status anyway. This
+ // applies to any wallet transaction that has inputs spent in the
+ // block, or that has ancestors in the wallet with inputs spent by
+ // the block.
+ // 3. Longstanding behavior since the sync implementation in
+ // https://github.com/bitcoin/bitcoin/pull/9371 and the prior sync
+ // implementation before that was to mark these transactions
+ // unconfirmed rather than conflicted.
+ //
+ // Nothing described above should be seen as an unchangeable requirement
+ // when improving this code in the future. The wallet's heuristics for
+ // distinguishing between conflicted and unconfirmed transactions are
+ // imperfect, and could be improved in general, see
+ // https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking
+ SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
+ }
}
void CWallet::blockConnected(const CBlock& block, int height)
@@ -1127,9 +1166,8 @@ void CWallet::blockConnected(const CBlock& block, int height)
m_last_block_processed_height = height;
m_last_block_processed = block_hash;
for (size_t index = 0; index < block.vtx.size(); index++) {
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, block_hash, index);
- SyncTransaction(block.vtx[index], confirm);
- transactionRemovedFromMempool(block.vtx[index]);
+ SyncTransaction(block.vtx[index], {CWalletTx::Status::CONFIRMED, height, block_hash, (int)index});
+ transactionRemovedFromMempool(block.vtx[index], MemPoolRemovalReason::BLOCK);
}
}
@@ -1144,8 +1182,7 @@ void CWallet::blockDisconnected(const CBlock& block, int height)
m_last_block_processed_height = height - 1;
m_last_block_processed = block.hashPrevBlock;
for (const CTransactionRef& ptx : block.vtx) {
- CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, /* block_height */ 0, {}, /* nIndex */ 0);
- SyncTransaction(ptx, confirm);
+ SyncTransaction(ptx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
}
}
@@ -1385,19 +1422,28 @@ bool CWallet::IsWalletFlagSet(uint64_t flag) const
return (m_wallet_flags & flag);
}
-bool CWallet::SetWalletFlags(uint64_t overwriteFlags, bool memonly)
+bool CWallet::LoadWalletFlags(uint64_t flags)
{
LOCK(cs_wallet);
- m_wallet_flags = overwriteFlags;
- if (((overwriteFlags & KNOWN_WALLET_FLAGS) >> 32) ^ (overwriteFlags >> 32)) {
+ if (((flags & KNOWN_WALLET_FLAGS) >> 32) ^ (flags >> 32)) {
// contains unknown non-tolerable wallet flags
return false;
}
- if (!memonly && !WalletBatch(*database).WriteWalletFlags(m_wallet_flags)) {
+ m_wallet_flags = flags;
+
+ return true;
+}
+
+bool CWallet::AddWalletFlags(uint64_t flags)
+{
+ LOCK(cs_wallet);
+ // We should never be writing unknown non-tolerable wallet flags
+ assert(((flags & KNOWN_WALLET_FLAGS) >> 32) == (flags >> 32));
+ if (!WalletBatch(*database).WriteWalletFlags(flags)) {
throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
}
- return true;
+ return LoadWalletFlags(flags);
}
int64_t CWalletTx::GetTxTime() const
@@ -1685,8 +1731,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
break;
}
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, block_height, block_hash, posInBlock);
- SyncTransaction(block.vtx[posInBlock], confirm, fUpdate);
+ SyncTransaction(block.vtx[posInBlock], {CWalletTx::Status::CONFIRMED, block_height, block_hash, (int)posInBlock}, fUpdate);
}
// scan succeeded, record block as most recent successfully scanned
result.last_scanned_block = block_hash;
@@ -1982,10 +2027,6 @@ void CWallet::ResendWalletTransactions()
nNextResend = GetTime() + (12 * 60 * 60) + GetRand(24 * 60 * 60);
if (fFirst) return;
- // Only do it if there's been a new block since last time
- if (m_best_block_time < nLastResend) return;
- nLastResend = GetTime();
-
int submitted_tx_count = 0;
{ // cs_wallet scope
@@ -2144,6 +2185,11 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
}
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
+ // Only consider selected coins if add_inputs is false
+ if (coinControl && !coinControl->m_add_inputs && !coinControl->IsSelected(COutPoint(entry.first, i))) {
+ continue;
+ }
+
if (wtx.tx->vout[i].nValue < nMinimumAmount || wtx.tx->vout[i].nValue > nMaximumAmount)
continue;
@@ -2455,8 +2501,11 @@ bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint,
return false;
}
-TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs) const
+TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs, size_t * n_signed) const
{
+ if (n_signed) {
+ *n_signed = 0;
+ }
LOCK(cs_wallet);
// Get all of the previous transactions
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
@@ -2467,13 +2516,8 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
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()) {
+ if (!input.non_witness_utxo) {
const uint256& txhash = txin.prevout.hash;
const auto it = mapWallet.find(txhash);
if (it != mapWallet.end()) {
@@ -2487,10 +2531,15 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
// Fill in information from ScriptPubKeyMans
for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
- TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs);
+ int n_signed_this_spkm = 0;
+ TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs, &n_signed_this_spkm);
if (res != TransactionError::OK) {
return res;
}
+
+ if (n_signed) {
+ (*n_signed) += n_signed_this_spkm;
+ }
}
// Complete if every input is now signed
@@ -2624,11 +2673,11 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uin
return locktime;
}
-OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend)
+OutputType CWallet::TransactionChangeType(const Optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend)
{
// If -changetype is specified, always use that change type.
- if (change_type != OutputType::CHANGE_AUTO) {
- return change_type;
+ if (change_type) {
+ return *change_type;
}
// if m_default_address_type is legacy, use legacy address as change (even
@@ -2725,6 +2774,12 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
// Get the fee rate to use effective values in coin selection
CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc);
+ // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
+ // provided one
+ if (coin_control.m_feerate && nFeeRateNeeded > *coin_control.m_feerate) {
+ error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(), nFeeRateNeeded.ToString());
+ return false;
+ }
nFeeRet = 0;
bool pick_new_inputs = true;
@@ -2974,7 +3029,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
}
if (nFeeRet > m_default_max_tx_fee) {
- error = Untranslated(TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED));
+ error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
return false;
}
@@ -3074,9 +3129,11 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
{
AssertLockHeld(cs_wallet);
DBErrors nZapSelectTxRet = WalletBatch(*database, "cr+").ZapSelectTx(vHashIn, vHashOut);
- for (uint256 hash : vHashOut) {
+ for (const uint256& hash : vHashOut) {
const auto& it = mapWallet.find(hash);
wtxOrdered.erase(it->second.m_it_wtxOrdered);
+ for (const auto& txin : it->second.tx->vin)
+ mapTxSpends.erase(txin.prevout);
mapWallet.erase(it);
NotifyTransactionChanged(this, hash, CT_DELETED);
}
@@ -3654,7 +3711,7 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values;
}
-bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, bilingual_str& error_string, std::vector<bilingual_str>& warnings)
+bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings)
{
// Do some checking on wallet path. It should be either a:
//
@@ -3683,27 +3740,14 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
}
// Keep same database environment instance across Verify/Recover calls below.
- std::unique_ptr<WalletDatabase> database = WalletDatabase::Create(wallet_path);
+ std::unique_ptr<WalletDatabase> database = CreateWalletDatabase(wallet_path);
try {
- if (!WalletBatch::VerifyEnvironment(wallet_path, error_string)) {
- return false;
- }
+ return database->Verify(error_string);
} catch (const fs::filesystem_error& e) {
error_string = Untranslated(strprintf("Error loading wallet %s. %s", location.GetName(), fsbridge::get_filesystem_error_message(e)));
return false;
}
-
- if (salvage_wallet) {
- // Recover readable keypairs:
- CWallet dummyWallet(&chain, WalletLocation(), WalletDatabase::CreateDummy());
- std::string backup_filename;
- if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
- return false;
- }
- }
-
- return WalletBatch::VerifyDatabaseFile(wallet_path, warnings, error_string);
}
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags)
@@ -3716,7 +3760,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
chain.initMessage(_("Zapping all transactions from wallet...").translated);
- std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(&chain, location, WalletDatabase::Create(location.GetPath()));
+ std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(&chain, location, CreateWalletDatabase(location.GetPath()));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DBErrors::LOAD_OK) {
error = strprintf(_("Error loading %s: Wallet corrupted"), walletFile);
@@ -3730,7 +3774,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
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, CreateWalletDatabase(location.GetPath())), ReleaseWallet);
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DBErrors::LOAD_OK) {
if (nLoadWalletRet == DBErrors::CORRUPT) {
@@ -3763,7 +3807,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key
walletInstance->SetMinVersion(FEATURE_LATEST);
- walletInstance->SetWalletFlags(wallet_creation_flags, false);
+ walletInstance->AddWalletFlags(wallet_creation_flags);
// Only create LegacyScriptPubKeyMan when not descriptor wallet
if (!walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
@@ -3800,14 +3844,20 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
}
- if (!gArgs.GetArg("-addresstype", "").empty() && !ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) {
- error = strprintf(_("Unknown address type '%s'"), gArgs.GetArg("-addresstype", ""));
- return nullptr;
+ if (!gArgs.GetArg("-addresstype", "").empty()) {
+ if (!ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) {
+ error = strprintf(_("Unknown address type '%s'"), gArgs.GetArg("-addresstype", ""));
+ return nullptr;
+ }
}
- if (!gArgs.GetArg("-changetype", "").empty() && !ParseOutputType(gArgs.GetArg("-changetype", ""), walletInstance->m_default_change_type)) {
- error = strprintf(_("Unknown change type '%s'"), gArgs.GetArg("-changetype", ""));
- return nullptr;
+ if (!gArgs.GetArg("-changetype", "").empty()) {
+ OutputType out_type;
+ if (!ParseOutputType(gArgs.GetArg("-changetype", ""), out_type)) {
+ error = strprintf(_("Unknown change type '%s'"), gArgs.GetArg("-changetype", ""));
+ return nullptr;
+ }
+ walletInstance->m_default_change_type = out_type;
}
if (gArgs.IsArgSet("-mintxfee")) {
@@ -4366,7 +4416,7 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
for (bool internal : {false, true}) {
for (OutputType t : OUTPUT_TYPES) {
- auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, t, internal));
+ auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, internal));
if (IsCrypted()) {
if (IsLocked()) {
throw std::runtime_error(std::string(__func__) + ": Wallet is locked, cannot setup new descriptors");
@@ -4375,28 +4425,31 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
throw std::runtime_error(std::string(__func__) + ": Could not encrypt new descriptors");
}
}
- spk_manager->SetupDescriptorGeneration(master_key);
+ spk_manager->SetupDescriptorGeneration(master_key, t);
uint256 id = spk_manager->GetID();
m_spk_managers[id] = std::move(spk_manager);
- SetActiveScriptPubKeyMan(id, t, internal);
+ AddActiveScriptPubKeyMan(id, t, internal);
}
}
}
-void CWallet::SetActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal, bool memonly)
+void CWallet::AddActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal)
+{
+ WalletBatch batch(*database);
+ if (!batch.WriteActiveScriptPubKeyMan(static_cast<uint8_t>(type), id, internal)) {
+ throw std::runtime_error(std::string(__func__) + ": writing active ScriptPubKeyMan id failed");
+ }
+ LoadActiveScriptPubKeyMan(id, type, internal);
+}
+
+void CWallet::LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal)
{
WalletLogPrintf("Setting spkMan to active: id = %s, type = %d, internal = %d\n", id.ToString(), static_cast<int>(type), static_cast<int>(internal));
auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers;
auto spk_man = m_spk_managers.at(id).get();
- spk_man->SetType(type, internal);
+ spk_man->SetInternal(internal);
spk_mans[type] = spk_man;
- if (!memonly) {
- WalletBatch batch(*database);
- if (!batch.WriteActiveScriptPubKeyMan(static_cast<uint8_t>(type), id, internal)) {
- throw std::runtime_error(std::string(__func__) + ": writing active ScriptPubKeyMan id failed");
- }
- }
NotifyCanGetAddressesChanged();
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index a29fa22207..8cb2a64484 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -13,11 +13,11 @@
#include <policy/feerate.h>
#include <psbt.h>
#include <tinyformat.h>
-#include <ui_interface.h>
#include <util/message.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
+#include <util/ui_change_type.h>
#include <validationinterface.h>
#include <wallet/coinselection.h>
#include <wallet/crypter.h>
@@ -51,7 +51,6 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet);
bool AddWallet(const std::shared_ptr<CWallet>& wallet);
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, bilingual_str& error, std::vector<bilingual_str>& warnings);
@@ -106,9 +105,6 @@ class ReserveDestination;
//! Default for -addresstype
constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::BECH32};
-//! Default for -changetype
-constexpr OutputType DEFAULT_CHANGE_TYPE{OutputType::CHANGE_AUTO};
-
static constexpr uint64_t KNOWN_WALLET_FLAGS =
WALLET_FLAG_AVOID_REUSE
| WALLET_FLAG_BLANK_WALLET
@@ -631,7 +627,6 @@ private:
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;
//! the current wallet version: clients below this version are not able to load the wallet
@@ -641,7 +636,6 @@ private:
int nWalletMaxVersion GUARDED_BY(cs_wallet) = FEATURE_BASE;
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};
@@ -923,7 +917,7 @@ public:
uint256 last_failed_block;
};
ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, Optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate);
- void transactionRemovedFromMempool(const CTransactionRef &ptx) override;
+ void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) override;
void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ResendWalletTransactions();
struct Balance {
@@ -937,7 +931,7 @@ public:
Balance GetBalance(int min_depth = 0, bool avoid_reuse = true) const;
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
- OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend);
+ OutputType TransactionChangeType(const Optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend);
/**
* Insert additional inputs into the transaction by
@@ -967,7 +961,8 @@ public:
bool& complete,
int sighash_type = 1 /* SIGHASH_ALL */,
bool sign = true,
- bool bip32derivs = true) const;
+ bool bip32derivs = true,
+ size_t* n_signed = nullptr) const;
/**
* Create a new transaction paying the recipients with a set of coins
@@ -1014,7 +1009,13 @@ public:
CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE};
CFeeRate m_discard_rate{DEFAULT_DISCARD_FEE};
OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE};
- OutputType m_default_change_type{DEFAULT_CHANGE_TYPE};
+ /**
+ * Default output type for change outputs. When unset, automatically choose type
+ * based on address type setting and the types other of non-change outputs
+ * (see -changetype option documentation and implementation in
+ * CWallet::TransactionChangeType for details).
+ */
+ Optional<OutputType> m_default_change_type{};
/** Absolute maximum transaction fee (in satoshis) used by default for the wallet */
CAmount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE};
@@ -1137,7 +1138,7 @@ public:
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
//! Verify wallet naming and perform salvage on the wallet if required
- static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, bilingual_str& error_string, std::vector<bilingual_str>& warnings);
+ static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
static std::shared_ptr<CWallet> CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags = 0);
@@ -1175,7 +1176,9 @@ public:
/** overwrite all flags by the given uint64_t
returns false if unknown, non-tolerable flags are present */
- bool SetWalletFlags(uint64_t overwriteFlags, bool memOnly);
+ bool AddWalletFlags(uint64_t flags);
+ /** Loads the flags into the wallet. (used by LoadWallet) */
+ bool LoadWalletFlags(uint64_t flags);
/** Determine if we are a legacy wallet */
bool IsLegacy() const;
@@ -1253,12 +1256,17 @@ public:
//! Instantiate a descriptor ScriptPubKeyMan from the WalletDescriptor and load it
void LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc);
- //! Sets the active ScriptPubKeyMan for the specified type and internal
+ //! Adds the active ScriptPubKeyMan for the specified type and internal. Writes it to the wallet file
+ //! @param[in] id The unique id for the ScriptPubKeyMan
+ //! @param[in] type The OutputType this ScriptPubKeyMan provides addresses for
+ //! @param[in] internal Whether this ScriptPubKeyMan provides change addresses
+ void AddActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal);
+
+ //! Loads an active ScriptPubKeyMan for the specified type and internal. (used by LoadWallet)
//! @param[in] id The unique id for the ScriptPubKeyMan
//! @param[in] type The OutputType this ScriptPubKeyMan provides addresses for
//! @param[in] internal Whether this ScriptPubKeyMan provides change addresses
- //! @param[in] memonly Whether to record this update to the database. Set to true for wallet loading, normally false when actually updating the wallet.
- void SetActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal, bool memonly = false);
+ void LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal);
//! Create new DescriptorScriptPubKeyMans and add them to the wallet
void SetupDescriptorScriptPubKeyMans();
@@ -1288,13 +1296,11 @@ public:
bool reserve()
{
assert(!m_could_reserve);
- std::lock_guard<std::mutex> lock(m_wallet.mutexScanning);
- if (m_wallet.fScanningWallet) {
+ if (m_wallet.fScanningWallet.exchange(true)) {
return false;
}
m_wallet.m_scanning_start = GetTimeMillis();
m_wallet.m_scanning_progress = 0;
- m_wallet.fScanningWallet = true;
m_could_reserve = true;
return true;
}
@@ -1306,7 +1312,6 @@ public:
~WalletRescanReserver()
{
- std::lock_guard<std::mutex> lock(m_wallet.mutexScanning);
if (m_could_reserve) {
m_wallet.fScanningWallet = false;
}
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 98597bdb0f..1478687bf9 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -10,6 +10,7 @@
#include <protocol.h>
#include <serialize.h>
#include <sync.h>
+#include <util/bip32.h>
#include <util/system.h>
#include <util/time.h>
#include <wallet/wallet.h>
@@ -17,8 +18,6 @@
#include <atomic>
#include <string>
-#include <boost/thread.hpp>
-
namespace DBKeys {
const std::string ACENTRY{"acentry"};
const std::string ACTIVEEXTERNALSPK{"activeexternalspk"};
@@ -115,8 +114,19 @@ bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
return false;
}
- if (!WriteIC(std::make_pair(DBKeys::CRYPTED_KEY, vchPubKey), vchCryptedSecret, false)) {
- return false;
+ // Compute a checksum of the encrypted key
+ uint256 checksum = Hash(vchCryptedSecret.begin(), vchCryptedSecret.end());
+
+ const auto key = std::make_pair(DBKeys::CRYPTED_KEY, vchPubKey);
+ if (!WriteIC(key, std::make_pair(vchCryptedSecret, checksum), false)) {
+ // It may already exist, so try writing just the checksum
+ std::vector<unsigned char> val;
+ if (!m_batch->Read(key, val)) {
+ return false;
+ }
+ if (!WriteIC(key, std::make_pair(val, checksum), true)) {
+ return false;
+ }
}
EraseIC(std::make_pair(DBKeys::KEY, vchPubKey));
return true;
@@ -156,8 +166,8 @@ bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
{
- if (m_batch.Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
- return m_batch.Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
+ if (m_batch->Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
+ return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
}
bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
@@ -167,7 +177,7 @@ bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool)
{
- return m_batch.Read(std::make_pair(DBKeys::POOL, nPool), keypool);
+ return m_batch->Read(std::make_pair(DBKeys::POOL, nPool), keypool);
}
bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool)
@@ -245,6 +255,7 @@ public:
std::map<uint256, DescriptorCache> m_descriptor_caches;
std::map<std::pair<uint256, CKeyID>, CKey> m_descriptor_keys;
std::map<std::pair<uint256, CKeyID>, std::pair<CPubKey, std::vector<unsigned char>>> m_descriptor_crypt_keys;
+ std::map<uint160, CHDChain> m_hd_chains;
CWalletScanState() {
}
@@ -397,9 +408,21 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
}
std::vector<unsigned char> vchPrivKey;
ssValue >> vchPrivKey;
+
+ // Get the checksum and check it
+ bool checksum_valid = false;
+ if (!ssValue.eof()) {
+ uint256 checksum;
+ ssValue >> checksum;
+ if ((checksum_valid = Hash(vchPrivKey.begin(), vchPrivKey.end()) != checksum)) {
+ strErr = "Error reading wallet database: Crypted key corrupt";
+ return false;
+ }
+ }
+
wss.nCKeys++;
- if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCryptedKey(vchPubKey, vchPrivKey))
+ if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCryptedKey(vchPubKey, vchPrivKey, checksum_valid))
{
strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCryptedKey failed";
return false;
@@ -412,6 +435,66 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssValue >> keyMeta;
wss.nKeyMeta++;
pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
+
+ // Extract some CHDChain info from this metadata if it has any
+ if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) {
+ // Get the path from the key origin or from the path string
+ // Not applicable when path is "s" or "m" as those indicate a seed
+ // See https://github.com/bitcoin/bitcoin/pull/12924
+ bool internal = false;
+ uint32_t index = 0;
+ if (keyMeta.hdKeypath != "s" && keyMeta.hdKeypath != "m") {
+ std::vector<uint32_t> path;
+ if (keyMeta.has_key_origin) {
+ // We have a key origin, so pull it from its path vector
+ path = keyMeta.key_origin.path;
+ } else {
+ // No key origin, have to parse the string
+ if (!ParseHDKeypath(keyMeta.hdKeypath, path)) {
+ strErr = "Error reading wallet database: keymeta with invalid HD keypath";
+ return false;
+ }
+ }
+
+ // Extract the index and internal from the path
+ // Path string is m/0'/k'/i'
+ // Path vector is [0', k', i'] (but as ints OR'd with the hardened bit
+ // k == 0 for external, 1 for internal. i is the index
+ if (path.size() != 3) {
+ strErr = "Error reading wallet database: keymeta found with unexpected path";
+ return false;
+ }
+ if (path[0] != 0x80000000) {
+ strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000) for the element at index 0", path[0]);
+ return false;
+ }
+ if (path[1] != 0x80000000 && path[1] != (1 | 0x80000000)) {
+ strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000 or 0x80000001) for the element at index 1", path[1]);
+ return false;
+ }
+ if ((path[2] & 0x80000000) == 0) {
+ strErr = strprintf("Unexpected path index of 0x%08x (expected to be greater than or equal to 0x80000000)", path[2]);
+ return false;
+ }
+ internal = path[1] == (1 | 0x80000000);
+ index = path[2] & ~0x80000000;
+ }
+
+ // Insert a new CHDChain, or get the one that already exists
+ auto ins = wss.m_hd_chains.emplace(keyMeta.hd_seed_id, CHDChain());
+ CHDChain& chain = ins.first->second;
+ if (ins.second) {
+ // For new chains, we want to default to VERSION_HD_BASE until we see an internal
+ chain.nVersion = CHDChain::VERSION_HD_BASE;
+ chain.seed_id = keyMeta.hd_seed_id;
+ }
+ if (internal) {
+ chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT;
+ chain.nInternalChainCounter = std::max(chain.nInternalChainCounter, index);
+ } else {
+ chain.nExternalChainCounter = std::max(chain.nExternalChainCounter, index);
+ }
+ }
} else if (strType == DBKeys::WATCHMETA) {
CScript script;
ssKey >> script;
@@ -456,11 +539,11 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
} else if (strType == DBKeys::HDCHAIN) {
CHDChain chain;
ssValue >> chain;
- pwallet->GetOrCreateLegacyScriptPubKeyMan()->SetHDChain(chain, true);
+ pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDChain(chain);
} else if (strType == DBKeys::FLAGS) {
uint64_t flags;
ssValue >> flags;
- if (!pwallet->SetWalletFlags(flags, true)) {
+ if (!pwallet->LoadWalletFlags(flags)) {
strErr = "Error reading wallet database: Unknown non-tolerable wallet flags found";
return false;
}
@@ -509,9 +592,6 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssValue >> ser_xpub;
CExtPubKey xpub;
xpub.Decode(ser_xpub.data());
- if (wss.m_descriptor_caches.count(desc_id)) {
- wss.m_descriptor_caches[desc_id] = DescriptorCache();
- }
if (parent) {
wss.m_descriptor_caches[desc_id].CacheParentExtPubKey(key_exp_index, xpub);
} else {
@@ -588,6 +668,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
return true;
}
+bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr)
+{
+ CWalletScanState dummy_wss;
+ LOCK(pwallet->cs_wallet);
+ return ReadKeyValue(pwallet, ssKey, ssValue, dummy_wss, strType, strErr);
+}
+
bool WalletBatch::IsKeyType(const std::string& strType)
{
return (strType == DBKeys::KEY ||
@@ -603,15 +690,14 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
LOCK(pwallet->cs_wallet);
try {
int nMinVersion = 0;
- if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) {
+ if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
if (nMinVersion > FEATURE_LATEST)
return DBErrors::TOO_NEW;
pwallet->LoadMinVersion(nMinVersion);
}
// Get cursor
- Dbc* pcursor = m_batch.GetCursor();
- if (!pcursor)
+ if (!m_batch->StartCursor())
{
pwallet->WalletLogPrintf("Error getting wallet database cursor\n");
return DBErrors::CORRUPT;
@@ -622,11 +708,14 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue);
- if (ret == DB_NOTFOUND)
+ bool complete;
+ bool ret = m_batch->ReadAtCursor(ssKey, ssValue, complete);
+ if (complete) {
break;
- else if (ret != 0)
+ }
+ else if (!ret)
{
+ m_batch->CloseCursor();
pwallet->WalletLogPrintf("Error reading next record from wallet database\n");
return DBErrors::CORRUPT;
}
@@ -653,21 +742,17 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
if (!strErr.empty())
pwallet->WalletLogPrintf("%s\n", strErr);
}
- pcursor->close();
- }
- catch (const boost::thread_interrupted&) {
- throw;
- }
- catch (...) {
+ } catch (...) {
result = DBErrors::CORRUPT;
}
+ m_batch->CloseCursor();
// Set the active ScriptPubKeyMans
for (auto spk_man_pair : wss.m_active_external_spks) {
- pwallet->SetActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /* internal */ false, /* memonly */ true);
+ pwallet->LoadActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /* internal */ false);
}
for (auto spk_man_pair : wss.m_active_internal_spks) {
- pwallet->SetActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /* internal */ true, /* memonly */ true);
+ pwallet->LoadActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /* internal */ true);
}
// Set the descriptor caches
@@ -697,7 +782,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
// Last client version to open this wallet, was previously the file version number
int last_client = CLIENT_VERSION;
- m_batch.Read(DBKeys::VERSION, last_client);
+ m_batch->Read(DBKeys::VERSION, last_client);
int wallet_version = pwallet->GetVersion();
pwallet->WalletLogPrintf("Wallet File Version = %d\n", wallet_version > 0 ? wallet_version : last_client);
@@ -722,7 +807,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
return DBErrors::NEED_REWRITE;
if (last_client < CLIENT_VERSION) // Update
- m_batch.Write(DBKeys::VERSION, CLIENT_VERSION);
+ m_batch->Write(DBKeys::VERSION, CLIENT_VERSION);
if (wss.fAnyUnordered)
result = pwallet->ReorderTransactions();
@@ -735,6 +820,20 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
result = DBErrors::CORRUPT;
}
+ // Set the inactive chain
+ if (wss.m_hd_chains.size() > 0) {
+ LegacyScriptPubKeyMan* legacy_spkm = pwallet->GetLegacyScriptPubKeyMan();
+ if (!legacy_spkm) {
+ pwallet->WalletLogPrintf("Inactive HD Chains found but no Legacy ScriptPubKeyMan\n");
+ return DBErrors::CORRUPT;
+ }
+ for (const auto& chain_pair : wss.m_hd_chains) {
+ if (chain_pair.first != pwallet->GetLegacyScriptPubKeyMan()->GetHDChain().seed_id) {
+ pwallet->GetLegacyScriptPubKeyMan()->AddInactiveHDChain(chain_pair.second);
+ }
+ }
+ }
+
return result;
}
@@ -744,14 +843,13 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
try {
int nMinVersion = 0;
- if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) {
+ if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
if (nMinVersion > FEATURE_LATEST)
return DBErrors::TOO_NEW;
}
// Get cursor
- Dbc* pcursor = m_batch.GetCursor();
- if (!pcursor)
+ if (!m_batch->StartCursor())
{
LogPrintf("Error getting wallet database cursor\n");
return DBErrors::CORRUPT;
@@ -762,11 +860,12 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue);
- if (ret == DB_NOTFOUND)
+ bool complete;
+ bool ret = m_batch->ReadAtCursor(ssKey, ssValue, complete);
+ if (complete) {
break;
- else if (ret != 0)
- {
+ } else if (!ret) {
+ m_batch->CloseCursor();
LogPrintf("Error reading next record from wallet database\n");
return DBErrors::CORRUPT;
}
@@ -781,14 +880,10 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
ssValue >> vWtx.back();
}
}
- pcursor->close();
- }
- catch (const boost::thread_interrupted&) {
- throw;
- }
- catch (...) {
+ } catch (...) {
result = DBErrors::CORRUPT;
}
+ m_batch->CloseCursor();
return result;
}
@@ -854,9 +949,6 @@ void MaybeCompactWalletDB()
if (fOneThread.exchange(true)) {
return;
}
- if (!gArgs.GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
- return;
- }
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
WalletDatabase& dbh = pwallet->GetDBHandle();
@@ -869,7 +961,7 @@ void MaybeCompactWalletDB()
}
if (dbh.nLastFlushed != nUpdateCounter && GetTime() - dbh.nLastWalletUpdate >= 2) {
- if (BerkeleyBatch::PeriodicFlush(dbh)) {
+ if (dbh.PeriodicFlush()) {
dbh.nLastFlushed = nUpdateCounter;
}
}
@@ -878,87 +970,62 @@ void MaybeCompactWalletDB()
fOneThread = false;
}
-//
-// Try to (very carefully!) recover wallet file if there is a problem.
-//
-bool WalletBatch::Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename)
+bool WalletBatch::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
{
- return BerkeleyBatch::Recover(wallet_path, callbackDataIn, recoverKVcallback, out_backup_filename);
+ return WriteIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(address, key)), value);
}
-bool WalletBatch::Recover(const fs::path& wallet_path, std::string& out_backup_filename)
+bool WalletBatch::EraseDestData(const std::string &address, const std::string &key)
{
- // recover without a key filter callback
- // results in recovering all record types
- return WalletBatch::Recover(wallet_path, nullptr, nullptr, out_backup_filename);
+ return EraseIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(address, key)));
}
-bool WalletBatch::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
-{
- CWallet *dummyWallet = reinterpret_cast<CWallet*>(callbackData);
- CWalletScanState dummyWss;
- std::string strType, strErr;
- bool fReadOK;
- {
- // Required in LoadKeyMetadata():
- LOCK(dummyWallet->cs_wallet);
- fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue,
- dummyWss, strType, strErr);
- }
- if (!IsKeyType(strType) && strType != DBKeys::HDCHAIN) {
- return false;
- }
- if (!fReadOK)
- {
- LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, strErr);
- return false;
- }
-
- return true;
-}
-bool WalletBatch::VerifyEnvironment(const fs::path& wallet_path, bilingual_str& errorStr)
+bool WalletBatch::WriteHDChain(const CHDChain& chain)
{
- return BerkeleyBatch::VerifyEnvironment(wallet_path, errorStr);
+ return WriteIC(DBKeys::HDCHAIN, chain);
}
-bool WalletBatch::VerifyDatabaseFile(const fs::path& wallet_path, std::vector<bilingual_str>& warnings, bilingual_str& errorStr)
+bool WalletBatch::WriteWalletFlags(const uint64_t flags)
{
- return BerkeleyBatch::VerifyDatabaseFile(wallet_path, warnings, errorStr, WalletBatch::Recover);
+ return WriteIC(DBKeys::FLAGS, flags);
}
-bool WalletBatch::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
+bool WalletBatch::TxnBegin()
{
- return WriteIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(address, key)), value);
+ return m_batch->TxnBegin();
}
-bool WalletBatch::EraseDestData(const std::string &address, const std::string &key)
+bool WalletBatch::TxnCommit()
{
- return EraseIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(address, key)));
+ return m_batch->TxnCommit();
}
-
-bool WalletBatch::WriteHDChain(const CHDChain& chain)
+bool WalletBatch::TxnAbort()
{
- return WriteIC(DBKeys::HDCHAIN, chain);
+ return m_batch->TxnAbort();
}
-bool WalletBatch::WriteWalletFlags(const uint64_t flags)
+bool IsWalletLoaded(const fs::path& wallet_path)
{
- return WriteIC(DBKeys::FLAGS, flags);
+ return IsBDBWalletLoaded(wallet_path);
}
-bool WalletBatch::TxnBegin()
+/** Return object for accessing database at specified path. */
+std::unique_ptr<BerkeleyDatabase> CreateWalletDatabase(const fs::path& path)
{
- return m_batch.TxnBegin();
+ std::string filename;
+ return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename));
}
-bool WalletBatch::TxnCommit()
+/** Return object for accessing dummy database with no read/write capabilities. */
+std::unique_ptr<BerkeleyDatabase> CreateDummyWalletDatabase()
{
- return m_batch.TxnCommit();
+ return MakeUnique<BerkeleyDatabase>();
}
-bool WalletBatch::TxnAbort()
+/** Return object for accessing temporary in-memory database. */
+std::unique_ptr<BerkeleyDatabase> CreateMockWalletDatabase()
{
- return m_batch.TxnAbort();
+ return MakeUnique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
}
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index e2bf229c68..6b55361c07 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -8,6 +8,7 @@
#include <amount.h>
#include <script/sign.h>
+#include <wallet/bdb.h>
#include <wallet/db.h>
#include <wallet/walletutil.h>
#include <key.h>
@@ -98,15 +99,13 @@ public:
int nVersion;
CHDChain() { SetNull(); }
- ADD_SERIALIZE_METHODS;
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action)
+
+ SERIALIZE_METHODS(CHDChain, obj)
{
- READWRITE(this->nVersion);
- READWRITE(nExternalChainCounter);
- READWRITE(seed_id);
- if (this->nVersion >= VERSION_HD_CHAIN_SPLIT)
- READWRITE(nInternalChainCounter);
+ READWRITE(obj.nVersion, obj.nExternalChainCounter, obj.seed_id);
+ if (obj.nVersion >= VERSION_HD_CHAIN_SPLIT) {
+ READWRITE(obj.nInternalChainCounter);
+ }
}
void SetNull()
@@ -116,6 +115,11 @@ public:
nInternalChainCounter = 0;
seed_id.SetNull();
}
+
+ bool operator==(const CHDChain& chain) const
+ {
+ return seed_id == chain.seed_id;
+ }
};
class CKeyMetadata
@@ -142,21 +146,16 @@ public:
nCreateTime = nCreateTime_;
}
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(this->nVersion);
- READWRITE(nCreateTime);
- if (this->nVersion >= VERSION_WITH_HDDATA)
- {
- READWRITE(hdKeypath);
- READWRITE(hd_seed_id);
+ SERIALIZE_METHODS(CKeyMetadata, obj)
+ {
+ READWRITE(obj.nVersion, obj.nCreateTime);
+ if (obj.nVersion >= VERSION_WITH_HDDATA) {
+ READWRITE(obj.hdKeypath, obj.hd_seed_id);
}
- if (this->nVersion >= VERSION_WITH_KEY_ORIGIN)
+ if (obj.nVersion >= VERSION_WITH_KEY_ORIGIN)
{
- READWRITE(key_origin);
- READWRITE(has_key_origin);
+ READWRITE(obj.key_origin);
+ READWRITE(obj.has_key_origin);
}
}
@@ -184,12 +183,12 @@ private:
template <typename K, typename T>
bool WriteIC(const K& key, const T& value, bool fOverwrite = true)
{
- if (!m_batch.Write(key, value, fOverwrite)) {
+ if (!m_batch->Write(key, value, fOverwrite)) {
return false;
}
m_database.IncrementUpdateCounter();
if (m_database.nUpdateCounter % 1000 == 0) {
- m_batch.Flush();
+ m_batch->Flush();
}
return true;
}
@@ -197,19 +196,19 @@ private:
template <typename K>
bool EraseIC(const K& key)
{
- if (!m_batch.Erase(key)) {
+ if (!m_batch->Erase(key)) {
return false;
}
m_database.IncrementUpdateCounter();
if (m_database.nUpdateCounter % 1000 == 0) {
- m_batch.Flush();
+ m_batch->Flush();
}
return true;
}
public:
explicit WalletBatch(WalletDatabase& database, const char* pszMode = "r+", bool _fFlushOnClose = true) :
- m_batch(database, pszMode, _fFlushOnClose),
+ m_batch(database.MakeBatch(pszMode, _fFlushOnClose)),
m_database(database)
{
}
@@ -263,18 +262,12 @@ public:
DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWalletTx>& vWtx);
DBErrors ZapWalletTx(std::list<CWalletTx>& vWtx);
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut);
- /* Try to (very carefully!) recover wallet database (with a possible key type filter) */
- static bool Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
- /* Recover convenience-function to bypass the key filter callback, called when verify fails, recovers everything */
- static bool Recover(const fs::path& wallet_path, std::string& out_backup_filename);
- /* Recover filter (used as callback), will only let keys (cryptographical keys) as KV/key-type pass through */
- static bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue);
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
static bool IsKeyType(const std::string& strType);
/* verifies the database environment */
static bool VerifyEnvironment(const fs::path& wallet_path, bilingual_str& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const fs::path& wallet_path, std::vector<bilingual_str>& warnings, bilingual_str& errorStr);
+ static bool VerifyDatabaseFile(const fs::path& wallet_path, bilingual_str& errorStr);
//! write the hdchain model (external chain child index counter)
bool WriteHDChain(const CHDChain& chain);
@@ -287,11 +280,26 @@ public:
//! Abort current transaction
bool TxnAbort();
private:
- BerkeleyBatch m_batch;
+ std::unique_ptr<BerkeleyBatch> m_batch;
WalletDatabase& m_database;
};
//! Compacts BDB state so that wallet.dat is self-contained (if there are changes)
void MaybeCompactWalletDB();
+//! Unserialize a given Key-Value pair and load it into the wallet
+bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr);
+
+/** Return whether a wallet database is currently loaded. */
+bool IsWalletLoaded(const fs::path& wallet_path);
+
+/** Return object for accessing database at specified path. */
+std::unique_ptr<BerkeleyDatabase> CreateWalletDatabase(const fs::path& path);
+
+/** Return object for accessing dummy database with no read/write capabilities. */
+std::unique_ptr<BerkeleyDatabase> CreateDummyWalletDatabase();
+
+/** Return object for accessing temporary in-memory database. */
+std::unique_ptr<BerkeleyDatabase> CreateMockWalletDatabase();
+
#endif // BITCOIN_WALLET_WALLETDB_H
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 522efaa884..8a45d81456 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -5,6 +5,7 @@
#include <fs.h>
#include <util/system.h>
#include <util/translation.h>
+#include <wallet/salvage.h>
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
@@ -27,7 +28,7 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
return nullptr;
}
// dummy chain interface
- std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet);
+ std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), CreateWalletDatabase(path)), WalletToolReleaseWallet);
LOCK(wallet_instance->cs_wallet);
bool first_run = true;
DBErrors load_wallet_ret = wallet_instance->LoadWallet(first_run);
@@ -56,7 +57,7 @@ static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::pa
}
// dummy chain interface
- std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet);
+ std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), CreateWalletDatabase(path)), WalletToolReleaseWallet);
DBErrors load_wallet_ret;
try {
bool first_run;
@@ -103,6 +104,27 @@ static void WalletShowInfo(CWallet* wallet_instance)
tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->m_address_book.size());
}
+static bool SalvageWallet(const fs::path& path)
+{
+ // Create a Database handle to allow for the db to be initialized before recovery
+ std::unique_ptr<WalletDatabase> database = CreateWalletDatabase(path);
+
+ // Initialize the environment before recovery
+ bilingual_str error_string;
+ try {
+ database->Verify(error_string);
+ } catch (const fs::filesystem_error& e) {
+ error_string = Untranslated(strprintf("Error loading wallet. %s", fsbridge::get_filesystem_error_message(e)));
+ }
+ if (!error_string.original.empty()) {
+ tfm::format(std::cerr, "Failed to open wallet for salvage :%s\n", error_string.original);
+ return false;
+ }
+
+ // Perform the recovery
+ return RecoverDatabaseFile(path);
+}
+
bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
{
fs::path path = fs::absolute(name, GetWalletDir());
@@ -113,20 +135,20 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
WalletShowInfo(wallet_instance.get());
wallet_instance->Flush(true);
}
- } else if (command == "info") {
+ } else if (command == "info" || command == "salvage") {
if (!fs::exists(path)) {
tfm::format(std::cerr, "Error: no wallet file at %s\n", name);
return false;
}
- bilingual_str error;
- if (!WalletBatch::VerifyEnvironment(path, error)) {
- tfm::format(std::cerr, "%s\nError loading %s. Is wallet being used by other process?\n", error.original, name);
- return false;
+
+ if (command == "info") {
+ std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
+ if (!wallet_instance) return false;
+ WalletShowInfo(wallet_instance.get());
+ wallet_instance->Flush(true);
+ } else if (command == "salvage") {
+ return SalvageWallet(path);
}
- std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
- if (!wallet_instance) return false;
- WalletShowInfo(wallet_instance.get());
- wallet_instance->Flush(true);
} else {
tfm::format(std::cerr, "Invalid command: %s\n", command);
return false;
diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h
index 599b1a9f5a..a4e4fda8a1 100644
--- a/src/wallet/walletutil.h
+++ b/src/wallet/walletutil.h
@@ -98,26 +98,22 @@ public:
int32_t next_index = 0; // Position of the next item to generate
DescriptorCache cache;
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- if (ser_action.ForRead()) {
- std::string desc;
- std::string error;
- READWRITE(desc);
- FlatSigningProvider keys;
- descriptor = Parse(desc, keys, error, true);
- if (!descriptor) {
- throw std::ios_base::failure("Invalid descriptor: " + error);
- }
- } else {
- READWRITE(descriptor->ToString());
+ void DeserializeDescriptor(const std::string& str)
+ {
+ std::string error;
+ FlatSigningProvider keys;
+ descriptor = Parse(str, keys, error, true);
+ if (!descriptor) {
+ throw std::ios_base::failure("Invalid descriptor: " + error);
}
- READWRITE(creation_time);
- READWRITE(next_index);
- READWRITE(range_start);
- READWRITE(range_end);
+ }
+
+ SERIALIZE_METHODS(WalletDescriptor, obj)
+ {
+ std::string descriptor_str;
+ SER_WRITE(obj, descriptor_str = obj.descriptor->ToString());
+ READWRITE(descriptor_str, obj.creation_time, obj.next_index, obj.range_start, obj.range_end);
+ SER_READ(obj, obj.DeserializeDescriptor(descriptor_str));
}
WalletDescriptor() {}
diff --git a/src/warnings.cpp b/src/warnings.cpp
index 467c3d0f65..501bf7e637 100644
--- a/src/warnings.cpp
+++ b/src/warnings.cpp
@@ -6,66 +6,71 @@
#include <warnings.h>
#include <sync.h>
+#include <util/string.h>
#include <util/system.h>
#include <util/translation.h>
-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;
+#include <vector>
-void SetMiscWarning(const std::string& strWarning)
+static Mutex g_warnings_mutex;
+static bilingual_str g_misc_warnings GUARDED_BY(g_warnings_mutex);
+static bool fLargeWorkForkFound GUARDED_BY(g_warnings_mutex) = false;
+static bool fLargeWorkInvalidChainFound GUARDED_BY(g_warnings_mutex) = false;
+
+void SetMiscWarning(const bilingual_str& warning)
{
- LOCK(cs_warnings);
- strMiscWarning = strWarning;
+ LOCK(g_warnings_mutex);
+ g_misc_warnings = warning;
}
void SetfLargeWorkForkFound(bool flag)
{
- LOCK(cs_warnings);
+ LOCK(g_warnings_mutex);
fLargeWorkForkFound = flag;
}
bool GetfLargeWorkForkFound()
{
- LOCK(cs_warnings);
+ LOCK(g_warnings_mutex);
return fLargeWorkForkFound;
}
void SetfLargeWorkInvalidChainFound(bool flag)
{
- LOCK(cs_warnings);
+ LOCK(g_warnings_mutex);
fLargeWorkInvalidChainFound = flag;
}
-std::string GetWarnings(bool verbose)
+bilingual_str GetWarnings(bool verbose)
{
- std::string warnings_concise;
- std::string warnings_verbose;
- const std::string warning_separator = "<hr />";
+ bilingual_str warnings_concise;
+ std::vector<bilingual_str> warnings_verbose;
- LOCK(cs_warnings);
+ LOCK(g_warnings_mutex);
// Pre-release build warning
if (!CLIENT_VERSION_IS_RELEASE) {
- warnings_concise = "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications";
- warnings_verbose = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications").translated;
+ warnings_concise = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications");
+ warnings_verbose.emplace_back(warnings_concise);
}
// Misc warnings like out of disk space and clock is wrong
- if (strMiscWarning != "") {
- warnings_concise = strMiscWarning;
- warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + strMiscWarning;
+ if (!g_misc_warnings.empty()) {
+ warnings_concise = g_misc_warnings;
+ warnings_verbose.emplace_back(warnings_concise);
}
if (fLargeWorkForkFound) {
- warnings_concise = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
- warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.").translated;
+ warnings_concise = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
+ warnings_verbose.emplace_back(warnings_concise);
} else if (fLargeWorkInvalidChainFound) {
- warnings_concise = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
- warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.").translated;
+ warnings_concise = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
+ warnings_verbose.emplace_back(warnings_concise);
+ }
+
+ if (verbose) {
+ return Join(warnings_verbose, Untranslated("<hr />"));
}
- if (verbose) return warnings_verbose;
- else return warnings_concise;
+ return warnings_concise;
}
diff --git a/src/warnings.h b/src/warnings.h
index 83b1add1ee..28546eb753 100644
--- a/src/warnings.h
+++ b/src/warnings.h
@@ -8,16 +8,18 @@
#include <string>
-void SetMiscWarning(const std::string& strWarning);
+struct bilingual_str;
+
+void SetMiscWarning(const bilingual_str& warning);
void SetfLargeWorkForkFound(bool flag);
bool GetfLargeWorkForkFound();
void SetfLargeWorkInvalidChainFound(bool flag);
/** Format a string that describes several potential problems detected by the core.
* @param[in] verbose bool
- * - if true, get all warnings, translated (where possible), separated by <hr />
+ * - if true, get all warnings separated by <hr />
* - if false, get the most important warning
* @returns the warning string
*/
-std::string GetWarnings(bool verbose);
+bilingual_str GetWarnings(bool verbose);
#endif // BITCOIN_WARNINGS_H