aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/Makefile.bench.include2
-rw-r--r--src/Makefile.test.include4
-rw-r--r--src/addrman.cpp1
-rw-r--r--src/arith_uint256.cpp1
-rw-r--r--src/bench/base58.cpp1
-rw-r--r--src/bench/bech32.cpp1
-rw-r--r--src/bench/bench.cpp5
-rw-r--r--src/bench/bench_bitcoin.cpp6
-rw-r--r--src/bench/ccoins_caching.cpp4
-rw-r--r--src/bench/chacha20.cpp45
-rw-r--r--src/bench/checkqueue.cpp1
-rw-r--r--src/bench/crypto_hash.cpp2
-rw-r--r--src/bench/duplicate_inputs.cpp7
-rw-r--r--src/bench/examples.cpp1
-rw-r--r--src/bench/rollingbloom.cpp9
-rw-r--r--src/bench/rpc_mempool.cpp1
-rw-r--r--src/bench/util_time.cpp42
-rw-r--r--src/bench/verify_script.cpp1
-rw-r--r--src/bench/wallet_balance.cpp1
-rw-r--r--src/bitcoin-cli.cpp16
-rw-r--r--src/bitcoin-tx.cpp22
-rw-r--r--src/bitcoin-wallet.cpp13
-rw-r--r--src/bitcoind.cpp22
-rw-r--r--src/bloom.cpp5
-rw-r--r--src/consensus/merkle.cpp1
-rw-r--r--src/core_read.cpp1
-rw-r--r--src/core_write.cpp1
-rw-r--r--src/crypto/aes.cpp1
-rw-r--r--src/crypto/chacha20.cpp132
-rw-r--r--src/crypto/chacha20.h18
-rw-r--r--src/crypto/hkdf_sha256_32.cpp21
-rw-r--r--src/crypto/hkdf_sha256_32.h25
-rw-r--r--src/crypto/ripemd160.cpp2
-rw-r--r--src/crypto/sha1.cpp2
-rw-r--r--src/crypto/sha256_avx2.cpp1
-rw-r--r--src/crypto/sha256_sse41.cpp1
-rw-r--r--src/crypto/sha512.cpp2
-rw-r--r--src/dbwrapper.cpp2
-rw-r--r--src/fs.h1
-rw-r--r--src/httprpc.cpp1
-rw-r--r--src/httpserver.cpp8
-rw-r--r--src/index/base.cpp16
-rw-r--r--src/index/txindex.cpp2
-rw-r--r--src/init.cpp61
-rw-r--r--src/interfaces/chain.cpp67
-rw-r--r--src/interfaces/chain.h11
-rw-r--r--src/interfaces/node.cpp11
-rw-r--r--src/interfaces/wallet.cpp10
-rw-r--r--src/interfaces/wallet.h4
-rw-r--r--src/key.cpp7
-rw-r--r--src/key.h3
-rw-r--r--src/key_io.cpp8
-rw-r--r--src/keystore.cpp9
-rw-r--r--src/logging.cpp76
-rw-r--r--src/logging.h21
-rw-r--r--src/merkleblock.cpp1
-rw-r--r--src/miner.cpp5
-rw-r--r--src/net.cpp6
-rw-r--r--src/net.h2
-rw-r--r--src/net_processing.cpp269
-rw-r--r--src/netaddress.cpp11
-rw-r--r--src/netaddress.h3
-rw-r--r--src/netbase.cpp3
-rw-r--r--src/noui.cpp36
-rw-r--r--src/outputtype.cpp18
-rw-r--r--src/policy/fees.cpp5
-rw-r--r--src/policy/policy.cpp4
-rw-r--r--src/prevector.h31
-rw-r--r--src/primitives/block.cpp1
-rw-r--r--src/psbt.cpp3
-rw-r--r--src/qt/addressbookpage.cpp1
-rw-r--r--src/qt/addresstablemodel.cpp1
-rw-r--r--src/qt/bantablemodel.cpp2
-rw-r--r--src/qt/bitcoin.cpp11
-rw-r--r--src/qt/bitcoingui.cpp58
-rw-r--r--src/qt/bitcoingui.h3
-rw-r--r--src/qt/bitcoinunits.cpp2
-rw-r--r--src/qt/clientmodel.cpp6
-rw-r--r--src/qt/coincontroldialog.cpp8
-rw-r--r--src/qt/forms/coincontroldialog.ui6
-rw-r--r--src/qt/forms/debugwindow.ui16
-rw-r--r--src/qt/guiutil.cpp11
-rw-r--r--src/qt/optionsmodel.cpp2
-rw-r--r--src/qt/paymentserver.cpp2
-rw-r--r--src/qt/peertablemodel.cpp1
-rw-r--r--src/qt/platformstyle.cpp2
-rw-r--r--src/qt/receivecoinsdialog.cpp2
-rw-r--r--src/qt/receiverequestdialog.cpp1
-rw-r--r--src/qt/rpcconsole.cpp3
-rw-r--r--src/qt/sendcoinsdialog.cpp44
-rw-r--r--src/qt/sendcoinsdialog.h2
-rw-r--r--src/qt/signverifymessagedialog.cpp10
-rw-r--r--src/qt/test/addressbooktests.cpp3
-rw-r--r--src/qt/test/apptests.cpp9
-rw-r--r--src/qt/test/rpcnestedtests.cpp8
-rw-r--r--src/qt/test/rpcnestedtests.h3
-rw-r--r--src/qt/test/test_main.cpp15
-rw-r--r--src/qt/test/wallettests.cpp10
-rw-r--r--src/qt/transactiondesc.cpp3
-rw-r--r--src/qt/transactionrecord.cpp4
-rw-r--r--src/qt/transactiontablemodel.cpp4
-rw-r--r--src/qt/transactionview.cpp1
-rw-r--r--src/qt/utilitydialog.cpp7
-rw-r--r--src/qt/walletmodel.cpp2
-rw-r--r--src/qt/walletmodel.h11
-rw-r--r--src/qt/walletmodeltransaction.cpp1
-rw-r--r--src/qt/walletview.cpp1
-rw-r--r--src/qt/winshutdownmonitor.cpp2
-rw-r--r--src/random.cpp60
-rw-r--r--src/random.h4
-rw-r--r--src/rest.cpp20
-rw-r--r--src/rpc/blockchain.cpp104
-rw-r--r--src/rpc/client.cpp4
-rw-r--r--src/rpc/mining.cpp26
-rw-r--r--src/rpc/misc.cpp34
-rw-r--r--src/rpc/net.cpp4
-rw-r--r--src/rpc/protocol.cpp1
-rw-r--r--src/rpc/rawtransaction.cpp71
-rw-r--r--src/rpc/rawtransaction_util.cpp1
-rw-r--r--src/rpc/server.cpp2
-rw-r--r--src/rpc/util.cpp48
-rw-r--r--src/rpc/util.h5
-rw-r--r--src/script/descriptor.cpp56
-rw-r--r--src/script/descriptor.h8
-rw-r--r--src/script/script.cpp1
-rw-r--r--src/script/sigcache.cpp1
-rw-r--r--src/script/standard.cpp18
-rw-r--r--src/script/standard.h22
-rw-r--r--src/secp256k1/.gitignore1
-rw-r--r--src/secp256k1/.travis.yml9
-rw-r--r--src/secp256k1/Makefile.am13
-rw-r--r--src/secp256k1/build-aux/m4/ax_jni_include_dir.m447
-rw-r--r--src/secp256k1/build-aux/m4/bitcoin_secp.m41
-rw-r--r--src/secp256k1/configure.ac89
-rw-r--r--src/secp256k1/include/secp256k1.h49
-rw-r--r--src/secp256k1/include/secp256k1_ecdh.h32
-rw-r--r--src/secp256k1/libsecp256k1.pc.in2
-rw-r--r--src/secp256k1/src/bench.h16
-rw-r--r--src/secp256k1/src/bench_ecdh.c10
-rw-r--r--src/secp256k1/src/bench_ecmult.c207
-rw-r--r--src/secp256k1/src/bench_internal.c83
-rw-r--r--src/secp256k1/src/bench_recover.c8
-rw-r--r--src/secp256k1/src/bench_sign.c12
-rw-r--r--src/secp256k1/src/eckey_impl.h2
-rw-r--r--src/secp256k1/src/ecmult.h19
-rw-r--r--src/secp256k1/src/ecmult_const.h4
-rw-r--r--src/secp256k1/src/ecmult_const_impl.h97
-rw-r--r--src/secp256k1/src/ecmult_gen_impl.h4
-rw-r--r--src/secp256k1/src/ecmult_impl.h929
-rw-r--r--src/secp256k1/src/field_10x26.h4
-rw-r--r--src/secp256k1/src/field_10x26_impl.h5
-rw-r--r--src/secp256k1/src/field_5x52.h4
-rw-r--r--src/secp256k1/src/field_5x52_impl.h2
-rw-r--r--src/secp256k1/src/field_5x52_int128_impl.h4
-rw-r--r--src/secp256k1/src/field_impl.h3
-rw-r--r--src/secp256k1/src/gen_context.c2
-rw-r--r--src/secp256k1/src/group.h10
-rw-r--r--src/secp256k1/src/group_impl.h75
-rw-r--r--src/secp256k1/src/hash.h26
-rw-r--r--src/secp256k1/src/hash_impl.h49
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java2
-rw-r--r--src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c6
-rw-r--r--src/secp256k1/src/modules/ecdh/main_impl.h41
-rw-r--r--src/secp256k1/src/modules/ecdh/tests_impl.h59
-rw-r--r--src/secp256k1/src/scalar_4x64_impl.h6
-rw-r--r--src/secp256k1/src/scratch.h39
-rw-r--r--src/secp256k1/src/scratch_impl.h86
-rw-r--r--src/secp256k1/src/secp256k1.c51
-rw-r--r--src/secp256k1/src/testrand_impl.h2
-rw-r--r--src/secp256k1/src/tests.c668
-rw-r--r--src/secp256k1/src/tests_exhaustive.c43
-rw-r--r--src/secp256k1/src/util.h10
-rw-r--r--src/serialize.h6
-rw-r--r--src/sync.cpp12
-rw-r--r--src/sync.h14
-rw-r--r--src/test/allocator_tests.cpp2
-rw-r--r--src/test/arith_uint256_tests.cpp1
-rw-r--r--src/test/bip32_tests.cpp3
-rw-r--r--src/test/blockchain_tests.cpp1
-rw-r--r--src/test/blockencodings_tests.cpp2
-rw-r--r--src/test/blockfilter_index_tests.cpp17
-rw-r--r--src/test/checkqueue_tests.cpp3
-rw-r--r--src/test/coins_tests.cpp11
-rw-r--r--src/test/compilerbug_tests.cpp43
-rw-r--r--src/test/crypto_tests.cpp88
-rw-r--r--src/test/dbwrapper_tests.cpp16
-rw-r--r--src/test/denialofservice_tests.cpp13
-rw-r--r--src/test/flatfile_tests.cpp11
-rw-r--r--src/test/fs_tests.cpp3
-rw-r--r--src/test/fuzz/deserialize.cpp5
-rw-r--r--src/test/fuzz/fuzz.h1
-rw-r--r--src/test/fuzz/script_flags.cpp1
-rw-r--r--src/test/hash_tests.cpp3
-rw-r--r--src/test/key_properties.cpp4
-rw-r--r--src/test/key_tests.cpp41
-rw-r--r--src/test/mempool_tests.cpp2
-rw-r--r--src/test/miner_tests.cpp71
-rw-r--r--src/test/multisig_tests.cpp2
-rw-r--r--src/test/net_tests.cpp7
-rw-r--r--src/test/netbase_tests.cpp1
-rw-r--r--src/test/policyestimator_tests.cpp1
-rw-r--r--src/test/pow_tests.cpp1
-rw-r--r--src/test/prevector_tests.cpp28
-rw-r--r--src/test/raii_event_tests.cpp2
-rw-r--r--src/test/rpc_tests.cpp4
-rw-r--r--src/test/script_p2sh_tests.cpp38
-rw-r--r--src/test/script_standard_tests.cpp400
-rw-r--r--src/test/script_tests.cpp6
-rw-r--r--src/test/scriptnum10.h1
-rw-r--r--src/test/serialize_tests.cpp1
-rw-r--r--src/test/setup_common.cpp30
-rw-r--r--src/test/setup_common.h9
-rw-r--r--src/test/sigopcount_tests.cpp12
-rw-r--r--src/test/streams_tests.cpp1
-rw-r--r--src/test/torcontrol_tests.cpp1
-rw-r--r--src/test/transaction_tests.cpp31
-rw-r--r--src/test/txindex_tests.cpp3
-rw-r--r--src/test/txvalidation_tests.cpp2
-rw-r--r--src/test/txvalidationcache_tests.cpp32
-rw-r--r--src/test/uint256_tests.cpp6
-rw-r--r--src/test/util.cpp8
-rw-r--r--src/test/util.h32
-rw-r--r--src/test/util_tests.cpp187
-rw-r--r--src/test/validation_block_tests.cpp8
-rw-r--r--src/test/versionbits_tests.cpp30
-rw-r--r--src/threadsafety.h11
-rw-r--r--src/timedata.cpp1
-rw-r--r--src/tinyformat.h1
-rw-r--r--src/torcontrol.cpp10
-rw-r--r--src/txdb.cpp3
-rw-r--r--src/txmempool.cpp4
-rw-r--r--src/txmempool.h2
-rw-r--r--src/ui_interface.cpp4
-rw-r--r--src/ui_interface.h3
-rw-r--r--src/util/system.cpp28
-rw-r--r--src/util/system.h7
-rw-r--r--src/util/threadnames.cpp5
-rw-r--r--src/util/time.cpp16
-rw-r--r--src/util/time.h27
-rw-r--r--src/validation.cpp548
-rw-r--r--src/validation.h206
-rw-r--r--src/validationinterface.cpp2
-rw-r--r--src/validationinterface.h1
-rw-r--r--src/wallet/coincontrol.cpp1
-rw-r--r--src/wallet/coincontrol.h4
-rw-r--r--src/wallet/crypter.cpp2
-rw-r--r--src/wallet/db.cpp17
-rw-r--r--src/wallet/db.h2
-rw-r--r--src/wallet/feebumper.cpp6
-rw-r--r--src/wallet/feebumper.h2
-rw-r--r--src/wallet/fees.cpp4
-rw-r--r--src/wallet/init.cpp9
-rw-r--r--src/wallet/ismine.cpp (renamed from src/script/ismine.cpp)15
-rw-r--r--src/wallet/ismine.h (renamed from src/script/ismine.h)16
-rw-r--r--src/wallet/load.cpp2
-rw-r--r--src/wallet/load.h2
-rw-r--r--src/wallet/psbtwallet.cpp2
-rw-r--r--src/wallet/rpcdump.cpp136
-rw-r--r--src/wallet/rpcwallet.cpp250
-rw-r--r--src/wallet/rpcwallet.h2
-rw-r--r--src/wallet/test/coinselector_tests.cpp2
-rw-r--r--src/wallet/test/db_tests.cpp14
-rw-r--r--src/wallet/test/init_test_fixture.cpp5
-rw-r--r--src/wallet/test/init_test_fixture.h2
-rw-r--r--src/wallet/test/init_tests.cpp8
-rw-r--r--src/wallet/test/ismine_tests.cpp398
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp5
-rw-r--r--src/wallet/test/wallet_crypto_tests.cpp2
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp6
-rw-r--r--src/wallet/test/wallet_test_fixture.h2
-rw-r--r--src/wallet/test/wallet_tests.cpp87
-rw-r--r--src/wallet/wallet.cpp254
-rw-r--r--src/wallet/wallet.h203
-rw-r--r--src/wallet/walletdb.cpp2
-rw-r--r--src/wallet/walletdb.h14
-rw-r--r--src/wallet/wallettool.cpp43
-rw-r--r--src/wallet/wallettool.h4
-rw-r--r--src/wallet/walletutil.cpp2
-rw-r--r--src/warnings.cpp9
-rw-r--r--src/zmq/zmqabstractnotifier.cpp1
-rw-r--r--src/zmq/zmqnotificationinterface.cpp1
282 files changed, 5826 insertions, 2589 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ed5cab7f04..39e8d3d689 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -181,7 +181,6 @@ BITCOIN_CORE_H = \
rpc/util.h \
scheduler.h \
script/descriptor.h \
- script/ismine.h \
script/sigcache.h \
script/sign.h \
script/standard.h \
@@ -223,6 +222,7 @@ BITCOIN_CORE_H = \
wallet/db.h \
wallet/feebumper.h \
wallet/fees.h \
+ wallet/ismine.h \
wallet/load.h \
wallet/psbtwallet.h \
wallet/rpcwallet.h \
@@ -328,6 +328,7 @@ libbitcoin_wallet_a_SOURCES = \
wallet/db.cpp \
wallet/feebumper.cpp \
wallet/fees.cpp \
+ wallet/ismine.cpp \
wallet/load.cpp \
wallet/psbtwallet.cpp \
wallet/rpcdump.cpp \
@@ -353,6 +354,8 @@ crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/chacha20.h \
crypto/chacha20.cpp \
crypto/common.h \
+ crypto/hkdf_sha256_32.cpp \
+ crypto/hkdf_sha256_32.h \
crypto/hmac_sha256.cpp \
crypto/hmac_sha256.h \
crypto/hmac_sha512.cpp \
@@ -456,7 +459,6 @@ libbitcoin_common_a_SOURCES = \
rpc/util.cpp \
scheduler.cpp \
script/descriptor.cpp \
- script/ismine.cpp \
script/sign.cpp \
script/standard.cpp \
versionbitsinfo.cpp \
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index ae7eb19ceb..c6162b5caa 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -21,12 +21,14 @@ bench_bench_bitcoin_SOURCES = \
bench/duplicate_inputs.cpp \
bench/examples.cpp \
bench/rollingbloom.cpp \
+ bench/chacha20.cpp \
bench/crypto_hash.cpp \
bench/ccoins_caching.cpp \
bench/gcs_filter.cpp \
bench/merkle_root.cpp \
bench/mempool_eviction.cpp \
bench/rpc_mempool.cpp \
+ bench/util_time.cpp \
bench/verify_script.cpp \
bench/base58.cpp \
bench/bech32.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 1144ca8a78..d3fe138133 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -97,6 +97,7 @@ BITCOIN_TESTS =\
test/bswap_tests.cpp \
test/checkqueue_tests.cpp \
test/coins_tests.cpp \
+ test/compilerbug_tests.cpp \
test/compress_tests.cpp \
test/crypto_tests.cpp \
test/cuckoocache_tests.cpp \
@@ -166,7 +167,8 @@ BITCOIN_TESTS += \
wallet/test/wallet_tests.cpp \
wallet/test/wallet_crypto_tests.cpp \
wallet/test/coinselector_tests.cpp \
- wallet/test/init_tests.cpp
+ wallet/test/init_tests.cpp \
+ wallet/test/ismine_tests.cpp
BITCOIN_TEST_SUITE += \
wallet/test/wallet_test_fixture.cpp \
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 8a5f78d1c5..32676f8fa5 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -7,7 +7,6 @@
#include <hash.h>
#include <serialize.h>
-#include <streams.h>
int CAddrInfo::GetTriedBucket(const uint256& nKey) const
{
diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp
index aa66d13102..be145a0e63 100644
--- a/src/arith_uint256.cpp
+++ b/src/arith_uint256.cpp
@@ -6,7 +6,6 @@
#include <arith_uint256.h>
#include <uint256.h>
-#include <util/strencodings.h>
#include <crypto/common.h>
#include <stdio.h>
diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp
index e7702ec461..0f4b52cf79 100644
--- a/src/bench/base58.cpp
+++ b/src/bench/base58.cpp
@@ -4,7 +4,6 @@
#include <bench/bench.h>
-#include <validation.h>
#include <base58.h>
#include <array>
diff --git a/src/bench/bech32.cpp b/src/bench/bech32.cpp
index 3c4b453a23..80f13eeb3b 100644
--- a/src/bench/bech32.cpp
+++ b/src/bench/bech32.cpp
@@ -4,7 +4,6 @@
#include <bench/bench.h>
-#include <validation.h>
#include <bech32.h>
#include <util/strencodings.h>
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index b08ecbb621..f2b520e893 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -114,8 +114,9 @@ void benchmark::BenchRunner::RunAll(Printer& printer, uint64_t num_evals, double
for (const auto& p : benchmarks()) {
TestingSetup test{CBaseChainParams::REGTEST};
{
- assert(::chainActive.Height() == 0);
- const bool witness_enabled{IsWitnessEnabled(::chainActive.Tip(), Params().GetConsensus())};
+ LOCK(cs_main);
+ assert(::ChainActive().Height() == 0);
+ const bool witness_enabled{IsWitnessEnabled(::ChainActive().Tip(), Params().GetConsensus())};
assert(witness_enabled);
}
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 3cf0bf9530..8eea96d930 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -4,8 +4,6 @@
#include <bench/bench.h>
-#include <crypto/sha256.h>
-#include <key.h>
#include <util/strencodings.h>
#include <util/system.h>
@@ -38,7 +36,7 @@ int main(int argc, char** argv)
SetupBenchArgs();
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
- fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
+ tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
return EXIT_FAILURE;
}
@@ -55,7 +53,7 @@ int main(int argc, char** argv)
double scaling_factor;
if (!ParseDouble(scaling_str, &scaling_factor)) {
- fprintf(stderr, "Error parsing scaling factor as double: %s\n", scaling_str.c_str());
+ tfm::format(std::cerr, "Error parsing scaling factor as double: %s\n", scaling_str.c_str());
return EXIT_FAILURE;
}
diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp
index 9cfd5d23ef..1041a22303 100644
--- a/src/bench/ccoins_caching.cpp
+++ b/src/bench/ccoins_caching.cpp
@@ -39,9 +39,9 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
dummyTransactions[1].vout.resize(2);
dummyTransactions[1].vout[0].nValue = 21 * COIN;
- dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
+ dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[2].GetPubKey()));
dummyTransactions[1].vout[1].nValue = 22 * COIN;
- dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
+ dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(PKHash(key[3].GetPubKey()));
AddCoins(coinsRet, CTransaction(dummyTransactions[1]), 0);
return dummyTransactions;
diff --git a/src/bench/chacha20.cpp b/src/bench/chacha20.cpp
new file mode 100644
index 0000000000..030067aca5
--- /dev/null
+++ b/src/bench/chacha20.cpp
@@ -0,0 +1,45 @@
+// Copyright (c) 2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <iostream>
+
+#include <bench/bench.h>
+#include <crypto/chacha20.h>
+
+/* Number of bytes to process per iteration */
+static const uint64_t BUFFER_SIZE_TINY = 64;
+static const uint64_t BUFFER_SIZE_SMALL = 256;
+static const uint64_t BUFFER_SIZE_LARGE = 1024*1024;
+
+static void CHACHA20(benchmark::State& state, size_t buffersize)
+{
+ std::vector<uint8_t> key(32,0);
+ ChaCha20 ctx(key.data(), key.size());
+ ctx.SetIV(0);
+ ctx.Seek(0);
+ std::vector<uint8_t> in(buffersize,0);
+ std::vector<uint8_t> out(buffersize,0);
+ while (state.KeepRunning()) {
+ ctx.Crypt(in.data(), out.data(), in.size());
+ }
+}
+
+static void CHACHA20_64BYTES(benchmark::State& state)
+{
+ CHACHA20(state, BUFFER_SIZE_TINY);
+}
+
+static void CHACHA20_256BYTES(benchmark::State& state)
+{
+ CHACHA20(state, BUFFER_SIZE_SMALL);
+}
+
+static void CHACHA20_1MB(benchmark::State& state)
+{
+ CHACHA20(state, BUFFER_SIZE_LARGE);
+}
+
+BENCHMARK(CHACHA20_64BYTES, 500000);
+BENCHMARK(CHACHA20_256BYTES, 250000);
+BENCHMARK(CHACHA20_1MB, 340);
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
index 6ab542067a..000a0259bb 100644
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -4,7 +4,6 @@
#include <bench/bench.h>
#include <util/system.h>
-#include <validation.h>
#include <checkqueue.h>
#include <prevector.h>
#include <vector>
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index dc0b054420..fb2bab9dee 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -5,11 +5,9 @@
#include <iostream>
#include <bench/bench.h>
-#include <bloom.h>
#include <hash.h>
#include <random.h>
#include <uint256.h>
-#include <util/time.h>
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
index 2d7a351523..2440341287 100644
--- a/src/bench/duplicate_inputs.cpp
+++ b/src/bench/duplicate_inputs.cpp
@@ -7,13 +7,9 @@
#include <coins.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
-#include <miner.h>
-#include <policy/policy.h>
#include <pow.h>
-#include <test/util.h>
#include <txmempool.h>
#include <validation.h>
-#include <validationinterface.h>
#include <list>
#include <vector>
@@ -29,7 +25,8 @@ static void DuplicateInputs(benchmark::State& state)
CMutableTransaction coinbaseTx{};
CMutableTransaction naughtyTx{};
- CBlockIndex* pindexPrev = ::chainActive.Tip();
+ LOCK(cs_main);
+ CBlockIndex* pindexPrev = ::ChainActive().Tip();
assert(pindexPrev != nullptr);
block.nBits = GetNextWorkRequired(pindexPrev, &block, chainparams.GetConsensus());
block.nNonce = 0;
diff --git a/src/bench/examples.cpp b/src/bench/examples.cpp
index e7ddd5a938..3595249559 100644
--- a/src/bench/examples.cpp
+++ b/src/bench/examples.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
-#include <validation.h>
#include <util/time.h>
// Sanity test: this should loop ten times, and
diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp
index 0a99ea3184..4016530dac 100644
--- a/src/bench/rollingbloom.cpp
+++ b/src/bench/rollingbloom.cpp
@@ -28,4 +28,13 @@ static void RollingBloom(benchmark::State& state)
}
}
+static void RollingBloomReset(benchmark::State& state)
+{
+ CRollingBloomFilter filter(120000, 0.000001);
+ while (state.KeepRunning()) {
+ filter.reset();
+ }
+}
+
BENCHMARK(RollingBloom, 1500 * 1000);
+BENCHMARK(RollingBloomReset, 20000);
diff --git a/src/bench/rpc_mempool.cpp b/src/bench/rpc_mempool.cpp
index 67d8a25564..b35a744055 100644
--- a/src/bench/rpc_mempool.cpp
+++ b/src/bench/rpc_mempool.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
-#include <policy/policy.h>
#include <rpc/blockchain.h>
#include <txmempool.h>
diff --git a/src/bench/util_time.cpp b/src/bench/util_time.cpp
new file mode 100644
index 0000000000..72d97354aa
--- /dev/null
+++ b/src/bench/util_time.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+
+#include <util/time.h>
+
+static void BenchTimeDeprecated(benchmark::State& state)
+{
+ while (state.KeepRunning()) {
+ (void)GetTime();
+ }
+}
+
+static void BenchTimeMock(benchmark::State& state)
+{
+ SetMockTime(111);
+ while (state.KeepRunning()) {
+ (void)GetTime<std::chrono::seconds>();
+ }
+ SetMockTime(0);
+}
+
+static void BenchTimeMillis(benchmark::State& state)
+{
+ while (state.KeepRunning()) {
+ (void)GetTime<std::chrono::milliseconds>();
+ }
+}
+
+static void BenchTimeMillisSys(benchmark::State& state)
+{
+ while (state.KeepRunning()) {
+ (void)GetTimeMillis();
+ }
+}
+
+BENCHMARK(BenchTimeDeprecated, 100000000);
+BENCHMARK(BenchTimeMillis, 6000000);
+BENCHMARK(BenchTimeMillisSys, 6000000);
+BENCHMARK(BenchTimeMock, 300000000);
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index 312b66e38a..4891c57b3a 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -8,7 +8,6 @@
#include <script/bitcoinconsensus.h>
#endif
#include <script/script.h>
-#include <script/sign.h>
#include <script/standard.h>
#include <streams.h>
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index 46ca12826b..313b5a3ba0 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -4,7 +4,6 @@
#include <bench/bench.h>
#include <interfaces/chain.h>
-#include <key_io.h>
#include <optional.h>
#include <test/util.h>
#include <validationinterface.h>
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 1009a771f8..38010c461e 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -101,7 +101,7 @@ static int AppInitRPC(int argc, char* argv[])
SetupCliArgs();
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
- fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
+ tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
return EXIT_FAILURE;
}
if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
@@ -115,26 +115,26 @@ static int AppInitRPC(int argc, char* argv[])
strUsage += "\n" + gArgs.GetHelpMessage();
}
- fprintf(stdout, "%s", strUsage.c_str());
+ tfm::format(std::cout, "%s", strUsage.c_str());
if (argc < 2) {
- fprintf(stderr, "Error: too few parameters\n");
+ tfm::format(std::cerr, "Error: too few parameters\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
if (!fs::is_directory(GetDataDir(false))) {
- fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
+ tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
return EXIT_FAILURE;
}
if (!gArgs.ReadConfigFiles(error, true)) {
- fprintf(stderr, "Error reading configuration file: %s\n", error.c_str());
+ tfm::format(std::cerr, "Error reading configuration file: %s\n", error.c_str());
return EXIT_FAILURE;
}
// Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
try {
SelectBaseParams(gArgs.GetChainName());
} catch (const std::exception& e) {
- fprintf(stderr, "Error: %s\n", e.what());
+ tfm::format(std::cerr, "Error: %s\n", e.what());
return EXIT_FAILURE;
}
return CONTINUE_EXECUTION;
@@ -495,7 +495,7 @@ static int CommandLineRPC(int argc, char *argv[])
}
if (strPrint != "") {
- fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
+ tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint.c_str());
}
return nRet;
}
@@ -508,7 +508,7 @@ int main(int argc, char* argv[])
#endif
SetupEnvironment();
if (!SetupNetworking()) {
- fprintf(stderr, "Error: Initializing networking failed\n");
+ tfm::format(std::cerr, "Error: Initializing networking failed\n");
return EXIT_FAILURE;
}
event_set_log_callback(&libevent_log_cb);
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 7f41ea7aed..933b34744d 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -82,7 +82,7 @@ static int AppInitRawTx(int argc, char* argv[])
SetupBitcoinTxArgs();
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
- fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
+ tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
return EXIT_FAILURE;
}
@@ -90,7 +90,7 @@ static int AppInitRawTx(int argc, char* argv[])
try {
SelectParams(gArgs.GetChainName());
} catch (const std::exception& e) {
- fprintf(stderr, "Error: %s\n", e.what());
+ tfm::format(std::cerr, "Error: %s\n", e.what());
return EXIT_FAILURE;
}
@@ -104,10 +104,10 @@ static int AppInitRawTx(int argc, char* argv[])
"\n";
strUsage += gArgs.GetHelpMessage();
- fprintf(stdout, "%s", strUsage.c_str());
+ tfm::format(std::cout, "%s", strUsage.c_str());
if (argc < 2) {
- fprintf(stderr, "Error: too few parameters\n");
+ tfm::format(std::cerr, "Error: too few parameters\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
@@ -323,7 +323,7 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str
}
if (bScriptHash) {
// Get the ID for the script, and then construct a P2SH destination for it.
- scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
+ scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
}
// construct TxOut, append to transaction output list
@@ -397,7 +397,7 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s
"redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
}
// Get the ID for the script, and then construct a P2SH destination for it.
- scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
+ scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
}
// construct TxOut, append to transaction output list
@@ -469,7 +469,7 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
throw std::runtime_error(strprintf(
"redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
}
- scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
+ scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
}
// construct TxOut, append to transaction output list
@@ -723,21 +723,21 @@ static void OutputTxJSON(const CTransaction& tx)
TxToUniv(tx, uint256(), entry);
std::string jsonOutput = entry.write(4);
- fprintf(stdout, "%s\n", jsonOutput.c_str());
+ tfm::format(std::cout, "%s\n", jsonOutput.c_str());
}
static void OutputTxHash(const CTransaction& tx)
{
std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
- fprintf(stdout, "%s\n", strHexHash.c_str());
+ tfm::format(std::cout, "%s\n", strHexHash.c_str());
}
static void OutputTxHex(const CTransaction& tx)
{
std::string strHex = EncodeHexTx(tx);
- fprintf(stdout, "%s\n", strHex.c_str());
+ tfm::format(std::cout, "%s\n", strHex.c_str());
}
static void OutputTx(const CTransaction& tx)
@@ -828,7 +828,7 @@ static int CommandLineRawTx(int argc, char* argv[])
}
if (strPrint != "") {
- fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
+ tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint.c_str());
}
return nRet;
}
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 32a539aac6..cbb4ea750c 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -8,7 +8,6 @@
#include <chainparams.h>
#include <chainparamsbase.h>
-#include <consensus/consensus.h>
#include <logging.h>
#include <util/system.h>
#include <util/strencodings.h>
@@ -37,7 +36,7 @@ static bool WalletAppInit(int argc, char* argv[])
SetupWalletToolArgs();
std::string error_message;
if (!gArgs.ParseParameters(argc, argv, error_message)) {
- fprintf(stderr, "Error parsing command line arguments: %s\n", error_message.c_str());
+ tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error_message.c_str());
return false;
}
if (argc < 2 || HelpRequested(gArgs)) {
@@ -49,7 +48,7 @@ static bool WalletAppInit(int argc, char* argv[])
" bitcoin-wallet [options] <command>\n\n" +
gArgs.GetHelpMessage();
- fprintf(stdout, "%s", usage.c_str());
+ tfm::format(std::cout, "%s", usage.c_str());
return false;
}
@@ -57,7 +56,7 @@ static bool WalletAppInit(int argc, char* argv[])
LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", gArgs.GetBoolArg("-debug", false));
if (!fs::is_directory(GetDataDir(false))) {
- fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
+ tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
return false;
}
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
@@ -88,7 +87,7 @@ int main(int argc, char* argv[])
for(int i = 1; i < argc; ++i) {
if (!IsSwitchChar(argv[i][0])) {
if (!method.empty()) {
- fprintf(stderr, "Error: two methods provided (%s and %s). Only one method should be provided.\n", method.c_str(), argv[i]);
+ tfm::format(std::cerr, "Error: two methods provided (%s and %s). Only one method should be provided.\n", method.c_str(), argv[i]);
return EXIT_FAILURE;
}
method = argv[i];
@@ -96,13 +95,13 @@ int main(int argc, char* argv[])
}
if (method.empty()) {
- fprintf(stderr, "No method provided. Run `bitcoin-wallet -help` for valid methods.\n");
+ tfm::format(std::cerr, "No method provided. Run `bitcoin-wallet -help` for valid methods.\n");
return EXIT_FAILURE;
}
// A name must be provided when creating a file
if (method == "create" && !gArgs.IsArgSet("-wallet")) {
- fprintf(stderr, "Wallet name must be provided when creating a new wallet.\n");
+ tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
return EXIT_FAILURE;
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index b31f86cdd9..ba6de702e0 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -12,16 +12,12 @@
#include <compat.h>
#include <fs.h>
#include <interfaces/chain.h>
-#include <rpc/server.h>
#include <init.h>
#include <noui.h>
#include <shutdown.h>
#include <util/system.h>
-#include <httpserver.h>
-#include <httprpc.h>
#include <util/threadnames.h>
#include <util/strencodings.h>
-#include <walletinitinterface.h>
#include <stdio.h>
@@ -74,7 +70,7 @@ static bool AppInit(int argc, char* argv[])
SetupServerArgs();
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
- fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
+ tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
return false;
}
@@ -92,7 +88,7 @@ static bool AppInit(int argc, char* argv[])
strUsage += "\n" + gArgs.GetHelpMessage();
}
- fprintf(stdout, "%s", strUsage.c_str());
+ tfm::format(std::cout, "%s", strUsage.c_str());
return true;
}
@@ -100,25 +96,25 @@ static bool AppInit(int argc, char* argv[])
{
if (!fs::is_directory(GetDataDir(false)))
{
- fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
+ tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
return false;
}
if (!gArgs.ReadConfigFiles(error, true)) {
- fprintf(stderr, "Error reading configuration file: %s\n", error.c_str());
+ tfm::format(std::cerr, "Error reading configuration file: %s\n", error.c_str());
return false;
}
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
try {
SelectParams(gArgs.GetChainName());
} catch (const std::exception& e) {
- fprintf(stderr, "Error: %s\n", e.what());
+ tfm::format(std::cerr, "Error: %s\n", e.what());
return false;
}
// Error out when loose non-argument tokens are encountered on command line
for (int i = 1; i < argc; i++) {
if (!IsSwitchChar(argv[i][0])) {
- fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
+ tfm::format(std::cerr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
return false;
}
}
@@ -150,18 +146,18 @@ static bool AppInit(int argc, char* argv[])
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
- fprintf(stdout, "Bitcoin server starting\n");
+ tfm::format(std::cout, "Bitcoin server starting\n");
// Daemonize
if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
- fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno));
+ tfm::format(std::cerr, "Error: daemon() failed: %s\n", strerror(errno));
return false;
}
#if defined(MAC_OSX)
#pragma GCC diagnostic pop
#endif
#else
- fprintf(stderr, "Error: -daemon is not supported on this operating system\n");
+ tfm::format(std::cerr, "Error: -daemon is not supported on this operating system\n");
return false;
#endif // HAVE_DECL_DAEMON
}
diff --git a/src/bloom.cpp b/src/bloom.cpp
index 7732cee275..a061925089 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -14,6 +14,7 @@
#include <math.h>
#include <stdlib.h>
+#include <algorithm>
#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
#define LN2 0.6931471805599453094172321214581765680755001343602552
@@ -304,7 +305,5 @@ void CRollingBloomFilter::reset()
nTweak = GetRand(std::numeric_limits<unsigned int>::max());
nEntriesThisGeneration = 0;
nGeneration = 1;
- for (std::vector<uint64_t>::iterator it = data.begin(); it != data.end(); it++) {
- *it = 0;
- }
+ std::fill(data.begin(), data.end(), 0);
}
diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp
index b47d9774ca..f87612edef 100644
--- a/src/consensus/merkle.cpp
+++ b/src/consensus/merkle.cpp
@@ -4,7 +4,6 @@
#include <consensus/merkle.h>
#include <hash.h>
-#include <util/strencodings.h>
/* WARNING! If you're reading this because you're learning about crypto
and/or designing a new system that will use merkle trees, keep in mind
diff --git a/src/core_read.cpp b/src/core_read.cpp
index a879a375ce..a3c9cf0159 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -11,7 +11,6 @@
#include <serialize.h>
#include <streams.h>
#include <univalue.h>
-#include <util/system.h>
#include <util/strencodings.h>
#include <version.h>
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 765a170307..4d64446d7b 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -13,7 +13,6 @@
#include <streams.h>
#include <univalue.h>
#include <util/system.h>
-#include <util/moneystr.h>
#include <util/strencodings.h>
UniValue ValueFromAmount(const CAmount& amount)
diff --git a/src/crypto/aes.cpp b/src/crypto/aes.cpp
index 2dc2133434..b3fb927760 100644
--- a/src/crypto/aes.cpp
+++ b/src/crypto/aes.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <crypto/aes.h>
-#include <crypto/common.h>
#include <assert.h>
#include <string.h>
diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp
index ac4470f04f..42a17f02ff 100644
--- a/src/crypto/chacha20.cpp
+++ b/src/crypto/chacha20.cpp
@@ -71,7 +71,7 @@ void ChaCha20::Seek(uint64_t pos)
input[13] = pos >> 32;
}
-void ChaCha20::Output(unsigned char* c, size_t bytes)
+void ChaCha20::Keystream(unsigned char* c, size_t bytes)
{
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
@@ -178,3 +178,133 @@ void ChaCha20::Output(unsigned char* c, size_t bytes)
c += 64;
}
}
+
+void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes)
+{
+ uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
+ uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
+ unsigned char *ctarget = nullptr;
+ unsigned char tmp[64];
+ unsigned int i;
+
+ if (!bytes) return;
+
+ j0 = input[0];
+ j1 = input[1];
+ j2 = input[2];
+ j3 = input[3];
+ j4 = input[4];
+ j5 = input[5];
+ j6 = input[6];
+ j7 = input[7];
+ j8 = input[8];
+ j9 = input[9];
+ j10 = input[10];
+ j11 = input[11];
+ j12 = input[12];
+ j13 = input[13];
+ j14 = input[14];
+ j15 = input[15];
+
+ for (;;) {
+ if (bytes < 64) {
+ // if m has fewer than 64 bytes available, copy m to tmp and
+ // read from tmp instead
+ for (i = 0;i < bytes;++i) tmp[i] = m[i];
+ m = tmp;
+ ctarget = c;
+ c = tmp;
+ }
+ x0 = j0;
+ x1 = j1;
+ x2 = j2;
+ x3 = j3;
+ x4 = j4;
+ x5 = j5;
+ x6 = j6;
+ x7 = j7;
+ x8 = j8;
+ x9 = j9;
+ x10 = j10;
+ x11 = j11;
+ x12 = j12;
+ x13 = j13;
+ x14 = j14;
+ x15 = j15;
+ for (i = 20;i > 0;i -= 2) {
+ QUARTERROUND( x0, x4, x8,x12)
+ QUARTERROUND( x1, x5, x9,x13)
+ QUARTERROUND( x2, x6,x10,x14)
+ QUARTERROUND( x3, x7,x11,x15)
+ QUARTERROUND( x0, x5,x10,x15)
+ QUARTERROUND( x1, x6,x11,x12)
+ QUARTERROUND( x2, x7, x8,x13)
+ QUARTERROUND( x3, x4, x9,x14)
+ }
+ x0 += j0;
+ x1 += j1;
+ x2 += j2;
+ x3 += j3;
+ x4 += j4;
+ x5 += j5;
+ x6 += j6;
+ x7 += j7;
+ x8 += j8;
+ x9 += j9;
+ x10 += j10;
+ x11 += j11;
+ x12 += j12;
+ x13 += j13;
+ x14 += j14;
+ x15 += j15;
+
+ x0 ^= ReadLE32(m + 0);
+ x1 ^= ReadLE32(m + 4);
+ x2 ^= ReadLE32(m + 8);
+ x3 ^= ReadLE32(m + 12);
+ x4 ^= ReadLE32(m + 16);
+ x5 ^= ReadLE32(m + 20);
+ x6 ^= ReadLE32(m + 24);
+ x7 ^= ReadLE32(m + 28);
+ x8 ^= ReadLE32(m + 32);
+ x9 ^= ReadLE32(m + 36);
+ x10 ^= ReadLE32(m + 40);
+ x11 ^= ReadLE32(m + 44);
+ x12 ^= ReadLE32(m + 48);
+ x13 ^= ReadLE32(m + 52);
+ x14 ^= ReadLE32(m + 56);
+ x15 ^= ReadLE32(m + 60);
+
+ ++j12;
+ if (!j12) ++j13;
+
+ WriteLE32(c + 0, x0);
+ WriteLE32(c + 4, x1);
+ WriteLE32(c + 8, x2);
+ WriteLE32(c + 12, x3);
+ WriteLE32(c + 16, x4);
+ WriteLE32(c + 20, x5);
+ WriteLE32(c + 24, x6);
+ WriteLE32(c + 28, x7);
+ WriteLE32(c + 32, x8);
+ WriteLE32(c + 36, x9);
+ WriteLE32(c + 40, x10);
+ WriteLE32(c + 44, x11);
+ WriteLE32(c + 48, x12);
+ WriteLE32(c + 52, x13);
+ WriteLE32(c + 56, x14);
+ WriteLE32(c + 60, x15);
+
+ if (bytes <= 64) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) ctarget[i] = c[i];
+ }
+ input[12] = j12;
+ input[13] = j13;
+ return;
+ }
+ bytes -= 64;
+ c += 64;
+ m += 64;
+ }
+}
diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h
index a305977bcd..5a4674f4a8 100644
--- a/src/crypto/chacha20.h
+++ b/src/crypto/chacha20.h
@@ -8,7 +8,8 @@
#include <stdint.h>
#include <stdlib.h>
-/** A PRNG class for ChaCha20. */
+/** A class for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein
+ https://cr.yp.to/chacha/chacha-20080128.pdf */
class ChaCha20
{
private:
@@ -17,10 +18,17 @@ private:
public:
ChaCha20();
ChaCha20(const unsigned char* key, size_t keylen);
- void SetKey(const unsigned char* key, size_t keylen);
- void SetIV(uint64_t iv);
- void Seek(uint64_t pos);
- void Output(unsigned char* output, size_t bytes);
+ void SetKey(const unsigned char* key, size_t keylen); //!< set key with flexible keylength; 256bit recommended */
+ void SetIV(uint64_t iv); // set the 64bit nonce
+ void Seek(uint64_t pos); // set the 64bit block counter
+
+ /** outputs the keystream of size <bytes> into <c> */
+ void Keystream(unsigned char* c, size_t bytes);
+
+ /** enciphers the message <input> of length <bytes> and write the enciphered representation into <output>
+ * Used for encryption and decryption (XOR)
+ */
+ void Crypt(const unsigned char* input, unsigned char* output, size_t bytes);
};
#endif // BITCOIN_CRYPTO_CHACHA20_H
diff --git a/src/crypto/hkdf_sha256_32.cpp b/src/crypto/hkdf_sha256_32.cpp
new file mode 100644
index 0000000000..9cea5995ec
--- /dev/null
+++ b/src/crypto/hkdf_sha256_32.cpp
@@ -0,0 +1,21 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <crypto/hkdf_sha256_32.h>
+
+#include <assert.h>
+#include <string.h>
+
+CHKDF_HMAC_SHA256_L32::CHKDF_HMAC_SHA256_L32(const unsigned char* ikm, size_t ikmlen, const std::string& salt)
+{
+ CHMAC_SHA256((const unsigned char*)salt.c_str(), salt.size()).Write(ikm, ikmlen).Finalize(m_prk);
+}
+
+void CHKDF_HMAC_SHA256_L32::Expand32(const std::string& info, unsigned char hash[OUTPUT_SIZE])
+{
+ // expand a 32byte key (single round)
+ assert(info.size() <= 128);
+ static const unsigned char one[1] = {1};
+ CHMAC_SHA256(m_prk, 32).Write((const unsigned char*)info.data(), info.size()).Write(one, 1).Finalize(hash);
+}
diff --git a/src/crypto/hkdf_sha256_32.h b/src/crypto/hkdf_sha256_32.h
new file mode 100644
index 0000000000..fa1e42aec1
--- /dev/null
+++ b/src/crypto/hkdf_sha256_32.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_CRYPTO_HKDF_SHA256_32_H
+#define BITCOIN_CRYPTO_HKDF_SHA256_32_H
+
+#include <crypto/hmac_sha256.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/** A rfc5869 HKDF implementation with HMAC_SHA256 and fixed key output length of 32 bytes (L=32) */
+class CHKDF_HMAC_SHA256_L32
+{
+private:
+ unsigned char m_prk[32];
+ static const size_t OUTPUT_SIZE = 32;
+
+public:
+ CHKDF_HMAC_SHA256_L32(const unsigned char* ikm, size_t ikmlen, const std::string& salt);
+ void Expand32(const std::string& info, unsigned char hash[OUTPUT_SIZE]);
+};
+
+#endif // BITCOIN_CRYPTO_HKDF_SHA256_32_H
diff --git a/src/crypto/ripemd160.cpp b/src/crypto/ripemd160.cpp
index a00331dcb7..edee06cc34 100644
--- a/src/crypto/ripemd160.cpp
+++ b/src/crypto/ripemd160.cpp
@@ -256,7 +256,7 @@ CRIPEMD160& CRIPEMD160::Write(const unsigned char* data, size_t len)
ripemd160::Transform(s, buf);
bufsize = 0;
}
- while (end >= data + 64) {
+ while (end - data >= 64) {
// Process full chunks directly from the source.
ripemd160::Transform(s, data);
bytes += 64;
diff --git a/src/crypto/sha1.cpp b/src/crypto/sha1.cpp
index 5c601c54a6..3dcdcb186e 100644
--- a/src/crypto/sha1.cpp
+++ b/src/crypto/sha1.cpp
@@ -163,7 +163,7 @@ CSHA1& CSHA1::Write(const unsigned char* data, size_t len)
sha1::Transform(s, buf);
bufsize = 0;
}
- while (end >= data + 64) {
+ while (end - data >= 64) {
// Process full chunks directly from the source.
sha1::Transform(s, data);
bytes += 64;
diff --git a/src/crypto/sha256_avx2.cpp b/src/crypto/sha256_avx2.cpp
index 068e0e5ff6..90a72516a4 100644
--- a/src/crypto/sha256_avx2.cpp
+++ b/src/crypto/sha256_avx2.cpp
@@ -3,7 +3,6 @@
#include <stdint.h>
#include <immintrin.h>
-#include <crypto/sha256.h>
#include <crypto/common.h>
namespace sha256d64_avx2 {
diff --git a/src/crypto/sha256_sse41.cpp b/src/crypto/sha256_sse41.cpp
index adca870e2d..fc79f46f7f 100644
--- a/src/crypto/sha256_sse41.cpp
+++ b/src/crypto/sha256_sse41.cpp
@@ -3,7 +3,6 @@
#include <stdint.h>
#include <immintrin.h>
-#include <crypto/sha256.h>
#include <crypto/common.h>
namespace sha256d64_sse41 {
diff --git a/src/crypto/sha512.cpp b/src/crypto/sha512.cpp
index bc64135cae..4e6aa363f7 100644
--- a/src/crypto/sha512.cpp
+++ b/src/crypto/sha512.cpp
@@ -168,7 +168,7 @@ CSHA512& CSHA512::Write(const unsigned char* data, size_t len)
sha512::Transform(s, buf);
bufsize = 0;
}
- while (end >= data + 128) {
+ while (end - data >= 128) {
// Process full chunks directly from the source.
sha512::Transform(s, data);
data += 128;
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 58d8cc2c9d..34896f7ab2 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -115,7 +115,7 @@ static leveldb::Options GetOptions(size_t nCacheSize)
}
CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
- : m_name(fs::basename(path))
+ : m_name{path.stem().string()}
{
penv = nullptr;
readoptions.verify_checksums = true;
diff --git a/src/fs.h b/src/fs.h
index 8af81f173b..c713297d6e 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -11,6 +11,7 @@
#include <ext/stdio_filebuf.h>
#endif
+#define BOOST_FILESYSTEM_NO_DEPRECATED
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index fcf760a4c6..c7a119440b 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -9,7 +9,6 @@
#include <key_io.h>
#include <rpc/protocol.h>
#include <rpc/server.h>
-#include <random.h>
#include <sync.h>
#include <util/system.h>
#include <util/strencodings.h>
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 63639fa3e0..d17667223b 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -139,15 +139,15 @@ struct HTTPPathHandler
//! libevent event loop
static struct event_base* eventBase = nullptr;
//! HTTP server
-struct evhttp* eventHTTP = nullptr;
+static struct evhttp* eventHTTP = nullptr;
//! List of subnets to allow RPC connections from
static std::vector<CSubNet> rpc_allow_subnets;
//! Work queue for handling longer requests off the event loop thread
static WorkQueue<HTTPClosure>* workQueue = nullptr;
//! Handlers for (sub)paths
-std::vector<HTTPPathHandler> pathHandlers;
+static std::vector<HTTPPathHandler> pathHandlers;
//! Bound listening sockets
-std::vector<evhttp_bound_socket *> boundSockets;
+static std::vector<evhttp_bound_socket *> boundSockets;
/** Check if a network address is allowed to access the HTTP server */
static bool ClientAllowed(const CNetAddr& netaddr)
@@ -420,7 +420,7 @@ bool UpdateHTTPServerLogging(bool enable) {
#endif
}
-std::thread threadHTTP;
+static std::thread threadHTTP;
static std::vector<std::thread> g_thread_http_workers;
void StartHTTPServer()
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 9e48f0bd27..bcc8e2ce7c 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -63,9 +63,9 @@ bool BaseIndex::Init()
if (locator.IsNull()) {
m_best_block_index = nullptr;
} else {
- m_best_block_index = FindForkInGlobalIndex(chainActive, locator);
+ m_best_block_index = FindForkInGlobalIndex(::ChainActive(), locator);
}
- m_synced = m_best_block_index.load() == chainActive.Tip();
+ m_synced = m_best_block_index.load() == ::ChainActive().Tip();
return true;
}
@@ -74,15 +74,15 @@ static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev) EXCLUSIV
AssertLockHeld(cs_main);
if (!pindex_prev) {
- return chainActive.Genesis();
+ return ::ChainActive().Genesis();
}
- const CBlockIndex* pindex = chainActive.Next(pindex_prev);
+ const CBlockIndex* pindex = ::ChainActive().Next(pindex_prev);
if (pindex) {
return pindex;
}
- return chainActive.Next(chainActive.FindFork(pindex_prev));
+ return ::ChainActive().Next(::ChainActive().FindFork(pindex_prev));
}
void BaseIndex::ThreadSync()
@@ -168,7 +168,7 @@ bool BaseIndex::Commit()
bool BaseIndex::CommitInternal(CDBBatch& batch)
{
LOCK(cs_main);
- GetDB().WriteBestBlock(batch, chainActive.GetLocator(m_best_block_index));
+ GetDB().WriteBestBlock(batch, ::ChainActive().GetLocator(m_best_block_index));
return true;
}
@@ -280,9 +280,9 @@ bool BaseIndex::BlockUntilSyncedToCurrentChain()
{
// Skip the queue-draining stuff if we know we're caught up with
- // chainActive.Tip().
+ // ::ChainActive().Tip().
LOCK(cs_main);
- const CBlockIndex* chain_tip = chainActive.Tip();
+ const CBlockIndex* chain_tip = ::ChainActive().Tip();
const CBlockIndex* best_block_index = m_best_block_index.load();
if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) {
return true;
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index 7367ec7cb6..929b85bfb5 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -236,7 +236,7 @@ bool TxIndex::Init()
// Attempt to migrate txindex from the old database to the new one. Even if
// chain_tip is null, the node could be reindexing and we still want to
// delete txindex records in the old database.
- if (!m_db->MigrateData(*pblocktree, chainActive.GetLocator())) {
+ if (!m_db->MigrateData(*pblocktree, ::ChainActive().GetLocator())) {
return false;
}
diff --git a/src/init.cpp b/src/init.cpp
index 92b3c9510a..6625080c6e 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -51,7 +51,6 @@
#include <util/moneystr.h>
#include <util/validation.h>
#include <validationinterface.h>
-#include <warnings.h>
#include <walletinitinterface.h>
#include <stdint.h>
#include <stdio.h>
@@ -74,7 +73,7 @@
#include <zmq/zmqrpc.h>
#endif
-bool fFeeEstimatesInitialized = false;
+static bool fFeeEstimatesInitialized = false;
static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
@@ -109,14 +108,13 @@ static fs::path GetPidFile()
NODISCARD static bool CreatePidFile()
{
- FILE* file = fsbridge::fopen(GetPidFile(), "w");
+ fsbridge::ofstream file{GetPidFile()};
if (file) {
#ifdef WIN32
- fprintf(file, "%d\n", GetCurrentProcessId());
+ tfm::format(file, "%d\n", GetCurrentProcessId());
#else
- fprintf(file, "%d\n", getpid());
+ tfm::format(file, "%d\n", getpid());
#endif
- fclose(file);
return true;
} else {
return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile().string(), std::strerror(errno)));
@@ -259,7 +257,7 @@ void Shutdown(InitInterfaces& interfaces)
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
if (pcoinsTip != nullptr) {
- FlushStateToDisk();
+ ::ChainstateActive().ForceFlushStateToDisk();
}
// After there are no more peers/RPC left to give us new data which may generate
@@ -275,7 +273,7 @@ void Shutdown(InitInterfaces& interfaces)
{
LOCK(cs_main);
if (pcoinsTip != nullptr) {
- FlushStateToDisk();
+ ::ChainstateActive().ForceFlushStateToDisk();
}
pcoinsTip.reset();
pcoinscatcher.reset();
@@ -380,10 +378,10 @@ void SetupServerArgs()
gArgs.AddArg("-version", "Print version and exit", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-alertnotify=<cmd>", "Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-assumevalid=<hex>", strprintf("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-blocksdir=<dir>", "Specify blocks directory (default: <datadir>/blocks)", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksdir=<dir>", "Specify directory to hold blocks subdirectory for *.dat files (default: <datadir>)", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-blocksonly", strprintf("Whether to operate in a blocks only mode (default: %u)", DEFAULT_BLOCKSONLY), true, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Transactions from the wallet or RPC are not affected. (default: %u)", DEFAULT_BLOCKSONLY), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS);
@@ -457,7 +455,7 @@ void SetupServerArgs()
#endif
gArgs.AddArg("-whitebind=<addr>", "Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6", false, OptionsCategory::CONNECTION);
gArgs.AddArg("-whitelist=<IP address or network>", "Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times."
- " Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway", false, OptionsCategory::CONNECTION);
+ " Whitelisted peers cannot be DoS banned", false, OptionsCategory::CONNECTION);
g_wallet_init_interface.AddWalletOptions();
@@ -490,7 +488,7 @@ void SetupServerArgs()
"and level 4 tries to reconnect the blocks, "
"each level includes the checks of the previous levels "
"(0-4, default: %u)", DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, ::ChainActive() and mapBlocksUnlinked occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", true, OptionsCategory::DEBUG_TEST);
@@ -525,10 +523,9 @@ void SetupServerArgs()
gArgs.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), false, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), false, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), false, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-mempoolreplacement", strprintf("Enable transaction replacement in the memory pool (default: %u)", DEFAULT_ENABLE_REPLACEMENT), false, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), false, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-whitelistforcerelay", strprintf("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)", DEFAULT_WHITELISTFORCERELAY), false, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-whitelistforcerelay", strprintf("Force relay of transactions from whitelisted peers even if the transactions were already in the mempool or violate local relay policy (default: %d)", DEFAULT_WHITELISTFORCERELAY), false, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-whitelistrelay", strprintf("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), false, OptionsCategory::NODE_RELAY);
@@ -855,12 +852,6 @@ void InitLogging()
{
LogInstance().m_print_to_file = !gArgs.IsArgNegated("-debuglogfile");
LogInstance().m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
-
- // Add newlines to the logfile to distinguish this execution from the last
- // one; called before console logging is set up, so this is only sent to
- // debug.log.
- LogPrintf("\n\n\n\n\n");
-
LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
LogInstance().m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
LogInstance().m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
@@ -1177,15 +1168,6 @@ bool AppInitParameterInteraction()
nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
- fEnableReplacement = gArgs.GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT);
- if ((!fEnableReplacement) && gArgs.IsArgSet("-mempoolreplacement")) {
- // Minimal effort at forwards compatibility
- std::string strReplacementModeList = gArgs.GetArg("-mempoolreplacement", ""); // default is impossible
- std::vector<std::string> vstrReplacementModes;
- boost::split(vstrReplacementModes, strReplacementModeList, boost::is_any_of(","));
- fEnableReplacement = (std::find(vstrReplacementModes.begin(), vstrReplacementModes.end(), "fee") != vstrReplacementModes.end());
- }
-
return true;
}
@@ -1249,10 +1231,10 @@ bool AppInitMain(InitInterfaces& interfaces)
// and because this needs to happen before any other debug.log printing
LogInstance().ShrinkDebugFile();
}
- if (!LogInstance().OpenDebugLog()) {
+ }
+ if (!LogInstance().StartLogging()) {
return InitError(strprintf("Could not open debug log file %s",
LogInstance().m_file_path.string()));
- }
}
if (!LogInstance().m_log_timestamps)
@@ -1426,7 +1408,7 @@ bool AppInitMain(InitInterfaces& interfaces)
// see Step 2: parameter interactions for more information about these
fListen = gArgs.GetBoolArg("-listen", DEFAULT_LISTEN);
fDiscover = gArgs.GetBoolArg("-discover", true);
- fRelayTxes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
+ g_relay_txes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
for (const std::string& strAddr : gArgs.GetArgs("-externalip")) {
CService addrLocal;
@@ -1522,6 +1504,7 @@ bool AppInitMain(InitInterfaces& interfaces)
// Note that it also sets fReindex based on the disk flag!
// From here on out fReindex and fReset mean something different!
if (!LoadBlockIndex(chainparams)) {
+ if (ShutdownRequested()) break;
strLoadError = _("Error loading block database");
break;
}
@@ -1572,12 +1555,12 @@ bool AppInitMain(InitInterfaces& interfaces)
is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull();
if (!is_coinsview_empty) {
- // LoadChainTip sets chainActive based on pcoinsTip's best block
+ // LoadChainTip sets ::ChainActive() based on pcoinsTip's best block
if (!LoadChainTip(chainparams)) {
strLoadError = _("Error initializing block database");
break;
}
- assert(chainActive.Tip() != nullptr);
+ assert(::ChainActive().Tip() != nullptr);
}
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
@@ -1587,7 +1570,7 @@ bool AppInitMain(InitInterfaces& interfaces)
if (!fReset) {
// Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
- // It both disconnects blocks based on chainActive, and drops block data in
+ // It both disconnects blocks based on ::ChainActive(), and drops block data in
// mapBlockIndex based on lack of available witness data.
uiInterface.InitMessage(_("Rewinding blocks..."));
if (!RewindBlockIndex(chainparams)) {
@@ -1605,7 +1588,7 @@ bool AppInitMain(InitInterfaces& interfaces)
MIN_BLOCKS_TO_KEEP);
}
- CBlockIndex* tip = chainActive.Tip();
+ CBlockIndex* tip = ::ChainActive().Tip();
RPCNotifyBlockChange(true, tip);
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
strLoadError = _("The block database contains a block which appears to be from the future. "
@@ -1692,7 +1675,7 @@ bool AppInitMain(InitInterfaces& interfaces)
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
if (!fReindex) {
uiInterface.InitMessage(_("Pruning blockstore..."));
- PruneAndFlush();
+ ::ChainstateActive().PruneAndFlush();
}
}
@@ -1719,7 +1702,7 @@ bool AppInitMain(InitInterfaces& interfaces)
// Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
// No locking, as this happens before any background thread is started.
boost::signals2::connection block_notify_genesis_wait_connection;
- if (chainActive.Tip() == nullptr) {
+ if (::ChainActive().Tip() == nullptr) {
block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(BlockNotifyGenesisWait);
} else {
fHaveGenesis = true;
@@ -1759,7 +1742,7 @@ bool AppInitMain(InitInterfaces& interfaces)
{
LOCK(cs_main);
LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size());
- chain_active_height = chainActive.Height();
+ chain_active_height = ::ChainActive().Height();
}
LogPrintf("nBestHeight = %d\n", chain_active_height);
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index 617be3ca71..161dd01ffe 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -37,11 +37,12 @@
namespace interfaces {
namespace {
-class LockImpl : public Chain::Lock
+class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
{
Optional<int> getHeight() override
{
- int height = ::chainActive.Height();
+ LockAssertion lock(::cs_main);
+ int height = ::ChainActive().Height();
if (height >= 0) {
return height;
}
@@ -49,8 +50,9 @@ class LockImpl : public Chain::Lock
}
Optional<int> getBlockHeight(const uint256& hash) override
{
+ LockAssertion lock(::cs_main);
CBlockIndex* block = LookupBlockIndex(hash);
- if (block && ::chainActive.Contains(block)) {
+ if (block && ::ChainActive().Contains(block)) {
return block->nHeight;
}
return nullopt;
@@ -63,30 +65,35 @@ class LockImpl : public Chain::Lock
}
uint256 getBlockHash(int height) override
{
- CBlockIndex* block = ::chainActive[height];
+ LockAssertion lock(::cs_main);
+ CBlockIndex* block = ::ChainActive()[height];
assert(block != nullptr);
return block->GetBlockHash();
}
int64_t getBlockTime(int height) override
{
- CBlockIndex* block = ::chainActive[height];
+ LockAssertion lock(::cs_main);
+ CBlockIndex* block = ::ChainActive()[height];
assert(block != nullptr);
return block->GetBlockTime();
}
int64_t getBlockMedianTimePast(int height) override
{
- CBlockIndex* block = ::chainActive[height];
+ LockAssertion lock(::cs_main);
+ CBlockIndex* block = ::ChainActive()[height];
assert(block != nullptr);
return block->GetMedianTimePast();
}
bool haveBlockOnDisk(int height) override
{
- CBlockIndex* block = ::chainActive[height];
+ LockAssertion lock(::cs_main);
+ CBlockIndex* block = ::ChainActive()[height];
return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0;
}
Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) override
{
- CBlockIndex* block = ::chainActive.FindEarliestAtLeast(time, height);
+ LockAssertion lock(::cs_main);
+ CBlockIndex* block = ::ChainActive().FindEarliestAtLeast(time, height);
if (block) {
if (hash) *hash = block->GetBlockHash();
return block->nHeight;
@@ -95,8 +102,9 @@ class LockImpl : public Chain::Lock
}
Optional<int> findPruned(int start_height, Optional<int> stop_height) override
{
+ LockAssertion lock(::cs_main);
if (::fPruneMode) {
- CBlockIndex* block = stop_height ? ::chainActive[*stop_height] : ::chainActive.Tip();
+ CBlockIndex* block = stop_height ? ::ChainActive()[*stop_height] : ::ChainActive().Tip();
while (block && block->nHeight >= start_height) {
if ((block->nStatus & BLOCK_HAVE_DATA) == 0) {
return block->nHeight;
@@ -108,8 +116,9 @@ class LockImpl : public Chain::Lock
}
Optional<int> findFork(const uint256& hash, Optional<int>* height) override
{
+ LockAssertion lock(::cs_main);
const CBlockIndex* block = LookupBlockIndex(hash);
- const CBlockIndex* fork = block ? ::chainActive.FindFork(block) : nullptr;
+ const CBlockIndex* fork = block ? ::ChainActive().FindFork(block) : nullptr;
if (height) {
if (block) {
*height = block->nHeight;
@@ -122,30 +131,31 @@ class LockImpl : public Chain::Lock
}
return nullopt;
}
- CBlockLocator getTipLocator() override { return ::chainActive.GetLocator(); }
+ CBlockLocator getTipLocator() override
+ {
+ LockAssertion lock(::cs_main);
+ return ::ChainActive().GetLocator();
+ }
Optional<int> findLocatorFork(const CBlockLocator& locator) override
{
- LockAnnotation lock(::cs_main);
- if (CBlockIndex* fork = FindForkInGlobalIndex(::chainActive, locator)) {
+ LockAssertion lock(::cs_main);
+ if (CBlockIndex* fork = FindForkInGlobalIndex(::ChainActive(), locator)) {
return fork->nHeight;
}
return nullopt;
}
bool checkFinalTx(const CTransaction& tx) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
return CheckFinalTx(tx);
}
bool submitToMemoryPool(const CTransactionRef& tx, CAmount absurd_fee, CValidationState& state) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
return AcceptToMemoryPool(::mempool, state, tx, nullptr /* missing inputs */, nullptr /* txn replaced */,
false /* bypass limits */, absurd_fee);
}
-};
-class LockingStateImpl : public LockImpl, public UniqueLock<CCriticalSection>
-{
using UniqueLock::UniqueLock;
};
@@ -236,13 +246,12 @@ class ChainImpl : public Chain
public:
std::unique_ptr<Chain::Lock> lock(bool try_lock) override
{
- auto result = MakeUnique<LockingStateImpl>(::cs_main, "cs_main", __FILE__, __LINE__, try_lock);
+ auto result = MakeUnique<LockImpl>(::cs_main, "cs_main", __FILE__, __LINE__, try_lock);
if (try_lock && result && !*result) return {};
// std::move necessary on some compilers due to conversion from
- // LockingStateImpl to Lock pointer
+ // LockImpl to Lock pointer
return std::move(result);
}
- std::unique_ptr<Chain::Lock> assumeLocked() override { return MakeUnique<LockImpl>(); }
bool findBlock(const uint256& hash, CBlock* block, int64_t* time, int64_t* time_max) override
{
CBlockIndex* index;
@@ -319,10 +328,14 @@ public:
CFeeRate relayMinFee() override { return ::minRelayTxFee; }
CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
CFeeRate relayDustFee() override { return ::dustRelayFee; }
- bool getPruneMode() override { return ::fPruneMode; }
+ bool havePruned() override
+ {
+ LOCK(cs_main);
+ return ::fHavePruned;
+ }
bool p2pEnabled() override { return g_connman != nullptr; }
- bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !IsInitialBlockDownload(); }
- bool isInitialBlockDownload() override { return IsInitialBlockDownload(); }
+ bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); }
+ bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); }
bool shutdownRequested() override { return ShutdownRequested(); }
int64_t getAdjustedTime() override { return GetAdjustedTime(); }
void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); }
@@ -341,9 +354,9 @@ public:
{
if (!old_tip.IsNull()) {
LOCK(::cs_main);
- if (old_tip == ::chainActive.Tip()->GetBlockHash()) return;
+ if (old_tip == ::ChainActive().Tip()->GetBlockHash()) return;
CBlockIndex* block = LookupBlockIndex(old_tip);
- if (block && block->GetAncestor(::chainActive.Height()) == ::chainActive.Tip()) return;
+ if (block && block->GetAncestor(::ChainActive().Height()) == ::ChainActive().Tip()) return;
}
SyncWithValidationInterfaceQueue();
}
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 0b7249a5ab..e675defd47 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -138,11 +138,6 @@ public:
//! unlocked when the returned interface is freed.
virtual std::unique_ptr<Lock> lock(bool try_lock = false) = 0;
- //! Return Lock interface assuming chain is already locked. This
- //! method is temporary and is only used in a few places to avoid changing
- //! behavior while code is transitioned to use the Chain::Lock interface.
- virtual std::unique_ptr<Lock> assumeLocked() = 0;
-
//! Return whether node has the block and optionally return block metadata
//! or contents.
//!
@@ -196,8 +191,8 @@ public:
//! Relay dust fee setting (-dustrelayfee), reflecting lowest rate it's economical to spend.
virtual CFeeRate relayDustFee() = 0;
- //! Check if pruning is enabled.
- virtual bool getPruneMode() = 0;
+ //! Check if any block has been pruned.
+ virtual bool havePruned() = 0;
//! Check if p2p enabled.
virtual bool p2pEnabled() = 0;
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index f3ee8fe364..584d218dba 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -23,7 +23,6 @@
#include <policy/settings.h>
#include <primitives/block.h>
#include <rpc/server.h>
-#include <scheduler.h>
#include <shutdown.h>
#include <sync.h>
#include <txmempool.h>
@@ -178,13 +177,13 @@ public:
int getNumBlocks() override
{
LOCK(::cs_main);
- return ::chainActive.Height();
+ return ::ChainActive().Height();
}
int64_t getLastBlockTime() override
{
LOCK(::cs_main);
- if (::chainActive.Tip()) {
- return ::chainActive.Tip()->GetBlockTime();
+ if (::ChainActive().Tip()) {
+ return ::ChainActive().Tip()->GetBlockTime();
}
return Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network
}
@@ -193,11 +192,11 @@ public:
const CBlockIndex* tip;
{
LOCK(::cs_main);
- tip = ::chainActive.Tip();
+ tip = ::ChainActive().Tip();
}
return GuessVerificationProgress(Params().TxData(), tip);
}
- bool isInitialBlockDownload() override { return IsInitialBlockDownload(); }
+ bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); }
bool getReindex() override { return ::fReindex; }
bool getImporting() override { return ::fImporting; }
void setNetworkActive(bool active) override
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index b57299d78d..34c982e1e6 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -5,29 +5,21 @@
#include <interfaces/wallet.h>
#include <amount.h>
-#include <chain.h>
#include <consensus/validation.h>
-#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
-#include <net.h>
#include <policy/feerate.h>
#include <policy/fees.h>
-#include <policy/policy.h>
#include <primitives/transaction.h>
-#include <rpc/server.h>
-#include <scheduler.h>
-#include <script/ismine.h>
#include <script/standard.h>
#include <support/allocators/secure.h>
#include <sync.h>
-#include <timedata.h>
#include <ui_interface.h>
#include <uint256.h>
#include <util/system.h>
-#include <validation.h>
#include <wallet/feebumper.h>
#include <wallet/fees.h>
+#include <wallet/ismine.h>
#include <wallet/rpcwallet.h>
#include <wallet/load.h>
#include <wallet/wallet.h>
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 7096f54047..9c9b29a813 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -7,7 +7,6 @@
#include <amount.h> // For CAmount
#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation)
-#include <script/ismine.h> // For isminefilter, isminetype
#include <script/standard.h> // For CTxDestination
#include <support/allocators/secure.h> // For SecureString
#include <ui_interface.h> // For ChangeType
@@ -25,7 +24,10 @@ class CCoinControl;
class CFeeRate;
class CKey;
class CWallet;
+enum isminetype : unsigned int;
enum class FeeReason;
+typedef uint8_t isminefilter;
+
enum class OutputType;
struct CRecipient;
diff --git a/src/key.cpp b/src/key.cpp
index 9d982fc44f..3ba21753a2 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -5,7 +5,6 @@
#include <key.h>
-#include <arith_uint256.h>
#include <crypto/common.h>
#include <crypto/hmac_sha512.h>
#include <random.h>
@@ -163,6 +162,12 @@ void CKey::MakeNewKey(bool fCompressedIn) {
fCompressed = fCompressedIn;
}
+bool CKey::Negate()
+{
+ assert(fValid);
+ return secp256k1_ec_privkey_negate(secp256k1_context_sign, keydata.data());
+}
+
CPrivKey CKey::GetPrivKey() const {
assert(fValid);
CPrivKey privkey;
diff --git a/src/key.h b/src/key.h
index 0f695c07b7..67e2cfc094 100644
--- a/src/key.h
+++ b/src/key.h
@@ -98,6 +98,9 @@ public:
//! Generate a new private key using a cryptographic PRNG.
void MakeNewKey(bool fCompressed);
+ //! Negate private key
+ bool Negate();
+
/**
* Convert the private key to a CPrivKey (serialized OpenSSL private key data).
* This is expensive.
diff --git a/src/key_io.cpp b/src/key_io.cpp
index 1d53a5e074..cd41a93549 100644
--- a/src/key_io.cpp
+++ b/src/key_io.cpp
@@ -26,14 +26,14 @@ private:
public:
explicit DestinationEncoder(const CChainParams& params) : m_params(params) {}
- std::string operator()(const CKeyID& id) const
+ std::string operator()(const PKHash& id) const
{
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
data.insert(data.end(), id.begin(), id.end());
return EncodeBase58Check(data);
}
- std::string operator()(const CScriptID& id) const
+ std::string operator()(const ScriptHash& id) const
{
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
data.insert(data.end(), id.begin(), id.end());
@@ -81,14 +81,14 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin());
- return CKeyID(hash);
+ return PKHash(hash);
}
// Script-hash-addresses have version 5 (or 196 testnet).
// The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) {
std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin());
- return CScriptID(hash);
+ return ScriptHash(hash);
}
}
data.clear();
diff --git a/src/keystore.cpp b/src/keystore.cpp
index 148979cf35..f6d19416ce 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -178,16 +178,17 @@ CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest)
{
// Only supports destinations which map to single public keys, i.e. P2PKH,
// P2WPKH, and P2SH-P2WPKH.
- if (auto id = boost::get<CKeyID>(&dest)) {
- return *id;
+ if (auto id = boost::get<PKHash>(&dest)) {
+ return CKeyID(*id);
}
if (auto witness_id = boost::get<WitnessV0KeyHash>(&dest)) {
return CKeyID(*witness_id);
}
- if (auto script_id = boost::get<CScriptID>(&dest)) {
+ if (auto script_hash = boost::get<ScriptHash>(&dest)) {
CScript script;
+ CScriptID script_id(*script_hash);
CTxDestination inner_dest;
- if (store.GetCScript(*script_id, script) && ExtractDestination(script, inner_dest)) {
+ if (store.GetCScript(script_id, script) && ExtractDestination(script, inner_dest)) {
if (auto inner_witness_id = boost::get<WitnessV0KeyHash>(&inner_dest)) {
return CKeyID(*inner_witness_id);
}
diff --git a/src/logging.cpp b/src/logging.cpp
index 3eda4995db..dc2d130a2a 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -39,28 +39,50 @@ static int FileWriteStr(const std::string &str, FILE *fp)
return fwrite(str.data(), 1, str.size(), fp);
}
-bool BCLog::Logger::OpenDebugLog()
+bool BCLog::Logger::StartLogging()
{
- std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
+ std::lock_guard<std::mutex> scoped_lock(m_cs);
+ assert(m_buffering);
assert(m_fileout == nullptr);
- assert(!m_file_path.empty());
- m_fileout = fsbridge::fopen(m_file_path, "a");
- if (!m_fileout) {
- return false;
+ if (m_print_to_file) {
+ assert(!m_file_path.empty());
+ m_fileout = fsbridge::fopen(m_file_path, "a");
+ if (!m_fileout) {
+ return false;
+ }
+
+ setbuf(m_fileout, nullptr); // unbuffered
+
+ // Add newlines to the logfile to distinguish this execution from the
+ // last one.
+ FileWriteStr("\n\n\n\n\n", m_fileout);
}
- setbuf(m_fileout, nullptr); // unbuffered
// dump buffered messages from before we opened the log
+ m_buffering = false;
while (!m_msgs_before_open.empty()) {
- FileWriteStr(m_msgs_before_open.front(), m_fileout);
+ const std::string& s = m_msgs_before_open.front();
+
+ if (m_print_to_file) FileWriteStr(s, m_fileout);
+ if (m_print_to_console) fwrite(s.data(), 1, s.size(), stdout);
+
m_msgs_before_open.pop_front();
}
+ if (m_print_to_console) fflush(stdout);
return true;
}
+void BCLog::Logger::DisconnectTestLogger()
+{
+ std::lock_guard<std::mutex> scoped_lock(m_cs);
+ m_buffering = true;
+ if (m_fileout != nullptr) fclose(m_fileout);
+ m_fileout = nullptr;
+}
+
void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
{
m_categories |= flag;
@@ -202,8 +224,9 @@ std::string BCLog::Logger::LogTimestampStr(const std::string& str)
return strStamped;
}
-void BCLog::Logger::LogPrintStr(const std::string &str)
+void BCLog::Logger::LogPrintStr(const std::string& str)
{
+ std::lock_guard<std::mutex> scoped_lock(m_cs);
std::string str_prefixed = str;
if (m_log_threadnames && m_started_new_line) {
@@ -214,32 +237,31 @@ void BCLog::Logger::LogPrintStr(const std::string &str)
m_started_new_line = !str.empty() && str[str.size()-1] == '\n';
+ if (m_buffering) {
+ // buffer if we haven't started logging yet
+ m_msgs_before_open.push_back(str_prefixed);
+ return;
+ }
+
if (m_print_to_console) {
// print to console
fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
fflush(stdout);
}
if (m_print_to_file) {
- std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
-
- // buffer if we haven't opened the log yet
- if (m_fileout == nullptr) {
- m_msgs_before_open.push_back(str_prefixed);
- }
- else
- {
- // reopen the log file, if requested
- if (m_reopen_file) {
- m_reopen_file = false;
- FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
- if (new_fileout) {
- setbuf(new_fileout, nullptr); // unbuffered
- fclose(m_fileout);
- m_fileout = new_fileout;
- }
+ assert(m_fileout != nullptr);
+
+ // reopen the log file, if requested
+ if (m_reopen_file) {
+ m_reopen_file = false;
+ FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
+ if (new_fileout) {
+ setbuf(new_fileout, nullptr); // unbuffered
+ fclose(m_fileout);
+ m_fileout = new_fileout;
}
- FileWriteStr(str_prefixed, m_fileout);
}
+ FileWriteStr(str_prefixed, m_fileout);
}
}
diff --git a/src/logging.h b/src/logging.h
index e399d4c307..75cd5353c0 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -60,9 +60,10 @@ namespace BCLog {
class Logger
{
private:
- FILE* m_fileout = nullptr;
- std::mutex m_file_mutex;
- std::list<std::string> m_msgs_before_open;
+ 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)
/**
* m_started_new_line is a state variable that will suppress printing of
@@ -88,12 +89,20 @@ namespace BCLog {
std::atomic<bool> m_reopen_file{false};
/** Send a string to the log output */
- void LogPrintStr(const std::string &str);
+ void LogPrintStr(const std::string& str);
/** Returns whether logs will be written to any output */
- bool Enabled() const { return m_print_to_console || m_print_to_file; }
+ bool Enabled() const
+ {
+ std::lock_guard<std::mutex> scoped_lock(m_cs);
+ return m_buffering || m_print_to_console || m_print_to_file;
+ }
+
+ /** Start logging (and flush all buffered messages) */
+ bool StartLogging();
+ /** Only for testing */
+ void DisconnectTestLogger();
- bool OpenDebugLog();
void ShrinkDebugFile();
uint32_t GetCategoryMask() const { return m_categories.load(); }
diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp
index a54268d655..052aebbc80 100644
--- a/src/merkleblock.cpp
+++ b/src/merkleblock.cpp
@@ -7,7 +7,6 @@
#include <hash.h>
#include <consensus/consensus.h>
-#include <util/strencodings.h>
CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids)
diff --git a/src/miner.cpp b/src/miner.cpp
index 6a88e8321d..015645c9c6 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -13,8 +13,6 @@
#include <consensus/merkle.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
-#include <hash.h>
-#include <net.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <pow.h>
@@ -24,7 +22,6 @@
#include <util/moneystr.h>
#include <util/system.h>
#include <util/validation.h>
-#include <validationinterface.h>
#include <algorithm>
#include <queue>
@@ -109,7 +106,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
LOCK2(cs_main, mempool.cs);
- CBlockIndex* pindexPrev = chainActive.Tip();
+ CBlockIndex* pindexPrev = ::ChainActive().Tip();
assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
diff --git a/src/net.cpp b/src/net.cpp
index 1335804b06..75a47d7ad2 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -79,7 +79,7 @@ static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // S
//
bool fDiscover = true;
bool fListen = true;
-bool fRelayTxes = true;
+bool g_relay_txes = !DEFAULT_BLOCKSONLY;
CCriticalSection cs_mapLocalHost;
std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost);
static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {};
@@ -2287,8 +2287,8 @@ public:
WSACleanup();
#endif
}
-}
-instance_of_cnetcleanup;
+};
+static CNetCleanup instance_of_cnetcleanup;
void CConnman::Interrupt()
{
diff --git a/src/net.h b/src/net.h
index 7af33ef13b..37aaf1a63b 100644
--- a/src/net.h
+++ b/src/net.h
@@ -519,7 +519,7 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices)
extern bool fDiscover;
extern bool fListen;
-extern bool fRelayTxes;
+extern bool g_relay_txes;
/** Subversion as sent to the P2P network in `version` messages */
extern std::string strSubVersion;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 71ebd72b83..4b43b2cdf2 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -25,9 +25,7 @@
#include <scheduler.h>
#include <tinyformat.h>
#include <txmempool.h>
-#include <ui_interface.h>
#include <util/system.h>
-#include <util/moneystr.h>
#include <util/strencodings.h>
#include <util/validation.h>
@@ -70,11 +68,13 @@ static constexpr int32_t MAX_PEER_TX_IN_FLIGHT = 100;
/** Maximum number of announced transactions from a peer */
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ;
/** How many microseconds to delay requesting transactions from inbound peers */
-static constexpr int64_t INBOUND_PEER_TX_DELAY = 2 * 1000000;
+static constexpr int64_t INBOUND_PEER_TX_DELAY = 2 * 1000000; // 2 seconds
/** How long to wait (in microseconds) before downloading a transaction from an additional peer */
-static constexpr int64_t GETDATA_TX_INTERVAL = 60 * 1000000;
+static constexpr int64_t GETDATA_TX_INTERVAL = 60 * 1000000; // 1 minute
/** Maximum delay (in microseconds) for transaction requests to avoid biasing some peers over others. */
-static constexpr int64_t MAX_GETDATA_RANDOM_DELAY = 2 * 1000000;
+static constexpr int64_t MAX_GETDATA_RANDOM_DELAY = 2 * 1000000; // 2 seconds
+/** How long to wait (in microseconds) before expiring an in-flight getdata request to a peer */
+static constexpr int64_t TX_EXPIRY_INTERVAL = 10 * GETDATA_TX_INTERVAL;
static_assert(INBOUND_PEER_TX_DELAY >= MAX_GETDATA_RANDOM_DELAY,
"To preserve security, MAX_GETDATA_RANDOM_DELAY should not exceed INBOUND_PEER_DELAY");
/** Limit to avoid sending big packets. Not used in processing incoming GETDATA for compatibility */
@@ -345,8 +345,11 @@ struct CNodeState {
//! Store all the transactions a peer has recently announced
std::set<uint256> m_tx_announced;
- //! Store transactions which were requested by us
- std::set<uint256> m_tx_in_flight;
+ //! Store transactions which were requested by us, with timestamp
+ std::map<uint256, int64_t> m_tx_in_flight;
+
+ //! Periodically check for stuck getdata requests
+ int64_t m_check_expiry_timer{0};
};
TxDownloadState m_tx_download;
@@ -422,7 +425,7 @@ static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
CAddress addrMe = CAddress(CService(), nLocalNodeServices);
connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
- nonce, strSubVersion, nNodeStartingHeight, ::fRelayTxes));
+ nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes));
if (fLogIPs) {
LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid);
@@ -579,7 +582,7 @@ static bool TipMayBeStale(const Consensus::Params &consensusParams) EXCLUSIVE_LO
static bool CanDirectFetch(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- return chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
+ return ::ChainActive().Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
}
static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -605,7 +608,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
// Make sure pindexBestKnownBlock is up to date, we'll need it.
ProcessBlockAvailability(nodeid);
- if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
+ if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < ::ChainActive().Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
// This peer has nothing interesting.
return;
}
@@ -613,7 +616,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
if (state->pindexLastCommonBlock == nullptr) {
// Bootstrap quickly by guessing a parent of our best tip is the forking point.
// Guessing wrong in either direction is not a problem.
- state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())];
+ state->pindexLastCommonBlock = ::ChainActive()[std::min(state->pindexBestKnownBlock->nHeight, ::ChainActive().Height())];
}
// If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor
@@ -655,7 +658,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
// We wouldn't download this block or its descendants from this peer.
return;
}
- if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) {
+ if (pindex->nStatus & BLOCK_HAVE_DATA || ::ChainActive().Contains(pindex)) {
if (pindex->HaveTxsDownloaded())
state->pindexLastCommonBlock = pindex;
} else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
@@ -704,30 +707,40 @@ void UpdateTxRequestTime(const uint256& txid, int64_t request_time) EXCLUSIVE_LO
}
}
-
-void RequestTx(CNodeState* state, const uint256& txid, int64_t nNow) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+int64_t CalculateTxGetDataTime(const uint256& txid, int64_t current_time, bool use_inbound_delay) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- CNodeState::TxDownloadState& peer_download_state = state->m_tx_download;
- if (peer_download_state.m_tx_announced.size() >= MAX_PEER_TX_ANNOUNCEMENTS || peer_download_state.m_tx_announced.count(txid)) {
- // Too many queued announcements from this peer, or we already have
- // this announcement
- return;
- }
- peer_download_state.m_tx_announced.insert(txid);
-
int64_t process_time;
int64_t last_request_time = GetTxRequestTime(txid);
// First time requesting this tx
if (last_request_time == 0) {
- process_time = nNow;
+ process_time = current_time;
} else {
// Randomize the delay to avoid biasing some peers over others (such as due to
// fixed ordering of peer processing in ThreadMessageHandler)
process_time = last_request_time + GETDATA_TX_INTERVAL + GetRand(MAX_GETDATA_RANDOM_DELAY);
}
- // We delay processing announcements from non-preferred (eg inbound) peers
- if (!state->fPreferredDownload) process_time += INBOUND_PEER_TX_DELAY;
+ // We delay processing announcements from inbound peers
+ if (use_inbound_delay) process_time += INBOUND_PEER_TX_DELAY;
+
+ return process_time;
+}
+
+void RequestTx(CNodeState* state, const uint256& txid, int64_t nNow) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ CNodeState::TxDownloadState& peer_download_state = state->m_tx_download;
+ if (peer_download_state.m_tx_announced.size() >= MAX_PEER_TX_ANNOUNCEMENTS ||
+ peer_download_state.m_tx_process_time.size() >= MAX_PEER_TX_ANNOUNCEMENTS ||
+ peer_download_state.m_tx_announced.count(txid)) {
+ // Too many queued announcements from this peer, or we already have
+ // this announcement
+ return;
+ }
+ peer_download_state.m_tx_announced.insert(txid);
+
+ // Calculate the time to try requesting this transaction. Use
+ // fPreferredDownload as a proxy for outbound peers.
+ int64_t process_time = CalculateTxGetDataTime(txid, nNow, !state->fPreferredDownload);
peer_download_state.m_tx_process_time.emplace(process_time, txid);
}
@@ -1071,7 +1084,7 @@ static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool v
static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
- if (chainActive.Contains(pindex)) return true;
+ if (::ChainActive().Contains(pindex)) return true;
return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) &&
(pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) &&
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
@@ -1183,7 +1196,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
/**
* Update our best height and announce any block hashes which weren't previously
- * in chainActive to our peers.
+ * in ::ChainActive() to our peers.
*/
void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
const int nNewHeight = pindexNew->nHeight;
@@ -1240,7 +1253,7 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
// the tip yet so we have no way to check this directly here. Instead we
// just check that there are currently no other blocks in flight.
else if (state.IsValid() &&
- !IsInitialBlockDownload() &&
+ !::ChainstateActive().IsInitialBlockDownload() &&
mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) {
if (it != mapBlockSource.end()) {
MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, connman);
@@ -1264,13 +1277,13 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
case MSG_WITNESS_TX:
{
assert(recentRejects);
- if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip)
+ if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip)
{
// If the chain tip has changed previously rejected transactions
// might be now valid, e.g. due to a nLockTime'd tx becoming valid,
// or a double-spend. Reset the rejects filter and give those
// txs a second chance.
- hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash();
+ hashRecentRejectsChainTip = ::ChainActive().Tip()->GetBlockHash();
recentRejects->reset();
}
@@ -1395,7 +1408,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
}
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
if (send && !pfrom->fWhitelisted && (
- (((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (chainActive.Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
+ (((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (::ChainActive().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
)) {
LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom->GetId());
@@ -1465,7 +1478,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
// instead we respond with the full, non-compact block.
bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- if (CanDirectFetch(consensusParams) && pindex->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) {
+ if (CanDirectFetch(consensusParams) && pindex->nHeight >= ::ChainActive().Height() - MAX_CMPCTBLOCK_DEPTH) {
if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
} else {
@@ -1485,7 +1498,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
// and we want it right after the last block so they don't
// wait for other stuff first.
std::vector<CInv> vInv;
- vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash()));
+ vInv.push_back(CInv(MSG_BLOCK, ::ChainActive().Tip()->GetBlockHash()));
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv));
pfrom->hashContinue.SetNull();
}
@@ -1542,16 +1555,28 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
}
}
+ // Unknown types in the GetData stay in vRecvGetData and block any future
+ // message from this peer, see vRecvGetData check in ProcessMessages().
+ // Depending on future p2p changes, we might either drop unknown getdata on
+ // the floor or disconnect the peer.
+
pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it);
if (!vNotFound.empty()) {
// Let the peer know that we didn't find what it asked for, so it doesn't
- // have to wait around forever. Currently only SPV clients actually care
- // about this message: it's needed when they are recursively walking the
- // dependencies of relevant unconfirmed transactions. SPV clients want to
- // do that because they want to know about (and store and rebroadcast and
- // risk analyze) the dependencies of transactions relevant to them, without
- // having to download the entire memory pool.
+ // have to wait around forever.
+ // SPV clients care about this message: it's needed when they are
+ // recursively walking the dependencies of relevant unconfirmed
+ // transactions. SPV clients want to do that because they want to know
+ // about (and store and rebroadcast and risk analyze) the dependencies
+ // of transactions relevant to them, without having to download the
+ // entire memory pool.
+ // Also, other nodes can use these messages to automatically request a
+ // transaction from some other peer that annnounced it, and stop
+ // waiting for us to respond.
+ // In normal operation, we often send NOTFOUND messages for parents of
+ // transactions that we relay; if a peer is missing a parent, they may
+ // assume we have them and request the parents from us.
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
}
}
@@ -1606,7 +1631,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
// nUnconnectingHeaders gets reset back to 0.
if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
nodestate->nUnconnectingHeaders++;
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
headers[0].GetHash().ToString(),
headers[0].hashPrevBlock.ToString(),
@@ -1663,26 +1688,26 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
// because it is set in UpdateBlockAvailability. Some nullptr checks
// are still present, however, as belt-and-suspenders.
- if (received_new_header && pindexLast->nChainWork > chainActive.Tip()->nChainWork) {
+ if (received_new_header && pindexLast->nChainWork > ::ChainActive().Tip()->nChainWork) {
nodestate->m_last_block_announcement = GetTime();
}
if (nCount == MAX_HEADERS_RESULTS) {
// Headers message had its maximum size; the peer may have more headers.
- // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue
+ // TODO: optimize: if pindexLast is an ancestor of ::ChainActive().Tip or pindexBestHeader, continue
// from there instead.
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight);
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256()));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
}
bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
// If this set of headers is valid and ends in a block with at least as
// much work as our tip, download as much as possible.
- if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) {
+ if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && ::ChainActive().Tip()->nChainWork <= pindexLast->nChainWork) {
std::vector<const CBlockIndex*> vToFetch;
const CBlockIndex *pindexWalk = pindexLast;
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
- while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ while (pindexWalk && !::ChainActive().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
!mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
(!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) {
@@ -1695,7 +1720,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
// very large reorg at a time we think we're close to caught up to
// the main chain -- this shouldn't really happen. Bail out on the
// direct fetch and rely on parallel download instead.
- if (!chainActive.Contains(pindexWalk)) {
+ if (!::ChainActive().Contains(pindexWalk)) {
LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
pindexLast->GetBlockHash().ToString(),
pindexLast->nHeight);
@@ -1728,7 +1753,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
}
// If we're in IBD, we want outbound peers that will serve us a useful
// chain. Disconnect peers that are on chains with insufficient work.
- if (IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
+ if (::ChainstateActive().IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
// When nCount < MAX_HEADERS_RESULTS, we know we have no more
// headers to fetch from this peer.
if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
@@ -1736,7 +1761,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
// us sync -- disconnect if using an outbound slot (unless
// whitelisted or addnode).
// Note: We compare their tip to nMinimumChainWork (rather than
- // chainActive.Tip()) because we won't start block download
+ // ::ChainActive().Tip()) because we won't start block download
// until we have a headers chain that has at least
// nMinimumChainWork, even if a peer has a chain past our tip,
// as an anti-DoS measure.
@@ -1750,7 +1775,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr) {
// If this is an outbound peer, check to see if we should protect
// it from the bad/lagging chain logic.
- if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
+ if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom->GetId());
nodestate->m_chain_sync.m_protect = true;
++g_outbound_peers_with_protect_from_disconnect;
@@ -1994,7 +2019,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!pfrom->fInbound)
{
// Advertise our address
- if (fListen && !IsInitialBlockDownload())
+ if (fListen && !::ChainstateActive().IsInitialBlockDownload())
{
CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices());
FastRandomContext insecure_rand;
@@ -2189,7 +2214,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return false;
}
- bool fBlocksOnly = !fRelayTxes;
+ bool fBlocksOnly = !g_relay_txes;
// Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))
@@ -2220,7 +2245,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// fell back to inv we probably have a reorg which we should get the headers for first,
// we now only provide a getheaders response here. When we receive the headers, we will
// then ask for the blocks we need.
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash));
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), inv.hash));
LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->GetId());
}
}
@@ -2229,7 +2254,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->AddInventoryKnown(inv);
if (fBlocksOnly) {
LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->GetId());
- } else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload()) {
+ } else if (!fAlreadyHave && !fImporting && !fReindex && !::ChainstateActive().IsInitialBlockDownload()) {
RequestTx(State(pfrom->GetId()), inv.hash, nNow);
}
}
@@ -2291,14 +2316,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(cs_main);
// Find the last block the caller has in the main chain
- const CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator);
+ const CBlockIndex* pindex = FindForkInGlobalIndex(::ChainActive(), locator);
// Send the rest of the chain
if (pindex)
- pindex = chainActive.Next(pindex);
+ pindex = ::ChainActive().Next(pindex);
int nLimit = 500;
LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->GetId());
- for (; pindex; pindex = chainActive.Next(pindex))
+ for (; pindex; pindex = ::ChainActive().Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
{
@@ -2308,7 +2333,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// If pruning, don't inv blocks unless we have on disk and are likely to still have
// for some reasonable time window (1 hour) that block relay might require.
const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / chainparams.GetConsensus().nPowTargetSpacing;
- if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= chainActive.Tip()->nHeight - nPrunedBlocksLikelyToHave))
+ if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= ::ChainActive().Tip()->nHeight - nPrunedBlocksLikelyToHave))
{
LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
break;
@@ -2350,7 +2375,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
- if (pindex->nHeight < chainActive.Height() - MAX_BLOCKTXN_DEPTH) {
+ if (pindex->nHeight < ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) {
// If an older block is requested (should never happen in practice,
// but can happen in tests) send a block response instead of a
// blocktxn response. Sending a full block response instead of a
@@ -2387,7 +2412,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
LOCK(cs_main);
- if (IsInitialBlockDownload() && !pfrom->fWhitelisted) {
+ if (::ChainstateActive().IsInitialBlockDownload() && !pfrom->fWhitelisted) {
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->GetId());
return true;
}
@@ -2410,23 +2435,23 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
else
{
// Find the last block the caller has in the main chain
- pindex = FindForkInGlobalIndex(chainActive, locator);
+ pindex = FindForkInGlobalIndex(::ChainActive(), locator);
if (pindex)
- pindex = chainActive.Next(pindex);
+ pindex = ::ChainActive().Next(pindex);
}
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
std::vector<CBlock> vHeaders;
int nLimit = MAX_HEADERS_RESULTS;
LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->GetId());
- for (; pindex; pindex = chainActive.Next(pindex))
+ for (; pindex; pindex = ::ChainActive().Next(pindex))
{
vHeaders.push_back(pindex->GetBlockHeader());
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}
- // pindex can be nullptr either if we sent chainActive.Tip() OR
- // if our peer has chainActive.Tip() (and thus we are sending an empty
+ // pindex can be nullptr either if we sent ::ChainActive().Tip() OR
+ // if our peer has ::ChainActive().Tip() (and thus we are sending an empty
// headers message). In both cases it's safe to update
// pindexBestHeaderSent to be our tip.
//
@@ -2437,7 +2462,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// without the new block. By resetting the BestHeaderSent, we ensure we
// will re-announce the new block via headers (or compact blocks again)
// in the SendMessages logic.
- nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip();
+ nodestate->pindexBestHeaderSent = pindex ? pindex : ::ChainActive().Tip();
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
return true;
}
@@ -2445,7 +2470,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (strCommand == NetMsgType::TX) {
// Stop processing the transaction early if
// We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
- if (!fRelayTxes && (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
+ if (!g_relay_txes && (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
{
LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId());
return true;
@@ -2609,8 +2634,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
// Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
- if (!IsInitialBlockDownload())
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
+ if (!::ChainstateActive().IsInitialBlockDownload())
+ connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
return true;
}
@@ -2654,7 +2679,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// If this was a new header with more work than our tip, update the
// peer's last block announcement time
- if (received_new_header && pindex->nChainWork > chainActive.Tip()->nChainWork) {
+ if (received_new_header && pindex->nChainWork > ::ChainActive().Tip()->nChainWork) {
nodestate->m_last_block_announcement = GetTime();
}
@@ -2664,7 +2689,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
return true;
- if (pindex->nChainWork <= chainActive.Tip()->nChainWork || // We know something better
+ if (pindex->nChainWork <= ::ChainActive().Tip()->nChainWork || // We know something better
pindex->nTx != 0) { // We had this block at some point, but pruned it
if (fAlreadyInFlight) {
// We requested this block for some reason, but our mempool will probably be useless
@@ -2688,7 +2713,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// We want to be a bit conservative just to be extra careful about DoS
// possibilities in compact block processing...
- if (pindex->nHeight <= chainActive.Height() + 2) {
+ if (pindex->nHeight <= ::ChainActive().Height() + 2) {
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
(fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
@@ -3148,8 +3173,27 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
if (strCommand == NetMsgType::NOTFOUND) {
- // We do not care about the NOTFOUND message, but logging an Unknown Command
- // message would be undesirable as we transmit it ourselves.
+ // Remove the NOTFOUND transactions from the peer
+ LOCK(cs_main);
+ CNodeState *state = State(pfrom->GetId());
+ std::vector<CInv> vInv;
+ vRecv >> vInv;
+ if (vInv.size() <= MAX_PEER_TX_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ for (CInv &inv : vInv) {
+ if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX) {
+ // If we receive a NOTFOUND message for a txid we requested, erase
+ // it from our data structures for this peer.
+ auto in_flight_it = state->m_tx_download.m_tx_in_flight.find(inv.hash);
+ if (in_flight_it == state->m_tx_download.m_tx_in_flight.end()) {
+ // Skip any further work if this is a spurious NOTFOUND
+ // message.
+ continue;
+ }
+ state->m_tx_download.m_tx_in_flight.erase(in_flight_it);
+ state->m_tx_download.m_tx_announced.erase(inv.hash);
+ }
+ }
+ }
return true;
}
@@ -3221,6 +3265,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
return false;
// this maintains the order of responses
+ // and prevents vRecvGetData to grow unbounded
if (!pfrom->vRecvGetData.empty()) return true;
if (!pfrom->orphan_work_set.empty()) return true;
@@ -3288,23 +3333,22 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
if (m_enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
}
- if (strstr(e.what(), "end of data"))
- {
+ if (strstr(e.what(), "end of data")) {
// Allow exceptions from under-length message on vRecv
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else if (strstr(e.what(), "size too large"))
- {
+ } else if (strstr(e.what(), "size too large")) {
// Allow exceptions from over-long size
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else if (strstr(e.what(), "non-canonical ReadCompactSize()"))
- {
+ } else if (strstr(e.what(), "non-canonical ReadCompactSize()")) {
// Allow exceptions from non-canonical encoding
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else
- {
+ } else if (strstr(e.what(), "Superfluous witness record")) {
+ // Allow exceptions from illegal witness encoding
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
+ } else if (strstr(e.what(), "Unknown transaction optional data")) {
+ // Allow exceptions from unknown witness encoding
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
+ } else {
PrintExceptionContinue(&e, "ProcessMessages()");
}
}
@@ -3338,7 +3382,7 @@ void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
// their chain has more work than ours, we should sync to it,
// unless it's invalid, in which case we should find that out and
// disconnect from them elsewhere).
- if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork) {
+ if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork) {
if (state.m_chain_sync.m_timeout != 0) {
state.m_chain_sync.m_timeout = 0;
state.m_chain_sync.m_work_header = nullptr;
@@ -3350,7 +3394,7 @@ void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
// where we checked against our tip.
// Either way, set a new timeout based on current tip.
state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT;
- state.m_chain_sync.m_work_header = chainActive.Tip();
+ state.m_chain_sync.m_work_header = ::ChainActive().Tip();
state.m_chain_sync.m_sent_getheaders = false;
} else if (state.m_chain_sync.m_timeout > 0 && time_in_seconds > state.m_chain_sync.m_timeout) {
// No evidence yet that our peer has synced to a chain with work equal to that
@@ -3363,7 +3407,7 @@ void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
} else {
assert(state.m_chain_sync.m_work_header);
LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
state.m_chain_sync.m_sent_getheaders = true;
constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes
// Bump the timeout to allow a response, which could clear the timeout
@@ -3525,7 +3569,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Address refresh broadcast
int64_t nNow = GetTimeMicros();
- if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
+ if (!::ChainstateActive().IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
AdvertiseLocal(pto);
pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}
@@ -3561,7 +3605,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Start block sync
if (pindexBestHeader == nullptr)
- pindexBestHeader = chainActive.Tip();
+ pindexBestHeader = ::ChainActive().Tip();
bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do.
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
@@ -3580,7 +3624,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (pindexStart->pprev)
pindexStart = pindexStart->pprev;
LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), pto->nStartingHeight);
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256()));
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexStart), uint256()));
}
}
@@ -3607,11 +3651,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
bool fFoundStartingHeader = false;
// Try to find first header that our peer doesn't have, and
// then send all headers past that one. If we come across any
- // headers that aren't on chainActive, give up.
+ // headers that aren't on ::ChainActive(), give up.
for (const uint256 &hash : pto->vBlockHashesToAnnounce) {
const CBlockIndex* pindex = LookupBlockIndex(hash);
assert(pindex);
- if (chainActive[pindex->nHeight] != pindex) {
+ if (::ChainActive()[pindex->nHeight] != pindex) {
// Bail out if we reorged away from this block
fRevertToInv = true;
break;
@@ -3707,9 +3751,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Warn if we're announcing a block that is not on the main chain.
// This should be very rare and could be optimized out.
// Just log for now.
- if (chainActive[pindex->nHeight] != pindex) {
+ if (::ChainActive()[pindex->nHeight] != pindex) {
LogPrint(BCLog::NET, "Announcing block %s not on main chain (tip=%s)\n",
- hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString());
+ hashToAnnounce.ToString(), ::ChainActive().Tip()->GetBlockHash().ToString());
}
// If the peer's chain has this block, don't inv it back.
@@ -3926,7 +3970,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Message: getdata (blocks)
//
std::vector<CInv> vGetData;
- if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !::ChainstateActive().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
std::vector<const CBlockIndex*> vToDownload;
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
@@ -3948,9 +3992,33 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
//
// Message: getdata (non-blocks)
//
+
+ // For robustness, expire old requests after a long timeout, so that
+ // we can resume downloading transactions from a peer even if they
+ // were unresponsive in the past.
+ // Eventually we should consider disconnecting peers, but this is
+ // conservative.
+ if (state.m_tx_download.m_check_expiry_timer <= nNow) {
+ for (auto it=state.m_tx_download.m_tx_in_flight.begin(); it != state.m_tx_download.m_tx_in_flight.end();) {
+ if (it->second <= nNow - TX_EXPIRY_INTERVAL) {
+ LogPrint(BCLog::NET, "timeout of inflight tx %s from peer=%d\n", it->first.ToString(), pto->GetId());
+ state.m_tx_download.m_tx_announced.erase(it->first);
+ state.m_tx_download.m_tx_in_flight.erase(it++);
+ } else {
+ ++it;
+ }
+ }
+ // On average, we do this check every TX_EXPIRY_INTERVAL. Randomize
+ // so that we're not doing this for all peers at the same time.
+ state.m_tx_download.m_check_expiry_timer = nNow + TX_EXPIRY_INTERVAL/2 + GetRand(TX_EXPIRY_INTERVAL);
+ }
+
auto& tx_process_time = state.m_tx_download.m_tx_process_time;
while (!tx_process_time.empty() && tx_process_time.begin()->first <= nNow && state.m_tx_download.m_tx_in_flight.size() < MAX_PEER_TX_IN_FLIGHT) {
- const uint256& txid = tx_process_time.begin()->second;
+ const uint256 txid = tx_process_time.begin()->second;
+ // Erase this entry from tx_process_time (it may be added back for
+ // processing at a later time, see below)
+ tx_process_time.erase(tx_process_time.begin());
CInv inv(MSG_TX | GetFetchFlags(pto), txid);
if (!AlreadyHave(inv)) {
// If this transaction was last requested more than 1 minute ago,
@@ -3964,20 +4032,20 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
vGetData.clear();
}
UpdateTxRequestTime(inv.hash, nNow);
- state.m_tx_download.m_tx_in_flight.insert(inv.hash);
+ state.m_tx_download.m_tx_in_flight.emplace(inv.hash, nNow);
} else {
// This transaction is in flight from someone else; queue
// up processing to happen after the download times out
// (with a slight delay for inbound peers, to prefer
// requests to outbound peers).
- RequestTx(&state, txid, nNow);
+ int64_t next_process_time = CalculateTxGetDataTime(txid, nNow, !state.fPreferredDownload);
+ tx_process_time.emplace(next_process_time, txid);
}
} else {
// We have already seen this transaction, no need to download.
state.m_tx_download.m_tx_announced.erase(inv.hash);
state.m_tx_download.m_tx_in_flight.erase(inv.hash);
}
- tx_process_time.erase(tx_process_time.begin());
}
@@ -4024,4 +4092,5 @@ public:
mapOrphanTransactions.clear();
mapOrphanTransactionsByPrev.clear();
}
-} instance_of_cnetprocessingcleanup;
+};
+static CNetProcessingCleanup instance_of_cnetprocessingcleanup;
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 6ee2d8a4b3..4fbfa2b5c8 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -204,6 +204,11 @@ bool CNetAddr::IsRFC4843() const
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10);
}
+bool CNetAddr::IsRFC7343() const
+{
+ return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20);
+}
+
/**
* @returns Whether or not this is a dummy address that maps an onion address
* into IPv6.
@@ -234,7 +239,7 @@ bool CNetAddr::IsLocal() const
* be used to refer to an actual host.
*
* @note A valid address may or may not be publicly routable on the global
- * internet. As in, the set of valid addreses is a superset of the set of
+ * internet. As in, the set of valid addresses is a superset of the set of
* publicly routable addresses.
*
* @see CNetAddr::IsRoutable()
@@ -282,14 +287,14 @@ bool CNetAddr::IsValid() const
* @returns Whether or not this network address is publicly routable on the
* global internet.
*
- * @note A routable address is always valid. As in, the set of routable addreses
+ * @note A routable address is always valid. As in, the set of routable addresses
* is a subset of the set of valid addresses.
*
* @see CNetAddr::IsValid()
*/
bool CNetAddr::IsRoutable() const
{
- return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal() || IsInternal());
+ return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsRFC7343() || IsLocal() || IsInternal());
}
/**
diff --git a/src/netaddress.h b/src/netaddress.h
index 8230e40606..673eaf8d7b 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -63,7 +63,8 @@ class CNetAddr
bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16)
bool IsRFC4193() const; // IPv6 unique local (FC00::/7)
bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32)
- bool IsRFC4843() const; // IPv6 ORCHID (2001:10::/28)
+ bool IsRFC4843() const; // IPv6 ORCHID (deprecated) (2001:10::/28)
+ bool IsRFC7343() const; // IPv6 ORCHIDv2 (2001:20::/28)
bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
bool IsRFC6052() const; // IPv6 well-known prefix for IPv4-embedded address (64:FF9B::/96)
bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765)
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 6c386a9ade..78b3b6ae3a 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -5,10 +5,7 @@
#include <netbase.h>
-#include <hash.h>
#include <sync.h>
-#include <uint256.h>
-#include <random.h>
#include <tinyformat.h>
#include <util/system.h>
#include <util/strencodings.h>
diff --git a/src/noui.cpp b/src/noui.cpp
index c7d8fee0ba..caab9f326e 100644
--- a/src/noui.cpp
+++ b/src/noui.cpp
@@ -18,26 +18,30 @@ bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& ca
{
bool fSecure = style & CClientUIInterface::SECURE;
style &= ~CClientUIInterface::SECURE;
+ bool prefix = !(style & CClientUIInterface::MSG_NOPREFIX);
+ style &= ~CClientUIInterface::MSG_NOPREFIX;
std::string strCaption;
- // Check for usage of predefined caption
- switch (style) {
- case CClientUIInterface::MSG_ERROR:
- strCaption += _("Error");
- break;
- case CClientUIInterface::MSG_WARNING:
- strCaption += _("Warning");
- break;
- case CClientUIInterface::MSG_INFORMATION:
- strCaption += _("Information");
- break;
- default:
- strCaption += caption; // Use supplied caption (can be empty)
+ 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)
+ }
}
- if (!fSecure)
- LogPrintf("%s: %s\n", strCaption, message);
- fprintf(stderr, "%s: %s\n", strCaption.c_str(), message.c_str());
+ if (!fSecure) {
+ LogPrintf("%s%s\n", strCaption, message);
+ }
+ tfm::format(std::cerr, "%s%s\n", strCaption.c_str(), message.c_str());
return false;
}
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
index 7e5690dfc5..73ffb801f2 100644
--- a/src/outputtype.cpp
+++ b/src/outputtype.cpp
@@ -45,14 +45,14 @@ const std::string& FormatOutputType(OutputType type)
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
{
switch (type) {
- case OutputType::LEGACY: return key.GetID();
+ case OutputType::LEGACY: return PKHash(key);
case OutputType::P2SH_SEGWIT:
case OutputType::BECH32: {
- if (!key.IsCompressed()) return key.GetID();
- CTxDestination witdest = WitnessV0KeyHash(key.GetID());
+ if (!key.IsCompressed()) return PKHash(key);
+ CTxDestination witdest = WitnessV0KeyHash(PKHash(key));
CScript witprog = GetScriptForDestination(witdest);
if (type == OutputType::P2SH_SEGWIT) {
- return CScriptID(witprog);
+ return ScriptHash(witprog);
} else {
return witdest;
}
@@ -63,10 +63,10 @@ CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
{
- CKeyID keyid = key.GetID();
+ PKHash keyid(key);
if (key.IsCompressed()) {
CTxDestination segwit = WitnessV0KeyHash(keyid);
- CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit));
+ CTxDestination p2sh = ScriptHash(GetScriptForDestination(segwit));
return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
} else {
return std::vector<CTxDestination>{std::move(keyid)};
@@ -80,19 +80,19 @@ CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript&
// Note that scripts over 520 bytes are not yet supported.
switch (type) {
case OutputType::LEGACY:
- return CScriptID(script);
+ return ScriptHash(script);
case OutputType::P2SH_SEGWIT:
case OutputType::BECH32: {
CTxDestination witdest = WitnessV0ScriptHash(script);
CScript witprog = GetScriptForDestination(witdest);
// Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
- if (!IsSolvable(keystore, witprog)) return CScriptID(script);
+ if (!IsSolvable(keystore, witprog)) return ScriptHash(script);
// Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
keystore.AddCScript(witprog);
if (type == OutputType::BECH32) {
return witdest;
} else {
- return CScriptID(witprog);
+ return ScriptHash(witprog);
}
}
default: assert(false);
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 524afd014e..5d538606c2 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -4,7 +4,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <policy/fees.h>
-#include <policy/policy.h>
#include <clientversion.h>
#include <primitives/transaction.h>
@@ -48,7 +47,7 @@ private:
std::vector<double> txCtAvg;
// Count the total # of txs confirmed within Y blocks in each bucket
- // Track the historical moving average of theses totals over blocks
+ // Track the historical moving average of these totals over blocks
std::vector<std::vector<double>> confAvg; // confAvg[Y][X]
// Track moving avg of txs which have been evicted from the mempool
@@ -526,7 +525,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
if (txHeight != nBestSeenHeight) {
// Ignore side chains and re-orgs; assuming they are random they don't
// affect the estimate. We'll potentially double count transactions in 1-block reorgs.
- // Ignore txs if BlockPolicyEstimator is not in sync with chainActive.Tip().
+ // Ignore txs if BlockPolicyEstimator is not in sync with ::ChainActive().Tip().
// It will be synced next time a block is processed.
return;
}
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 63a3d06267..51de5841ec 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -9,10 +9,6 @@
#include <consensus/validation.h>
#include <coins.h>
-#include <policy/settings.h>
-#include <tinyformat.h>
-#include <util/system.h>
-#include <util/strencodings.h>
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
diff --git a/src/prevector.h b/src/prevector.h
index 99e5751634..9d576321b6 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -147,14 +147,14 @@ public:
};
private:
- size_type _size;
+ size_type _size = 0;
union direct_or_indirect {
char direct[sizeof(T) * N];
struct {
size_type capacity;
char* indirect;
};
- } _union;
+ } _union = {};
T* direct_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.direct) + pos; }
const T* direct_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.direct) + pos; }
@@ -230,34 +230,34 @@ public:
fill(item_ptr(0), first, last);
}
- prevector() : _size(0), _union{{}} {}
+ prevector() {}
- explicit prevector(size_type n) : prevector() {
+ explicit prevector(size_type n) {
resize(n);
}
- explicit prevector(size_type n, const T& val) : prevector() {
+ explicit prevector(size_type n, const T& val) {
change_capacity(n);
_size += n;
fill(item_ptr(0), n, val);
}
template<typename InputIterator>
- prevector(InputIterator first, InputIterator last) : prevector() {
+ prevector(InputIterator first, InputIterator last) {
size_type n = last - first;
change_capacity(n);
_size += n;
fill(item_ptr(0), first, last);
}
- prevector(const prevector<N, T, Size, Diff>& other) : prevector() {
+ prevector(const prevector<N, T, Size, Diff>& other) {
size_type n = other.size();
change_capacity(n);
_size += n;
fill(item_ptr(0), other.begin(), other.end());
}
- prevector(prevector<N, T, Size, Diff>&& other) : prevector() {
+ prevector(prevector<N, T, Size, Diff>&& other) {
swap(other);
}
@@ -378,6 +378,21 @@ public:
fill(ptr, first, last);
}
+ inline void resize_uninitialized(size_type new_size) {
+ // resize_uninitialized changes the size of the prevector but does not initialize it.
+ // If size < new_size, the added elements must be initialized explicitly.
+ if (capacity() < new_size) {
+ change_capacity(new_size);
+ _size += new_size - size();
+ return;
+ }
+ if (new_size < size()) {
+ erase(item_ptr(new_size), end());
+ } else {
+ _size += new_size - size();
+ }
+ }
+
iterator erase(iterator pos) {
return erase(pos, pos + 1);
}
diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp
index a0c2e3f125..60c7c2d160 100644
--- a/src/primitives/block.cpp
+++ b/src/primitives/block.cpp
@@ -7,7 +7,6 @@
#include <hash.h>
#include <tinyformat.h>
-#include <util/strencodings.h>
#include <crypto/common.h>
uint256 CBlockHeader::GetHash() const
diff --git a/src/psbt.cpp b/src/psbt.cpp
index 97bda51a63..d765133190 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -2,9 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <coins.h>
-#include <consensus/tx_verify.h>
-#include <policy/policy.h>
#include <psbt.h>
#include <util/strencodings.h>
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 50d6afabcd..d8c39e8862 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -10,7 +10,6 @@
#include <qt/forms/ui_addressbookpage.h>
#include <qt/addresstablemodel.h>
-#include <qt/bitcoingui.h>
#include <qt/csvmodelwriter.h>
#include <qt/editaddressdialog.h>
#include <qt/guiutil.h>
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index c3143e948a..fa6c9c9f7a 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -7,7 +7,6 @@
#include <qt/guiutil.h>
#include <qt/walletmodel.h>
-#include <interfaces/node.h>
#include <key_io.h>
#include <wallet/wallet.h>
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index 00c446cc63..8a6b205cd8 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -5,8 +5,6 @@
#include <qt/bantablemodel.h>
#include <qt/clientmodel.h>
-#include <qt/guiconstants.h>
-#include <qt/guiutil.h>
#include <interfaces/node.h>
#include <sync.h>
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 81255aaae9..2fdbcca043 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -31,13 +31,9 @@
#include <interfaces/node.h>
#include <noui.h>
#include <util/threadnames.h>
-#include <rpc/server.h>
#include <ui_interface.h>
#include <uint256.h>
#include <util/system.h>
-#include <warnings.h>
-
-#include <walletinitinterface.h>
#include <memory>
#include <stdint.h>
@@ -334,7 +330,7 @@ void BitcoinApplication::initializeResult(bool success)
if(success)
{
// Log this only after AppInitMain finishes, as then logging setup is guaranteed complete
- qWarning() << "Platform customization:" << platformStyle->getName();
+ qInfo() << "Platform customization:" << platformStyle->getName();
#ifdef ENABLE_WALLET
m_wallet_controller = new WalletController(m_node, platformStyle, optionsModel, this);
#ifdef ENABLE_BIP70
@@ -440,16 +436,17 @@ int GuiMain(int argc, char* argv[])
Q_INIT_RESOURCE(bitcoin);
Q_INIT_RESOURCE(bitcoin_locale);
- BitcoinApplication app(*node, argc, argv);
// Generate high-dpi pixmaps
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if QT_VERSION >= 0x050600
- QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
#ifdef Q_OS_MAC
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
#endif
+ BitcoinApplication app(*node, argc, argv);
+
// Register meta types used for QMetaObject::invokeMethod
qRegisterMetaType< bool* >();
// Need to pass name here as CAmount is a typedef (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index abf9136eee..babb2ce518 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -335,8 +335,9 @@ void BitcoinGUI::createActions()
openAction->setStatusTip(tr("Open a bitcoin: URI or payment request"));
m_open_wallet_action = new QAction(tr("Open Wallet"), this);
- m_open_wallet_action->setMenu(new QMenu(this));
+ m_open_wallet_action->setEnabled(false);
m_open_wallet_action->setStatusTip(tr("Open a wallet"));
+ m_open_wallet_menu = new QMenu(this);
m_close_wallet_action = new QAction(tr("Close Wallet..."), this);
m_close_wallet_action->setStatusTip(tr("Close wallet"));
@@ -368,11 +369,20 @@ void BitcoinGUI::createActions()
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses);
connect(openAction, &QAction::triggered, this, &BitcoinGUI::openClicked);
- connect(m_open_wallet_action->menu(), &QMenu::aboutToShow, [this] {
- m_open_wallet_action->menu()->clear();
- for (std::string path : m_wallet_controller->getWalletsAvailableToOpen()) {
+ connect(m_open_wallet_menu, &QMenu::aboutToShow, [this] {
+ m_open_wallet_menu->clear();
+ std::vector<std::string> available_wallets = m_wallet_controller->getWalletsAvailableToOpen();
+ std::vector<std::string> wallets = m_node.listWalletDir();
+ for (const auto& path : wallets) {
QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
- QAction* action = m_open_wallet_action->menu()->addAction(name);
+ QAction* action = m_open_wallet_menu->addAction(name);
+
+ if (std::find(available_wallets.begin(), available_wallets.end(), path) == available_wallets.end()) {
+ // This wallet is already loaded
+ action->setEnabled(false);
+ continue;
+ }
+
connect(action, &QAction::triggered, [this, name, path] {
OpenWalletActivity* activity = m_wallet_controller->openWallet(path);
@@ -400,6 +410,10 @@ void BitcoinGUI::createActions()
assert(invoked);
});
}
+ if (wallets.empty()) {
+ QAction* action = m_open_wallet_menu->addAction(tr("No wallets available"));
+ action->setEnabled(false);
+ }
});
connect(m_close_wallet_action, &QAction::triggered, [this] {
m_wallet_controller->closeWallet(walletFrame->currentWalletModel(), this);
@@ -620,6 +634,9 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller)
m_wallet_controller = wallet_controller;
+ m_open_wallet_action->setEnabled(true);
+ m_open_wallet_action->setMenu(m_open_wallet_menu);
+
connect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet);
connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet);
@@ -1022,49 +1039,51 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
progressBar->setToolTip(tooltip);
}
-void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style, bool *ret)
+void BitcoinGUI::message(const QString& title, QString message, unsigned int style, bool* ret)
{
- QString strTitle = tr("Bitcoin"); // default title
+ // Default title. On macOS, the window title is ignored (as required by the macOS Guidelines).
+ QString strTitle{PACKAGE_NAME};
// Default to information icon
int nMBoxIcon = QMessageBox::Information;
int nNotifyIcon = Notificator::Information;
- QString msgType;
+ bool prefix = !(style & CClientUIInterface::MSG_NOPREFIX);
+ style &= ~CClientUIInterface::MSG_NOPREFIX;
- // Prefer supplied title over style based title
+ QString msgType;
if (!title.isEmpty()) {
msgType = title;
- }
- else {
+ } else {
switch (style) {
case CClientUIInterface::MSG_ERROR:
msgType = tr("Error");
+ if (prefix) message = tr("Error: %1").arg(message);
break;
case CClientUIInterface::MSG_WARNING:
msgType = tr("Warning");
+ if (prefix) message = tr("Warning: %1").arg(message);
break;
case CClientUIInterface::MSG_INFORMATION:
msgType = tr("Information");
+ // No need to prepend the prefix here.
break;
default:
break;
}
}
- // Append title to "Bitcoin - "
- if (!msgType.isEmpty())
+
+ if (!msgType.isEmpty()) {
strTitle += " - " + msgType;
+ }
- // Check for error/warning icon
if (style & CClientUIInterface::ICON_ERROR) {
nMBoxIcon = QMessageBox::Critical;
nNotifyIcon = Notificator::Critical;
- }
- else if (style & CClientUIInterface::ICON_WARNING) {
+ } else if (style & CClientUIInterface::ICON_WARNING) {
nMBoxIcon = QMessageBox::Warning;
nNotifyIcon = Notificator::Warning;
}
- // Display message
if (style & CClientUIInterface::MODAL) {
// Check for buttons, use OK as default, if none was supplied
QMessageBox::StandardButton buttons;
@@ -1077,9 +1096,9 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned
int r = mBox.exec();
if (ret != nullptr)
*ret = r == QMessageBox::Ok;
- }
- else
+ } else {
notificator->notify(static_cast<Notificator::Class>(nNotifyIcon), strTitle, message);
+ }
}
void BitcoinGUI::changeEvent(QEvent *e)
@@ -1328,6 +1347,7 @@ void BitcoinGUI::showProgress(const QString &title, int nProgress)
if (progressDialog) {
progressDialog->close();
progressDialog->deleteLater();
+ progressDialog = nullptr;
}
} else if (progressDialog) {
progressDialog->setValue(nProgress);
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index b58ccbb455..46ced79007 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -148,6 +148,7 @@ private:
QAction* openAction = nullptr;
QAction* showHelpMessageAction = nullptr;
QAction* m_open_wallet_action{nullptr};
+ QMenu* m_open_wallet_menu{nullptr};
QAction* m_close_wallet_action{nullptr};
QAction* m_wallet_selector_label_action = nullptr;
QAction* m_wallet_selector_action = nullptr;
@@ -219,7 +220,7 @@ public Q_SLOTS:
@see CClientUIInterface::MessageBoxFlags
@param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only)
*/
- void message(const QString &title, const QString &message, unsigned int style, bool *ret = nullptr);
+ void message(const QString& title, QString message, unsigned int style, bool* ret = nullptr);
#ifdef ENABLE_WALLET
void setCurrentWallet(WalletModel* wallet_model);
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index 8aa1fdba4d..b27f8a744f 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -4,8 +4,6 @@
#include <qt/bitcoinunits.h>
-#include <primitives/transaction.h>
-
#include <QStringList>
BitcoinUnits::BitcoinUnits(QObject *parent):
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 88a35534c2..ce950150df 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -9,18 +9,12 @@
#include <qt/guiutil.h>
#include <qt/peertablemodel.h>
-#include <chain.h>
-#include <chainparams.h>
#include <clientversion.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
-#include <validation.h>
#include <net.h>
#include <netbase.h>
-#include <txmempool.h>
-#include <ui_interface.h>
#include <util/system.h>
-#include <warnings.h>
#include <stdint.h>
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 0d9f1adcd2..6b9f79aaf8 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -10,12 +10,10 @@
#include <qt/forms/ui_coincontroldialog.h>
#include <qt/addresstablemodel.h>
-#include <base58.h>
#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
-#include <txmempool.h>
#include <qt/walletmodel.h>
#include <wallet/coincontrol.h>
@@ -23,8 +21,6 @@
#include <key_io.h>
#include <policy/fees.h>
#include <policy/policy.h>
-#include <validation.h> // For mempool
-#include <wallet/fees.h>
#include <wallet/wallet.h>
#include <QApplication>
@@ -471,8 +467,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
else if(ExtractDestination(out.txout.scriptPubKey, address))
{
CPubKey pubkey;
- CKeyID *keyid = boost::get<CKeyID>(&address);
- if (keyid && model->wallet().getPubKey(*keyid, pubkey))
+ PKHash *pkhash = boost::get<PKHash>(&address);
+ if (pkhash && model->wallet().getPubKey(CKeyID(*pkhash), pubkey))
{
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
}
diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui
index bd7f3c5f56..5ce469ee96 100644
--- a/src/qt/forms/coincontroldialog.ui
+++ b/src/qt/forms/coincontroldialog.ui
@@ -467,12 +467,6 @@
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index f0b976001e..6e52c5e477 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -636,6 +636,9 @@
<property name="placeholderText">
<string/>
</property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
</widget>
</item>
</layout>
@@ -1480,6 +1483,19 @@
</property>
</widget>
</item>
+ <item row="18" column="0">
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
</widget>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 45f21d50fc..70e52c9f1d 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -60,6 +60,7 @@
#include <objc/objc-runtime.h>
#include <CoreServices/CoreServices.h>
+#include <QProcess>
#endif
namespace GUIUtil {
@@ -399,7 +400,15 @@ bool openBitcoinConf()
configFile.close();
/* Open bitcoin.conf with the associated application */
- return QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig)));
+ bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig)));
+#ifdef Q_OS_MAC
+ // Workaround for macOS-specific behavior; see #15409.
+ if (!res) {
+ res = QProcess::startDetached("/usr/bin/open", QStringList{"-t", boostPathToQString(pathConfig)});
+ }
+#endif
+
+ return res;
}
ToolTipToRichTextFilter::ToolTipToRichTextFilter(int _size_threshold, QObject *parent) :
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 5b4fb4cc18..f3974b1c85 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -172,7 +172,7 @@ static void CopySettings(QSettings& dst, const QSettings& src)
/** Back up a QSettings to an ini-formatted file. */
static void BackupSettings(const fs::path& filename, const QSettings& src)
{
- qWarning() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename);
+ qInfo() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename);
QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat);
dst.clear();
CopySettings(dst, src);
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 43dccec4ea..c99515fe1c 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -488,7 +488,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store)
continue;
}
}
- qWarning() << "PaymentServer::LoadRootCAs: Loaded " << nRootCerts << " root certificates";
+ qInfo() << "PaymentServer::LoadRootCAs: Loaded " << nRootCerts << " root certificates";
// Project for another day:
// Fetch certificate revocation lists, and add them to certStore.
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 496aeebf7d..85b691c470 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -9,7 +9,6 @@
#include <qt/guiutil.h>
#include <interfaces/node.h>
-#include <validation.h> // for cs_main
#include <sync.h>
#include <QDebug>
diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp
index d537d759de..fca2a4e8c5 100644
--- a/src/qt/platformstyle.cpp
+++ b/src/qt/platformstyle.cpp
@@ -4,8 +4,6 @@
#include <qt/platformstyle.h>
-#include <qt/guiconstants.h>
-
#include <QApplication>
#include <QColor>
#include <QImage>
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index fc58090dcd..c58717e21e 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -7,9 +7,7 @@
#include <qt/receivecoinsdialog.h>
#include <qt/forms/ui_receivecoinsdialog.h>
-#include <qt/addressbookpage.h>
#include <qt/addresstablemodel.h>
-#include <qt/bitcoinunits.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/receiverequestdialog.h>
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index 20b29145a0..e492502002 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -8,7 +8,6 @@
#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
-#include <qt/qrimagewidget.h>
#include <QClipboard>
#include <QPixmap>
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 8b6dcf0445..071a197c3c 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -677,6 +677,9 @@ void RPCConsole::setClientModel(ClientModel *model)
wordList.sort();
autoCompleter = new QCompleter(wordList, this);
autoCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel);
+ // ui->lineEdit is initially disabled because running commands is only
+ // possible from now on.
+ ui->lineEdit->setEnabled(true);
ui->lineEdit->setCompleter(autoCompleter);
autoCompleter->popup()->installEventFilter(this);
// Start thread to execute RPC commands.
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 8a0b265834..cb9efe9319 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -279,18 +279,16 @@ void SendCoinsDialog::on_sendButton_clicked()
QStringList formatted;
for (const SendCoinsRecipient &rcp : currentTransaction.getRecipients())
{
- // generate bold amount string with wallet name in case of multiwallet
- QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
+ // generate amount string with wallet name in case of multiwallet
+ QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
if (model->isMultiwallet()) {
- amount.append(" <u>"+tr("from wallet %1").arg(GUIUtil::HtmlEscape(model->getWalletName()))+"</u> ");
+ amount.append(tr(" from wallet '%1'").arg(model->getWalletName()));
}
- amount.append("</b>");
- // generate monospace address string
- QString address = "<span style='font-family: monospace;'>" + rcp.address;
- address.append("</span>");
+
+ // generate address string
+ QString address = rcp.address;
QString recipientElement;
- recipientElement = "<br />";
#ifdef ENABLE_BIP70
if (!rcp.paymentRequest.IsInitialized()) // normal payment
@@ -298,7 +296,7 @@ void SendCoinsDialog::on_sendButton_clicked()
{
if(rcp.label.length() > 0) // label with address
{
- recipientElement.append(tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label)));
+ recipientElement.append(tr("%1 to '%2'").arg(amount, rcp.label));
recipientElement.append(QString(" (%1)").arg(address));
}
else // just address
@@ -309,7 +307,7 @@ void SendCoinsDialog::on_sendButton_clicked()
#ifdef ENABLE_BIP70
else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request
{
- recipientElement.append(tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)));
+ recipientElement.append(tr("%1 to '%2'").arg(amount, rcp.authenticatedMerchant));
}
else // unauthenticated payment request
{
@@ -323,7 +321,7 @@ void SendCoinsDialog::on_sendButton_clicked()
QString questionString = tr("Are you sure you want to send?");
questionString.append("<br /><span style='font-size:10pt;'>");
questionString.append(tr("Please, review your transaction."));
- questionString.append("</span><br />%1");
+ questionString.append("</span>%1");
if(txFee > 0)
{
@@ -364,8 +362,17 @@ void SendCoinsDialog::on_sendButton_clicked()
questionString.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
.arg(alternativeUnits.join(" " + tr("or") + " ")));
- SendConfirmationDialog confirmationDialog(tr("Confirm send coins"),
- questionString.arg(formatted.join("<br />")), SEND_CONFIRM_DELAY, this);
+ QString informative_text;
+ QString detailed_text;
+ if (formatted.size() > 1) {
+ questionString = questionString.arg("");
+ informative_text = tr("To review recipient list click \"Show Details...\"");
+ detailed_text = formatted.join("\n\n");
+ } else {
+ questionString = questionString.arg("<br /><br />" + formatted.at(0));
+ }
+
+ SendConfirmationDialog confirmationDialog(tr("Confirm send coins"), questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, this);
confirmationDialog.exec();
QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
@@ -881,10 +888,15 @@ void SendCoinsDialog::coinControlUpdateLabels()
}
}
-SendConfirmationDialog::SendConfirmationDialog(const QString &title, const QString &text, int _secDelay,
- QWidget *parent) :
- QMessageBox(QMessageBox::Question, title, text, QMessageBox::Yes | QMessageBox::Cancel, parent), secDelay(_secDelay)
+SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, QWidget* parent)
+ : QMessageBox(parent), secDelay(_secDelay)
{
+ setIcon(QMessageBox::Question);
+ setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
+ setText(text);
+ setInformativeText(informative_text);
+ setDetailedText(detailed_text);
+ setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
setDefaultButton(QMessageBox::Cancel);
yesButton = button(QMessageBox::Yes);
updateYesButton();
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 337a72b878..c6c1816877 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -108,7 +108,7 @@ class SendConfirmationDialog : public QMessageBox
Q_OBJECT
public:
- SendConfirmationDialog(const QString &title, const QString &text, int secDelay = SEND_CONFIRM_DELAY, QWidget *parent = nullptr);
+ SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, QWidget* parent = nullptr);
int exec();
private Q_SLOTS:
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 64cc85d623..71f5f2ae75 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -120,8 +120,8 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
return;
}
- const CKeyID* keyID = boost::get<CKeyID>(&destination);
- if (!keyID) {
+ const PKHash* pkhash = boost::get<PKHash>(&destination);
+ if (!pkhash) {
ui->addressIn_SM->setValid(false);
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again."));
@@ -137,7 +137,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
}
CKey key;
- if (!model->wallet().getPrivKey(*keyID, key))
+ if (!model->wallet().getPrivKey(CKeyID(*pkhash), key))
{
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("Private key for the entered address is not available."));
@@ -198,7 +198,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
return;
}
- if (!boost::get<CKeyID>(&destination)) {
+ if (!boost::get<PKHash>(&destination)) {
ui->addressIn_VM->setValid(false);
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again."));
@@ -229,7 +229,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
return;
}
- if (!(CTxDestination(pubkey.GetID()) == destination)) {
+ if (!(CTxDestination(PKHash(pubkey)) == destination)) {
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>"));
return;
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 2ba1c2604c..11a518ebd2 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -4,8 +4,6 @@
#include <interfaces/chain.h>
#include <interfaces/node.h>
-#include <qt/addressbookpage.h>
-#include <qt/addresstablemodel.h>
#include <qt/editaddressdialog.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
@@ -14,7 +12,6 @@
#include <key.h>
#include <key_io.h>
-#include <pubkey.h>
#include <wallet/wallet.h>
#include <QApplication>
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index da25d83175..a900ec0198 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -5,7 +5,7 @@
#include <qt/test/apptests.h>
#include <chainparams.h>
-#include <init.h>
+#include <key.h>
#include <qt/bitcoin.h>
#include <qt/bitcoingui.h>
#include <qt/networkstyle.h>
@@ -16,9 +16,6 @@
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
-#ifdef ENABLE_WALLET
-#include <wallet/db.h>
-#endif
#include <QAction>
#include <QEventLoop>
@@ -29,7 +26,6 @@
#include <QtGlobal>
#include <QtTest/QtTestWidgets>
#include <QtTest/QtTestGui>
-#include <new>
#include <string>
#include <univalue.h>
@@ -66,6 +62,9 @@ void AppTests::appTests()
}
#endif
+ ECC_Stop(); // Already started by the common test setup, so stop it to avoid interference
+ LogInstance().DisconnectTestLogger();
+
m_app.parameterSetup();
m_app.createOptionsModel(true /* reset settings */);
QScopedPointer<const NetworkStyle> style(
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index b0bd89b290..3735f41f9d 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -4,12 +4,7 @@
#include <qt/test/rpcnestedtests.h>
-#include <chainparams.h>
-#include <consensus/validation.h>
-#include <fs.h>
#include <interfaces/node.h>
-#include <validation.h>
-#include <rpc/register.h>
#include <rpc/server.h>
#include <qt/rpcconsole.h>
#include <test/setup_common.h>
@@ -39,6 +34,9 @@ void RPCNestedTests::rpcNestedTests()
tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]);
//mempool.setSanityCheck(1.0);
+ ECC_Stop(); // Already started by the common test setup, so stop it to avoid interference
+ LogInstance().DisconnectTestLogger();
+
TestingSetup test;
if (RPCIsInWarmup(nullptr)) SetRPCWarmupFinished();
diff --git a/src/qt/test/rpcnestedtests.h b/src/qt/test/rpcnestedtests.h
index e33f4e3da1..97143ff78a 100644
--- a/src/qt/test/rpcnestedtests.h
+++ b/src/qt/test/rpcnestedtests.h
@@ -8,9 +8,6 @@
#include <QObject>
#include <QTest>
-#include <txdb.h>
-#include <txmempool.h>
-
class RPCNestedTests : public QObject
{
Q_OBJECT
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index a2bf53973b..79d88ab742 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -6,14 +6,13 @@
#include <config/bitcoin-config.h>
#endif
-#include <chainparams.h>
#include <interfaces/node.h>
#include <qt/bitcoin.h>
#include <qt/test/apptests.h>
#include <qt/test/rpcnestedtests.h>
-#include <util/system.h>
#include <qt/test/uritests.h>
#include <qt/test/compattests.h>
+#include <test/setup_common.h>
#ifdef ENABLE_WALLET
#include <qt/test/addressbooktests.h>
@@ -48,14 +47,8 @@ extern void noui_connect();
// This is all you need to run all the tests
int main(int argc, char *argv[])
{
- SetupEnvironment();
- SetupNetworking();
- SelectParams(CBaseChainParams::REGTEST);
- noui_connect();
- ClearDatadirCache();
- fs::path pathTemp = fs::temp_directory_path() / strprintf("test_bitcoin-qt_%lu_%i", (unsigned long)GetTime(), (int)GetRand(100000));
- fs::create_directories(pathTemp);
- gArgs.ForceSetArg("-datadir", pathTemp.string());
+ BasicTestingSetup test{CBaseChainParams::REGTEST};
+
auto node = interfaces::MakeNode();
bool fInvalid = false;
@@ -109,7 +102,5 @@ int main(int argc, char *argv[])
}
#endif
- fs::remove_all(pathTemp);
-
return fInvalid;
}
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 9e3518fd53..e54915ec75 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -1,10 +1,8 @@
#include <qt/test/wallettests.h>
#include <qt/test/util.h>
-#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/node.h>
-#include <base58.h>
#include <qt/bitcoinamountfield.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
@@ -145,11 +143,13 @@ void TestGUI()
}
{
auto locked_chain = wallet->chain().lock();
+ LockAssertion lock(::cs_main);
+
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
CWallet::ScanResult result = wallet->ScanForWalletTransactions(locked_chain->getBlockHash(0), {} /* stop_block */, reserver, true /* fUpdate */);
QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
- QCOMPARE(result.last_scanned_block, chainActive.Tip()->GetBlockHash());
+ QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
QVERIFY(result.last_failed_block.IsNull());
}
wallet->SetBroadcastTransactions(true);
@@ -169,8 +169,8 @@ void TestGUI()
// Send two transactions, and verify they are added to transaction list.
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
QCOMPARE(transactionTableModel->rowCount({}), 105);
- uint256 txid1 = SendCoins(*wallet.get(), sendCoinsDialog, CKeyID(), 5 * COIN, false /* rbf */);
- uint256 txid2 = SendCoins(*wallet.get(), sendCoinsDialog, CKeyID(), 10 * COIN, true /* rbf */);
+ uint256 txid1 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 5 * COIN, false /* rbf */);
+ uint256 txid2 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 10 * COIN, true /* rbf */);
QCOMPARE(transactionTableModel->rowCount({}), 107);
QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
QVERIFY(FindTx(*transactionTableModel, txid2).isValid());
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 0d070d9e87..ebe7925368 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -20,9 +20,8 @@
#include <script/script.h>
#include <timedata.h>
#include <util/system.h>
-#include <wallet/db.h>
-#include <wallet/wallet.h>
#include <policy/policy.h>
+#include <wallet/ismine.h>
#include <stdint.h>
#include <string>
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index aa785553c8..9de90759fa 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -5,11 +5,9 @@
#include <qt/transactionrecord.h>
#include <chain.h>
-#include <consensus/consensus.h>
#include <interfaces/wallet.h>
#include <key_io.h>
-#include <timedata.h>
-#include <validation.h>
+#include <wallet/ismine.h>
#include <stdint.h>
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 631a9b891d..6fe35b13cf 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -15,11 +15,7 @@
#include <core_io.h>
#include <interfaces/handler.h>
-#include <interfaces/node.h>
-#include <sync.h>
#include <uint256.h>
-#include <util/system.h>
-#include <validation.h>
#include <QColor>
#include <QDateTime>
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 762ec434a1..17e174e57a 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -10,7 +10,6 @@
#include <qt/editaddressdialog.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
-#include <qt/sendcoinsdialog.h>
#include <qt/transactiondescdialog.h>
#include <qt/transactionfilterproxy.h>
#include <qt/transactionrecord.h>
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index b051dd159b..aa810690c9 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -11,17 +11,12 @@
#include <qt/forms/ui_helpmessagedialog.h>
#include <qt/bitcoingui.h>
-#include <qt/clientmodel.h>
-#include <qt/guiconstants.h>
-#include <qt/intro.h>
#ifdef ENABLE_BIP70
#include <qt/paymentrequestplus.h>
#endif
-#include <qt/guiutil.h>
#include <clientversion.h>
#include <init.h>
-#include <interfaces/node.h>
#include <util/system.h>
#include <util/strencodings.h>
@@ -129,7 +124,7 @@ HelpMessageDialog::~HelpMessageDialog()
void HelpMessageDialog::printToConsole()
{
// On other operating systems, the expected action is to print the message to the console.
- fprintf(stdout, "%s\n", qPrintable(text));
+ tfm::format(std::cout, "%s\n", qPrintable(text));
}
void HelpMessageDialog::showOrPrint()
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index fd392b7cf7..a2b295df21 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -482,7 +482,7 @@ WalletModel::UnlockContext::~UnlockContext()
}
}
-void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs)
+void WalletModel::UnlockContext::CopyFrom(UnlockContext&& rhs)
{
// Transfer context; old object no longer relocks wallet
*this = rhs;
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index b123befbb4..54428aec08 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -194,15 +194,18 @@ public:
bool isValid() const { return valid; }
- // Copy operator and constructor transfer the context
- UnlockContext(const UnlockContext& obj) { CopyFrom(obj); }
- UnlockContext& operator=(const UnlockContext& rhs) { CopyFrom(rhs); return *this; }
+ // Copy constructor is disabled.
+ UnlockContext(const UnlockContext&) = delete;
+ // Move operator and constructor transfer the context
+ UnlockContext(UnlockContext&& obj) { CopyFrom(std::move(obj)); }
+ UnlockContext& operator=(UnlockContext&& rhs) { CopyFrom(std::move(rhs)); return *this; }
private:
WalletModel *wallet;
bool valid;
mutable bool relock; // mutable, as it can be set to false by copying
- void CopyFrom(const UnlockContext& rhs);
+ UnlockContext& operator=(const UnlockContext&) = default;
+ void CopyFrom(UnlockContext&& rhs);
};
UnlockContext requestUnlock();
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index 2694d67800..8c0dc276b0 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -8,7 +8,6 @@
#include <qt/walletmodeltransaction.h>
-#include <interfaces/node.h>
#include <policy/policy.h>
WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &_recipients) :
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 5f6f93d948..be47f67f95 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -316,6 +316,7 @@ void WalletView::showProgress(const QString &title, int nProgress)
if (progressDialog) {
progressDialog->close();
progressDialog->deleteLater();
+ progressDialog = nullptr;
}
} else if (progressDialog) {
if (progressDialog->wasCanceled()) {
diff --git a/src/qt/winshutdownmonitor.cpp b/src/qt/winshutdownmonitor.cpp
index 08cae76add..b177b22b3f 100644
--- a/src/qt/winshutdownmonitor.cpp
+++ b/src/qt/winshutdownmonitor.cpp
@@ -63,7 +63,7 @@ void WinShutdownMonitor::registerShutdownBlockReason(const QString& strReason, c
}
if (shutdownBRCreate(mainWinId, strReason.toStdWString().c_str()))
- qWarning() << "registerShutdownBlockReason: Successfully registered: " + strReason;
+ qInfo() << "registerShutdownBlockReason: Successfully registered: " + strReason;
else
qWarning() << "registerShutdownBlockReason: Failed to register: " + strReason;
}
diff --git a/src/random.cpp b/src/random.cpp
index 1aa78a9034..de26e6de1a 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -237,6 +237,34 @@ static void SeedHardwareSlow(CSHA512& hasher) noexcept {
#endif
}
+/** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */
+static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA512& hasher) noexcept
+{
+ CSHA512 inner_hasher;
+ inner_hasher.Write(seed, sizeof(seed));
+
+ // Hash loop
+ unsigned char buffer[64];
+ int64_t stop = GetTimeMicros() + microseconds;
+ do {
+ for (int i = 0; i < 1000; ++i) {
+ inner_hasher.Finalize(buffer);
+ inner_hasher.Reset();
+ inner_hasher.Write(buffer, sizeof(buffer));
+ }
+ // Benchmark operation and feed it into outer hasher.
+ int64_t perf = GetPerformanceCounter();
+ hasher.Write((const unsigned char*)&perf, sizeof(perf));
+ } while (GetTimeMicros() < stop);
+
+ // Produce output from inner state and feed it to outer hasher.
+ inner_hasher.Finalize(buffer);
+ hasher.Write(buffer, sizeof(buffer));
+ // Try to clean up.
+ inner_hasher.Reset();
+ memory_cleanse(buffer, sizeof(buffer));
+}
+
static void RandAddSeedPerfmon(CSHA512& hasher)
{
#ifdef WIN32
@@ -529,7 +557,23 @@ static void SeedSlow(CSHA512& hasher) noexcept
SeedTimestamp(hasher);
}
-static void SeedSleep(CSHA512& hasher)
+/** Extract entropy from rng, strengthen it, and feed it into hasher. */
+static void SeedStrengthen(CSHA512& hasher, RNGState& rng) noexcept
+{
+ static std::atomic<int64_t> last_strengthen{0};
+ int64_t last_time = last_strengthen.load();
+ int64_t current_time = GetTimeMicros();
+ if (current_time > last_time + 60000000) { // Only run once a minute
+ // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
+ unsigned char strengthen_seed[32];
+ rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
+ // Strengthen it for 10ms (100ms on first run), and feed it into hasher.
+ Strengthen(strengthen_seed, last_time == 0 ? 100000 : 10000, hasher);
+ last_strengthen = current_time;
+ }
+}
+
+static void SeedSleep(CSHA512& hasher, RNGState& rng)
{
// Everything that the 'fast' seeder includes
SeedFast(hasher);
@@ -545,9 +589,12 @@ static void SeedSleep(CSHA512& hasher)
// Windows performance monitor data (once every 10 minutes)
RandAddSeedPerfmon(hasher);
+
+ // Strengthen every minute
+ SeedStrengthen(hasher, rng);
}
-static void SeedStartup(CSHA512& hasher) noexcept
+static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
{
#ifdef WIN32
RAND_screen();
@@ -561,6 +608,9 @@ static void SeedStartup(CSHA512& hasher) noexcept
// Windows performance monitor data.
RandAddSeedPerfmon(hasher);
+
+ // Strengthen
+ SeedStrengthen(hasher, rng);
}
enum class RNGLevel {
@@ -585,7 +635,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
SeedSlow(hasher);
break;
case RNGLevel::SLEEP:
- SeedSleep(hasher);
+ SeedSleep(hasher, rng);
break;
}
@@ -593,7 +643,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
if (!rng.MixExtract(out, num, std::move(hasher), false)) {
// On the first invocation, also seed with SeedStartup().
CSHA512 startup_hasher;
- SeedStartup(startup_hasher);
+ SeedStartup(startup_hasher, rng);
rng.MixExtract(out, num, std::move(startup_hasher), true);
}
@@ -652,7 +702,7 @@ std::vector<unsigned char> FastRandomContext::randbytes(size_t len)
if (requires_seed) RandomSeed();
std::vector<unsigned char> ret(len);
if (len > 0) {
- rng.Output(&ret[0], len);
+ rng.Keystream(&ret[0], len);
}
return ret;
}
diff --git a/src/random.h b/src/random.h
index 1c035f87ba..75d037738d 100644
--- a/src/random.h
+++ b/src/random.h
@@ -43,6 +43,7 @@
* - RandAddSeedSleep() seeds everything that fast seeding includes, but additionally:
* - A high-precision timestamp before and after sleeping 1ms.
* - (On Windows) Once every 10 minutes, performance monitoring data from the OS.
+ - - Once every minute, strengthen the entropy for 10 ms using repeated SHA512.
* These just exploit the fact the system is idle to improve the quality of the RNG
* slightly.
*
@@ -51,6 +52,7 @@
* - 256 bits from the hardware RNG (rdseed or rdrand) when available.
* - (On Windows) Performance monitoring data from the OS.
* - (On Windows) Through OpenSSL, the screen contents.
+ * - Strengthen the entropy for 100 ms using repeated SHA512.
*
* When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and
* (up to) the first 32 bytes of H are produced as output, while the last 32 bytes
@@ -111,7 +113,7 @@ private:
if (requires_seed) {
RandomSeed();
}
- rng.Output(bytebuf, sizeof(bytebuf));
+ rng.Keystream(bytebuf, sizeof(bytebuf));
bytebuf_size = sizeof(bytebuf);
}
diff --git a/src/rest.cpp b/src/rest.cpp
index baad3b2ce9..ab409947d3 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -141,13 +141,13 @@ static bool rest_headers(HTTPRequest* req,
headers.reserve(count);
{
LOCK(cs_main);
- tip = chainActive.Tip();
+ tip = ::ChainActive().Tip();
const CBlockIndex* pindex = LookupBlockIndex(hash);
- while (pindex != nullptr && chainActive.Contains(pindex)) {
+ while (pindex != nullptr && ::ChainActive().Contains(pindex)) {
headers.push_back(pindex);
if (headers.size() == (unsigned long)count)
break;
- pindex = chainActive.Next(pindex);
+ pindex = ::ChainActive().Next(pindex);
}
}
@@ -209,7 +209,7 @@ static bool rest_block(HTTPRequest* req,
CBlockIndex* tip = nullptr;
{
LOCK(cs_main);
- tip = chainActive.Tip();
+ tip = ::ChainActive().Tip();
pblockindex = LookupBlockIndex(hash);
if (!pblockindex) {
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
@@ -522,7 +522,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
// serialize data
// use exact same output as mentioned in Bip64
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
- ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs;
+ ssGetUTXOResponse << ::ChainActive().Height() << ::ChainActive().Tip()->GetBlockHash() << bitmap << outs;
std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
req->WriteHeader("Content-Type", "application/octet-stream");
@@ -532,7 +532,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
case RetFormat::HEX: {
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
- ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs;
+ ssGetUTXOResponse << ::ChainActive().Height() << ::ChainActive().Tip()->GetBlockHash() << bitmap << outs;
std::string strHex = HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + "\n";
req->WriteHeader("Content-Type", "text/plain");
@@ -545,8 +545,8 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
// pack in some essentials
// use more or less the same output as mentioned in Bip64
- objGetUTXOResponse.pushKV("chainHeight", chainActive.Height());
- objGetUTXOResponse.pushKV("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex());
+ objGetUTXOResponse.pushKV("chainHeight", ::ChainActive().Height());
+ objGetUTXOResponse.pushKV("chaintipHash", ::ChainActive().Tip()->GetBlockHash().GetHex());
objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
UniValue utxos(UniValue::VARR);
@@ -590,10 +590,10 @@ static bool rest_blockhash_by_height(HTTPRequest* req,
CBlockIndex* pblockindex = nullptr;
{
LOCK(cs_main);
- if (blockheight > chainActive.Height()) {
+ if (blockheight > ::ChainActive().Height()) {
return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
}
- pblockindex = chainActive[blockheight];
+ pblockindex = ::ChainActive()[blockheight];
}
switch (rf) {
case RetFormat::BINARY: {
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index f140999622..50c4589d9f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -6,7 +6,6 @@
#include <rpc/blockchain.h>
#include <amount.h>
-#include <base58.h>
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
@@ -15,7 +14,6 @@
#include <core_io.h>
#include <hash.h>
#include <index/blockfilterindex.h>
-#include <index/txindex.h>
#include <key_io.h>
#include <policy/feerate.h>
#include <policy/policy.h>
@@ -28,6 +26,7 @@
#include <sync.h>
#include <txdb.h>
#include <txmempool.h>
+#include <undo.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <util/validation.h>
@@ -182,7 +181,7 @@ static UniValue getblockcount(const JSONRPCRequest& request)
}.ToString());
LOCK(cs_main);
- return chainActive.Height();
+ return ::ChainActive().Height();
}
static UniValue getbestblockhash(const JSONRPCRequest& request)
@@ -202,7 +201,7 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
}.ToString());
LOCK(cs_main);
- return chainActive.Tip()->GetBlockHash().GetHex();
+ return ::ChainActive().Tip()->GetBlockHash().GetHex();
}
void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
@@ -381,7 +380,7 @@ static UniValue getdifficulty(const JSONRPCRequest& request)
}.ToString());
LOCK(cs_main);
- return GetDifficulty(chainActive.Tip());
+ return GetDifficulty(::ChainActive().Tip());
}
static std::string EntryDescriptionString()
@@ -485,7 +484,10 @@ UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose)
const uint256& hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ);
entryToJSON(pool, info, e);
- o.pushKV(hash.ToString(), info);
+ // Mempool has unique entries so there is no advantage in using
+ // UniValue::pushKV, which checks if the key already exists in O(N).
+ // UniValue::__pushKV is used instead which currently is O(1).
+ o.__pushKV(hash.ToString(), info);
}
return o;
} else {
@@ -732,10 +734,10 @@ static UniValue getblockhash(const JSONRPCRequest& request)
LOCK(cs_main);
int nHeight = request.params[0].get_int();
- if (nHeight < 0 || nHeight > chainActive.Height())
+ if (nHeight < 0 || nHeight > ::ChainActive().Height())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
- CBlockIndex* pblockindex = chainActive[nHeight];
+ CBlockIndex* pblockindex = ::ChainActive()[nHeight];
return pblockindex->GetBlockHash().GetHex();
}
@@ -791,7 +793,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
{
LOCK(cs_main);
pblockindex = LookupBlockIndex(hash);
- tip = chainActive.Tip();
+ tip = ::ChainActive().Tip();
}
if (!pblockindex) {
@@ -828,6 +830,20 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
return block;
}
+static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
+{
+ CBlockUndo blockUndo;
+ if (IsBlockPruned(pblockindex)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
+ }
+
+ if (!UndoReadFromDisk(blockUndo, pblockindex)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Can't read undo data from disk");
+ }
+
+ return blockUndo;
+}
+
static UniValue getblock(const JSONRPCRequest& request)
{
const RPCHelpMan help{"getblock",
@@ -904,7 +920,7 @@ static UniValue getblock(const JSONRPCRequest& request)
{
LOCK(cs_main);
pblockindex = LookupBlockIndex(hash);
- tip = chainActive.Tip();
+ tip = ::ChainActive().Tip();
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
@@ -1026,7 +1042,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
// too low to be a block time (corresponds to timestamp from Sep 2001).
if (heightParam > 1000000000) {
// Add a 2 hour buffer to include blocks which might have had old timestamps
- CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
+ CBlockIndex* pindex = ::ChainActive().FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
}
@@ -1034,7 +1050,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
}
unsigned int height = (unsigned int) heightParam;
- unsigned int chainHeight = (unsigned int) chainActive.Height();
+ unsigned int chainHeight = (unsigned int) ::ChainActive().Height();
if (chainHeight < Params().PruneAfterHeight())
throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning.");
else if (height > chainHeight)
@@ -1045,7 +1061,12 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
}
PruneBlockFilesManual(height);
- return uint64_t(height);
+ const CBlockIndex* block = ::ChainActive().Tip();
+ assert(block);
+ while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
+ block = block->pprev;
+ }
+ return uint64_t(block->nHeight);
}
static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
@@ -1077,7 +1098,7 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
UniValue ret(UniValue::VOBJ);
CCoinsStats stats;
- FlushStateToDisk();
+ ::ChainstateActive().ForceFlushStateToDisk();
if (GetUTXOStats(pcoinsdbview.get(), stats)) {
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
@@ -1333,16 +1354,16 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
LOCK(cs_main);
- const CBlockIndex* tip = chainActive.Tip();
+ const CBlockIndex* tip = ::ChainActive().Tip();
UniValue obj(UniValue::VOBJ);
obj.pushKV("chain", Params().NetworkIDString());
- obj.pushKV("blocks", (int)chainActive.Height());
+ obj.pushKV("blocks", (int)::ChainActive().Height());
obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
obj.pushKV("difficulty", (double)GetDifficulty(tip));
obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
- obj.pushKV("initialblockdownload", IsInitialBlockDownload());
+ obj.pushKV("initialblockdownload", ::ChainstateActive().IsInitialBlockDownload());
obj.pushKV("chainwork", tip->nChainWork.GetHex());
obj.pushKV("size_on_disk", CalculateCurrentUsage());
obj.pushKV("pruned", fPruneMode);
@@ -1433,11 +1454,11 @@ static UniValue getchaintips(const JSONRPCRequest& request)
LOCK(cs_main);
/*
- * Idea: the set of chain tips is chainActive.tip, plus orphan blocks which do not have another orphan building off of them.
+ * Idea: the set of chain tips is ::ChainActive().tip, plus orphan blocks which do not have another orphan building off of them.
* Algorithm:
* - Make one pass through mapBlockIndex, picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
* - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip.
- * - add chainActive.Tip()
+ * - add ::ChainActive().Tip()
*/
std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
std::set<const CBlockIndex*> setOrphans;
@@ -1445,7 +1466,7 @@ static UniValue getchaintips(const JSONRPCRequest& request)
for (const std::pair<const uint256, CBlockIndex*>& item : mapBlockIndex)
{
- if (!chainActive.Contains(item.second)) {
+ if (!::ChainActive().Contains(item.second)) {
setOrphans.insert(item.second);
setPrevs.insert(item.second->pprev);
}
@@ -1459,7 +1480,7 @@ static UniValue getchaintips(const JSONRPCRequest& request)
}
// Always report the currently active tip.
- setTips.insert(chainActive.Tip());
+ setTips.insert(::ChainActive().Tip());
/* Construct the output array. */
UniValue res(UniValue::VARR);
@@ -1469,11 +1490,11 @@ static UniValue getchaintips(const JSONRPCRequest& request)
obj.pushKV("height", block->nHeight);
obj.pushKV("hash", block->phashBlock->GetHex());
- const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight;
+ const int branchLen = block->nHeight - ::ChainActive().FindFork(block)->nHeight;
obj.pushKV("branchlen", branchLen);
std::string status;
- if (chainActive.Contains(block)) {
+ if (::ChainActive().Contains(block)) {
// This block is part of the currently active chain.
status = "active";
} else if (block->nStatus & BLOCK_FAILED_MASK) {
@@ -1694,7 +1715,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
if (request.params[1].isNull()) {
LOCK(cs_main);
- pindex = chainActive.Tip();
+ pindex = ::ChainActive().Tip();
} else {
uint256 hash(ParseHashV(request.params[1], "blockhash"));
LOCK(cs_main);
@@ -1702,7 +1723,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- if (!chainActive.Contains(pindex)) {
+ if (!::ChainActive().Contains(pindex)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain");
}
}
@@ -1799,8 +1820,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
{
const RPCHelpMan help{"getblockstats",
"\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
- "It won't work for some heights with pruning.\n"
- "It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n",
+ "It won't work for some heights with pruning.\n",
{
{"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", "", {"", "string or numeric"}},
{"stats", RPCArg::Type::ARR, /* default */ "all values", "Values to plot (see result below)",
@@ -1863,7 +1883,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
CBlockIndex* pindex;
if (request.params[0].isNum()) {
const int height = request.params[0].get_int();
- const int current_tip = chainActive.Height();
+ const int current_tip = ::ChainActive().Height();
if (height < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height));
}
@@ -1871,14 +1891,14 @@ static UniValue getblockstats(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", height, current_tip));
}
- pindex = chainActive[height];
+ pindex = ::ChainActive()[height];
} else {
const uint256 hash(ParseHashV(request.params[0], "hash_or_height"));
pindex = LookupBlockIndex(hash);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- if (!chainActive.Contains(pindex)) {
+ if (!::ChainActive().Contains(pindex)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Block is not in chain %s", Params().NetworkIDString()));
}
}
@@ -1895,6 +1915,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
}
const CBlock block = GetBlockChecked(pindex);
+ const CBlockUndo blockUndo = GetUndoChecked(pindex);
const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
@@ -1908,10 +1929,6 @@ static UniValue getblockstats(const JSONRPCRequest& request)
const bool do_calculate_weight = do_all || SetHasKeys(stats, "total_weight", "avgfeerate", "swtotal_weight", "avgfeerate", "feerate_percentiles", "minfeerate", "maxfeerate");
const bool do_calculate_sw = do_all || SetHasKeys(stats, "swtxs", "swtotal_size", "swtotal_weight");
- if (loop_inputs && !g_txindex) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled");
- }
-
CAmount maxfee = 0;
CAmount maxfeerate = 0;
CAmount minfee = MAX_MONEY;
@@ -1932,7 +1949,8 @@ static UniValue getblockstats(const JSONRPCRequest& request)
std::vector<std::pair<CAmount, int64_t>> feerate_array;
std::vector<int64_t> txsize_array;
- for (const auto& tx : block.vtx) {
+ for (size_t i = 0; i < block.vtx.size(); ++i) {
+ const auto& tx = block.vtx.at(i);
outputs += tx->vout.size();
CAmount tx_total_out = 0;
@@ -1976,14 +1994,9 @@ static UniValue getblockstats(const JSONRPCRequest& request)
if (loop_inputs) {
CAmount tx_total_in = 0;
- for (const CTxIn& in : tx->vin) {
- CTransactionRef tx_in;
- uint256 hashBlock;
- if (!GetTransaction(in.prevout.hash, tx_in, Params().GetConsensus(), hashBlock)) {
- throw JSONRPCError(RPC_INTERNAL_ERROR, std::string("Unexpected internal error (tx index seems corrupt)"));
- }
-
- CTxOut prevoutput = tx_in->vout[in.prevout.n];
+ const auto& txundo = blockUndo.vtxundo.at(i - 1);
+ for (const Coin& coin: txundo.vprevout) {
+ const CTxOut& prevoutput = coin.out;
tx_total_in += prevoutput.nValue;
utxo_size_inc -= GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
@@ -2244,8 +2257,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
desc_str = desc_uni.get_str();
UniValue range_uni = find_value(scanobject, "range");
if (!range_uni.isNull()) {
- range = ParseRange(range_uni);
- if (range.first < 0 || (range.second >> 31) != 0 || range.second >= range.first + 1000000) throw JSONRPCError(RPC_INVALID_PARAMETER, "range out of range");
+ range = ParseDescriptorRange(range_uni);
}
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
@@ -2283,7 +2295,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
std::unique_ptr<CCoinsViewCursor> pcursor;
{
LOCK(cs_main);
- FlushStateToDisk();
+ ::ChainstateActive().ForceFlushStateToDisk();
pcursor = std::unique_ptr<CCoinsViewCursor>(pcoinsdbview->Cursor());
assert(pcursor);
}
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 4144a17bc3..8f6bdc21aa 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -36,6 +36,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendtoaddress", 4, "subtractfeefromamount" },
{ "sendtoaddress", 5 , "replaceable" },
{ "sendtoaddress", 6 , "conf_target" },
+ { "sendtoaddress", 8, "avoid_reuse" },
{ "settxfee", 0, "amount" },
{ "sethdseed", 0, "newkeypool" },
{ "getreceivedbyaddress", 1, "minconf" },
@@ -48,6 +49,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listreceivedbylabel", 2, "include_watchonly" },
{ "getbalance", 1, "minconf" },
{ "getbalance", 2, "include_watchonly" },
+ { "getbalance", 3, "avoid_reuse" },
{ "getblockhash", 0, "height" },
{ "waitforblockheight", 0, "height" },
{ "waitforblockheight", 1, "timeout" },
@@ -141,6 +143,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "setban", 2, "bantime" },
{ "setban", 3, "absolute" },
{ "setnetworkactive", 0, "state" },
+ { "setwalletflag", 1, "value" },
{ "getmempoolancestors", 1, "verbose" },
{ "getmempooldescendants", 1, "verbose" },
{ "bumpfee", 1, "options" },
@@ -162,6 +165,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "rescanblockchain", 1, "stop_height"},
{ "createwallet", 1, "disable_private_keys"},
{ "createwallet", 2, "blank"},
+ { "createwallet", 4, "avoid_reuse"},
{ "getnodeaddresses", 0, "count"},
{ "stop", 0, "wait" },
};
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 4de738a756..477f05f46c 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -40,10 +40,10 @@
* If 'height' is nonnegative, compute the estimate at the time when a given block was found.
*/
static UniValue GetNetworkHashPS(int lookup, int height) {
- CBlockIndex *pb = chainActive.Tip();
+ CBlockIndex *pb = ::ChainActive().Tip();
- if (height >= 0 && height < chainActive.Height())
- pb = chainActive[height];
+ if (height >= 0 && height < ::ChainActive().Height())
+ pb = ::ChainActive()[height];
if (pb == nullptr || !pb->nHeight)
return 0;
@@ -109,7 +109,7 @@ static UniValue generateBlocks(const CScript& coinbase_script, int nGenerate, ui
{ // Don't keep cs_main locked
LOCK(cs_main);
- nHeight = chainActive.Height();
+ nHeight = ::ChainActive().Height();
nHeightEnd = nHeight+nGenerate;
}
unsigned int nExtraNonce = 0;
@@ -122,7 +122,7 @@ static UniValue generateBlocks(const CScript& coinbase_script, int nGenerate, ui
CBlock *pblock = &pblocktemplate->block;
{
LOCK(cs_main);
- IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
+ IncrementExtraNonce(pblock, ::ChainActive().Tip(), nExtraNonce);
}
while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
++pblock->nNonce;
@@ -210,10 +210,10 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
- obj.pushKV("blocks", (int)chainActive.Height());
+ obj.pushKV("blocks", (int)::ChainActive().Height());
if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
- obj.pushKV("difficulty", (double)GetDifficulty(chainActive.Tip()));
+ obj.pushKV("difficulty", (double)GetDifficulty(::ChainActive().Tip()));
obj.pushKV("networkhashps", getnetworkhashps(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
obj.pushKV("chain", Params().NetworkIDString());
@@ -409,7 +409,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
return "duplicate-inconclusive";
}
- CBlockIndex* const pindexPrev = chainActive.Tip();
+ CBlockIndex* const pindexPrev = ::ChainActive().Tip();
// TestBlockValidity only supports blocks built on the current Tip
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
return "inconclusive-not-best-prevblk";
@@ -442,7 +442,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0)
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
- if (IsInitialBlockDownload())
+ if (::ChainstateActive().IsInitialBlockDownload())
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks...");
static unsigned int nTransactionsUpdatedLast;
@@ -465,7 +465,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
else
{
// NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
- hashWatchedChain = chainActive.Tip()->GetBlockHash();
+ hashWatchedChain = ::ChainActive().Tip()->GetBlockHash();
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
}
@@ -503,7 +503,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
static CBlockIndex* pindexPrev;
static int64_t nStart;
static std::unique_ptr<CBlockTemplate> pblocktemplate;
- if (pindexPrev != chainActive.Tip() ||
+ if (pindexPrev != ::ChainActive().Tip() ||
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
{
// Clear pindexPrev so future calls make a new block, despite any failures from here on
@@ -511,7 +511,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
// Store the pindexBest used before CreateNewBlock, to avoid races
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
- CBlockIndex* pindexPrevNew = chainActive.Tip();
+ CBlockIndex* pindexPrevNew = ::ChainActive().Tip();
nStart = GetTime();
// Create new block
@@ -646,7 +646,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
result.pushKV("transactions", transactions);
result.pushKV("coinbaseaux", aux);
result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue);
- result.pushKV("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast));
+ result.pushKV("longpollid", ::ChainActive().Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast));
result.pushKV("target", hashTarget.GetHex());
result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1);
result.pushKV("mutable", aMutable);
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index bfb559f0db..7a1bdec7b9 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -3,27 +3,20 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <chain.h>
-#include <clientversion.h>
-#include <core_io.h>
#include <crypto/ripemd160.h>
#include <key_io.h>
-#include <validation.h>
#include <httpserver.h>
-#include <net.h>
-#include <netbase.h>
#include <outputtype.h>
#include <rpc/blockchain.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/descriptor.h>
-#include <timedata.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <util/validation.h>
-#include <warnings.h>
#include <stdint.h>
+#include <tuple>
#ifdef HAVE_MALLOC_INFO
#include <malloc.h>
#endif
@@ -129,9 +122,9 @@ static UniValue createmultisig(const JSONRPCRequest& request)
}
// Construct using pay-to-script-hash:
- const CScript inner = CreateMultisigRedeemscript(required, pubkeys);
CBasicKeyStore keystore;
- const CTxDestination dest = AddAndGetDestinationForScript(keystore, inner, output_type);
+ CScript inner;
+ const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, keystore, inner);
UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(dest));
@@ -203,7 +196,7 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
},
RPCExamples{
"First three native segwit receive addresses\n" +
- HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#trd0mf0l\" \"[0,2]\"")
+ HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu\" \"[0,2]\"")
}}.ToString()
);
}
@@ -215,18 +208,7 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
int64_t range_end = 0;
if (request.params.size() >= 2 && !request.params[1].isNull()) {
- auto range = ParseRange(request.params[1]);
- if (range.first < 0) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0");
- }
- if ((range.second >> 31) != 0) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high");
- }
- if (range.second >= range.first + 1000000) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large");
- }
- range_begin = range.first;
- range_end = range.second;
+ std::tie(range_begin, range_end) = ParseDescriptorRange(request.params[1]);
}
FlatSigningProvider key_provider;
@@ -307,8 +289,8 @@ static UniValue verifymessage(const JSONRPCRequest& request)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
}
- const CKeyID *keyID = boost::get<CKeyID>(&destination);
- if (!keyID) {
+ const PKHash *pkhash = boost::get<PKHash>(&destination);
+ if (!pkhash) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
@@ -326,7 +308,7 @@ static UniValue verifymessage(const JSONRPCRequest& request)
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
return false;
- return (pubkey.GetID() == *keyID);
+ return (pubkey.GetID() == *pkhash);
}
static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index e8cdce623c..d993a88458 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -5,7 +5,6 @@
#include <rpc/server.h>
#include <banman.h>
-#include <chainparams.h>
#include <clientversion.h>
#include <core_io.h>
#include <net.h>
@@ -17,7 +16,6 @@
#include <rpc/util.h>
#include <sync.h>
#include <timedata.h>
-#include <ui_interface.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <validation.h>
@@ -496,7 +494,7 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
obj.pushKV("protocolversion",PROTOCOL_VERSION);
if(g_connman)
obj.pushKV("localservices", strprintf("%016x", g_connman->GetLocalServices()));
- obj.pushKV("localrelay", fRelayTxes);
+ obj.pushKV("localrelay", g_relay_txes);
obj.pushKV("timeoffset", GetTimeOffset());
if (g_connman) {
obj.pushKV("networkactive", g_connman->GetNetworkActive());
diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp
index 23999b305a..33b0130a94 100644
--- a/src/rpc/protocol.cpp
+++ b/src/rpc/protocol.cpp
@@ -10,7 +10,6 @@
#include <util/system.h>
#include <util/strencodings.h>
#include <util/time.h>
-#include <version.h>
/**
* JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 78d7bbc80c..9da24afe79 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -6,21 +6,16 @@
#include <chain.h>
#include <coins.h>
#include <compat/byteswap.h>
-#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <index/txindex.h>
-#include <init.h>
-#include <interfaces/chain.h>
#include <key_io.h>
#include <keystore.h>
#include <merkleblock.h>
#include <node/coin.h>
#include <node/psbt.h>
#include <node/transaction.h>
-#include <policy/policy.h>
#include <policy/rbf.h>
-#include <policy/settings.h>
#include <primitives/transaction.h>
#include <psbt.h>
#include <rpc/rawtransaction_util.h>
@@ -31,7 +26,6 @@
#include <script/sign.h>
#include <script/standard.h>
#include <uint256.h>
-#include <util/bip32.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
#include <validation.h>
@@ -45,7 +39,7 @@
/** High fee for sendrawtransaction and testmempoolaccept.
* By default, transaction with a fee higher than this will be rejected by the
- * RPCs. This can be overriden with the maxfeerate argument.
+ * RPCs. This can be overridden with the maxfeerate argument.
*/
constexpr static CAmount DEFAULT_MAX_RAW_TX_FEE{COIN / 10};
@@ -64,8 +58,8 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
entry.pushKV("blockhash", hashBlock.GetHex());
CBlockIndex* pindex = LookupBlockIndex(hashBlock);
if (pindex) {
- if (chainActive.Contains(pindex)) {
- entry.pushKV("confirmations", 1 + chainActive.Height() - pindex->nHeight);
+ if (::ChainActive().Contains(pindex)) {
+ entry.pushKV("confirmations", 1 + ::ChainActive().Height() - pindex->nHeight);
entry.pushKV("time", pindex->GetBlockTime());
entry.pushKV("blocktime", pindex->GetBlockTime());
}
@@ -184,7 +178,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
if (!blockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found");
}
- in_active_chain = chainActive.Contains(blockindex);
+ in_active_chain = ::ChainActive().Contains(blockindex);
}
bool f_txindex_ready = false;
@@ -202,7 +196,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
}
errmsg = "No such transaction found in the provided block";
} else if (!g_txindex) {
- errmsg = "No such mempool transaction. Use -txindex to enable blockchain transaction queries";
+ errmsg = "No such mempool transaction. Use -txindex or provide a block hash to enable blockchain transaction queries";
} else if (!f_txindex_ready) {
errmsg = "No such mempool transaction. Blockchain transactions are still in the process of being indexed";
} else {
@@ -274,7 +268,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
for (const auto& tx : setTxids) {
const Coin& coin = AccessByTxid(*pcoinsTip, tx);
if (!coin.IsSpent()) {
- pblockindex = chainActive[coin.nHeight];
+ pblockindex = ::ChainActive()[coin.nHeight];
break;
}
}
@@ -348,7 +342,7 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
LOCK(cs_main);
const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash());
- if (!pindex || !chainActive.Contains(pindex) || pindex->nTx == 0) {
+ if (!pindex || !::ChainActive().Contains(pindex) || pindex->nTx == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
}
@@ -432,14 +426,17 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
static UniValue decoderawtransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
- RPCHelpMan{"decoderawtransaction",
+ const RPCHelpMan help{"decoderawtransaction",
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction hex string"},
- {"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction\n"
- " If iswitness is not present, heuristic tests will be used in decoding"},
+ {"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
+ "If iswitness is not present, heuristic tests will be used in decoding.\n"
+ "If true, only witness deserialization will be tried.\n"
+ "If false, only non-witness deserialization will be tried.\n"
+ "This boolean should reflect whether the transaction has inputs\n"
+ "(e.g. fully valid, or on-chain transactions), if known by the caller."
+ },
},
RPCResult{
"{\n"
@@ -486,7 +483,11 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
HelpExampleCli("decoderawtransaction", "\"hexstring\"")
+ HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
@@ -572,7 +573,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
if (type.isStr() && type.get_str() != "scripthash") {
// P2SH cannot be wrapped in a P2SH. If this script is already a P2SH,
// don't return the address for a P2SH of the P2SH.
- r.pushKV("p2sh", EncodeDestination(CScriptID(script)));
+ r.pushKV("p2sh", EncodeDestination(ScriptHash(script)));
// P2SH and witness programs cannot be wrapped in P2WSH, if this script
// is a witness program, don't return addresses for a segwit programs.
if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") {
@@ -599,7 +600,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script));
}
ScriptPubKeyToUniv(segwitScr, sr, /* fIncludeHex */ true);
- sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr)));
+ sr.pushKV("p2sh-segwit", EncodeDestination(ScriptHash(segwitScr)));
r.pushKV("segwit", sr);
}
}
@@ -748,8 +749,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
"}\n"
},
RPCExamples{
- HelpExampleCli("signrawtransactionwithkey", "\"myhex\"")
- + HelpExampleRpc("signrawtransactionwithkey", "\"myhex\"")
+ HelpExampleCli("signrawtransactionwithkey", "\"myhex\" \"[\\\"key1\\\",\\\"key2\\\"]\"")
+ + HelpExampleRpc("signrawtransactionwithkey", "\"myhex\", \"[\\\"key1\\\",\\\"key2\\\"]\"")
},
}.ToString());
@@ -1423,19 +1424,20 @@ UniValue createpsbt(const JSONRPCRequest& request)
UniValue converttopsbt(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
- throw std::runtime_error(
- RPCHelpMan{"converttopsbt",
+ const RPCHelpMan help{"converttopsbt",
"\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n"
"createpsbt and walletcreatefundedpsbt should be used for new applications.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of a raw transaction"},
- {"permitsigdata", RPCArg::Type::BOOL, /* default */ "false", "If true, any signatures in the input will be discarded and conversion.\n"
+ {"permitsigdata", RPCArg::Type::BOOL, /* default */ "false", "If true, any signatures in the input will be discarded and conversion\n"
" will continue. If false, RPC will fail if any signatures are present."},
{"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
- " If iswitness is not present, heuristic tests will be used in decoding. If true, only witness deserializaion\n"
- " will be tried. If false, only non-witness deserialization will be tried. Only has an effect if\n"
- " permitsigdata is true."},
+ "If iswitness is not present, heuristic tests will be used in decoding.\n"
+ "If true, only witness deserialization will be tried.\n"
+ "If false, only non-witness deserialization will be tried.\n"
+ "This boolean should reflect whether the transaction has inputs\n"
+ "(e.g. fully valid, or on-chain transactions), if known by the caller."
+ },
},
RPCResult{
" \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
@@ -1446,8 +1448,11 @@ UniValue converttopsbt(const JSONRPCRequest& request)
"\nConvert the transaction to a PSBT\n"
+ HelpExampleCli("converttopsbt", "\"rawtransaction\"")
},
- }.ToString());
+ };
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VBOOL}, true);
@@ -1456,8 +1461,8 @@ UniValue converttopsbt(const JSONRPCRequest& request)
bool permitsigdata = request.params[1].isNull() ? false : request.params[1].get_bool();
bool witness_specified = !request.params[2].isNull();
bool iswitness = witness_specified ? request.params[2].get_bool() : false;
- bool try_witness = permitsigdata ? (witness_specified ? iswitness : true) : false;
- bool try_no_witness = permitsigdata ? (witness_specified ? !iswitness : true) : true;
+ const bool try_witness = witness_specified ? iswitness : true;
+ const bool try_no_witness = witness_specified ? !iswitness : true;
if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 8af491977c..9c4cdc3a90 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -7,7 +7,6 @@
#include <coins.h>
#include <core_io.h>
-#include <interfaces/chain.h>
#include <key_io.h>
#include <keystore.h>
#include <policy/policy.h>
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 9df4070cbb..ca17d379bc 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -7,11 +7,9 @@
#include <fs.h>
#include <key_io.h>
-#include <random.h>
#include <rpc/util.h>
#include <shutdown.h>
#include <sync.h>
-#include <ui_interface.h>
#include <util/strencodings.h>
#include <util/system.h>
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 1a87c9f935..4642cf16b1 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -4,10 +4,13 @@
#include <key_io.h>
#include <keystore.h>
+#include <outputtype.h>
#include <rpc/util.h>
#include <tinyformat.h>
#include <util/strencodings.h>
+#include <tuple>
+
InitInterfaces* g_rpc_interfaces = nullptr;
void RPCTypeCheck(const UniValue& params,
@@ -148,8 +151,8 @@ CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in)
return vchPubKey;
}
-// Creates a multisig redeemscript from a given list of public keys and number required.
-CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys)
+// Creates a multisig address from a given list of public keys, number of signatures required, and the address type
+CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, CKeyStore& keystore, CScript& script_out)
{
// Gather public keys
if (required < 1) {
@@ -162,13 +165,24 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey
throw JSONRPCError(RPC_INVALID_PARAMETER, "Number of keys involved in the multisignature address creation > 16\nReduce the number");
}
- CScript result = GetScriptForMultisig(required, pubkeys);
+ script_out = GetScriptForMultisig(required, pubkeys);
- if (result.size() > MAX_SCRIPT_ELEMENT_SIZE) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", result.size(), MAX_SCRIPT_ELEMENT_SIZE)));
+ if (script_out.size() > MAX_SCRIPT_ELEMENT_SIZE) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", script_out.size(), MAX_SCRIPT_ELEMENT_SIZE)));
}
- return result;
+ // Check if any keys are uncompressed. If so, the type is legacy
+ for (const CPubKey& pk : pubkeys) {
+ if (!pk.IsCompressed()) {
+ type = OutputType::LEGACY;
+ break;
+ }
+ }
+
+ // Make the address
+ CTxDestination dest = AddAndGetDestinationForScript(keystore, script_out, type);
+
+ return dest;
}
class DescribeAddressVisitor : public boost::static_visitor<UniValue>
@@ -181,7 +195,7 @@ public:
return UniValue(UniValue::VOBJ);
}
- UniValue operator()(const CKeyID& keyID) const
+ UniValue operator()(const PKHash& keyID) const
{
UniValue obj(UniValue::VOBJ);
obj.pushKV("isscript", false);
@@ -189,7 +203,7 @@ public:
return obj;
}
- UniValue operator()(const CScriptID& scriptID) const
+ UniValue operator()(const ScriptHash& scriptID) const
{
UniValue obj(UniValue::VOBJ);
obj.pushKV("isscript", true);
@@ -654,7 +668,7 @@ std::string RPCArg::ToString(const bool oneline) const
assert(false);
}
-std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
+static std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
{
if (value.isNum()) {
return {0, value.get_int64()};
@@ -667,3 +681,19 @@ std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
}
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified as end or as [begin,end]");
}
+
+std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value)
+{
+ int64_t low, high;
+ std::tie(low, high) = ParseRange(value);
+ if (low < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0");
+ }
+ if ((high >> 31) != 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high");
+ }
+ if (high >= low + 1000000) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large");
+ }
+ return {low, high};
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index b5b5789253..0eb2fef5c3 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -6,6 +6,7 @@
#define BITCOIN_RPC_UTIL_H
#include <node/transaction.h>
+#include <outputtype.h>
#include <pubkey.h>
#include <rpc/protocol.h>
#include <script/standard.h>
@@ -70,7 +71,7 @@ extern std::string HelpExampleRpc(const std::string& methodname, const std::stri
CPubKey HexToPubKey(const std::string& hex_in);
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
-CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys);
+CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, CKeyStore& keystore, CScript& script_out);
UniValue DescribeAddress(const CTxDestination& dest);
@@ -81,7 +82,7 @@ RPCErrorCode RPCErrorFromTransactionError(TransactionError terr);
UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string = "");
//! Parse a JSON range specified as int64, or [int64, int64]
-std::pair<int64_t, int64_t> ParseRange(const UniValue& value);
+std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value);
struct RPCArg {
enum class Type {
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index a333d4d4ac..50119ba184 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -164,6 +164,9 @@ struct PubkeyProvider
/** Get the descriptor string form including private data (if available in arg). */
virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
+
+ /** Derive a private key, if private data is available in arg. */
+ virtual bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const = 0;
};
class OriginPubkeyProvider final : public PubkeyProvider
@@ -195,6 +198,10 @@ public:
ret = "[" + OriginString() + "]" + std::move(sub);
return true;
}
+ bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
+ {
+ return m_provider->GetPrivKey(pos, arg, key);
+ }
};
/** An object representing a parsed constant public key in a descriptor. */
@@ -222,6 +229,10 @@ public:
ret = EncodeSecret(key);
return true;
}
+ bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
+ {
+ return arg.GetKey(m_pubkey.GetID(), key);
+ }
};
enum class DeriveType {
@@ -266,14 +277,9 @@ public:
{
if (key) {
if (IsHardened()) {
- CExtKey extkey;
- if (!GetExtKey(arg, extkey)) return false;
- for (auto entry : m_path) {
- extkey.Derive(extkey, entry);
- }
- if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
- if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
- *key = extkey.Neuter().pubkey;
+ CKey priv_key;
+ if (!GetPrivKey(pos, arg, priv_key)) return false;
+ *key = priv_key.GetPubKey();
} else {
// TODO: optimize by caching
CExtPubKey extkey = m_extkey;
@@ -312,6 +318,18 @@ public:
}
return true;
}
+ bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
+ {
+ CExtKey extkey;
+ if (!GetExtKey(arg, extkey)) return false;
+ for (auto entry : m_path) {
+ extkey.Derive(extkey, entry);
+ }
+ if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
+ if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
+ key = extkey.key;
+ return true;
+ }
};
/** Base class for all Descriptor implementations. */
@@ -462,6 +480,20 @@ public:
Span<const unsigned char> span = MakeSpan(cache);
return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &span, output_scripts, out, nullptr) && span.size() == 0;
}
+
+ void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const final
+ {
+ for (const auto& p : m_pubkey_args) {
+ CKey key;
+ if (!p->GetPrivKey(pos, provider, key)) continue;
+ out.keys.emplace(key.GetPubKey().GetID(), key);
+ }
+ if (m_script_arg) {
+ FlatSigningProvider subprovider;
+ m_script_arg->ExpandPrivate(pos, provider, subprovider);
+ out = Merge(out, subprovider);
+ }
+ }
};
/** Construct a vector with one element, which is moved into it. */
@@ -514,7 +546,7 @@ protected:
{
CKeyID id = keys[0].GetID();
out.pubkeys.emplace(id, keys[0]);
- return Singleton(GetScriptForDestination(id));
+ return Singleton(GetScriptForDestination(PKHash(id)));
}
public:
PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "pkh") {}
@@ -544,12 +576,12 @@ protected:
CKeyID id = keys[0].GetID();
out.pubkeys.emplace(id, keys[0]);
ret.emplace_back(GetScriptForRawPubKey(keys[0])); // P2PK
- ret.emplace_back(GetScriptForDestination(id)); // P2PKH
+ ret.emplace_back(GetScriptForDestination(PKHash(id))); // P2PKH
if (keys[0].IsCompressed()) {
CScript p2wpkh = GetScriptForDestination(WitnessV0KeyHash(id));
out.scripts.emplace(CScriptID(p2wpkh), p2wpkh);
ret.emplace_back(p2wpkh);
- ret.emplace_back(GetScriptForDestination(CScriptID(p2wpkh))); // P2SH-P2WPKH
+ ret.emplace_back(GetScriptForDestination(ScriptHash(p2wpkh))); // P2SH-P2WPKH
}
return ret;
}
@@ -572,7 +604,7 @@ public:
class SHDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(CScriptID(*script))); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(ScriptHash(*script))); }
public:
SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {}
};
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index 907a102284..af7ae229ca 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -60,6 +60,14 @@ struct Descriptor {
* out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider).
*/
virtual bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
+
+ /** Expand the private key for a descriptor at a specified position, if possible.
+ *
+ * pos: the position at which to expand the descriptor. If IsRange() is false, this is ignored.
+ * provider: the provider to query for the private keys.
+ * out: any private keys available for the specified pos will be placed here.
+ */
+ virtual void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const = 0;
};
/** Parse a descriptor string. Included private keys are put in out.
diff --git a/src/script/script.cpp b/src/script/script.cpp
index 982aa241e7..0666a385d1 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -5,7 +5,6 @@
#include <script/script.h>
-#include <tinyformat.h>
#include <util/strencodings.h>
const char* GetOpName(opcodetype opcode)
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index 94005cf6f3..eaf5363bd7 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -5,7 +5,6 @@
#include <script/sigcache.h>
-#include <memusage.h>
#include <pubkey.h>
#include <random.h>
#include <uint256.h>
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 31bfd04b0f..b7d6cd925c 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -8,8 +8,6 @@
#include <crypto/sha256.h>
#include <pubkey.h>
#include <script/script.h>
-#include <util/system.h>
-#include <util/strencodings.h>
typedef std::vector<unsigned char> valtype;
@@ -19,6 +17,10 @@ unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
+ScriptHash::ScriptHash(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
+
+PKHash::PKHash(const CPubKey& pubkey) : uint160(pubkey.GetID()) {}
+
WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
{
CSHA256().Write(in.data(), in.size()).Finalize(begin());
@@ -162,17 +164,17 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
if (!pubKey.IsValid())
return false;
- addressRet = pubKey.GetID();
+ addressRet = PKHash(pubKey);
return true;
}
else if (whichType == TX_PUBKEYHASH)
{
- addressRet = CKeyID(uint160(vSolutions[0]));
+ addressRet = PKHash(uint160(vSolutions[0]));
return true;
}
else if (whichType == TX_SCRIPTHASH)
{
- addressRet = CScriptID(uint160(vSolutions[0]));
+ addressRet = ScriptHash(uint160(vSolutions[0]));
return true;
} else if (whichType == TX_WITNESS_V0_KEYHASH) {
WitnessV0KeyHash hash;
@@ -217,7 +219,7 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::
if (!pubKey.IsValid())
continue;
- CTxDestination address = pubKey.GetID();
+ CTxDestination address = PKHash(pubKey);
addressRet.push_back(address);
}
@@ -250,13 +252,13 @@ public:
return false;
}
- bool operator()(const CKeyID &keyID) const {
+ bool operator()(const PKHash &keyID) const {
script->clear();
*script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
return true;
}
- bool operator()(const CScriptID &scriptID) const {
+ bool operator()(const ScriptHash &scriptID) const {
script->clear();
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
return true;
diff --git a/src/script/standard.h b/src/script/standard.h
index f16068c413..e45e2d92cc 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -73,6 +73,22 @@ public:
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
};
+struct PKHash : public uint160
+{
+ PKHash() : uint160() {}
+ explicit PKHash(const uint160& hash) : uint160(hash) {}
+ explicit PKHash(const CPubKey& pubkey);
+ using uint160::uint160;
+};
+
+struct ScriptHash : public uint160
+{
+ ScriptHash() : uint160() {}
+ explicit ScriptHash(const uint160& hash) : uint160(hash) {}
+ explicit ScriptHash(const CScript& script);
+ using uint160::uint160;
+};
+
struct WitnessV0ScriptHash : public uint256
{
WitnessV0ScriptHash() : uint256() {}
@@ -113,14 +129,14 @@ struct WitnessUnknown
/**
* A txout script template with a specific destination. It is either:
* * CNoDestination: no destination set
- * * CKeyID: TX_PUBKEYHASH destination (P2PKH)
- * * CScriptID: TX_SCRIPTHASH destination (P2SH)
+ * * PKHash: TX_PUBKEYHASH destination (P2PKH)
+ * * ScriptHash: TX_SCRIPTHASH destination (P2SH)
* * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH)
* * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH)
* * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???)
* A CTxDestination is the internal data type encoded in a bitcoin address
*/
-typedef boost::variant<CNoDestination, CKeyID, CScriptID, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
+typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
/** Check whether a CTxDestination is a CNoDestination. */
bool IsValidDestination(const CTxDestination& dest);
diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore
index 87fea161ba..55d325aeef 100644
--- a/src/secp256k1/.gitignore
+++ b/src/secp256k1/.gitignore
@@ -1,5 +1,6 @@
bench_inv
bench_ecdh
+bench_ecmult
bench_sign
bench_verify
bench_schnorr_verify
diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml
index 2439529242..74f658f4d1 100644
--- a/src/secp256k1/.travis.yml
+++ b/src/secp256k1/.travis.yml
@@ -1,5 +1,5 @@
language: c
-sudo: false
+os: linux
addons:
apt:
packages: libgmp-dev
@@ -11,7 +11,7 @@ cache:
- src/java/guava/
env:
global:
- - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no
+ - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no JNI=no
- GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar
matrix:
- SCALAR=32bit RECOVERY=yes
@@ -29,7 +29,7 @@ env:
- BUILD=distcheck
- EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
- EXTRAFLAGS=CFLAGS=-O0
- - BUILD=check-java ECDH=yes EXPERIMENTAL=yes
+ - BUILD=check-java JNI=yes ECDH=yes EXPERIMENTAL=yes
matrix:
fast_finish: true
include:
@@ -65,5 +65,4 @@ before_script: ./autogen.sh
script:
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
- - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
-os: linux
+ - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am
index c071fbe275..9e5b7dcce0 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -42,6 +42,8 @@ noinst_HEADERS += src/field_5x52_asm_impl.h
noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h
noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h
noinst_HEADERS += src/util.h
+noinst_HEADERS += src/scratch.h
+noinst_HEADERS += src/scratch_impl.h
noinst_HEADERS += src/testrand.h
noinst_HEADERS += src/testrand_impl.h
noinst_HEADERS += src/hash.h
@@ -79,7 +81,7 @@ libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES)
noinst_PROGRAMS =
if USE_BENCHMARK
-noinst_PROGRAMS += bench_verify bench_sign bench_internal
+noinst_PROGRAMS += bench_verify bench_sign bench_internal bench_ecmult
bench_verify_SOURCES = src/bench_verify.c
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_sign_SOURCES = src/bench_sign.c
@@ -87,6 +89,9 @@ bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_internal_SOURCES = src/bench_internal.c
bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB)
bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
+bench_ecmult_SOURCES = src/bench_ecmult.c
+bench_ecmult_LDADD = $(SECP_LIBS) $(COMMON_LIB)
+bench_ecmult_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
endif
TESTS =
@@ -109,7 +114,7 @@ exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src $(SECP_INCLUDE
if !ENABLE_COVERAGE
exhaustive_tests_CPPFLAGS += -DVERIFY
endif
-exhaustive_tests_LDADD = $(SECP_LIBS)
+exhaustive_tests_LDADD = $(SECP_LIBS) $(COMMON_LIB)
exhaustive_tests_LDFLAGS = -static
TESTS += exhaustive_tests
endif
@@ -146,7 +151,6 @@ endif
if USE_ECMULT_STATIC_PRECOMPUTATION
CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)
-CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function
gen_context_OBJECTS = gen_context.o
gen_context_BIN = gen_context$(BUILD_EXEEXT)
@@ -154,11 +158,12 @@ gen_%.o: src/gen_%.c
$(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@
$(gen_context_BIN): $(gen_context_OBJECTS)
- $(CC_FOR_BUILD) $^ -o $@
+ $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $^ -o $@
$(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h
$(tests_OBJECTS): src/ecmult_static_context.h
$(bench_internal_OBJECTS): src/ecmult_static_context.h
+$(bench_ecmult_OBJECTS): src/ecmult_static_context.h
src/ecmult_static_context.h: $(gen_context_BIN)
./$(gen_context_BIN)
diff --git a/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
index 1fc3627614..cdc78d87d4 100644
--- a/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
+++ b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
@@ -1,5 +1,5 @@
# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
+# https://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
# ===========================================================================
#
# SYNOPSIS
@@ -44,7 +44,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 10
+#serial 14
AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR])
AC_DEFUN([AX_JNI_INCLUDE_DIR],[
@@ -66,9 +66,17 @@ else
fi
case "$host_os" in
- darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
- _JINC="$_JTOPDIR/Headers";;
- *) _JINC="$_JTOPDIR/include";;
+ darwin*) # Apple Java headers are inside the Xcode bundle.
+ macos_version=$(sw_vers -productVersion | sed -n -e 's/^@<:@0-9@:>@*.\(@<:@0-9@:>@*\).@<:@0-9@:>@*/\1/p')
+ if @<:@ "$macos_version" -gt "7" @:>@; then
+ _JTOPDIR="$(xcrun --show-sdk-path)/System/Library/Frameworks/JavaVM.framework"
+ _JINC="$_JTOPDIR/Headers"
+ else
+ _JTOPDIR="/System/Library/Frameworks/JavaVM.framework"
+ _JINC="$_JTOPDIR/Headers"
+ fi
+ ;;
+ *) _JINC="$_JTOPDIR/include";;
esac
_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR])
_AS_ECHO_LOG([_JINC=$_JINC])
@@ -76,30 +84,27 @@ _AS_ECHO_LOG([_JINC=$_JINC])
# On Mac OS X 10.6.4, jni.h is a symlink:
# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h
# -> ../../CurrentJDK/Headers/jni.h.
-
AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path,
[
-if test -f "$_JINC/jni.h"; then
- ac_cv_jni_header_path="$_JINC"
- JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
-else
- _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
- if test -f "$_JTOPDIR/include/jni.h"; then
- ac_cv_jni_header_path="$_JTOPDIR/include"
+ if test -f "$_JINC/jni.h"; then
+ ac_cv_jni_header_path="$_JINC"
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
else
- ac_cv_jni_header_path=none
+ _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
+ if test -f "$_JTOPDIR/include/jni.h"; then
+ ac_cv_jni_header_path="$_JTOPDIR/include"
+ JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
+ else
+ ac_cv_jni_header_path=none
+ fi
fi
-fi
])
-
-
# get the likely subdirectories for system specific java includes
case "$host_os" in
bsdi*) _JNI_INC_SUBDIRS="bsdos";;
-darwin*) _JNI_INC_SUBDIRS="darwin";;
freebsd*) _JNI_INC_SUBDIRS="freebsd";;
+darwin*) _JNI_INC_SUBDIRS="darwin";;
linux*) _JNI_INC_SUBDIRS="linux genunix";;
osf*) _JNI_INC_SUBDIRS="alpha";;
solaris*) _JNI_INC_SUBDIRS="solaris";;
@@ -112,9 +117,9 @@ if test "x$ac_cv_jni_header_path" != "xnone"; then
# add any subdirectories that are present
for JINCSUBDIR in $_JNI_INC_SUBDIRS
do
- if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
- JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
- fi
+ if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
+ JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
+ fi
done
fi
])
diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
index b74acb8c13..3b3975cbdd 100644
--- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4
+++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
@@ -48,7 +48,6 @@ if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
EC_KEY_free(eckey);
ECDSA_SIG *sig_openssl;
sig_openssl = ECDSA_SIG_new();
- (void)sig_openssl->r;
ECDSA_SIG_free(sig_openssl);
]])],[has_openssl_ec=yes],[has_openssl_ec=no])
AC_MSG_RESULT([$has_openssl_ec])
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index e5fcbcb4ed..3b7a328c8a 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -85,9 +85,9 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
])
AC_ARG_ENABLE(benchmark,
- AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is no)]),
+ AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is yes)]),
[use_benchmark=$enableval],
- [use_benchmark=no])
+ [use_benchmark=yes])
AC_ARG_ENABLE(coverage,
AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]),
@@ -135,9 +135,9 @@ AC_ARG_ENABLE(module_recovery,
[enable_module_recovery=no])
AC_ARG_ENABLE(jni,
- AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]),
+ AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is no)]),
[use_jni=$enableval],
- [use_jni=auto])
+ [use_jni=no])
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto])
@@ -153,12 +153,6 @@ AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto]
AC_CHECK_TYPES([__int128])
-AC_MSG_CHECKING([for __builtin_expect])
-AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])],
- [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_EXPECT,1,[Define this symbol if __builtin_expect is available]) ],
- [ AC_MSG_RESULT([no])
- ])
-
if test x"$enable_coverage" = x"yes"; then
AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code])
CFLAGS="$CFLAGS -O0 --coverage"
@@ -168,27 +162,54 @@ else
fi
if test x"$use_ecmult_static_precomputation" != x"no"; then
+ # Temporarily switch to an environment for the native compiler
save_cross_compiling=$cross_compiling
cross_compiling=no
- TEMP_CC="$CC"
+ SAVE_CC="$CC"
CC="$CC_FOR_BUILD"
- AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}])
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS_FOR_BUILD"
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS_FOR_BUILD"
+ SAVE_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS_FOR_BUILD"
+
+ warn_CFLAGS_FOR_BUILD="-Wall -Wextra -Wno-unused-function"
+ saved_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $warn_CFLAGS_FOR_BUILD"
+ AC_MSG_CHECKING([if native ${CC_FOR_BUILD} supports ${warn_CFLAGS_FOR_BUILD}])
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
+ [ AC_MSG_RESULT([yes]) ],
+ [ AC_MSG_RESULT([no])
+ CFLAGS="$saved_CFLAGS"
+ ])
+
+ AC_MSG_CHECKING([for working native compiler: ${CC_FOR_BUILD}])
AC_RUN_IFELSE(
- [AC_LANG_PROGRAM([], [return 0])],
+ [AC_LANG_PROGRAM([], [])],
[working_native_cc=yes],
[working_native_cc=no],[dnl])
- CC="$TEMP_CC"
+
+ CFLAGS_FOR_BUILD="$CFLAGS"
+
+ # Restore the environment
cross_compiling=$save_cross_compiling
+ CC="$SAVE_CC"
+ CFLAGS="$SAVE_CFLAGS"
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
if test x"$working_native_cc" = x"no"; then
+ AC_MSG_RESULT([no])
set_precomp=no
+ m4_define([please_set_for_build], [Please set CC_FOR_BUILD, CFLAGS_FOR_BUILD, CPPFLAGS_FOR_BUILD, and/or LDFLAGS_FOR_BUILD.])
if test x"$use_ecmult_static_precomputation" = x"yes"; then
- AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
+ AC_MSG_ERROR([native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
else
- AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
+ AC_MSG_WARN([Disabling statically generated ecmult table because the native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
fi
else
- AC_MSG_RESULT([ok])
+ AC_MSG_RESULT([yes])
set_precomp=yes
fi
else
@@ -441,17 +462,6 @@ if test x"$use_external_asm" = x"yes"; then
AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
fi
-AC_MSG_NOTICE([Using static precomputation: $set_precomp])
-AC_MSG_NOTICE([Using assembly optimizations: $set_asm])
-AC_MSG_NOTICE([Using field implementation: $set_field])
-AC_MSG_NOTICE([Using bignum implementation: $set_bignum])
-AC_MSG_NOTICE([Using scalar implementation: $set_scalar])
-AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism])
-AC_MSG_NOTICE([Building for coverage analysis: $enable_coverage])
-AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
-AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery])
-AC_MSG_NOTICE([Using jni: $use_jni])
-
if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([******])
AC_MSG_NOTICE([WARNING: experimental build])
@@ -481,7 +491,7 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
-AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
+AM_CONDITIONAL([USE_JNI], [test x"$use_jni" = x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
@@ -491,3 +501,24 @@ unset PKG_CONFIG_PATH
PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP"
AC_OUTPUT
+
+echo
+echo "Build Options:"
+echo " with endomorphism = $use_endomorphism"
+echo " with ecmult precomp = $set_precomp"
+echo " with jni = $use_jni"
+echo " with benchmarks = $use_benchmark"
+echo " with coverage = $enable_coverage"
+echo " module ecdh = $enable_module_ecdh"
+echo " module recovery = $enable_module_recovery"
+echo
+echo " asm = $set_asm"
+echo " bignum = $set_bignum"
+echo " field = $set_field"
+echo " scalar = $set_scalar"
+echo
+echo " CC = $CC"
+echo " CFLAGS = $CFLAGS"
+echo " CPPFLAGS = $CPPFLAGS"
+echo " LDFLAGS = $LDFLAGS"
+echo
diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h
index 3e9c098d19..43af09c330 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -42,6 +42,19 @@ extern "C" {
*/
typedef struct secp256k1_context_struct secp256k1_context;
+/** Opaque data structure that holds rewriteable "scratch space"
+ *
+ * The purpose of this structure is to replace dynamic memory allocations,
+ * because we target architectures where this may not be available. It is
+ * essentially a resizable (within specified parameters) block of bytes,
+ * which is initially created either by memory allocation or TODO as a pointer
+ * into some fixed rewritable space.
+ *
+ * Unlike the context object, this cannot safely be shared between threads
+ * without additional synchronization logic.
+ */
+typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space;
+
/** Opaque data structure that holds a parsed and valid public key.
*
* The exact representation of data inside is implementation defined and not
@@ -166,6 +179,13 @@ typedef int (*secp256k1_nonce_function)(
#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06
#define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07
+/** A simple secp256k1 context object with no precomputed tables. These are useful for
+ * type serialization/parsing functions which require a context object to maintain
+ * API consistency, but currently do not require expensive precomputations or dynamic
+ * allocations.
+ */
+SECP256K1_API extern const secp256k1_context *secp256k1_context_no_precomp;
+
/** Create a secp256k1 context object.
*
* Returns: a newly created context object.
@@ -243,6 +263,26 @@ SECP256K1_API void secp256k1_context_set_error_callback(
const void* data
) SECP256K1_ARG_NONNULL(1);
+/** Create a secp256k1 scratch space object.
+ *
+ * Returns: a newly created scratch space.
+ * Args: ctx: an existing context object (cannot be NULL)
+ * In: max_size: maximum amount of memory to allocate
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create(
+ const secp256k1_context* ctx,
+ size_t max_size
+) SECP256K1_ARG_NONNULL(1);
+
+/** Destroy a secp256k1 scratch space.
+ *
+ * The pointer may not be used afterwards.
+ * Args: scratch: space to destroy
+ */
+SECP256K1_API void secp256k1_scratch_space_destroy(
+ secp256k1_scratch_space* scratch
+);
+
/** Parse a variable-length public key into the pubkey object.
*
* Returns: 1 if the public key was fully valid.
@@ -498,7 +538,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
*
* Returns: 1 always
* Args: ctx: pointer to a context object
- * In/Out: pubkey: pointer to the public key to be negated (cannot be NULL)
+ * In/Out: seckey: pointer to the 32-byte private key to be negated (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_negate(
const secp256k1_context* ctx,
@@ -575,7 +615,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Updates the context randomization to protect against side-channel leakage.
- * Returns: 1: randomization successfully updated
+ * Returns: 1: randomization successfully updated or nothing to randomize
* 0: error
* Args: ctx: pointer to a context object (cannot be NULL)
* In: seed32: pointer to a 32-byte random seed (NULL resets to initial state)
@@ -590,6 +630,11 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
* that it does not affect function results, but shields against attacks which
* rely on any input-dependent behaviour.
*
+ * This function has currently an effect only on contexts initialized for signing
+ * because randomization is currently used only for signing. However, this is not
+ * guaranteed and may change in the future. It is safe to call this function on
+ * contexts not initialized for signing; then it will have no effect and return 1.
+ *
* You should call this after secp256k1_context_create or
* secp256k1_context_clone, and may call this repeatedly afterwards.
*/
diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h
index 88492dc1a4..df5fde235c 100644
--- a/src/secp256k1/include/secp256k1_ecdh.h
+++ b/src/secp256k1/include/secp256k1_ecdh.h
@@ -7,21 +7,45 @@
extern "C" {
#endif
+/** A pointer to a function that applies hash function to a point
+ *
+ * Returns: 1 if a point was successfully hashed. 0 will cause ecdh to fail
+ * Out: output: pointer to an array to be filled by the function
+ * In: x: pointer to a 32-byte x coordinate
+ * y: pointer to a 32-byte y coordinate
+ * data: Arbitrary data pointer that is passed through
+ */
+typedef int (*secp256k1_ecdh_hash_function)(
+ unsigned char *output,
+ const unsigned char *x,
+ const unsigned char *y,
+ void *data
+);
+
+/** An implementation of SHA256 hash function that applies to compressed public key. */
+SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256;
+
+/** A default ecdh hash function (currently equal to secp256k1_ecdh_hash_function_sha256). */
+SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default;
+
/** Compute an EC Diffie-Hellman secret in constant time
* Returns: 1: exponentiation was successful
* 0: scalar was invalid (zero or overflow)
* Args: ctx: pointer to a context object (cannot be NULL)
- * Out: result: a 32-byte array which will be populated by an ECDH
- * secret computed from the point and scalar
+ * Out: output: pointer to an array to be filled by the function
* In: pubkey: a pointer to a secp256k1_pubkey containing an
* initialized public key
* privkey: a 32-byte scalar with which to multiply the point
+ * hashfp: pointer to a hash function. If NULL, secp256k1_ecdh_hash_function_sha256 is used
+ * data: Arbitrary data pointer that is passed through
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh(
const secp256k1_context* ctx,
- unsigned char *result,
+ unsigned char *output,
const secp256k1_pubkey *pubkey,
- const unsigned char *privkey
+ const unsigned char *privkey,
+ secp256k1_ecdh_hash_function hashfp,
+ void *data
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
#ifdef __cplusplus
diff --git a/src/secp256k1/libsecp256k1.pc.in b/src/secp256k1/libsecp256k1.pc.in
index a0d006f113..694e98eef5 100644
--- a/src/secp256k1/libsecp256k1.pc.in
+++ b/src/secp256k1/libsecp256k1.pc.in
@@ -8,6 +8,6 @@ Description: Optimized C library for EC operations on curve secp256k1
URL: https://github.com/bitcoin-core/secp256k1
Version: @PACKAGE_VERSION@
Cflags: -I${includedir}
-Libs.private: @SECP_LIBS@
Libs: -L${libdir} -lsecp256k1
+Libs.private: @SECP_LIBS@
diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h
index d5ebe01301..5b59783f68 100644
--- a/src/secp256k1/src/bench.h
+++ b/src/secp256k1/src/bench.h
@@ -8,6 +8,7 @@
#define SECP256K1_BENCH_H
#include <stdio.h>
+#include <string.h>
#include <math.h>
#include "sys/time.h"
@@ -63,4 +64,19 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v
printf("us\n");
}
+int have_flag(int argc, char** argv, char *flag) {
+ char** argm = argv + argc;
+ argv++;
+ if (argv == argm) {
+ return 1;
+ }
+ while (argv != NULL && argv != argm) {
+ if (strcmp(*argv, flag) == 0) {
+ return 1;
+ }
+ argv++;
+ }
+ return 0;
+}
+
#endif /* SECP256K1_BENCH_H */
diff --git a/src/secp256k1/src/bench_ecdh.c b/src/secp256k1/src/bench_ecdh.c
index cde5e2dbb4..c1dd5a6ac9 100644
--- a/src/secp256k1/src/bench_ecdh.c
+++ b/src/secp256k1/src/bench_ecdh.c
@@ -15,11 +15,11 @@ typedef struct {
secp256k1_context *ctx;
secp256k1_pubkey point;
unsigned char scalar[32];
-} bench_ecdh_t;
+} bench_ecdh_data;
static void bench_ecdh_setup(void* arg) {
int i;
- bench_ecdh_t *data = (bench_ecdh_t*)arg;
+ bench_ecdh_data *data = (bench_ecdh_data*)arg;
const unsigned char point[] = {
0x03,
0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06,
@@ -39,15 +39,15 @@ static void bench_ecdh_setup(void* arg) {
static void bench_ecdh(void* arg) {
int i;
unsigned char res[32];
- bench_ecdh_t *data = (bench_ecdh_t*)arg;
+ bench_ecdh_data *data = (bench_ecdh_data*)arg;
for (i = 0; i < 20000; i++) {
- CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar) == 1);
+ CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar, NULL, NULL) == 1);
}
}
int main(void) {
- bench_ecdh_t data;
+ bench_ecdh_data data;
run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, 20000);
return 0;
diff --git a/src/secp256k1/src/bench_ecmult.c b/src/secp256k1/src/bench_ecmult.c
new file mode 100644
index 0000000000..6d0ed1f436
--- /dev/null
+++ b/src/secp256k1/src/bench_ecmult.c
@@ -0,0 +1,207 @@
+/**********************************************************************
+ * Copyright (c) 2017 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+#include <stdio.h>
+
+#include "include/secp256k1.h"
+
+#include "util.h"
+#include "hash_impl.h"
+#include "num_impl.h"
+#include "field_impl.h"
+#include "group_impl.h"
+#include "scalar_impl.h"
+#include "ecmult_impl.h"
+#include "bench.h"
+#include "secp256k1.c"
+
+#define POINTS 32768
+#define ITERS 10000
+
+typedef struct {
+ /* Setup once in advance */
+ secp256k1_context* ctx;
+ secp256k1_scratch_space* scratch;
+ secp256k1_scalar* scalars;
+ secp256k1_ge* pubkeys;
+ secp256k1_scalar* seckeys;
+ secp256k1_gej* expected_output;
+ secp256k1_ecmult_multi_func ecmult_multi;
+
+ /* Changes per test */
+ size_t count;
+ int includes_g;
+
+ /* Changes per test iteration */
+ size_t offset1;
+ size_t offset2;
+
+ /* Test output. */
+ secp256k1_gej* output;
+} bench_data;
+
+static int bench_callback(secp256k1_scalar* sc, secp256k1_ge* ge, size_t idx, void* arg) {
+ bench_data* data = (bench_data*)arg;
+ if (data->includes_g) ++idx;
+ if (idx == 0) {
+ *sc = data->scalars[data->offset1];
+ *ge = secp256k1_ge_const_g;
+ } else {
+ *sc = data->scalars[(data->offset1 + idx) % POINTS];
+ *ge = data->pubkeys[(data->offset2 + idx - 1) % POINTS];
+ }
+ return 1;
+}
+
+static void bench_ecmult(void* arg) {
+ bench_data* data = (bench_data*)arg;
+
+ size_t count = data->count;
+ int includes_g = data->includes_g;
+ size_t iters = 1 + ITERS / count;
+ size_t iter;
+
+ for (iter = 0; iter < iters; ++iter) {
+ data->ecmult_multi(&data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g);
+ data->offset1 = (data->offset1 + count) % POINTS;
+ data->offset2 = (data->offset2 + count - 1) % POINTS;
+ }
+}
+
+static void bench_ecmult_setup(void* arg) {
+ bench_data* data = (bench_data*)arg;
+ data->offset1 = (data->count * 0x537b7f6f + 0x8f66a481) % POINTS;
+ data->offset2 = (data->count * 0x7f6f537b + 0x6a1a8f49) % POINTS;
+}
+
+static void bench_ecmult_teardown(void* arg) {
+ bench_data* data = (bench_data*)arg;
+ size_t iters = 1 + ITERS / data->count;
+ size_t iter;
+ /* Verify the results in teardown, to avoid doing comparisons while benchmarking. */
+ for (iter = 0; iter < iters; ++iter) {
+ secp256k1_gej tmp;
+ secp256k1_gej_add_var(&tmp, &data->output[iter], &data->expected_output[iter], NULL);
+ CHECK(secp256k1_gej_is_infinity(&tmp));
+ }
+}
+
+static void generate_scalar(uint32_t num, secp256k1_scalar* scalar) {
+ secp256k1_sha256 sha256;
+ unsigned char c[11] = {'e', 'c', 'm', 'u', 'l', 't', 0, 0, 0, 0};
+ unsigned char buf[32];
+ int overflow = 0;
+ c[6] = num;
+ c[7] = num >> 8;
+ c[8] = num >> 16;
+ c[9] = num >> 24;
+ secp256k1_sha256_initialize(&sha256);
+ secp256k1_sha256_write(&sha256, c, sizeof(c));
+ secp256k1_sha256_finalize(&sha256, buf);
+ secp256k1_scalar_set_b32(scalar, buf, &overflow);
+ CHECK(!overflow);
+}
+
+static void run_test(bench_data* data, size_t count, int includes_g) {
+ char str[32];
+ static const secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
+ size_t iters = 1 + ITERS / count;
+ size_t iter;
+
+ data->count = count;
+ data->includes_g = includes_g;
+
+ /* Compute (the negation of) the expected results directly. */
+ data->offset1 = (data->count * 0x537b7f6f + 0x8f66a481) % POINTS;
+ data->offset2 = (data->count * 0x7f6f537b + 0x6a1a8f49) % POINTS;
+ for (iter = 0; iter < iters; ++iter) {
+ secp256k1_scalar tmp;
+ secp256k1_scalar total = data->scalars[(data->offset1++) % POINTS];
+ size_t i = 0;
+ for (i = 0; i + 1 < count; ++i) {
+ secp256k1_scalar_mul(&tmp, &data->seckeys[(data->offset2++) % POINTS], &data->scalars[(data->offset1++) % POINTS]);
+ secp256k1_scalar_add(&total, &total, &tmp);
+ }
+ secp256k1_scalar_negate(&total, &total);
+ secp256k1_ecmult(&data->ctx->ecmult_ctx, &data->expected_output[iter], NULL, &zero, &total);
+ }
+
+ /* Run the benchmark. */
+ sprintf(str, includes_g ? "ecmult_%ig" : "ecmult_%i", (int)count);
+ run_benchmark(str, bench_ecmult, bench_ecmult_setup, bench_ecmult_teardown, data, 10, count * (1 + ITERS / count));
+}
+
+int main(int argc, char **argv) {
+ bench_data data;
+ int i, p;
+ secp256k1_gej* pubkeys_gej;
+ size_t scratch_size;
+
+ data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16;
+ data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size);
+ data.ecmult_multi = secp256k1_ecmult_multi_var;
+
+ if (argc > 1) {
+ if(have_flag(argc, argv, "pippenger_wnaf")) {
+ printf("Using pippenger_wnaf:\n");
+ data.ecmult_multi = secp256k1_ecmult_pippenger_batch_single;
+ } else if(have_flag(argc, argv, "strauss_wnaf")) {
+ printf("Using strauss_wnaf:\n");
+ data.ecmult_multi = secp256k1_ecmult_strauss_batch_single;
+ } else if(have_flag(argc, argv, "simple")) {
+ printf("Using simple algorithm:\n");
+ data.ecmult_multi = secp256k1_ecmult_multi_var;
+ secp256k1_scratch_space_destroy(data.scratch);
+ data.scratch = NULL;
+ } else {
+ fprintf(stderr, "%s: unrecognized argument '%s'.\n", argv[0], argv[1]);
+ fprintf(stderr, "Use 'pippenger_wnaf', 'strauss_wnaf', 'simple' or no argument to benchmark a combined algorithm.\n");
+ return 1;
+ }
+ }
+
+ /* Allocate stuff */
+ data.scalars = malloc(sizeof(secp256k1_scalar) * POINTS);
+ data.seckeys = malloc(sizeof(secp256k1_scalar) * POINTS);
+ data.pubkeys = malloc(sizeof(secp256k1_ge) * POINTS);
+ data.expected_output = malloc(sizeof(secp256k1_gej) * (ITERS + 1));
+ data.output = malloc(sizeof(secp256k1_gej) * (ITERS + 1));
+
+ /* Generate a set of scalars, and private/public keypairs. */
+ pubkeys_gej = malloc(sizeof(secp256k1_gej) * POINTS);
+ secp256k1_gej_set_ge(&pubkeys_gej[0], &secp256k1_ge_const_g);
+ secp256k1_scalar_set_int(&data.seckeys[0], 1);
+ for (i = 0; i < POINTS; ++i) {
+ generate_scalar(i, &data.scalars[i]);
+ if (i) {
+ secp256k1_gej_double_var(&pubkeys_gej[i], &pubkeys_gej[i - 1], NULL);
+ secp256k1_scalar_add(&data.seckeys[i], &data.seckeys[i - 1], &data.seckeys[i - 1]);
+ }
+ }
+ secp256k1_ge_set_all_gej_var(data.pubkeys, pubkeys_gej, POINTS);
+ free(pubkeys_gej);
+
+ for (i = 1; i <= 8; ++i) {
+ run_test(&data, i, 1);
+ }
+
+ for (p = 0; p <= 11; ++p) {
+ for (i = 9; i <= 16; ++i) {
+ run_test(&data, i << p, 1);
+ }
+ }
+ secp256k1_context_destroy(data.ctx);
+ if (data.scratch != NULL) {
+ secp256k1_scratch_space_destroy(data.scratch);
+ }
+ free(data.scalars);
+ free(data.pubkeys);
+ free(data.seckeys);
+ free(data.output);
+ free(data.expected_output);
+
+ return(0);
+}
diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c
index 0809f77bda..9071724331 100644
--- a/src/secp256k1/src/bench_internal.c
+++ b/src/secp256k1/src/bench_internal.c
@@ -25,10 +25,10 @@ typedef struct {
secp256k1_gej gej_x, gej_y;
unsigned char data[64];
int wnaf[256];
-} bench_inv_t;
+} bench_inv;
void bench_setup(void* arg) {
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
static const unsigned char init_x[32] = {
0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13,
@@ -58,7 +58,7 @@ void bench_setup(void* arg) {
void bench_scalar_add(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 2000000; i++) {
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
@@ -67,7 +67,7 @@ void bench_scalar_add(void* arg) {
void bench_scalar_negate(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 2000000; i++) {
secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x);
@@ -76,7 +76,7 @@ void bench_scalar_negate(void* arg) {
void bench_scalar_sqr(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x);
@@ -85,7 +85,7 @@ void bench_scalar_sqr(void* arg) {
void bench_scalar_mul(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y);
@@ -95,7 +95,7 @@ void bench_scalar_mul(void* arg) {
#ifdef USE_ENDOMORPHISM
void bench_scalar_split(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_scalar l, r;
@@ -107,7 +107,7 @@ void bench_scalar_split(void* arg) {
void bench_scalar_inverse(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 2000; i++) {
secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x);
@@ -117,7 +117,7 @@ void bench_scalar_inverse(void* arg) {
void bench_scalar_inverse_var(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 2000; i++) {
secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x);
@@ -127,7 +127,7 @@ void bench_scalar_inverse_var(void* arg) {
void bench_field_normalize(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 2000000; i++) {
secp256k1_fe_normalize(&data->fe_x);
@@ -136,7 +136,7 @@ void bench_field_normalize(void* arg) {
void bench_field_normalize_weak(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 2000000; i++) {
secp256k1_fe_normalize_weak(&data->fe_x);
@@ -145,7 +145,7 @@ void bench_field_normalize_weak(void* arg) {
void bench_field_mul(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y);
@@ -154,7 +154,7 @@ void bench_field_mul(void* arg) {
void bench_field_sqr(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_fe_sqr(&data->fe_x, &data->fe_x);
@@ -163,7 +163,7 @@ void bench_field_sqr(void* arg) {
void bench_field_inverse(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_fe_inv(&data->fe_x, &data->fe_x);
@@ -173,7 +173,7 @@ void bench_field_inverse(void* arg) {
void bench_field_inverse_var(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_fe_inv_var(&data->fe_x, &data->fe_x);
@@ -183,17 +183,19 @@ void bench_field_inverse_var(void* arg) {
void bench_field_sqrt(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
+ secp256k1_fe t;
for (i = 0; i < 20000; i++) {
- secp256k1_fe_sqrt(&data->fe_x, &data->fe_x);
+ t = data->fe_x;
+ secp256k1_fe_sqrt(&data->fe_x, &t);
secp256k1_fe_add(&data->fe_x, &data->fe_y);
}
}
void bench_group_double_var(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL);
@@ -202,7 +204,7 @@ void bench_group_double_var(void* arg) {
void bench_group_add_var(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL);
@@ -211,7 +213,7 @@ void bench_group_add_var(void* arg) {
void bench_group_add_affine(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y);
@@ -220,7 +222,7 @@ void bench_group_add_affine(void* arg) {
void bench_group_add_affine_var(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 200000; i++) {
secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL);
@@ -229,7 +231,7 @@ void bench_group_add_affine_var(void* arg) {
void bench_group_jacobi_var(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_gej_has_quad_y_var(&data->gej_x);
@@ -238,7 +240,7 @@ void bench_group_jacobi_var(void* arg) {
void bench_ecmult_wnaf(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A);
@@ -248,10 +250,10 @@ void bench_ecmult_wnaf(void* arg) {
void bench_wnaf_const(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) {
- secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A);
+ secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A, 256);
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
}
@@ -259,8 +261,8 @@ void bench_wnaf_const(void* arg) {
void bench_sha256(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
- secp256k1_sha256_t sha;
+ bench_inv *data = (bench_inv*)arg;
+ secp256k1_sha256 sha;
for (i = 0; i < 20000; i++) {
secp256k1_sha256_initialize(&sha);
@@ -271,8 +273,8 @@ void bench_sha256(void* arg) {
void bench_hmac_sha256(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
- secp256k1_hmac_sha256_t hmac;
+ bench_inv *data = (bench_inv*)arg;
+ secp256k1_hmac_sha256 hmac;
for (i = 0; i < 20000; i++) {
secp256k1_hmac_sha256_initialize(&hmac, data->data, 32);
@@ -283,8 +285,8 @@ void bench_hmac_sha256(void* arg) {
void bench_rfc6979_hmac_sha256(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
- secp256k1_rfc6979_hmac_sha256_t rng;
+ bench_inv *data = (bench_inv*)arg;
+ secp256k1_rfc6979_hmac_sha256 rng;
for (i = 0; i < 20000; i++) {
secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64);
@@ -311,7 +313,7 @@ void bench_context_sign(void* arg) {
#ifndef USE_NUM_NONE
void bench_num_jacobi(void* arg) {
int i;
- bench_inv_t *data = (bench_inv_t*)arg;
+ bench_inv *data = (bench_inv*)arg;
secp256k1_num nx, norder;
secp256k1_scalar_get_num(&nx, &data->scalar_x);
@@ -324,23 +326,8 @@ void bench_num_jacobi(void* arg) {
}
#endif
-int have_flag(int argc, char** argv, char *flag) {
- char** argm = argv + argc;
- argv++;
- if (argv == argm) {
- return 1;
- }
- while (argv != NULL && argv != argm) {
- if (strcmp(*argv, flag) == 0) {
- return 1;
- }
- argv++;
- }
- return 0;
-}
-
int main(int argc, char **argv) {
- bench_inv_t data;
+ bench_inv data;
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, 2000000);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, 2000000);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, 200000);
diff --git a/src/secp256k1/src/bench_recover.c b/src/secp256k1/src/bench_recover.c
index 6489378cc6..b806eed94e 100644
--- a/src/secp256k1/src/bench_recover.c
+++ b/src/secp256k1/src/bench_recover.c
@@ -13,11 +13,11 @@ typedef struct {
secp256k1_context *ctx;
unsigned char msg[32];
unsigned char sig[64];
-} bench_recover_t;
+} bench_recover_data;
void bench_recover(void* arg) {
int i;
- bench_recover_t *data = (bench_recover_t*)arg;
+ bench_recover_data *data = (bench_recover_data*)arg;
secp256k1_pubkey pubkey;
unsigned char pubkeyc[33];
@@ -38,7 +38,7 @@ void bench_recover(void* arg) {
void bench_recover_setup(void* arg) {
int i;
- bench_recover_t *data = (bench_recover_t*)arg;
+ bench_recover_data *data = (bench_recover_data*)arg;
for (i = 0; i < 32; i++) {
data->msg[i] = 1 + i;
@@ -49,7 +49,7 @@ void bench_recover_setup(void* arg) {
}
int main(void) {
- bench_recover_t data;
+ bench_recover_data data;
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
diff --git a/src/secp256k1/src/bench_sign.c b/src/secp256k1/src/bench_sign.c
index ed7224d757..544b43963c 100644
--- a/src/secp256k1/src/bench_sign.c
+++ b/src/secp256k1/src/bench_sign.c
@@ -12,11 +12,11 @@ typedef struct {
secp256k1_context* ctx;
unsigned char msg[32];
unsigned char key[32];
-} bench_sign_t;
+} bench_sign;
static void bench_sign_setup(void* arg) {
int i;
- bench_sign_t *data = (bench_sign_t*)arg;
+ bench_sign *data = (bench_sign*)arg;
for (i = 0; i < 32; i++) {
data->msg[i] = i + 1;
@@ -26,9 +26,9 @@ static void bench_sign_setup(void* arg) {
}
}
-static void bench_sign(void* arg) {
+static void bench_sign_run(void* arg) {
int i;
- bench_sign_t *data = (bench_sign_t*)arg;
+ bench_sign *data = (bench_sign*)arg;
unsigned char sig[74];
for (i = 0; i < 20000; i++) {
@@ -45,11 +45,11 @@ static void bench_sign(void* arg) {
}
int main(void) {
- bench_sign_t data;
+ bench_sign data;
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
- run_benchmark("ecdsa_sign", bench_sign, bench_sign_setup, NULL, &data, 10, 20000);
+ run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, 20000);
secp256k1_context_destroy(data.ctx);
return 0;
diff --git a/src/secp256k1/src/eckey_impl.h b/src/secp256k1/src/eckey_impl.h
index 1ab9a68ec0..7c5b789325 100644
--- a/src/secp256k1/src/eckey_impl.h
+++ b/src/secp256k1/src/eckey_impl.h
@@ -18,7 +18,7 @@ static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char
if (size == 33 && (pub[0] == SECP256K1_TAG_PUBKEY_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_ODD)) {
secp256k1_fe x;
return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD);
- } else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) {
+ } else if (size == 65 && (pub[0] == SECP256K1_TAG_PUBKEY_UNCOMPRESSED || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) {
secp256k1_fe x, y;
if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) {
return 0;
diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h
index 6d44aba60b..3d75a960f4 100644
--- a/src/secp256k1/src/ecmult.h
+++ b/src/secp256k1/src/ecmult.h
@@ -1,5 +1,5 @@
/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
@@ -9,6 +9,8 @@
#include "num.h"
#include "group.h"
+#include "scalar.h"
+#include "scratch.h"
typedef struct {
/* For accelerating the computation of a*P + b*G: */
@@ -28,4 +30,19 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx
/** Double multiply: R = na*A + ng*G */
static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng);
+typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data);
+
+/**
+ * Multi-multiply: R = inp_g_sc * G + sum_i ni * Ai.
+ * Chooses the right algorithm for a given number of points and scratch space
+ * size. Resets and overwrites the given scratch space. If the points do not
+ * fit in the scratch space the algorithm is repeatedly run with batches of
+ * points. If no scratch space is given then a simple algorithm is used that
+ * simply multiplies the points with the corresponding scalars and adds them up.
+ * Returns: 1 on success (including when inp_g_sc is NULL and n is 0)
+ * 0 if there is not enough scratch space for a single point or
+ * callback returns 0
+ */
+static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n);
+
#endif /* SECP256K1_ECMULT_H */
diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h
index 72bf7d7582..d4804b8b68 100644
--- a/src/secp256k1/src/ecmult_const.h
+++ b/src/secp256k1/src/ecmult_const.h
@@ -10,6 +10,8 @@
#include "scalar.h"
#include "group.h"
-static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q);
+/* Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus
+ * one because we internally sometimes add 2 to the number during the WNAF conversion. */
+static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits);
#endif /* SECP256K1_ECMULT_CONST_H */
diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h
index 7d7a172b7b..8411752eb0 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -12,13 +12,6 @@
#include "ecmult_const.h"
#include "ecmult_impl.h"
-#ifdef USE_ENDOMORPHISM
- #define WNAF_BITS 128
-#else
- #define WNAF_BITS 256
-#endif
-#define WNAF_SIZE(w) ((WNAF_BITS + (w) - 1) / (w))
-
/* This is like `ECMULT_TABLE_GET_GE` but is constant time */
#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \
int m; \
@@ -55,7 +48,7 @@
*
* Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335
*/
-static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
+static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size) {
int global_sign;
int skew = 0;
int word = 0;
@@ -74,9 +67,14 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
* and we'd lose any performance benefit. Instead, we use a technique from
* Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even)
* or 2 (for odd) to the number we are encoding, returning a skew value indicating
- * this, and having the caller compensate after doing the multiplication. */
-
- /* Negative numbers will be negated to keep their bit representation below the maximum width */
+ * this, and having the caller compensate after doing the multiplication.
+ *
+ * In fact, we _do_ want to negate numbers to minimize their bit-lengths (and in
+ * particular, to ensure that the outputs from the endomorphism-split fit into
+ * 128 bits). If we negate, the parity of our number flips, inverting which of
+ * {1, 2} we want to add to the scalar when ensuring that it's odd. Further
+ * complicating things, -1 interacts badly with `secp256k1_scalar_cadd_bit` and
+ * we need to special-case it in this logic. */
flip = secp256k1_scalar_is_high(&s);
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
bit = flip ^ !secp256k1_scalar_is_even(&s);
@@ -95,7 +93,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
/* 4 */
u_last = secp256k1_scalar_shr_int(&s, w);
- while (word * w < WNAF_BITS) {
+ while (word * w < size) {
int sign;
int even;
@@ -115,37 +113,44 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
wnaf[word] = u * global_sign;
VERIFY_CHECK(secp256k1_scalar_is_zero(&s));
- VERIFY_CHECK(word == WNAF_SIZE(w));
+ VERIFY_CHECK(word == WNAF_SIZE_BITS(size, w));
return skew;
}
-
-static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar) {
+static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar, int size) {
secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)];
secp256k1_ge tmpa;
secp256k1_fe Z;
int skew_1;
- int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
#ifdef USE_ENDOMORPHISM
secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)];
int skew_lam;
secp256k1_scalar q_1, q_lam;
#endif
+ int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
int i;
secp256k1_scalar sc = *scalar;
/* build wnaf representation for q. */
+ int rsize = size;
+#ifdef USE_ENDOMORPHISM
+ if (size > 128) {
+ rsize = 128;
+ /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */
+ secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc);
+ skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1, 128);
+ skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1, 128);
+ } else
+#endif
+ {
+ skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1, size);
#ifdef USE_ENDOMORPHISM
- /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */
- secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc);
- skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1);
- skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1);
-#else
- skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1);
+ skew_lam = 0;
#endif
+ }
/* Calculate odd multiples of a.
* All multiples are brought to the same Z 'denominator', which is stored
@@ -159,26 +164,30 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
secp256k1_fe_normalize_weak(&pre_a[i].y);
}
#ifdef USE_ENDOMORPHISM
- for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
- secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
+ if (size > 128) {
+ for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
+ secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
+ }
}
#endif
/* first loop iteration (separated out so we can directly set r, rather
* than having it start at infinity, get doubled several times, then have
* its new value added to it) */
- i = wnaf_1[WNAF_SIZE(WINDOW_A - 1)];
+ i = wnaf_1[WNAF_SIZE_BITS(rsize, WINDOW_A - 1)];
VERIFY_CHECK(i != 0);
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A);
secp256k1_gej_set_ge(r, &tmpa);
#ifdef USE_ENDOMORPHISM
- i = wnaf_lam[WNAF_SIZE(WINDOW_A - 1)];
- VERIFY_CHECK(i != 0);
- ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A);
- secp256k1_gej_add_ge(r, r, &tmpa);
+ if (size > 128) {
+ i = wnaf_lam[WNAF_SIZE_BITS(rsize, WINDOW_A - 1)];
+ VERIFY_CHECK(i != 0);
+ ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A);
+ secp256k1_gej_add_ge(r, r, &tmpa);
+ }
#endif
/* remaining loop iterations */
- for (i = WNAF_SIZE(WINDOW_A - 1) - 1; i >= 0; i--) {
+ for (i = WNAF_SIZE_BITS(rsize, WINDOW_A - 1) - 1; i >= 0; i--) {
int n;
int j;
for (j = 0; j < WINDOW_A - 1; ++j) {
@@ -190,10 +199,12 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
VERIFY_CHECK(n != 0);
secp256k1_gej_add_ge(r, r, &tmpa);
#ifdef USE_ENDOMORPHISM
- n = wnaf_lam[i];
- ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
- VERIFY_CHECK(n != 0);
- secp256k1_gej_add_ge(r, r, &tmpa);
+ if (size > 128) {
+ n = wnaf_lam[i];
+ ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
+ VERIFY_CHECK(n != 0);
+ secp256k1_gej_add_ge(r, r, &tmpa);
+ }
#endif
}
@@ -213,14 +224,18 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
secp256k1_ge_set_gej(&correction, &tmpj);
secp256k1_ge_to_storage(&correction_1_stor, a);
#ifdef USE_ENDOMORPHISM
- secp256k1_ge_to_storage(&correction_lam_stor, a);
+ if (size > 128) {
+ secp256k1_ge_to_storage(&correction_lam_stor, a);
+ }
#endif
secp256k1_ge_to_storage(&a2_stor, &correction);
/* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */
secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2);
#ifdef USE_ENDOMORPHISM
- secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2);
+ if (size > 128) {
+ secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2);
+ }
#endif
/* Apply the correction */
@@ -229,10 +244,12 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
secp256k1_gej_add_ge(r, r, &correction);
#ifdef USE_ENDOMORPHISM
- secp256k1_ge_from_storage(&correction, &correction_lam_stor);
- secp256k1_ge_neg(&correction, &correction);
- secp256k1_ge_mul_lambda(&correction, &correction);
- secp256k1_gej_add_ge(r, r, &correction);
+ if (size > 128) {
+ secp256k1_ge_from_storage(&correction, &correction_lam_stor);
+ secp256k1_ge_neg(&correction, &correction);
+ secp256k1_ge_mul_lambda(&correction, &correction);
+ secp256k1_gej_add_ge(r, r, &correction);
+ }
#endif
}
}
diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h
index 9615b932dd..d64505dc00 100644
--- a/src/secp256k1/src/ecmult_gen_impl.h
+++ b/src/secp256k1/src/ecmult_gen_impl.h
@@ -77,7 +77,7 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL);
}
}
- secp256k1_ge_set_all_gej_var(prec, precj, 1024, cb);
+ secp256k1_ge_set_all_gej_var(prec, precj, 1024);
}
for (j = 0; j < 64; j++) {
for (i = 0; i < 16; i++) {
@@ -161,7 +161,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
secp256k1_gej gb;
secp256k1_fe s;
unsigned char nonce32[32];
- secp256k1_rfc6979_hmac_sha256_t rng;
+ secp256k1_rfc6979_hmac_sha256 rng;
int retry;
unsigned char keydata[64] = {0};
if (seed32 == NULL) {
diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h
index 93d3794cb4..1986914a4f 100644
--- a/src/secp256k1/src/ecmult_impl.h
+++ b/src/secp256k1/src/ecmult_impl.h
@@ -1,13 +1,14 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/*****************************************************************************
+ * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php. *
+ *****************************************************************************/
#ifndef SECP256K1_ECMULT_IMPL_H
#define SECP256K1_ECMULT_IMPL_H
#include <string.h>
+#include <stdint.h>
#include "group.h"
#include "scalar.h"
@@ -41,9 +42,36 @@
#endif
#endif
+#ifdef USE_ENDOMORPHISM
+ #define WNAF_BITS 128
+#else
+ #define WNAF_BITS 256
+#endif
+#define WNAF_SIZE_BITS(bits, w) (((bits) + (w) - 1) / (w))
+#define WNAF_SIZE(w) WNAF_SIZE_BITS(WNAF_BITS, w)
+
/** The number of entries a table with precomputed multiples needs to have. */
#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2))
+/* The number of objects allocated on the scratch space for ecmult_multi algorithms */
+#define PIPPENGER_SCRATCH_OBJECTS 6
+#define STRAUSS_SCRATCH_OBJECTS 6
+
+#define PIPPENGER_MAX_BUCKET_WINDOW 12
+
+/* Minimum number of points for which pippenger_wnaf is faster than strauss wnaf */
+#ifdef USE_ENDOMORPHISM
+ #define ECMULT_PIPPENGER_THRESHOLD 88
+#else
+ #define ECMULT_PIPPENGER_THRESHOLD 160
+#endif
+
+#ifdef USE_ENDOMORPHISM
+ #define ECMULT_MAX_POINTS_PER_BATCH 5000000
+#else
+ #define ECMULT_MAX_POINTS_PER_BATCH 10000000
+#endif
+
/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain
* the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will
* contain prej[0].z / a.z. The other zr[i] values = prej[i].z / prej[i-1].z.
@@ -109,24 +137,135 @@ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *p
secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr);
}
-static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge_storage *pre, const secp256k1_gej *a, const secp256k1_callback *cb) {
- secp256k1_gej *prej = (secp256k1_gej*)checked_malloc(cb, sizeof(secp256k1_gej) * n);
- secp256k1_ge *prea = (secp256k1_ge*)checked_malloc(cb, sizeof(secp256k1_ge) * n);
- secp256k1_fe *zr = (secp256k1_fe*)checked_malloc(cb, sizeof(secp256k1_fe) * n);
+static void secp256k1_ecmult_odd_multiples_table_storage_var(const int n, secp256k1_ge_storage *pre, const secp256k1_gej *a) {
+ secp256k1_gej d;
+ secp256k1_ge d_ge, p_ge;
+ secp256k1_gej pj;
+ secp256k1_fe zi;
+ secp256k1_fe zr;
+ secp256k1_fe dx_over_dz_squared;
int i;
- /* Compute the odd multiples in Jacobian form. */
- secp256k1_ecmult_odd_multiples_table(n, prej, zr, a);
- /* Convert them in batch to affine coordinates. */
- secp256k1_ge_set_table_gej_var(prea, prej, zr, n);
- /* Convert them to compact storage form. */
- for (i = 0; i < n; i++) {
- secp256k1_ge_to_storage(&pre[i], &prea[i]);
+ VERIFY_CHECK(!a->infinity);
+
+ secp256k1_gej_double_var(&d, a, NULL);
+
+ /* First, we perform all the additions in an isomorphic curve obtained by multiplying
+ * all `z` coordinates by 1/`d.z`. In these coordinates `d` is affine so we can use
+ * `secp256k1_gej_add_ge_var` to perform the additions. For each addition, we store
+ * the resulting y-coordinate and the z-ratio, since we only have enough memory to
+ * store two field elements. These are sufficient to efficiently undo the isomorphism
+ * and recompute all the `x`s.
+ */
+ d_ge.x = d.x;
+ d_ge.y = d.y;
+ d_ge.infinity = 0;
+
+ secp256k1_ge_set_gej_zinv(&p_ge, a, &d.z);
+ pj.x = p_ge.x;
+ pj.y = p_ge.y;
+ pj.z = a->z;
+ pj.infinity = 0;
+
+ for (i = 0; i < (n - 1); i++) {
+ secp256k1_fe_normalize_var(&pj.y);
+ secp256k1_fe_to_storage(&pre[i].y, &pj.y);
+ secp256k1_gej_add_ge_var(&pj, &pj, &d_ge, &zr);
+ secp256k1_fe_normalize_var(&zr);
+ secp256k1_fe_to_storage(&pre[i].x, &zr);
}
- free(prea);
- free(prej);
- free(zr);
+ /* Invert d.z in the same batch, preserving pj.z so we can extract 1/d.z */
+ secp256k1_fe_mul(&zi, &pj.z, &d.z);
+ secp256k1_fe_inv_var(&zi, &zi);
+
+ /* Directly set `pre[n - 1]` to `pj`, saving the inverted z-coordinate so
+ * that we can combine it with the saved z-ratios to compute the other zs
+ * without any more inversions. */
+ secp256k1_ge_set_gej_zinv(&p_ge, &pj, &zi);
+ secp256k1_ge_to_storage(&pre[n - 1], &p_ge);
+
+ /* Compute the actual x-coordinate of D, which will be needed below. */
+ secp256k1_fe_mul(&d.z, &zi, &pj.z); /* d.z = 1/d.z */
+ secp256k1_fe_sqr(&dx_over_dz_squared, &d.z);
+ secp256k1_fe_mul(&dx_over_dz_squared, &dx_over_dz_squared, &d.x);
+
+ /* Going into the second loop, we have set `pre[n-1]` to its final affine
+ * form, but still need to set `pre[i]` for `i` in 0 through `n-2`. We
+ * have `zi = (p.z * d.z)^-1`, where
+ *
+ * `p.z` is the z-coordinate of the point on the isomorphic curve
+ * which was ultimately assigned to `pre[n-1]`.
+ * `d.z` is the multiplier that must be applied to all z-coordinates
+ * to move from our isomorphic curve back to secp256k1; so the
+ * product `p.z * d.z` is the z-coordinate of the secp256k1
+ * point assigned to `pre[n-1]`.
+ *
+ * All subsequent inverse-z-coordinates can be obtained by multiplying this
+ * factor by successive z-ratios, which is much more efficient than directly
+ * computing each one.
+ *
+ * Importantly, these inverse-zs will be coordinates of points on secp256k1,
+ * while our other stored values come from computations on the isomorphic
+ * curve. So in the below loop, we will take care not to actually use `zi`
+ * or any derived values until we're back on secp256k1.
+ */
+ i = n - 1;
+ while (i > 0) {
+ secp256k1_fe zi2, zi3;
+ const secp256k1_fe *rzr;
+ i--;
+
+ secp256k1_ge_from_storage(&p_ge, &pre[i]);
+
+ /* For each remaining point, we extract the z-ratio from the stored
+ * x-coordinate, compute its z^-1 from that, and compute the full
+ * point from that. */
+ rzr = &p_ge.x;
+ secp256k1_fe_mul(&zi, &zi, rzr);
+ secp256k1_fe_sqr(&zi2, &zi);
+ secp256k1_fe_mul(&zi3, &zi2, &zi);
+ /* To compute the actual x-coordinate, we use the stored z ratio and
+ * y-coordinate, which we obtained from `secp256k1_gej_add_ge_var`
+ * in the loop above, as well as the inverse of the square of its
+ * z-coordinate. We store the latter in the `zi2` variable, which is
+ * computed iteratively starting from the overall Z inverse then
+ * multiplying by each z-ratio in turn.
+ *
+ * Denoting the z-ratio as `rzr`, we observe that it is equal to `h`
+ * from the inside of the above `gej_add_ge_var` call. This satisfies
+ *
+ * rzr = d_x * z^2 - x * d_z^2
+ *
+ * where (`d_x`, `d_z`) are Jacobian coordinates of `D` and `(x, z)`
+ * are Jacobian coordinates of our desired point -- except both are on
+ * the isomorphic curve that we were using when we called `gej_add_ge_var`.
+ * To get back to secp256k1, we must multiply both `z`s by `d_z`, or
+ * equivalently divide both `x`s by `d_z^2`. Our equation then becomes
+ *
+ * rzr = d_x * z^2 / d_z^2 - x
+ *
+ * (The left-hand-side, being a ratio of z-coordinates, is unaffected
+ * by the isomorphism.)
+ *
+ * Rearranging to solve for `x`, we have
+ *
+ * x = d_x * z^2 / d_z^2 - rzr
+ *
+ * But what we actually want is the affine coordinate `X = x/z^2`,
+ * which will satisfy
+ *
+ * X = d_x / d_z^2 - rzr / z^2
+ * = dx_over_dz_squared - rzr * zi2
+ */
+ secp256k1_fe_mul(&p_ge.x, rzr, &zi2);
+ secp256k1_fe_negate(&p_ge.x, &p_ge.x, 1);
+ secp256k1_fe_add(&p_ge.x, &dx_over_dz_squared);
+ /* y is stored_y/z^3, as we expect */
+ secp256k1_fe_mul(&p_ge.y, &p_ge.y, &zi3);
+ /* Store */
+ secp256k1_ge_to_storage(&pre[i], &p_ge);
+ }
}
/** The following two macro retrieves a particular odd multiple from a table
@@ -138,7 +277,8 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge
if ((n) > 0) { \
*(r) = (pre)[((n)-1)/2]; \
} else { \
- secp256k1_ge_neg((r), &(pre)[(-(n)-1)/2]); \
+ *(r) = (pre)[(-(n)-1)/2]; \
+ secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \
} \
} while(0)
@@ -150,7 +290,7 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge
secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \
} else { \
secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \
- secp256k1_ge_neg((r), (r)); \
+ secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \
} \
} while(0)
@@ -174,7 +314,7 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G));
/* precompute the tables with odd multiples */
- secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj, cb);
+ secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj);
#ifdef USE_ENDOMORPHISM
{
@@ -188,7 +328,7 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
for (i = 0; i < 128; i++) {
secp256k1_gej_double_var(&g_128j, &g_128j, NULL);
}
- secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j, cb);
+ secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j);
}
#endif
}
@@ -283,50 +423,78 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a,
return last_set_bit + 1;
}
-static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) {
- secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)];
- secp256k1_ge tmpa;
- secp256k1_fe Z;
+struct secp256k1_strauss_point_state {
#ifdef USE_ENDOMORPHISM
- secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
secp256k1_scalar na_1, na_lam;
- /* Splitted G factors. */
- secp256k1_scalar ng_1, ng_128;
int wnaf_na_1[130];
int wnaf_na_lam[130];
int bits_na_1;
int bits_na_lam;
- int wnaf_ng_1[129];
- int bits_ng_1;
- int wnaf_ng_128[129];
- int bits_ng_128;
#else
int wnaf_na[256];
int bits_na;
+#endif
+ size_t input_pos;
+};
+
+struct secp256k1_strauss_state {
+ secp256k1_gej* prej;
+ secp256k1_fe* zr;
+ secp256k1_ge* pre_a;
+#ifdef USE_ENDOMORPHISM
+ secp256k1_ge* pre_a_lam;
+#endif
+ struct secp256k1_strauss_point_state* ps;
+};
+
+static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, const struct secp256k1_strauss_state *state, secp256k1_gej *r, int num, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) {
+ secp256k1_ge tmpa;
+ secp256k1_fe Z;
+#ifdef USE_ENDOMORPHISM
+ /* Splitted G factors. */
+ secp256k1_scalar ng_1, ng_128;
+ int wnaf_ng_1[129];
+ int bits_ng_1 = 0;
+ int wnaf_ng_128[129];
+ int bits_ng_128 = 0;
+#else
int wnaf_ng[256];
- int bits_ng;
+ int bits_ng = 0;
#endif
int i;
- int bits;
+ int bits = 0;
+ int np;
+ int no = 0;
+ for (np = 0; np < num; ++np) {
+ if (secp256k1_scalar_is_zero(&na[np]) || secp256k1_gej_is_infinity(&a[np])) {
+ continue;
+ }
+ state->ps[no].input_pos = np;
#ifdef USE_ENDOMORPHISM
- /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */
- secp256k1_scalar_split_lambda(&na_1, &na_lam, na);
-
- /* build wnaf representation for na_1 and na_lam. */
- bits_na_1 = secp256k1_ecmult_wnaf(wnaf_na_1, 130, &na_1, WINDOW_A);
- bits_na_lam = secp256k1_ecmult_wnaf(wnaf_na_lam, 130, &na_lam, WINDOW_A);
- VERIFY_CHECK(bits_na_1 <= 130);
- VERIFY_CHECK(bits_na_lam <= 130);
- bits = bits_na_1;
- if (bits_na_lam > bits) {
- bits = bits_na_lam;
- }
+ /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */
+ secp256k1_scalar_split_lambda(&state->ps[no].na_1, &state->ps[no].na_lam, &na[np]);
+
+ /* build wnaf representation for na_1 and na_lam. */
+ state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1, 130, &state->ps[no].na_1, WINDOW_A);
+ state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 130, &state->ps[no].na_lam, WINDOW_A);
+ VERIFY_CHECK(state->ps[no].bits_na_1 <= 130);
+ VERIFY_CHECK(state->ps[no].bits_na_lam <= 130);
+ if (state->ps[no].bits_na_1 > bits) {
+ bits = state->ps[no].bits_na_1;
+ }
+ if (state->ps[no].bits_na_lam > bits) {
+ bits = state->ps[no].bits_na_lam;
+ }
#else
- /* build wnaf representation for na. */
- bits_na = secp256k1_ecmult_wnaf(wnaf_na, 256, na, WINDOW_A);
- bits = bits_na;
+ /* build wnaf representation for na. */
+ state->ps[no].bits_na = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na, 256, &na[np], WINDOW_A);
+ if (state->ps[no].bits_na > bits) {
+ bits = state->ps[no].bits_na;
+ }
#endif
+ ++no;
+ }
/* Calculate odd multiples of a.
* All multiples are brought to the same Z 'denominator', which is stored
@@ -338,29 +506,51 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
* of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same
* isomorphism to efficiently add with a known Z inverse.
*/
- secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, a);
+ if (no > 0) {
+ /* Compute the odd multiples in Jacobian form. */
+ secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej, state->zr, &a[state->ps[0].input_pos]);
+ for (np = 1; np < no; ++np) {
+ secp256k1_gej tmp = a[state->ps[np].input_pos];
+#ifdef VERIFY
+ secp256k1_fe_normalize_var(&(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z));
+#endif
+ secp256k1_gej_rescale(&tmp, &(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z));
+ secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &tmp);
+ secp256k1_fe_mul(state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &(a[state->ps[np].input_pos].z));
+ }
+ /* Bring them to the same Z denominator. */
+ secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, &Z, state->prej, state->zr);
+ } else {
+ secp256k1_fe_set_int(&Z, 1);
+ }
#ifdef USE_ENDOMORPHISM
- for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
- secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
+ for (np = 0; np < no; ++np) {
+ for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
+ secp256k1_ge_mul_lambda(&state->pre_a_lam[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i]);
+ }
}
- /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */
- secp256k1_scalar_split_128(&ng_1, &ng_128, ng);
+ if (ng) {
+ /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */
+ secp256k1_scalar_split_128(&ng_1, &ng_128, ng);
- /* Build wnaf representation for ng_1 and ng_128 */
- bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G);
- bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G);
- if (bits_ng_1 > bits) {
- bits = bits_ng_1;
- }
- if (bits_ng_128 > bits) {
- bits = bits_ng_128;
+ /* Build wnaf representation for ng_1 and ng_128 */
+ bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G);
+ bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G);
+ if (bits_ng_1 > bits) {
+ bits = bits_ng_1;
+ }
+ if (bits_ng_128 > bits) {
+ bits = bits_ng_128;
+ }
}
#else
- bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G);
- if (bits_ng > bits) {
- bits = bits_ng;
+ if (ng) {
+ bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G);
+ if (bits_ng > bits) {
+ bits = bits_ng;
+ }
}
#endif
@@ -370,13 +560,15 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
int n;
secp256k1_gej_double_var(r, r, NULL);
#ifdef USE_ENDOMORPHISM
- if (i < bits_na_1 && (n = wnaf_na_1[i])) {
- ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A);
- secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
- }
- if (i < bits_na_lam && (n = wnaf_na_lam[i])) {
- ECMULT_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
- secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
+ for (np = 0; np < no; ++np) {
+ if (i < state->ps[np].bits_na_1 && (n = state->ps[np].wnaf_na_1[i])) {
+ ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
+ secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
+ }
+ if (i < state->ps[np].bits_na_lam && (n = state->ps[np].wnaf_na_lam[i])) {
+ ECMULT_TABLE_GET_GE(&tmpa, state->pre_a_lam + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
+ secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
+ }
}
if (i < bits_ng_1 && (n = wnaf_ng_1[i])) {
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G);
@@ -387,9 +579,11 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
}
#else
- if (i < bits_na && (n = wnaf_na[i])) {
- ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A);
- secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
+ for (np = 0; np < no; ++np) {
+ if (i < state->ps[np].bits_na && (n = state->ps[np].wnaf_na[i])) {
+ ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
+ secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
+ }
}
if (i < bits_ng && (n = wnaf_ng[i])) {
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G);
@@ -403,4 +597,585 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
}
}
+static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) {
+ secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)];
+ secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)];
+ secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)];
+ struct secp256k1_strauss_point_state ps[1];
+#ifdef USE_ENDOMORPHISM
+ secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
+#endif
+ struct secp256k1_strauss_state state;
+
+ state.prej = prej;
+ state.zr = zr;
+ state.pre_a = pre_a;
+#ifdef USE_ENDOMORPHISM
+ state.pre_a_lam = pre_a_lam;
+#endif
+ state.ps = ps;
+ secp256k1_ecmult_strauss_wnaf(ctx, &state, r, 1, a, na, ng);
+}
+
+static size_t secp256k1_strauss_scratch_size(size_t n_points) {
+#ifdef USE_ENDOMORPHISM
+ static const size_t point_size = (2 * sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
+#else
+ static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
+#endif
+ return n_points*point_size;
+}
+
+static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
+ secp256k1_gej* points;
+ secp256k1_scalar* scalars;
+ struct secp256k1_strauss_state state;
+ size_t i;
+
+ secp256k1_gej_set_infinity(r);
+ if (inp_g_sc == NULL && n_points == 0) {
+ return 1;
+ }
+
+ if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) {
+ return 0;
+ }
+ points = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_gej));
+ scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_scalar));
+ state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
+ state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
+#ifdef USE_ENDOMORPHISM
+ state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
+ state.pre_a_lam = state.pre_a + n_points * ECMULT_TABLE_SIZE(WINDOW_A);
+#else
+ state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
+#endif
+ state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
+
+ for (i = 0; i < n_points; i++) {
+ secp256k1_ge point;
+ if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) {
+ secp256k1_scratch_deallocate_frame(scratch);
+ return 0;
+ }
+ secp256k1_gej_set_ge(&points[i], &point);
+ }
+ secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc);
+ secp256k1_scratch_deallocate_frame(scratch);
+ return 1;
+}
+
+/* Wrapper for secp256k1_ecmult_multi_func interface */
+static int secp256k1_ecmult_strauss_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
+ return secp256k1_ecmult_strauss_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
+}
+
+static size_t secp256k1_strauss_max_points(secp256k1_scratch *scratch) {
+ return secp256k1_scratch_max_allocation(scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
+}
+
+/** Convert a number to WNAF notation.
+ * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val.
+ * It has the following guarantees:
+ * - each wnaf[i] is either 0 or an odd integer between -(1 << w) and (1 << w)
+ * - the number of words set is always WNAF_SIZE(w)
+ * - the returned skew is 0 or 1
+ */
+static int secp256k1_wnaf_fixed(int *wnaf, const secp256k1_scalar *s, int w) {
+ int skew = 0;
+ int pos;
+ int max_pos;
+ int last_w;
+ const secp256k1_scalar *work = s;
+
+ if (secp256k1_scalar_is_zero(s)) {
+ for (pos = 0; pos < WNAF_SIZE(w); pos++) {
+ wnaf[pos] = 0;
+ }
+ return 0;
+ }
+
+ if (secp256k1_scalar_is_even(s)) {
+ skew = 1;
+ }
+
+ wnaf[0] = secp256k1_scalar_get_bits_var(work, 0, w) + skew;
+ /* Compute last window size. Relevant when window size doesn't divide the
+ * number of bits in the scalar */
+ last_w = WNAF_BITS - (WNAF_SIZE(w) - 1) * w;
+
+ /* Store the position of the first nonzero word in max_pos to allow
+ * skipping leading zeros when calculating the wnaf. */
+ for (pos = WNAF_SIZE(w) - 1; pos > 0; pos--) {
+ int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w);
+ if(val != 0) {
+ break;
+ }
+ wnaf[pos] = 0;
+ }
+ max_pos = pos;
+ pos = 1;
+
+ while (pos <= max_pos) {
+ int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w);
+ if ((val & 1) == 0) {
+ wnaf[pos - 1] -= (1 << w);
+ wnaf[pos] = (val + 1);
+ } else {
+ wnaf[pos] = val;
+ }
+ /* Set a coefficient to zero if it is 1 or -1 and the proceeding digit
+ * is strictly negative or strictly positive respectively. Only change
+ * coefficients at previous positions because above code assumes that
+ * wnaf[pos - 1] is odd.
+ */
+ if (pos >= 2 && ((wnaf[pos - 1] == 1 && wnaf[pos - 2] < 0) || (wnaf[pos - 1] == -1 && wnaf[pos - 2] > 0))) {
+ if (wnaf[pos - 1] == 1) {
+ wnaf[pos - 2] += 1 << w;
+ } else {
+ wnaf[pos - 2] -= 1 << w;
+ }
+ wnaf[pos - 1] = 0;
+ }
+ ++pos;
+ }
+
+ return skew;
+}
+
+struct secp256k1_pippenger_point_state {
+ int skew_na;
+ size_t input_pos;
+};
+
+struct secp256k1_pippenger_state {
+ int *wnaf_na;
+ struct secp256k1_pippenger_point_state* ps;
+};
+
+/*
+ * pippenger_wnaf computes the result of a multi-point multiplication as
+ * follows: The scalars are brought into wnaf with n_wnaf elements each. Then
+ * for every i < n_wnaf, first each point is added to a "bucket" corresponding
+ * to the point's wnaf[i]. Second, the buckets are added together such that
+ * r += 1*bucket[0] + 3*bucket[1] + 5*bucket[2] + ...
+ */
+static int secp256k1_ecmult_pippenger_wnaf(secp256k1_gej *buckets, int bucket_window, struct secp256k1_pippenger_state *state, secp256k1_gej *r, const secp256k1_scalar *sc, const secp256k1_ge *pt, size_t num) {
+ size_t n_wnaf = WNAF_SIZE(bucket_window+1);
+ size_t np;
+ size_t no = 0;
+ int i;
+ int j;
+
+ for (np = 0; np < num; ++np) {
+ if (secp256k1_scalar_is_zero(&sc[np]) || secp256k1_ge_is_infinity(&pt[np])) {
+ continue;
+ }
+ state->ps[no].input_pos = np;
+ state->ps[no].skew_na = secp256k1_wnaf_fixed(&state->wnaf_na[no*n_wnaf], &sc[np], bucket_window+1);
+ no++;
+ }
+ secp256k1_gej_set_infinity(r);
+
+ if (no == 0) {
+ return 1;
+ }
+
+ for (i = n_wnaf - 1; i >= 0; i--) {
+ secp256k1_gej running_sum;
+
+ for(j = 0; j < ECMULT_TABLE_SIZE(bucket_window+2); j++) {
+ secp256k1_gej_set_infinity(&buckets[j]);
+ }
+
+ for (np = 0; np < no; ++np) {
+ int n = state->wnaf_na[np*n_wnaf + i];
+ struct secp256k1_pippenger_point_state point_state = state->ps[np];
+ secp256k1_ge tmp;
+ int idx;
+
+ if (i == 0) {
+ /* correct for wnaf skew */
+ int skew = point_state.skew_na;
+ if (skew) {
+ secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]);
+ secp256k1_gej_add_ge_var(&buckets[0], &buckets[0], &tmp, NULL);
+ }
+ }
+ if (n > 0) {
+ idx = (n - 1)/2;
+ secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &pt[point_state.input_pos], NULL);
+ } else if (n < 0) {
+ idx = -(n + 1)/2;
+ secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]);
+ secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &tmp, NULL);
+ }
+ }
+
+ for(j = 0; j < bucket_window; j++) {
+ secp256k1_gej_double_var(r, r, NULL);
+ }
+
+ secp256k1_gej_set_infinity(&running_sum);
+ /* Accumulate the sum: bucket[0] + 3*bucket[1] + 5*bucket[2] + 7*bucket[3] + ...
+ * = bucket[0] + bucket[1] + bucket[2] + bucket[3] + ...
+ * + 2 * (bucket[1] + 2*bucket[2] + 3*bucket[3] + ...)
+ * using an intermediate running sum:
+ * running_sum = bucket[0] + bucket[1] + bucket[2] + ...
+ *
+ * The doubling is done implicitly by deferring the final window doubling (of 'r').
+ */
+ for(j = ECMULT_TABLE_SIZE(bucket_window+2) - 1; j > 0; j--) {
+ secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[j], NULL);
+ secp256k1_gej_add_var(r, r, &running_sum, NULL);
+ }
+
+ secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[0], NULL);
+ secp256k1_gej_double_var(r, r, NULL);
+ secp256k1_gej_add_var(r, r, &running_sum, NULL);
+ }
+ return 1;
+}
+
+/**
+ * Returns optimal bucket_window (number of bits of a scalar represented by a
+ * set of buckets) for a given number of points.
+ */
+static int secp256k1_pippenger_bucket_window(size_t n) {
+#ifdef USE_ENDOMORPHISM
+ if (n <= 1) {
+ return 1;
+ } else if (n <= 4) {
+ return 2;
+ } else if (n <= 20) {
+ return 3;
+ } else if (n <= 57) {
+ return 4;
+ } else if (n <= 136) {
+ return 5;
+ } else if (n <= 235) {
+ return 6;
+ } else if (n <= 1260) {
+ return 7;
+ } else if (n <= 4420) {
+ return 9;
+ } else if (n <= 7880) {
+ return 10;
+ } else if (n <= 16050) {
+ return 11;
+ } else {
+ return PIPPENGER_MAX_BUCKET_WINDOW;
+ }
+#else
+ if (n <= 1) {
+ return 1;
+ } else if (n <= 11) {
+ return 2;
+ } else if (n <= 45) {
+ return 3;
+ } else if (n <= 100) {
+ return 4;
+ } else if (n <= 275) {
+ return 5;
+ } else if (n <= 625) {
+ return 6;
+ } else if (n <= 1850) {
+ return 7;
+ } else if (n <= 3400) {
+ return 8;
+ } else if (n <= 9630) {
+ return 9;
+ } else if (n <= 17900) {
+ return 10;
+ } else if (n <= 32800) {
+ return 11;
+ } else {
+ return PIPPENGER_MAX_BUCKET_WINDOW;
+ }
+#endif
+}
+
+/**
+ * Returns the maximum optimal number of points for a bucket_window.
+ */
+static size_t secp256k1_pippenger_bucket_window_inv(int bucket_window) {
+ switch(bucket_window) {
+#ifdef USE_ENDOMORPHISM
+ case 1: return 1;
+ case 2: return 4;
+ case 3: return 20;
+ case 4: return 57;
+ case 5: return 136;
+ case 6: return 235;
+ case 7: return 1260;
+ case 8: return 1260;
+ case 9: return 4420;
+ case 10: return 7880;
+ case 11: return 16050;
+ case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX;
+#else
+ case 1: return 1;
+ case 2: return 11;
+ case 3: return 45;
+ case 4: return 100;
+ case 5: return 275;
+ case 6: return 625;
+ case 7: return 1850;
+ case 8: return 3400;
+ case 9: return 9630;
+ case 10: return 17900;
+ case 11: return 32800;
+ case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX;
+#endif
+ }
+ return 0;
+}
+
+
+#ifdef USE_ENDOMORPHISM
+SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, secp256k1_scalar *s2, secp256k1_ge *p1, secp256k1_ge *p2) {
+ secp256k1_scalar tmp = *s1;
+ secp256k1_scalar_split_lambda(s1, s2, &tmp);
+ secp256k1_ge_mul_lambda(p2, p1);
+
+ if (secp256k1_scalar_is_high(s1)) {
+ secp256k1_scalar_negate(s1, s1);
+ secp256k1_ge_neg(p1, p1);
+ }
+ if (secp256k1_scalar_is_high(s2)) {
+ secp256k1_scalar_negate(s2, s2);
+ secp256k1_ge_neg(p2, p2);
+ }
+}
+#endif
+
+/**
+ * Returns the scratch size required for a given number of points (excluding
+ * base point G) without considering alignment.
+ */
+static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_window) {
+#ifdef USE_ENDOMORPHISM
+ size_t entries = 2*n_points + 2;
+#else
+ size_t entries = n_points + 1;
+#endif
+ size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int);
+ return (sizeof(secp256k1_gej) << bucket_window) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size;
+}
+
+static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
+ /* Use 2(n+1) with the endomorphism, n+1 without, when calculating batch
+ * sizes. The reason for +1 is that we add the G scalar to the list of
+ * other scalars. */
+#ifdef USE_ENDOMORPHISM
+ size_t entries = 2*n_points + 2;
+#else
+ size_t entries = n_points + 1;
+#endif
+ secp256k1_ge *points;
+ secp256k1_scalar *scalars;
+ secp256k1_gej *buckets;
+ struct secp256k1_pippenger_state *state_space;
+ size_t idx = 0;
+ size_t point_idx = 0;
+ int i, j;
+ int bucket_window;
+
+ (void)ctx;
+ secp256k1_gej_set_infinity(r);
+ if (inp_g_sc == NULL && n_points == 0) {
+ return 1;
+ }
+
+ bucket_window = secp256k1_pippenger_bucket_window(n_points);
+ if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) {
+ return 0;
+ }
+ points = (secp256k1_ge *) secp256k1_scratch_alloc(scratch, entries * sizeof(*points));
+ scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(scratch, entries * sizeof(*scalars));
+ state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(scratch, sizeof(*state_space));
+ state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(scratch, entries * sizeof(*state_space->ps));
+ state_space->wnaf_na = (int *) secp256k1_scratch_alloc(scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
+ buckets = (secp256k1_gej *) secp256k1_scratch_alloc(scratch, sizeof(*buckets) << bucket_window);
+
+ if (inp_g_sc != NULL) {
+ scalars[0] = *inp_g_sc;
+ points[0] = secp256k1_ge_const_g;
+ idx++;
+#ifdef USE_ENDOMORPHISM
+ secp256k1_ecmult_endo_split(&scalars[0], &scalars[1], &points[0], &points[1]);
+ idx++;
+#endif
+ }
+
+ while (point_idx < n_points) {
+ if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) {
+ secp256k1_scratch_deallocate_frame(scratch);
+ return 0;
+ }
+ idx++;
+#ifdef USE_ENDOMORPHISM
+ secp256k1_ecmult_endo_split(&scalars[idx - 1], &scalars[idx], &points[idx - 1], &points[idx]);
+ idx++;
+#endif
+ point_idx++;
+ }
+
+ secp256k1_ecmult_pippenger_wnaf(buckets, bucket_window, state_space, r, scalars, points, idx);
+
+ /* Clear data */
+ for(i = 0; (size_t)i < idx; i++) {
+ secp256k1_scalar_clear(&scalars[i]);
+ state_space->ps[i].skew_na = 0;
+ for(j = 0; j < WNAF_SIZE(bucket_window+1); j++) {
+ state_space->wnaf_na[i * WNAF_SIZE(bucket_window+1) + j] = 0;
+ }
+ }
+ for(i = 0; i < 1<<bucket_window; i++) {
+ secp256k1_gej_clear(&buckets[i]);
+ }
+ secp256k1_scratch_deallocate_frame(scratch);
+ return 1;
+}
+
+/* Wrapper for secp256k1_ecmult_multi_func interface */
+static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
+ return secp256k1_ecmult_pippenger_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
+}
+
+/**
+ * Returns the maximum number of points in addition to G that can be used with
+ * a given scratch space. The function ensures that fewer points may also be
+ * used.
+ */
+static size_t secp256k1_pippenger_max_points(secp256k1_scratch *scratch) {
+ size_t max_alloc = secp256k1_scratch_max_allocation(scratch, PIPPENGER_SCRATCH_OBJECTS);
+ int bucket_window;
+ size_t res = 0;
+
+ for (bucket_window = 1; bucket_window <= PIPPENGER_MAX_BUCKET_WINDOW; bucket_window++) {
+ size_t n_points;
+ size_t max_points = secp256k1_pippenger_bucket_window_inv(bucket_window);
+ size_t space_for_points;
+ size_t space_overhead;
+ size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int);
+
+#ifdef USE_ENDOMORPHISM
+ entry_size = 2*entry_size;
+#endif
+ space_overhead = (sizeof(secp256k1_gej) << bucket_window) + entry_size + sizeof(struct secp256k1_pippenger_state);
+ if (space_overhead > max_alloc) {
+ break;
+ }
+ space_for_points = max_alloc - space_overhead;
+
+ n_points = space_for_points/entry_size;
+ n_points = n_points > max_points ? max_points : n_points;
+ if (n_points > res) {
+ res = n_points;
+ }
+ if (n_points < max_points) {
+ /* A larger bucket_window may support even more points. But if we
+ * would choose that then the caller couldn't safely use any number
+ * smaller than what this function returns */
+ break;
+ }
+ }
+ return res;
+}
+
+/* Computes ecmult_multi by simply multiplying and adding each point. Does not
+ * require a scratch space */
+static int secp256k1_ecmult_multi_simple_var(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points) {
+ size_t point_idx;
+ secp256k1_scalar szero;
+ secp256k1_gej tmpj;
+
+ secp256k1_scalar_set_int(&szero, 0);
+ secp256k1_gej_set_infinity(r);
+ secp256k1_gej_set_infinity(&tmpj);
+ /* r = inp_g_sc*G */
+ secp256k1_ecmult(ctx, r, &tmpj, &szero, inp_g_sc);
+ for (point_idx = 0; point_idx < n_points; point_idx++) {
+ secp256k1_ge point;
+ secp256k1_gej pointj;
+ secp256k1_scalar scalar;
+ if (!cb(&scalar, &point, point_idx, cbdata)) {
+ return 0;
+ }
+ /* r += scalar*point */
+ secp256k1_gej_set_ge(&pointj, &point);
+ secp256k1_ecmult(ctx, &tmpj, &pointj, &scalar, NULL);
+ secp256k1_gej_add_var(r, r, &tmpj, NULL);
+ }
+ return 1;
+}
+
+/* Compute the number of batches and the batch size given the maximum batch size and the
+ * total number of points */
+static int secp256k1_ecmult_multi_batch_size_helper(size_t *n_batches, size_t *n_batch_points, size_t max_n_batch_points, size_t n) {
+ if (max_n_batch_points == 0) {
+ return 0;
+ }
+ if (max_n_batch_points > ECMULT_MAX_POINTS_PER_BATCH) {
+ max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH;
+ }
+ if (n == 0) {
+ *n_batches = 0;
+ *n_batch_points = 0;
+ return 1;
+ }
+ /* Compute ceil(n/max_n_batch_points) and ceil(n/n_batches) */
+ *n_batches = 1 + (n - 1) / max_n_batch_points;
+ *n_batch_points = 1 + (n - 1) / *n_batches;
+ return 1;
+}
+
+typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t);
+static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
+ size_t i;
+
+ int (*f)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t);
+ size_t n_batches;
+ size_t n_batch_points;
+
+ secp256k1_gej_set_infinity(r);
+ if (inp_g_sc == NULL && n == 0) {
+ return 1;
+ } else if (n == 0) {
+ secp256k1_scalar szero;
+ secp256k1_scalar_set_int(&szero, 0);
+ secp256k1_ecmult(ctx, r, r, &szero, inp_g_sc);
+ return 1;
+ }
+ if (scratch == NULL) {
+ return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
+ }
+
+ /* Compute the batch sizes for pippenger given a scratch space. If it's greater than a threshold
+ * use pippenger. Otherwise use strauss */
+ if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_pippenger_max_points(scratch), n)) {
+ return 0;
+ }
+ if (n_batch_points >= ECMULT_PIPPENGER_THRESHOLD) {
+ f = secp256k1_ecmult_pippenger_batch;
+ } else {
+ if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_strauss_max_points(scratch), n)) {
+ return 0;
+ }
+ f = secp256k1_ecmult_strauss_batch;
+ }
+ for(i = 0; i < n_batches; i++) {
+ size_t nbp = n < n_batch_points ? n : n_batch_points;
+ size_t offset = n_batch_points*i;
+ secp256k1_gej tmp;
+ if (!f(ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) {
+ return 0;
+ }
+ secp256k1_gej_add_var(r, r, &tmp, NULL);
+ n -= nbp;
+ }
+ return 1;
+}
+
#endif /* SECP256K1_ECMULT_IMPL_H */
diff --git a/src/secp256k1/src/field_10x26.h b/src/secp256k1/src/field_10x26.h
index 727c5267fb..5ff03c8abc 100644
--- a/src/secp256k1/src/field_10x26.h
+++ b/src/secp256k1/src/field_10x26.h
@@ -10,7 +10,9 @@
#include <stdint.h>
typedef struct {
- /* X = sum(i=0..9, elem[i]*2^26) mod n */
+ /* X = sum(i=0..9, n[i]*2^(i*26)) mod p
+ * where p = 2^256 - 0x1000003D1
+ */
uint32_t n[10];
#ifdef VERIFY
int magnitude;
diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h
index 94f8132fc8..4ae4fdcec8 100644
--- a/src/secp256k1/src/field_10x26_impl.h
+++ b/src/secp256k1/src/field_10x26_impl.h
@@ -8,7 +8,6 @@
#define SECP256K1_FIELD_REPR_IMPL_H
#include "util.h"
-#include "num.h"
#include "field.h"
#ifdef VERIFY
@@ -486,7 +485,8 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t
VERIFY_BITS(b[9], 26);
/** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n.
- * px is a shorthand for sum(a[i]*b[x-i], i=0..x).
+ * for 0 <= x <= 9, px is a shorthand for sum(a[i]*b[x-i], i=0..x).
+ * for 9 <= x <= 18, px is a shorthand for sum(a[i]*b[x-i], i=(x-9)..9)
* Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0].
*/
@@ -1069,6 +1069,7 @@ static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp2
secp256k1_fe_verify(a);
secp256k1_fe_verify(b);
VERIFY_CHECK(r != b);
+ VERIFY_CHECK(a != b);
#endif
secp256k1_fe_mul_inner(r->n, a->n, b->n);
#ifdef VERIFY
diff --git a/src/secp256k1/src/field_5x52.h b/src/secp256k1/src/field_5x52.h
index bccd8feb4d..fc5bfe357e 100644
--- a/src/secp256k1/src/field_5x52.h
+++ b/src/secp256k1/src/field_5x52.h
@@ -10,7 +10,9 @@
#include <stdint.h>
typedef struct {
- /* X = sum(i=0..4, elem[i]*2^52) mod n */
+ /* X = sum(i=0..4, n[i]*2^(i*52)) mod p
+ * where p = 2^256 - 0x1000003D1
+ */
uint64_t n[5];
#ifdef VERIFY
int magnitude;
diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h
index 957c61b014..f4263320d5 100644
--- a/src/secp256k1/src/field_5x52_impl.h
+++ b/src/secp256k1/src/field_5x52_impl.h
@@ -12,7 +12,6 @@
#endif
#include "util.h"
-#include "num.h"
#include "field.h"
#if defined(USE_ASM_X86_64)
@@ -422,6 +421,7 @@ static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp2
secp256k1_fe_verify(a);
secp256k1_fe_verify(b);
VERIFY_CHECK(r != b);
+ VERIFY_CHECK(a != b);
#endif
secp256k1_fe_mul_inner(r->n, a->n, b->n);
#ifdef VERIFY
diff --git a/src/secp256k1/src/field_5x52_int128_impl.h b/src/secp256k1/src/field_5x52_int128_impl.h
index 95a0d1791c..bcbfb92ac2 100644
--- a/src/secp256k1/src/field_5x52_int128_impl.h
+++ b/src/secp256k1/src/field_5x52_int128_impl.h
@@ -32,9 +32,11 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t
VERIFY_BITS(b[3], 56);
VERIFY_BITS(b[4], 52);
VERIFY_CHECK(r != b);
+ VERIFY_CHECK(a != b);
/* [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n.
- * px is a shorthand for sum(a[i]*b[x-i], i=0..x).
+ * for 0 <= x <= 4, px is a shorthand for sum(a[i]*b[x-i], i=0..x).
+ * for 4 <= x <= 8, px is a shorthand for sum(a[i]*b[x-i], i=(x-4)..4)
* Note that [x 0 0 0 0 0] = [x*R].
*/
diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h
index 20428648af..6070caccfe 100644
--- a/src/secp256k1/src/field_impl.h
+++ b/src/secp256k1/src/field_impl.h
@@ -12,6 +12,7 @@
#endif
#include "util.h"
+#include "num.h"
#if defined(USE_FIELD_10X26)
#include "field_10x26_impl.h"
@@ -48,6 +49,8 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) {
secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1;
int j;
+ VERIFY_CHECK(r != a);
+
/** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in
* { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block:
* 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]
diff --git a/src/secp256k1/src/gen_context.c b/src/secp256k1/src/gen_context.c
index 1835fd491d..87d296ebf0 100644
--- a/src/secp256k1/src/gen_context.c
+++ b/src/secp256k1/src/gen_context.c
@@ -41,7 +41,7 @@ int main(int argc, char **argv) {
fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
- fprintf(fp, "#include \"group.h\"\n");
+ fprintf(fp, "#include \"src/group.h\"\n");
fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n");
fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n");
diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h
index ea1302deb8..8e122ab429 100644
--- a/src/secp256k1/src/group.h
+++ b/src/secp256k1/src/group.h
@@ -65,12 +65,7 @@ static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a);
static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a);
/** Set a batch of group elements equal to the inputs given in jacobian coordinates */
-static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb);
-
-/** Set a batch of group elements equal to the inputs given in jacobian
- * coordinates (with known z-ratios). zr must contain the known z-ratios such
- * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */
-static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len);
+static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len);
/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to
* the same global z "denominator". zr must contain the known z-ratios such
@@ -79,6 +74,9 @@ static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej
* stored in globalz. */
static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr);
+/** Set a group element (affine) equal to the point at infinity. */
+static void secp256k1_ge_set_infinity(secp256k1_ge *r);
+
/** Set a group element (jacobian) equal to the point at infinity. */
static void secp256k1_gej_set_infinity(secp256k1_gej *r);
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index b31b6c12ef..9b93c39e92 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -38,22 +38,22 @@
*/
#if defined(EXHAUSTIVE_TEST_ORDER)
# if EXHAUSTIVE_TEST_ORDER == 199
-const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
+static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069,
0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18,
0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868,
0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED
);
-const int CURVE_B = 4;
+static const int CURVE_B = 4;
# elif EXHAUSTIVE_TEST_ORDER == 13
-const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
+static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0,
0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15,
0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e,
0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac
);
-const int CURVE_B = 2;
+static const int CURVE_B = 2;
# else
# error No known generator for the specified exhaustive test group order.
# endif
@@ -68,7 +68,7 @@ static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL
);
-const int CURVE_B = 7;
+static const int CURVE_B = 7;
#endif
static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
@@ -126,46 +126,43 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) {
r->y = a->y;
}
-static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb) {
- secp256k1_fe *az;
- secp256k1_fe *azi;
+static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len) {
+ secp256k1_fe u;
size_t i;
- size_t count = 0;
- az = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * len);
+ size_t last_i = SIZE_MAX;
+
for (i = 0; i < len; i++) {
if (!a[i].infinity) {
- az[count++] = a[i].z;
+ /* Use destination's x coordinates as scratch space */
+ if (last_i == SIZE_MAX) {
+ r[i].x = a[i].z;
+ } else {
+ secp256k1_fe_mul(&r[i].x, &r[last_i].x, &a[i].z);
+ }
+ last_i = i;
}
}
+ if (last_i == SIZE_MAX) {
+ return;
+ }
+ secp256k1_fe_inv_var(&u, &r[last_i].x);
- azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count);
- secp256k1_fe_inv_all_var(azi, az, count);
- free(az);
-
- count = 0;
- for (i = 0; i < len; i++) {
- r[i].infinity = a[i].infinity;
+ i = last_i;
+ while (i > 0) {
+ i--;
if (!a[i].infinity) {
- secp256k1_ge_set_gej_zinv(&r[i], &a[i], &azi[count++]);
+ secp256k1_fe_mul(&r[last_i].x, &r[i].x, &u);
+ secp256k1_fe_mul(&u, &u, &a[last_i].z);
+ last_i = i;
}
}
- free(azi);
-}
-
-static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len) {
- size_t i = len - 1;
- secp256k1_fe zi;
+ VERIFY_CHECK(!a[last_i].infinity);
+ r[last_i].x = u;
- if (len > 0) {
- /* Compute the inverse of the last z coordinate, and use it to compute the last affine output. */
- secp256k1_fe_inv(&zi, &a[i].z);
- secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi);
-
- /* Work out way backwards, using the z-ratios to scale the x/y values. */
- while (i > 0) {
- secp256k1_fe_mul(&zi, &zi, &zr[i]);
- i--;
- secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi);
+ for (i = 0; i < len; i++) {
+ r[i].infinity = a[i].infinity;
+ if (!a[i].infinity) {
+ secp256k1_ge_set_gej_zinv(&r[i], &a[i], &r[i].x);
}
}
}
@@ -178,6 +175,8 @@ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp
/* The z of the final point gives us the "global Z" for the table. */
r[i].x = a[i].x;
r[i].y = a[i].y;
+ /* Ensure all y values are in weak normal form for fast negation of points */
+ secp256k1_fe_normalize_weak(&r[i].y);
*globalz = a[i].z;
r[i].infinity = 0;
zs = zr[i];
@@ -200,6 +199,12 @@ static void secp256k1_gej_set_infinity(secp256k1_gej *r) {
secp256k1_fe_clear(&r->z);
}
+static void secp256k1_ge_set_infinity(secp256k1_ge *r) {
+ r->infinity = 1;
+ secp256k1_fe_clear(&r->x);
+ secp256k1_fe_clear(&r->y);
+}
+
static void secp256k1_gej_clear(secp256k1_gej *r) {
r->infinity = 0;
secp256k1_fe_clear(&r->x);
diff --git a/src/secp256k1/src/hash.h b/src/secp256k1/src/hash.h
index e08d25d225..de26e4b89f 100644
--- a/src/secp256k1/src/hash.h
+++ b/src/secp256k1/src/hash.h
@@ -14,28 +14,28 @@ typedef struct {
uint32_t s[8];
uint32_t buf[16]; /* In big endian */
size_t bytes;
-} secp256k1_sha256_t;
+} secp256k1_sha256;
-static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash);
-static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t size);
-static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32);
+static void secp256k1_sha256_initialize(secp256k1_sha256 *hash);
+static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t size);
+static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32);
typedef struct {
- secp256k1_sha256_t inner, outer;
-} secp256k1_hmac_sha256_t;
+ secp256k1_sha256 inner, outer;
+} secp256k1_hmac_sha256;
-static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t size);
-static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size);
-static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32);
+static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t size);
+static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size);
+static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32);
typedef struct {
unsigned char v[32];
unsigned char k[32];
int retry;
-} secp256k1_rfc6979_hmac_sha256_t;
+} secp256k1_rfc6979_hmac_sha256;
-static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen);
-static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen);
-static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng);
+static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen);
+static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen);
+static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng);
#endif /* SECP256K1_HASH_H */
diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h
index 4c9964ee06..009f26beba 100644
--- a/src/secp256k1/src/hash_impl.h
+++ b/src/secp256k1/src/hash_impl.h
@@ -33,7 +33,7 @@
#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24))
#endif
-static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash) {
+static void secp256k1_sha256_initialize(secp256k1_sha256 *hash) {
hash->s[0] = 0x6a09e667ul;
hash->s[1] = 0xbb67ae85ul;
hash->s[2] = 0x3c6ef372ul;
@@ -128,14 +128,15 @@ static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) {
s[7] += h;
}
-static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t len) {
+static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t len) {
size_t bufsize = hash->bytes & 0x3F;
hash->bytes += len;
while (bufsize + len >= 64) {
/* Fill the buffer, and process it. */
- memcpy(((unsigned char*)hash->buf) + bufsize, data, 64 - bufsize);
- data += 64 - bufsize;
- len -= 64 - bufsize;
+ size_t chunk_len = 64 - bufsize;
+ memcpy(((unsigned char*)hash->buf) + bufsize, data, chunk_len);
+ data += chunk_len;
+ len -= chunk_len;
secp256k1_sha256_transform(hash->s, hash->buf);
bufsize = 0;
}
@@ -145,7 +146,7 @@ static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char
}
}
-static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32) {
+static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32) {
static const unsigned char pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
uint32_t sizedesc[2];
uint32_t out[8];
@@ -161,14 +162,14 @@ static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *o
memcpy(out32, (const unsigned char*)out, 32);
}
-static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t keylen) {
- int n;
+static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) {
+ size_t n;
unsigned char rkey[64];
- if (keylen <= 64) {
+ if (keylen <= sizeof(rkey)) {
memcpy(rkey, key, keylen);
- memset(rkey + keylen, 0, 64 - keylen);
+ memset(rkey + keylen, 0, sizeof(rkey) - keylen);
} else {
- secp256k1_sha256_t sha256;
+ secp256k1_sha256 sha256;
secp256k1_sha256_initialize(&sha256);
secp256k1_sha256_write(&sha256, key, keylen);
secp256k1_sha256_finalize(&sha256, rkey);
@@ -176,24 +177,24 @@ static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, cons
}
secp256k1_sha256_initialize(&hash->outer);
- for (n = 0; n < 64; n++) {
+ for (n = 0; n < sizeof(rkey); n++) {
rkey[n] ^= 0x5c;
}
- secp256k1_sha256_write(&hash->outer, rkey, 64);
+ secp256k1_sha256_write(&hash->outer, rkey, sizeof(rkey));
secp256k1_sha256_initialize(&hash->inner);
- for (n = 0; n < 64; n++) {
+ for (n = 0; n < sizeof(rkey); n++) {
rkey[n] ^= 0x5c ^ 0x36;
}
- secp256k1_sha256_write(&hash->inner, rkey, 64);
- memset(rkey, 0, 64);
+ secp256k1_sha256_write(&hash->inner, rkey, sizeof(rkey));
+ memset(rkey, 0, sizeof(rkey));
}
-static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size) {
+static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size) {
secp256k1_sha256_write(&hash->inner, data, size);
}
-static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32) {
+static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32) {
unsigned char temp[32];
secp256k1_sha256_finalize(&hash->inner, temp);
secp256k1_sha256_write(&hash->outer, temp, 32);
@@ -202,8 +203,8 @@ static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsign
}
-static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen) {
- secp256k1_hmac_sha256_t hmac;
+static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen) {
+ secp256k1_hmac_sha256 hmac;
static const unsigned char zero[1] = {0x00};
static const unsigned char one[1] = {0x01};
@@ -232,11 +233,11 @@ static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha2
rng->retry = 0;
}
-static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen) {
+static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen) {
/* RFC6979 3.2.h. */
static const unsigned char zero[1] = {0x00};
if (rng->retry) {
- secp256k1_hmac_sha256_t hmac;
+ secp256k1_hmac_sha256 hmac;
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32);
secp256k1_hmac_sha256_write(&hmac, rng->v, 32);
secp256k1_hmac_sha256_write(&hmac, zero, 1);
@@ -247,7 +248,7 @@ static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256
}
while (outlen > 0) {
- secp256k1_hmac_sha256_t hmac;
+ secp256k1_hmac_sha256 hmac;
int now = outlen;
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32);
secp256k1_hmac_sha256_write(&hmac, rng->v, 32);
@@ -263,7 +264,7 @@ static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256
rng->retry = 1;
}
-static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng) {
+static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng) {
memset(rng->k, 0, 32);
memset(rng->v, 0, 32);
rng->retry = 0;
diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
index c00d08899b..d766a1029c 100644
--- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
+++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
@@ -52,7 +52,7 @@ public class NativeSecp256k1Test {
}
/**
- * This tests secret key verify() for a invalid secretkey
+ * This tests secret key verify() for an invalid secretkey
*/
public static void testSecKeyVerifyNeg() throws AssertFailException{
boolean result = false;
diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
index bcef7b32ce..b50970b4f2 100644
--- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
+++ b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
@@ -83,7 +83,7 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
secp256k1_ecdsa_signature sig[72];
- int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL );
+ int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL);
unsigned char outputSer[72];
size_t outputLen = 72;
@@ -353,7 +353,9 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
ctx,
nonce_res,
&pubkey,
- secdata
+ secdata,
+ NULL,
+ NULL
);
}
diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h
index 01ecba4d53..44cb68e750 100644
--- a/src/secp256k1/src/modules/ecdh/main_impl.h
+++ b/src/secp256k1/src/modules/ecdh/main_impl.h
@@ -10,16 +10,35 @@
#include "include/secp256k1_ecdh.h"
#include "ecmult_const_impl.h"
-int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const secp256k1_pubkey *point, const unsigned char *scalar) {
+static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) {
+ unsigned char version = (y[31] & 0x01) | 0x02;
+ secp256k1_sha256 sha;
+ (void)data;
+
+ secp256k1_sha256_initialize(&sha);
+ secp256k1_sha256_write(&sha, &version, 1);
+ secp256k1_sha256_write(&sha, x, 32);
+ secp256k1_sha256_finalize(&sha, output);
+
+ return 1;
+}
+
+const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256 = ecdh_hash_function_sha256;
+const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default = ecdh_hash_function_sha256;
+
+int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pubkey *point, const unsigned char *scalar, secp256k1_ecdh_hash_function hashfp, void *data) {
int ret = 0;
int overflow = 0;
secp256k1_gej res;
secp256k1_ge pt;
secp256k1_scalar s;
VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(result != NULL);
+ ARG_CHECK(output != NULL);
ARG_CHECK(point != NULL);
ARG_CHECK(scalar != NULL);
+ if (hashfp == NULL) {
+ hashfp = secp256k1_ecdh_hash_function_default;
+ }
secp256k1_pubkey_load(ctx, &pt, point);
secp256k1_scalar_set_b32(&s, scalar, &overflow);
@@ -27,24 +46,18 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const se
ret = 0;
} else {
unsigned char x[32];
- unsigned char y[1];
- secp256k1_sha256_t sha;
+ unsigned char y[32];
- secp256k1_ecmult_const(&res, &pt, &s);
+ secp256k1_ecmult_const(&res, &pt, &s, 256);
secp256k1_ge_set_gej(&pt, &res);
- /* Compute a hash of the point in compressed form
- * Note we cannot use secp256k1_eckey_pubkey_serialize here since it does not
- * expect its output to be secret and has a timing sidechannel. */
+
+ /* Compute a hash of the point */
secp256k1_fe_normalize(&pt.x);
secp256k1_fe_normalize(&pt.y);
secp256k1_fe_get_b32(x, &pt.x);
- y[0] = 0x02 | secp256k1_fe_is_odd(&pt.y);
+ secp256k1_fe_get_b32(y, &pt.y);
- secp256k1_sha256_initialize(&sha);
- secp256k1_sha256_write(&sha, y, sizeof(y));
- secp256k1_sha256_write(&sha, x, sizeof(x));
- secp256k1_sha256_finalize(&sha, result);
- ret = 1;
+ ret = hashfp(output, x, y, data);
}
secp256k1_scalar_clear(&s);
diff --git a/src/secp256k1/src/modules/ecdh/tests_impl.h b/src/secp256k1/src/modules/ecdh/tests_impl.h
index cec30b67c6..fe26e8fb69 100644
--- a/src/secp256k1/src/modules/ecdh/tests_impl.h
+++ b/src/secp256k1/src/modules/ecdh/tests_impl.h
@@ -7,6 +7,23 @@
#ifndef SECP256K1_MODULE_ECDH_TESTS_H
#define SECP256K1_MODULE_ECDH_TESTS_H
+int ecdh_hash_function_test_fail(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) {
+ (void)output;
+ (void)x;
+ (void)y;
+ (void)data;
+ return 0;
+}
+
+int ecdh_hash_function_custom(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) {
+ (void)data;
+ /* Save x and y as uncompressed public key */
+ output[0] = 0x04;
+ memcpy(output + 1, x, 32);
+ memcpy(output + 33, y, 32);
+ return 1;
+}
+
void test_ecdh_api(void) {
/* Setup context that just counts errors */
secp256k1_context *tctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
@@ -21,15 +38,15 @@ void test_ecdh_api(void) {
CHECK(secp256k1_ec_pubkey_create(tctx, &point, s_one) == 1);
/* Check all NULLs are detected */
- CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1);
+ CHECK(secp256k1_ecdh(tctx, res, &point, s_one, NULL, NULL) == 1);
CHECK(ecount == 0);
- CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one) == 0);
+ CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one, NULL, NULL) == 0);
CHECK(ecount == 1);
- CHECK(secp256k1_ecdh(tctx, res, NULL, s_one) == 0);
+ CHECK(secp256k1_ecdh(tctx, res, NULL, s_one, NULL, NULL) == 0);
CHECK(ecount == 2);
- CHECK(secp256k1_ecdh(tctx, res, &point, NULL) == 0);
+ CHECK(secp256k1_ecdh(tctx, res, &point, NULL, NULL, NULL) == 0);
CHECK(ecount == 3);
- CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1);
+ CHECK(secp256k1_ecdh(tctx, res, &point, s_one, NULL, NULL) == 1);
CHECK(ecount == 3);
/* Cleanup */
@@ -44,29 +61,36 @@ void test_ecdh_generator_basepoint(void) {
s_one[31] = 1;
/* Check against pubkey creation when the basepoint is the generator */
for (i = 0; i < 100; ++i) {
- secp256k1_sha256_t sha;
+ secp256k1_sha256 sha;
unsigned char s_b32[32];
- unsigned char output_ecdh[32];
+ unsigned char output_ecdh[65];
unsigned char output_ser[32];
- unsigned char point_ser[33];
+ unsigned char point_ser[65];
size_t point_ser_len = sizeof(point_ser);
secp256k1_scalar s;
random_scalar_order(&s);
secp256k1_scalar_get_b32(s_b32, &s);
- /* compute using ECDH function */
CHECK(secp256k1_ec_pubkey_create(ctx, &point[0], s_one) == 1);
- CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32) == 1);
- /* compute "explicitly" */
CHECK(secp256k1_ec_pubkey_create(ctx, &point[1], s_b32) == 1);
+
+ /* compute using ECDH function with custom hash function */
+ CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32, ecdh_hash_function_custom, NULL) == 1);
+ /* compute "explicitly" */
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_UNCOMPRESSED) == 1);
+ /* compare */
+ CHECK(memcmp(output_ecdh, point_ser, 65) == 0);
+
+ /* compute using ECDH function with default hash function */
+ CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32, NULL, NULL) == 1);
+ /* compute "explicitly" */
CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1);
- CHECK(point_ser_len == sizeof(point_ser));
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, point_ser, point_ser_len);
secp256k1_sha256_finalize(&sha, output_ser);
/* compare */
- CHECK(memcmp(output_ecdh, output_ser, sizeof(output_ser)) == 0);
+ CHECK(memcmp(output_ecdh, output_ser, 32) == 0);
}
}
@@ -89,11 +113,14 @@ void test_bad_scalar(void) {
CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_rand) == 1);
/* Try to multiply it by bad values */
- CHECK(secp256k1_ecdh(ctx, output, &point, s_zero) == 0);
- CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 0);
+ CHECK(secp256k1_ecdh(ctx, output, &point, s_zero, NULL, NULL) == 0);
+ CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, NULL, NULL) == 0);
/* ...and a good one */
s_overflow[31] -= 1;
- CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 1);
+ CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, NULL, NULL) == 1);
+
+ /* Hash function failure results in ecdh failure */
+ CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, ecdh_hash_function_test_fail, NULL) == 0);
}
void run_ecdh_tests(void) {
diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h
index db1ebf94be..d378335d99 100644
--- a/src/secp256k1/src/scalar_4x64_impl.h
+++ b/src/secp256k1/src/scalar_4x64_impl.h
@@ -376,7 +376,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
/* extract m6 */
"movq %%r8, %q6\n"
: "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6)
- : "S"(l), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1)
+ : "S"(l), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
: "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc");
/* Reduce 385 bits into 258. */
@@ -455,7 +455,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
/* extract p4 */
"movq %%r9, %q4\n"
: "=&g"(p0), "=&g"(p1), "=&g"(p2), "=g"(p3), "=g"(p4)
- : "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1)
+ : "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
: "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc");
/* Reduce 258 bits into 256. */
@@ -501,7 +501,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
/* Extract c */
"movq %%r9, %q0\n"
: "=g"(c)
- : "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1)
+ : "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
: "rax", "rdx", "r8", "r9", "r10", "cc", "memory");
#else
uint128_t c;
diff --git a/src/secp256k1/src/scratch.h b/src/secp256k1/src/scratch.h
new file mode 100644
index 0000000000..fef377af0d
--- /dev/null
+++ b/src/secp256k1/src/scratch.h
@@ -0,0 +1,39 @@
+/**********************************************************************
+ * Copyright (c) 2017 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_SCRATCH_
+#define _SECP256K1_SCRATCH_
+
+#define SECP256K1_SCRATCH_MAX_FRAMES 5
+
+/* The typedef is used internally; the struct name is used in the public API
+ * (where it is exposed as a different typedef) */
+typedef struct secp256k1_scratch_space_struct {
+ void *data[SECP256K1_SCRATCH_MAX_FRAMES];
+ size_t offset[SECP256K1_SCRATCH_MAX_FRAMES];
+ size_t frame_size[SECP256K1_SCRATCH_MAX_FRAMES];
+ size_t frame;
+ size_t max_size;
+ const secp256k1_callback* error_callback;
+} secp256k1_scratch;
+
+static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size);
+
+static void secp256k1_scratch_destroy(secp256k1_scratch* scratch);
+
+/** Attempts to allocate a new stack frame with `n` available bytes. Returns 1 on success, 0 on failure */
+static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects);
+
+/** Deallocates a stack frame */
+static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch);
+
+/** Returns the maximum allocation the scratch space will allow */
+static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t n_objects);
+
+/** Returns a pointer into the most recently allocated frame, or NULL if there is insufficient available space */
+static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t n);
+
+#endif
diff --git a/src/secp256k1/src/scratch_impl.h b/src/secp256k1/src/scratch_impl.h
new file mode 100644
index 0000000000..abed713b21
--- /dev/null
+++ b/src/secp256k1/src/scratch_impl.h
@@ -0,0 +1,86 @@
+/**********************************************************************
+ * Copyright (c) 2017 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_SCRATCH_IMPL_H_
+#define _SECP256K1_SCRATCH_IMPL_H_
+
+#include "scratch.h"
+
+/* Using 16 bytes alignment because common architectures never have alignment
+ * requirements above 8 for any of the types we care about. In addition we
+ * leave some room because currently we don't care about a few bytes.
+ * TODO: Determine this at configure time. */
+#define ALIGNMENT 16
+
+static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size) {
+ secp256k1_scratch* ret = (secp256k1_scratch*)checked_malloc(error_callback, sizeof(*ret));
+ if (ret != NULL) {
+ memset(ret, 0, sizeof(*ret));
+ ret->max_size = max_size;
+ ret->error_callback = error_callback;
+ }
+ return ret;
+}
+
+static void secp256k1_scratch_destroy(secp256k1_scratch* scratch) {
+ if (scratch != NULL) {
+ VERIFY_CHECK(scratch->frame == 0);
+ free(scratch);
+ }
+}
+
+static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t objects) {
+ size_t i = 0;
+ size_t allocated = 0;
+ for (i = 0; i < scratch->frame; i++) {
+ allocated += scratch->frame_size[i];
+ }
+ if (scratch->max_size - allocated <= objects * ALIGNMENT) {
+ return 0;
+ }
+ return scratch->max_size - allocated - objects * ALIGNMENT;
+}
+
+static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects) {
+ VERIFY_CHECK(scratch->frame < SECP256K1_SCRATCH_MAX_FRAMES);
+
+ if (n <= secp256k1_scratch_max_allocation(scratch, objects)) {
+ n += objects * ALIGNMENT;
+ scratch->data[scratch->frame] = checked_malloc(scratch->error_callback, n);
+ if (scratch->data[scratch->frame] == NULL) {
+ return 0;
+ }
+ scratch->frame_size[scratch->frame] = n;
+ scratch->offset[scratch->frame] = 0;
+ scratch->frame++;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch) {
+ VERIFY_CHECK(scratch->frame > 0);
+ scratch->frame -= 1;
+ free(scratch->data[scratch->frame]);
+}
+
+static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t size) {
+ void *ret;
+ size_t frame = scratch->frame - 1;
+ size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
+
+ if (scratch->frame == 0 || size + scratch->offset[frame] > scratch->frame_size[frame]) {
+ return NULL;
+ }
+ ret = (void *) ((unsigned char *) scratch->data[frame] + scratch->offset[frame]);
+ memset(ret, 0, size);
+ scratch->offset[frame] += size;
+
+ return ret;
+}
+
+#endif
diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c
index 4f8c01655b..15981f46e2 100644
--- a/src/secp256k1/src/secp256k1.c
+++ b/src/secp256k1/src/secp256k1.c
@@ -17,6 +17,7 @@
#include "ecdsa_impl.h"
#include "eckey_impl.h"
#include "hash_impl.h"
+#include "scratch_impl.h"
#define ARG_CHECK(cond) do { \
if (EXPECT(!(cond), 0)) { \
@@ -55,6 +56,14 @@ struct secp256k1_context_struct {
secp256k1_callback error_callback;
};
+static const secp256k1_context secp256k1_context_no_precomp_ = {
+ { 0 },
+ { 0 },
+ { default_illegal_callback_fn, 0 },
+ { default_error_callback_fn, 0 }
+};
+const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_no_precomp_;
+
secp256k1_context* secp256k1_context_create(unsigned int flags) {
secp256k1_context* ret = (secp256k1_context*)checked_malloc(&default_error_callback, sizeof(secp256k1_context));
ret->illegal_callback = default_illegal_callback;
@@ -90,6 +99,7 @@ secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) {
}
void secp256k1_context_destroy(secp256k1_context* ctx) {
+ CHECK(ctx != secp256k1_context_no_precomp);
if (ctx != NULL) {
secp256k1_ecmult_context_clear(&ctx->ecmult_ctx);
secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx);
@@ -99,6 +109,7 @@ void secp256k1_context_destroy(secp256k1_context* ctx) {
}
void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) {
+ CHECK(ctx != secp256k1_context_no_precomp);
if (fun == NULL) {
fun = default_illegal_callback_fn;
}
@@ -107,6 +118,7 @@ void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(
}
void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) {
+ CHECK(ctx != secp256k1_context_no_precomp);
if (fun == NULL) {
fun = default_error_callback_fn;
}
@@ -114,13 +126,22 @@ void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(co
ctx->error_callback.data = data;
}
+secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context* ctx, size_t max_size) {
+ VERIFY_CHECK(ctx != NULL);
+ return secp256k1_scratch_create(&ctx->error_callback, max_size);
+}
+
+void secp256k1_scratch_space_destroy(secp256k1_scratch_space* scratch) {
+ secp256k1_scratch_destroy(scratch);
+}
+
static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) {
if (sizeof(secp256k1_ge_storage) == 64) {
/* When the secp256k1_ge_storage type is exactly 64 byte, use its
* representation inside secp256k1_pubkey, as conversion is very fast.
* Note that secp256k1_pubkey_save must use the same representation. */
secp256k1_ge_storage s;
- memcpy(&s, &pubkey->data[0], 64);
+ memcpy(&s, &pubkey->data[0], sizeof(s));
secp256k1_ge_from_storage(ge, &s);
} else {
/* Otherwise, fall back to 32-byte big endian for X and Y. */
@@ -137,7 +158,7 @@ static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) {
if (sizeof(secp256k1_ge_storage) == 64) {
secp256k1_ge_storage s;
secp256k1_ge_to_storage(&s, ge);
- memcpy(&pubkey->data[0], &s, 64);
+ memcpy(&pubkey->data[0], &s, sizeof(s));
} else {
VERIFY_CHECK(!secp256k1_ge_is_infinity(ge));
secp256k1_fe_normalize_var(&ge->x);
@@ -307,10 +328,15 @@ int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_s
secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &r, &s, &q, &m));
}
+static SECP256K1_INLINE void buffer_append(unsigned char *buf, unsigned int *offset, const void *data, unsigned int len) {
+ memcpy(buf + *offset, data, len);
+ *offset += len;
+}
+
static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) {
unsigned char keydata[112];
- int keylen = 64;
- secp256k1_rfc6979_hmac_sha256_t rng;
+ unsigned int offset = 0;
+ secp256k1_rfc6979_hmac_sha256 rng;
unsigned int i;
/* We feed a byte array to the PRNG as input, consisting of:
* - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d.
@@ -320,17 +346,15 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m
* different argument mixtures to emulate each other and result in the same
* nonces.
*/
- memcpy(keydata, key32, 32);
- memcpy(keydata + 32, msg32, 32);
+ buffer_append(keydata, &offset, key32, 32);
+ buffer_append(keydata, &offset, msg32, 32);
if (data != NULL) {
- memcpy(keydata + 64, data, 32);
- keylen = 96;
+ buffer_append(keydata, &offset, data, 32);
}
if (algo16 != NULL) {
- memcpy(keydata + keylen, algo16, 16);
- keylen += 16;
+ buffer_append(keydata, &offset, algo16, 16);
}
- secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen);
+ secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, offset);
memset(keydata, 0, sizeof(keydata));
for (i = 0; i <= counter; i++) {
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
@@ -546,8 +570,9 @@ int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey
int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) {
VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
- secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32);
+ if (secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)) {
+ secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32);
+ }
return 1;
}
diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h
index 1255574209..30a91e5296 100644
--- a/src/secp256k1/src/testrand_impl.h
+++ b/src/secp256k1/src/testrand_impl.h
@@ -13,7 +13,7 @@
#include "testrand.h"
#include "hash.h"
-static secp256k1_rfc6979_hmac_sha256_t secp256k1_test_rng;
+static secp256k1_rfc6979_hmac_sha256 secp256k1_test_rng;
static uint32_t secp256k1_test_rng_precomputed[8];
static int secp256k1_test_rng_precomputed_used = 8;
static uint64_t secp256k1_test_rng_integer;
diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c
index 3d9bd5ebb4..f1c4db929a 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -23,6 +23,9 @@
#include "openssl/ec.h"
#include "openssl/ecdsa.h"
#include "openssl/obj_mac.h"
+# if OPENSSL_VERSION_NUMBER < 0x10100000L
+void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {*pr = sig->r; *ps = sig->s;}
+# endif
#endif
#include "contrib/lax_der_parsing.c"
@@ -215,8 +218,12 @@ void run_context_tests(void) {
CHECK(ecount == 3);
CHECK(secp256k1_ec_pubkey_tweak_mul(vrfy, &pubkey, ctmp) == 1);
CHECK(ecount == 3);
- CHECK(secp256k1_context_randomize(vrfy, ctmp) == 0);
- CHECK(ecount == 4);
+ CHECK(secp256k1_context_randomize(vrfy, ctmp) == 1);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_context_randomize(vrfy, NULL) == 1);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_context_randomize(sign, ctmp) == 1);
+ CHECK(ecount2 == 14);
CHECK(secp256k1_context_randomize(sign, NULL) == 1);
CHECK(ecount2 == 14);
secp256k1_context_set_illegal_callback(vrfy, NULL, NULL);
@@ -248,6 +255,44 @@ void run_context_tests(void) {
secp256k1_context_destroy(NULL);
}
+void run_scratch_tests(void) {
+ int32_t ecount = 0;
+ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
+ secp256k1_scratch_space *scratch;
+
+ /* Test public API */
+ secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
+
+ scratch = secp256k1_scratch_space_create(none, 1000);
+ CHECK(scratch != NULL);
+ CHECK(ecount == 0);
+
+ /* Test internal API */
+ CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
+ CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 1000);
+
+ /* Allocating 500 bytes with no frame fails */
+ CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
+ CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
+
+ /* ...but pushing a new stack frame does affect the max allocation */
+ CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1 == 1));
+ CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 500); /* 500 - ALIGNMENT */
+ CHECK(secp256k1_scratch_alloc(scratch, 500) != NULL);
+ CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
+
+ CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1) == 0);
+
+ /* ...and this effect is undone by popping the frame */
+ secp256k1_scratch_deallocate_frame(scratch);
+ CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
+ CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
+
+ /* cleanup */
+ secp256k1_scratch_space_destroy(scratch);
+ secp256k1_context_destroy(none);
+}
+
/***** HASH TESTS *****/
void run_sha256_tests(void) {
@@ -270,7 +315,7 @@ void run_sha256_tests(void) {
int i;
for (i = 0; i < 8; i++) {
unsigned char out[32];
- secp256k1_sha256_t hasher;
+ secp256k1_sha256 hasher;
secp256k1_sha256_initialize(&hasher);
secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i]));
secp256k1_sha256_finalize(&hasher, out);
@@ -313,7 +358,7 @@ void run_hmac_sha256_tests(void) {
};
int i;
for (i = 0; i < 6; i++) {
- secp256k1_hmac_sha256_t hasher;
+ secp256k1_hmac_sha256 hasher;
unsigned char out[32];
secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i]));
secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i]));
@@ -345,7 +390,7 @@ void run_rfc6979_hmac_sha256_tests(void) {
{0x75, 0x97, 0x88, 0x7c, 0xbd, 0x76, 0x32, 0x1f, 0x32, 0xe3, 0x04, 0x40, 0x67, 0x9a, 0x22, 0xcf, 0x7f, 0x8d, 0x9d, 0x2e, 0xac, 0x39, 0x0e, 0x58, 0x1f, 0xea, 0x09, 0x1c, 0xe2, 0x02, 0xba, 0x94}
};
- secp256k1_rfc6979_hmac_sha256_t rng;
+ secp256k1_rfc6979_hmac_sha256 rng;
unsigned char out[32];
int i;
@@ -2054,7 +2099,6 @@ void test_ge(void) {
/* Test batch gej -> ge conversion with and without known z ratios. */
{
secp256k1_fe *zr = (secp256k1_fe *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_fe));
- secp256k1_ge *ge_set_table = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge));
secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge));
for (i = 0; i < 4 * runs + 1; i++) {
/* Compute gej[i + 1].z / gez[i].z (with gej[n].z taken to be 1). */
@@ -2062,20 +2106,33 @@ void test_ge(void) {
secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z);
}
}
- secp256k1_ge_set_table_gej_var(ge_set_table, gej, zr, 4 * runs + 1);
- secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1, &ctx->error_callback);
+ secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1);
for (i = 0; i < 4 * runs + 1; i++) {
secp256k1_fe s;
random_fe_non_zero(&s);
secp256k1_gej_rescale(&gej[i], &s);
- ge_equals_gej(&ge_set_table[i], &gej[i]);
ge_equals_gej(&ge_set_all[i], &gej[i]);
}
- free(ge_set_table);
free(ge_set_all);
free(zr);
}
+ /* Test batch gej -> ge conversion with many infinities. */
+ for (i = 0; i < 4 * runs + 1; i++) {
+ random_group_element_test(&ge[i]);
+ /* randomly set half the points to infinitiy */
+ if(secp256k1_fe_is_odd(&ge[i].x)) {
+ secp256k1_ge_set_infinity(&ge[i]);
+ }
+ secp256k1_gej_set_ge(&gej[i], &ge[i]);
+ }
+ /* batch invert */
+ secp256k1_ge_set_all_gej_var(ge, gej, 4 * runs + 1);
+ /* check result */
+ for (i = 0; i < 4 * runs + 1; i++) {
+ ge_equals_gej(&ge[i], &gej[i]);
+ }
+
free(ge);
free(gej);
free(zinv);
@@ -2405,7 +2462,7 @@ void ecmult_const_random_mult(void) {
0xb84e4e1b, 0xfb77e21f, 0x96baae2a, 0x63dec956
);
secp256k1_gej b;
- secp256k1_ecmult_const(&b, &a, &xn);
+ secp256k1_ecmult_const(&b, &a, &xn, 256);
CHECK(secp256k1_ge_is_valid_var(&a));
ge_equals_gej(&expected_b, &b);
@@ -2421,12 +2478,12 @@ void ecmult_const_commutativity(void) {
random_scalar_order_test(&a);
random_scalar_order_test(&b);
- secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a);
- secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b);
+ secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a, 256);
+ secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b, 256);
secp256k1_ge_set_gej(&mid1, &res1);
secp256k1_ge_set_gej(&mid2, &res2);
- secp256k1_ecmult_const(&res1, &mid1, &b);
- secp256k1_ecmult_const(&res2, &mid2, &a);
+ secp256k1_ecmult_const(&res1, &mid1, &b, 256);
+ secp256k1_ecmult_const(&res2, &mid2, &a, 256);
secp256k1_ge_set_gej(&mid1, &res1);
secp256k1_ge_set_gej(&mid2, &res2);
ge_equals_ge(&mid1, &mid2);
@@ -2442,13 +2499,13 @@ void ecmult_const_mult_zero_one(void) {
secp256k1_scalar_negate(&negone, &one);
random_group_element_test(&point);
- secp256k1_ecmult_const(&res1, &point, &zero);
+ secp256k1_ecmult_const(&res1, &point, &zero, 3);
secp256k1_ge_set_gej(&res2, &res1);
CHECK(secp256k1_ge_is_infinity(&res2));
- secp256k1_ecmult_const(&res1, &point, &one);
+ secp256k1_ecmult_const(&res1, &point, &one, 2);
secp256k1_ge_set_gej(&res2, &res1);
ge_equals_ge(&res2, &point);
- secp256k1_ecmult_const(&res1, &point, &negone);
+ secp256k1_ecmult_const(&res1, &point, &negone, 256);
secp256k1_gej_neg(&res1, &res1);
secp256k1_ge_set_gej(&res2, &res1);
ge_equals_ge(&res2, &point);
@@ -2474,7 +2531,7 @@ void ecmult_const_chain_multiply(void) {
for (i = 0; i < 100; ++i) {
secp256k1_ge tmp;
secp256k1_ge_set_gej(&tmp, &point);
- secp256k1_ecmult_const(&point, &tmp, &scalar);
+ secp256k1_ecmult_const(&point, &tmp, &scalar, 256);
}
secp256k1_ge_set_gej(&res, &point);
ge_equals_gej(&res, &expected_point);
@@ -2487,6 +2544,446 @@ void run_ecmult_const_tests(void) {
ecmult_const_chain_multiply();
}
+typedef struct {
+ secp256k1_scalar *sc;
+ secp256k1_ge *pt;
+} ecmult_multi_data;
+
+static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) {
+ ecmult_multi_data *data = (ecmult_multi_data*) cbdata;
+ *sc = data->sc[idx];
+ *pt = data->pt[idx];
+ return 1;
+}
+
+static int ecmult_multi_false_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) {
+ (void)sc;
+ (void)pt;
+ (void)idx;
+ (void)cbdata;
+ return 0;
+}
+
+void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func ecmult_multi) {
+ int ncount;
+ secp256k1_scalar szero;
+ secp256k1_scalar sc[32];
+ secp256k1_ge pt[32];
+ secp256k1_gej r;
+ secp256k1_gej r2;
+ ecmult_multi_data data;
+ secp256k1_scratch *scratch_empty;
+
+ data.sc = sc;
+ data.pt = pt;
+ secp256k1_scalar_set_int(&szero, 0);
+
+ /* No points to multiply */
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, NULL, ecmult_multi_callback, &data, 0));
+
+ /* Check 1- and 2-point multiplies against ecmult */
+ for (ncount = 0; ncount < count; ncount++) {
+ secp256k1_ge ptg;
+ secp256k1_gej ptgj;
+ random_scalar_order(&sc[0]);
+ random_scalar_order(&sc[1]);
+
+ random_group_element_test(&ptg);
+ secp256k1_gej_set_ge(&ptgj, &ptg);
+ pt[0] = ptg;
+ pt[1] = secp256k1_ge_const_g;
+
+ /* only G scalar */
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &szero, &sc[0]);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0));
+ secp256k1_gej_neg(&r2, &r2);
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+
+ /* 1-point */
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &szero);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 1));
+ secp256k1_gej_neg(&r2, &r2);
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+
+ /* Try to multiply 1 point, but scratch space is empty */
+ scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0);
+ CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1));
+ secp256k1_scratch_destroy(scratch_empty);
+
+ /* Try to multiply 1 point, but callback returns false */
+ CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1));
+
+ /* 2-point */
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 2));
+ secp256k1_gej_neg(&r2, &r2);
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+
+ /* 2-point with G scalar */
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1));
+ secp256k1_gej_neg(&r2, &r2);
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ /* Check infinite outputs of various forms */
+ for (ncount = 0; ncount < count; ncount++) {
+ secp256k1_ge ptg;
+ size_t i, j;
+ size_t sizes[] = { 2, 10, 32 };
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 32; i++) {
+ random_scalar_order(&sc[i]);
+ secp256k1_ge_set_infinity(&pt[i]);
+ }
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 32; i++) {
+ random_group_element_test(&ptg);
+ pt[i] = ptg;
+ secp256k1_scalar_set_int(&sc[i], 0);
+ }
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ for (j = 0; j < 3; j++) {
+ random_group_element_test(&ptg);
+ for (i = 0; i < 16; i++) {
+ random_scalar_order(&sc[2*i]);
+ secp256k1_scalar_negate(&sc[2*i + 1], &sc[2*i]);
+ pt[2 * i] = ptg;
+ pt[2 * i + 1] = ptg;
+ }
+
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(secp256k1_gej_is_infinity(&r));
+
+ random_scalar_order(&sc[0]);
+ for (i = 0; i < 16; i++) {
+ random_group_element_test(&ptg);
+
+ sc[2*i] = sc[0];
+ sc[2*i+1] = sc[0];
+ pt[2 * i] = ptg;
+ secp256k1_ge_neg(&pt[2*i+1], &pt[2*i]);
+ }
+
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ random_group_element_test(&ptg);
+ secp256k1_scalar_set_int(&sc[0], 0);
+ pt[0] = ptg;
+ for (i = 1; i < 32; i++) {
+ pt[i] = ptg;
+
+ random_scalar_order(&sc[i]);
+ secp256k1_scalar_add(&sc[0], &sc[0], &sc[i]);
+ secp256k1_scalar_negate(&sc[i], &sc[i]);
+ }
+
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32));
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ /* Check random points, constant scalar */
+ for (ncount = 0; ncount < count; ncount++) {
+ size_t i;
+ secp256k1_gej_set_infinity(&r);
+
+ random_scalar_order(&sc[0]);
+ for (i = 0; i < 20; i++) {
+ secp256k1_ge ptg;
+ sc[i] = sc[0];
+ random_group_element_test(&ptg);
+ pt[i] = ptg;
+ secp256k1_gej_add_ge_var(&r, &r, &pt[i], NULL);
+ }
+
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r, &sc[0], &szero);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
+ secp256k1_gej_neg(&r2, &r2);
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ /* Check random scalars, constant point */
+ for (ncount = 0; ncount < count; ncount++) {
+ size_t i;
+ secp256k1_ge ptg;
+ secp256k1_gej p0j;
+ secp256k1_scalar rs;
+ secp256k1_scalar_set_int(&rs, 0);
+
+ random_group_element_test(&ptg);
+ for (i = 0; i < 20; i++) {
+ random_scalar_order(&sc[i]);
+ pt[i] = ptg;
+ secp256k1_scalar_add(&rs, &rs, &sc[i]);
+ }
+
+ secp256k1_gej_set_ge(&p0j, &pt[0]);
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &p0j, &rs, &szero);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
+ secp256k1_gej_neg(&r2, &r2);
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+ }
+
+ /* Sanity check that zero scalars don't cause problems */
+ for (ncount = 0; ncount < 20; ncount++) {
+ random_scalar_order(&sc[ncount]);
+ random_group_element_test(&pt[ncount]);
+ }
+
+ secp256k1_scalar_clear(&sc[0]);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
+ secp256k1_scalar_clear(&sc[1]);
+ secp256k1_scalar_clear(&sc[2]);
+ secp256k1_scalar_clear(&sc[3]);
+ secp256k1_scalar_clear(&sc[4]);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 6));
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 5));
+ CHECK(secp256k1_gej_is_infinity(&r));
+
+ /* Run through s0*(t0*P) + s1*(t1*P) exhaustively for many small values of s0, s1, t0, t1 */
+ {
+ const size_t TOP = 8;
+ size_t s0i, s1i;
+ size_t t0i, t1i;
+ secp256k1_ge ptg;
+ secp256k1_gej ptgj;
+
+ random_group_element_test(&ptg);
+ secp256k1_gej_set_ge(&ptgj, &ptg);
+
+ for(t0i = 0; t0i < TOP; t0i++) {
+ for(t1i = 0; t1i < TOP; t1i++) {
+ secp256k1_gej t0p, t1p;
+ secp256k1_scalar t0, t1;
+
+ secp256k1_scalar_set_int(&t0, (t0i + 1) / 2);
+ secp256k1_scalar_cond_negate(&t0, t0i & 1);
+ secp256k1_scalar_set_int(&t1, (t1i + 1) / 2);
+ secp256k1_scalar_cond_negate(&t1, t1i & 1);
+
+ secp256k1_ecmult(&ctx->ecmult_ctx, &t0p, &ptgj, &t0, &szero);
+ secp256k1_ecmult(&ctx->ecmult_ctx, &t1p, &ptgj, &t1, &szero);
+
+ for(s0i = 0; s0i < TOP; s0i++) {
+ for(s1i = 0; s1i < TOP; s1i++) {
+ secp256k1_scalar tmp1, tmp2;
+ secp256k1_gej expected, actual;
+
+ secp256k1_ge_set_gej(&pt[0], &t0p);
+ secp256k1_ge_set_gej(&pt[1], &t1p);
+
+ secp256k1_scalar_set_int(&sc[0], (s0i + 1) / 2);
+ secp256k1_scalar_cond_negate(&sc[0], s0i & 1);
+ secp256k1_scalar_set_int(&sc[1], (s1i + 1) / 2);
+ secp256k1_scalar_cond_negate(&sc[1], s1i & 1);
+
+ secp256k1_scalar_mul(&tmp1, &t0, &sc[0]);
+ secp256k1_scalar_mul(&tmp2, &t1, &sc[1]);
+ secp256k1_scalar_add(&tmp1, &tmp1, &tmp2);
+
+ secp256k1_ecmult(&ctx->ecmult_ctx, &expected, &ptgj, &tmp1, &szero);
+ CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &actual, &szero, ecmult_multi_callback, &data, 2));
+ secp256k1_gej_neg(&expected, &expected);
+ secp256k1_gej_add_var(&actual, &actual, &expected, NULL);
+ CHECK(secp256k1_gej_is_infinity(&actual));
+ }
+ }
+ }
+ }
+ }
+}
+
+void test_secp256k1_pippenger_bucket_window_inv(void) {
+ int i;
+
+ CHECK(secp256k1_pippenger_bucket_window_inv(0) == 0);
+ for(i = 1; i <= PIPPENGER_MAX_BUCKET_WINDOW; i++) {
+#ifdef USE_ENDOMORPHISM
+ /* Bucket_window of 8 is not used with endo */
+ if (i == 8) {
+ continue;
+ }
+#endif
+ CHECK(secp256k1_pippenger_bucket_window(secp256k1_pippenger_bucket_window_inv(i)) == i);
+ if (i != PIPPENGER_MAX_BUCKET_WINDOW) {
+ CHECK(secp256k1_pippenger_bucket_window(secp256k1_pippenger_bucket_window_inv(i)+1) > i);
+ }
+ }
+}
+
+/**
+ * Probabilistically test the function returning the maximum number of possible points
+ * for a given scratch space.
+ */
+void test_ecmult_multi_pippenger_max_points(void) {
+ size_t scratch_size = secp256k1_rand_int(256);
+ size_t max_size = secp256k1_pippenger_scratch_size(secp256k1_pippenger_bucket_window_inv(PIPPENGER_MAX_BUCKET_WINDOW-1)+512, 12);
+ secp256k1_scratch *scratch;
+ size_t n_points_supported;
+ int bucket_window = 0;
+
+ for(; scratch_size < max_size; scratch_size+=256) {
+ scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
+ CHECK(scratch != NULL);
+ n_points_supported = secp256k1_pippenger_max_points(scratch);
+ if (n_points_supported == 0) {
+ secp256k1_scratch_destroy(scratch);
+ continue;
+ }
+ bucket_window = secp256k1_pippenger_bucket_window(n_points_supported);
+ CHECK(secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points_supported, bucket_window), PIPPENGER_SCRATCH_OBJECTS));
+ secp256k1_scratch_deallocate_frame(scratch);
+ secp256k1_scratch_destroy(scratch);
+ }
+ CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW);
+}
+
+void test_ecmult_multi_batch_size_helper(void) {
+ size_t n_batches, n_batch_points, max_n_batch_points, n;
+
+ max_n_batch_points = 0;
+ n = 1;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 0);
+
+ max_n_batch_points = 1;
+ n = 0;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
+ CHECK(n_batches == 0);
+ CHECK(n_batch_points == 0);
+
+ max_n_batch_points = 2;
+ n = 5;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
+ CHECK(n_batches == 3);
+ CHECK(n_batch_points == 2);
+
+ max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH;
+ n = ECMULT_MAX_POINTS_PER_BATCH;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
+ CHECK(n_batches == 1);
+ CHECK(n_batch_points == ECMULT_MAX_POINTS_PER_BATCH);
+
+ max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH + 1;
+ n = ECMULT_MAX_POINTS_PER_BATCH + 1;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
+ CHECK(n_batches == 2);
+ CHECK(n_batch_points == ECMULT_MAX_POINTS_PER_BATCH/2 + 1);
+
+ max_n_batch_points = 1;
+ n = SIZE_MAX;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
+ CHECK(n_batches == SIZE_MAX);
+ CHECK(n_batch_points == 1);
+
+ max_n_batch_points = 2;
+ n = SIZE_MAX;
+ CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
+ CHECK(n_batches == SIZE_MAX/2 + 1);
+ CHECK(n_batch_points == 2);
+}
+
+/**
+ * Run secp256k1_ecmult_multi_var with num points and a scratch space restricted to
+ * 1 <= i <= num points.
+ */
+void test_ecmult_multi_batching(void) {
+ static const int n_points = 2*ECMULT_PIPPENGER_THRESHOLD;
+ secp256k1_scalar scG;
+ secp256k1_scalar szero;
+ secp256k1_scalar *sc = (secp256k1_scalar *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_scalar) * n_points);
+ secp256k1_ge *pt = (secp256k1_ge *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_ge) * n_points);
+ secp256k1_gej r;
+ secp256k1_gej r2;
+ ecmult_multi_data data;
+ int i;
+ secp256k1_scratch *scratch;
+
+ secp256k1_gej_set_infinity(&r2);
+ secp256k1_scalar_set_int(&szero, 0);
+
+ /* Get random scalars and group elements and compute result */
+ random_scalar_order(&scG);
+ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r2, &szero, &scG);
+ for(i = 0; i < n_points; i++) {
+ secp256k1_ge ptg;
+ secp256k1_gej ptgj;
+ random_group_element_test(&ptg);
+ secp256k1_gej_set_ge(&ptgj, &ptg);
+ pt[i] = ptg;
+ random_scalar_order(&sc[i]);
+ secp256k1_ecmult(&ctx->ecmult_ctx, &ptgj, &ptgj, &sc[i], NULL);
+ secp256k1_gej_add_var(&r2, &r2, &ptgj, NULL);
+ }
+ data.sc = sc;
+ data.pt = pt;
+
+ /* Test with empty scratch space */
+ scratch = secp256k1_scratch_create(&ctx->error_callback, 0);
+ CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1));
+ secp256k1_scratch_destroy(scratch);
+
+ /* Test with space for 1 point in pippenger. That's not enough because
+ * ecmult_multi selects strauss which requires more memory. */
+ scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
+ CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1));
+ secp256k1_scratch_destroy(scratch);
+
+ secp256k1_gej_neg(&r2, &r2);
+ for(i = 1; i <= n_points; i++) {
+ if (i > ECMULT_PIPPENGER_THRESHOLD) {
+ int bucket_window = secp256k1_pippenger_bucket_window(i);
+ size_t scratch_size = secp256k1_pippenger_scratch_size(i, bucket_window);
+ scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
+ } else {
+ size_t scratch_size = secp256k1_strauss_scratch_size(i);
+ scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
+ }
+ CHECK(secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points));
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+ secp256k1_scratch_destroy(scratch);
+ }
+ free(sc);
+ free(pt);
+}
+
+void run_ecmult_multi_tests(void) {
+ secp256k1_scratch *scratch;
+
+ test_secp256k1_pippenger_bucket_window_inv();
+ test_ecmult_multi_pippenger_max_points();
+ scratch = secp256k1_scratch_create(&ctx->error_callback, 819200);
+ test_ecmult_multi(scratch, secp256k1_ecmult_multi_var);
+ test_ecmult_multi(NULL, secp256k1_ecmult_multi_var);
+ test_ecmult_multi(scratch, secp256k1_ecmult_pippenger_batch_single);
+ test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single);
+ secp256k1_scratch_destroy(scratch);
+
+ /* Run test_ecmult_multi with space for exactly one point */
+ scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
+ test_ecmult_multi(scratch, secp256k1_ecmult_multi_var);
+ secp256k1_scratch_destroy(scratch);
+
+ test_ecmult_multi_batch_size_helper();
+ test_ecmult_multi_batching();
+}
+
void test_wnaf(const secp256k1_scalar *number, int w) {
secp256k1_scalar x, two, t;
int wnaf[256];
@@ -2541,6 +3038,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
int wnaf[256] = {0};
int i;
int skew;
+ int bits = 256;
secp256k1_scalar num = *number;
secp256k1_scalar_set_int(&x, 0);
@@ -2550,10 +3048,11 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
for (i = 0; i < 16; ++i) {
secp256k1_scalar_shr_int(&num, 8);
}
+ bits = 128;
#endif
- skew = secp256k1_wnaf_const(wnaf, num, w);
+ skew = secp256k1_wnaf_const(wnaf, num, w, bits);
- for (i = WNAF_SIZE(w); i >= 0; --i) {
+ for (i = WNAF_SIZE_BITS(bits, w); i >= 0; --i) {
secp256k1_scalar t;
int v = wnaf[i];
CHECK(v != 0); /* check nonzero */
@@ -2575,6 +3074,110 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
CHECK(secp256k1_scalar_eq(&x, &num));
}
+void test_fixed_wnaf(const secp256k1_scalar *number, int w) {
+ secp256k1_scalar x, shift;
+ int wnaf[256] = {0};
+ int i;
+ int skew;
+ secp256k1_scalar num = *number;
+
+ secp256k1_scalar_set_int(&x, 0);
+ secp256k1_scalar_set_int(&shift, 1 << w);
+ /* With USE_ENDOMORPHISM on we only consider 128-bit numbers */
+#ifdef USE_ENDOMORPHISM
+ for (i = 0; i < 16; ++i) {
+ secp256k1_scalar_shr_int(&num, 8);
+ }
+#endif
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+
+ for (i = WNAF_SIZE(w)-1; i >= 0; --i) {
+ secp256k1_scalar t;
+ int v = wnaf[i];
+ CHECK(v == 0 || v & 1); /* check parity */
+ CHECK(v > -(1 << w)); /* check range above */
+ CHECK(v < (1 << w)); /* check range below */
+
+ secp256k1_scalar_mul(&x, &x, &shift);
+ if (v >= 0) {
+ secp256k1_scalar_set_int(&t, v);
+ } else {
+ secp256k1_scalar_set_int(&t, -v);
+ secp256k1_scalar_negate(&t, &t);
+ }
+ secp256k1_scalar_add(&x, &x, &t);
+ }
+ /* If skew is 1 then add 1 to num */
+ secp256k1_scalar_cadd_bit(&num, 0, skew == 1);
+ CHECK(secp256k1_scalar_eq(&x, &num));
+}
+
+/* Checks that the first 8 elements of wnaf are equal to wnaf_expected and the
+ * rest is 0.*/
+void test_fixed_wnaf_small_helper(int *wnaf, int *wnaf_expected, int w) {
+ int i;
+ for (i = WNAF_SIZE(w)-1; i >= 8; --i) {
+ CHECK(wnaf[i] == 0);
+ }
+ for (i = 7; i >= 0; --i) {
+ CHECK(wnaf[i] == wnaf_expected[i]);
+ }
+}
+
+void test_fixed_wnaf_small(void) {
+ int w = 4;
+ int wnaf[256] = {0};
+ int i;
+ int skew;
+ secp256k1_scalar num;
+
+ secp256k1_scalar_set_int(&num, 0);
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+ for (i = WNAF_SIZE(w)-1; i >= 0; --i) {
+ int v = wnaf[i];
+ CHECK(v == 0);
+ }
+ CHECK(skew == 0);
+
+ secp256k1_scalar_set_int(&num, 1);
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+ for (i = WNAF_SIZE(w)-1; i >= 1; --i) {
+ int v = wnaf[i];
+ CHECK(v == 0);
+ }
+ CHECK(wnaf[0] == 1);
+ CHECK(skew == 0);
+
+ {
+ int wnaf_expected[8] = { 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf };
+ secp256k1_scalar_set_int(&num, 0xffffffff);
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+ test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w);
+ CHECK(skew == 0);
+ }
+ {
+ int wnaf_expected[8] = { -1, -1, -1, -1, -1, -1, -1, 0xf };
+ secp256k1_scalar_set_int(&num, 0xeeeeeeee);
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+ test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w);
+ CHECK(skew == 1);
+ }
+ {
+ int wnaf_expected[8] = { 1, 0, 1, 0, 1, 0, 1, 0 };
+ secp256k1_scalar_set_int(&num, 0x01010101);
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+ test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w);
+ CHECK(skew == 0);
+ }
+ {
+ int wnaf_expected[8] = { -0xf, 0, 0xf, -0xf, 0, 0xf, 1, 0 };
+ secp256k1_scalar_set_int(&num, 0x01ef1ef1);
+ skew = secp256k1_wnaf_fixed(wnaf, &num, w);
+ test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w);
+ CHECK(skew == 0);
+ }
+}
+
void run_wnaf(void) {
int i;
secp256k1_scalar n = {{0}};
@@ -2585,12 +3188,15 @@ void run_wnaf(void) {
test_constant_wnaf(&n, 4);
n.d[0] = 2;
test_constant_wnaf(&n, 4);
+ /* Test 0 */
+ test_fixed_wnaf_small();
/* Random tests */
for (i = 0; i < count; i++) {
random_scalar_order(&n);
test_wnaf(&n, 4+(i%10));
test_constant_wnaf_negate(&n);
test_constant_wnaf(&n, 4 + (i % 10));
+ test_fixed_wnaf(&n, 4 + (i % 10));
}
secp256k1_scalar_set_int(&n, 0);
CHECK(secp256k1_scalar_cond_negate(&n, 1) == -1);
@@ -3055,6 +3661,7 @@ void run_ec_pubkey_parse_test(void) {
ecount = 0;
VG_UNDEF(&pubkey, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 65) == 1);
+ CHECK(secp256k1_ec_pubkey_parse(secp256k1_context_no_precomp, &pubkey, pubkeyc, 65) == 1);
VG_CHECK(&pubkey, sizeof(pubkey));
CHECK(ecount == 0);
VG_UNDEF(&ge, sizeof(ge));
@@ -3177,7 +3784,7 @@ void run_eckey_edge_case_test(void) {
VG_CHECK(&pubkey, sizeof(pubkey));
CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
pubkey_negone = pubkey;
- /* Tweak of zero leaves the value changed. */
+ /* Tweak of zero leaves the value unchanged. */
memset(ctmp2, 0, 32);
CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, ctmp2) == 1);
CHECK(memcmp(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40);
@@ -3668,6 +4275,7 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_
#ifdef ENABLE_OPENSSL_TESTS
ECDSA_SIG *sig_openssl;
+ const BIGNUM *r = NULL, *s = NULL;
const unsigned char *sigptr;
unsigned char roundtrip_openssl[2048];
int len_openssl = 2048;
@@ -3719,15 +4327,16 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_
sigptr = sig;
parsed_openssl = (d2i_ECDSA_SIG(&sig_openssl, &sigptr, siglen) != NULL);
if (parsed_openssl) {
- valid_openssl = !BN_is_negative(sig_openssl->r) && !BN_is_negative(sig_openssl->s) && BN_num_bits(sig_openssl->r) > 0 && BN_num_bits(sig_openssl->r) <= 256 && BN_num_bits(sig_openssl->s) > 0 && BN_num_bits(sig_openssl->s) <= 256;
+ ECDSA_SIG_get0(sig_openssl, &r, &s);
+ valid_openssl = !BN_is_negative(r) && !BN_is_negative(s) && BN_num_bits(r) > 0 && BN_num_bits(r) <= 256 && BN_num_bits(s) > 0 && BN_num_bits(s) <= 256;
if (valid_openssl) {
unsigned char tmp[32] = {0};
- BN_bn2bin(sig_openssl->r, tmp + 32 - BN_num_bytes(sig_openssl->r));
+ BN_bn2bin(r, tmp + 32 - BN_num_bytes(r));
valid_openssl = memcmp(tmp, max_scalar, 32) < 0;
}
if (valid_openssl) {
unsigned char tmp[32] = {0};
- BN_bn2bin(sig_openssl->s, tmp + 32 - BN_num_bytes(sig_openssl->s));
+ BN_bn2bin(s, tmp + 32 - BN_num_bytes(s));
valid_openssl = memcmp(tmp, max_scalar, 32) < 0;
}
}
@@ -4431,8 +5040,9 @@ int main(int argc, char **argv) {
}
} else {
FILE *frand = fopen("/dev/urandom", "r");
- if ((frand == NULL) || !fread(&seed16, sizeof(seed16), 1, frand)) {
+ if ((frand == NULL) || fread(&seed16, 1, sizeof(seed16), frand) != sizeof(seed16)) {
uint64_t t = time(NULL) * (uint64_t)1337;
+ fprintf(stderr, "WARNING: could not read 16 bytes from /dev/urandom; falling back to insecure PRNG\n");
seed16[0] ^= t;
seed16[1] ^= t >> 8;
seed16[2] ^= t >> 16;
@@ -4442,7 +5052,9 @@ int main(int argc, char **argv) {
seed16[6] ^= t >> 48;
seed16[7] ^= t >> 56;
}
- fclose(frand);
+ if (frand) {
+ fclose(frand);
+ }
}
secp256k1_rand_seed(seed16);
@@ -4451,6 +5063,7 @@ int main(int argc, char **argv) {
/* initialize */
run_context_tests();
+ run_scratch_tests();
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
if (secp256k1_rand_bits(1)) {
secp256k1_rand256(run32);
@@ -4492,6 +5105,7 @@ int main(int argc, char **argv) {
run_ecmult_constants();
run_ecmult_gen_blind();
run_ecmult_const_tests();
+ run_ecmult_multi_tests();
run_ec_combine();
/* endomorphism tests */
diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c
index b040bb0733..ab9779b02f 100644
--- a/src/secp256k1/src/tests_exhaustive.c
+++ b/src/secp256k1/src/tests_exhaustive.c
@@ -174,7 +174,7 @@ void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *gr
ge_equals_gej(&group[(i * r_log + j) % order], &tmp);
if (i > 0) {
- secp256k1_ecmult_const(&tmp, &group[i], &ng);
+ secp256k1_ecmult_const(&tmp, &group[i], &ng, 256);
ge_equals_gej(&group[(i * j) % order], &tmp);
}
}
@@ -182,6 +182,46 @@ void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *gr
}
}
+typedef struct {
+ secp256k1_scalar sc[2];
+ secp256k1_ge pt[2];
+} ecmult_multi_data;
+
+static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) {
+ ecmult_multi_data *data = (ecmult_multi_data*) cbdata;
+ *sc = data->sc[idx];
+ *pt = data->pt[idx];
+ return 1;
+}
+
+void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
+ int i, j, k, x, y;
+ secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, 4096);
+ for (i = 0; i < order; i++) {
+ for (j = 0; j < order; j++) {
+ for (k = 0; k < order; k++) {
+ for (x = 0; x < order; x++) {
+ for (y = 0; y < order; y++) {
+ secp256k1_gej tmp;
+ secp256k1_scalar g_sc;
+ ecmult_multi_data data;
+
+ secp256k1_scalar_set_int(&data.sc[0], i);
+ secp256k1_scalar_set_int(&data.sc[1], j);
+ secp256k1_scalar_set_int(&g_sc, k);
+ data.pt[0] = group[x];
+ data.pt[1] = group[y];
+
+ secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2);
+ ge_equals_gej(&group[(i * x + j * y + k) % order], &tmp);
+ }
+ }
+ }
+ }
+ }
+ secp256k1_scratch_destroy(scratch);
+}
+
void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) {
secp256k1_fe x;
unsigned char x_bin[32];
@@ -456,6 +496,7 @@ int main(void) {
#endif
test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER);
test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER);
+ test_exhaustive_ecmult_multi(ctx, group, EXHAUSTIVE_TEST_ORDER);
test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER);
test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER);
diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h
index b0441d8e30..e1f5b76452 100644
--- a/src/secp256k1/src/util.h
+++ b/src/secp256k1/src/util.h
@@ -36,7 +36,7 @@ static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback *
} while(0)
#endif
-#ifdef HAVE_BUILTIN_EXPECT
+#if SECP256K1_GNUC_PREREQ(3, 0)
#define EXPECT(x,c) __builtin_expect((x),(c))
#else
#define EXPECT(x,c) (x)
@@ -76,6 +76,14 @@ static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_
return ret;
}
+static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void *ptr, size_t size) {
+ void *ret = realloc(ptr, size);
+ if (ret == NULL) {
+ secp256k1_callback_call(cb, "Out of memory");
+ }
+ return ret;
+}
+
/* Macro for restrict, when available and not in a VERIFY build. */
#if defined(SECP256K1_BUILD) && defined(VERIFY)
# define SECP256K1_RESTRICT
diff --git a/src/serialize.h b/src/serialize.h
index b001ee1324..1dc27d84eb 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -670,7 +670,7 @@ void Unserialize_impl(Stream& is, prevector<N, T>& v, const unsigned char&)
while (i < nSize)
{
unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T)));
- v.resize(i + blk);
+ v.resize_uninitialized(i + blk);
is.read((char*)&v[i], blk * sizeof(T));
i += blk;
}
@@ -688,8 +688,8 @@ void Unserialize_impl(Stream& is, prevector<N, T>& v, const V&)
nMid += 5000000 / sizeof(T);
if (nMid > nSize)
nMid = nSize;
- v.resize(nMid);
- for (; i < nMid; i++)
+ v.resize_uninitialized(nMid);
+ for (; i < nMid; ++i)
Unserialize(is, v[i]);
}
}
diff --git a/src/sync.cpp b/src/sync.cpp
index e7c0a6f9bc..20258d8e9a 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <sync.h>
#include <tinyformat.h>
@@ -53,7 +57,7 @@ struct CLockLocation {
std::string ToString() const
{
- return tfm::format(
+ return strprintf(
"%s %s:%s%s (in thread %s)",
mutexName, sourceFile, itostr(sourceLine), (fTry ? " (TRY)" : ""), m_thread_name);
}
@@ -114,7 +118,7 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
LogPrintf(" %s\n", i.second.ToString());
}
if (g_debug_lockorder_abort) {
- fprintf(stderr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__);
+ tfm::format(std::cerr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__);
abort();
}
throw std::logic_error("potential deadlock detected");
@@ -171,7 +175,7 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine,
for (const std::pair<void*, CLockLocation>& i : g_lockstack)
if (i.first == cs)
return;
- fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
+ tfm::format(std::cerr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
abort();
}
@@ -179,7 +183,7 @@ void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLi
{
for (const std::pair<void*, CLockLocation>& i : g_lockstack) {
if (i.first == cs) {
- fprintf(stderr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
+ tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
abort();
}
}
diff --git a/src/sync.h b/src/sync.h
index 2667fb52f9..bdbdde1a2a 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -304,4 +304,18 @@ public:
}
};
+// Utility class for indicating to compiler thread analysis that a mutex is
+// locked (when it couldn't be determined otherwise).
+struct SCOPED_LOCKABLE LockAssertion
+{
+ template <typename Mutex>
+ explicit LockAssertion(Mutex& mutex) EXCLUSIVE_LOCK_FUNCTION(mutex)
+ {
+#ifdef DEBUG_LOCKORDER
+ AssertLockHeld(mutex);
+#endif
+ }
+ ~LockAssertion() UNLOCK_FUNCTION() {}
+};
+
#endif // BITCOIN_SYNC_H
diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp
index f255691704..e333763f27 100644
--- a/src/test/allocator_tests.cpp
+++ b/src/test/allocator_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <util/memory.h>
#include <util/system.h>
-#include <support/allocators/secure.h>
#include <test/setup_common.h>
#include <memory>
diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp
index 809c627d27..9ac87261b6 100644
--- a/src/test/arith_uint256_tests.cpp
+++ b/src/test/arith_uint256_tests.cpp
@@ -11,7 +11,6 @@
#include <uint256.h>
#include <arith_uint256.h>
#include <string>
-#include <version.h>
#include <test/setup_common.h>
BOOST_FIXTURE_TEST_SUITE(arith_uint256_tests, BasicTestingSetup)
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index 0c0423c0db..662878750e 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -4,9 +4,10 @@
#include <boost/test/unit_test.hpp>
+#include <clientversion.h>
#include <key.h>
#include <key_io.h>
-#include <uint256.h>
+#include <streams.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <test/setup_common.h>
diff --git a/src/test/blockchain_tests.cpp b/src/test/blockchain_tests.cpp
index 13afcca375..ca75563ef0 100644
--- a/src/test/blockchain_tests.cpp
+++ b/src/test/blockchain_tests.cpp
@@ -2,6 +2,7 @@
#include <stdlib.h>
+#include <chain.h>
#include <rpc/blockchain.h>
#include <test/setup_common.h>
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index f57e1a0ebd..dac201a35f 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -6,7 +6,7 @@
#include <consensus/merkle.h>
#include <chainparams.h>
#include <pow.h>
-#include <random.h>
+#include <streams.h>
#include <test/setup_common.h>
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index db0b973463..cf87aa9303 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -10,6 +10,7 @@
#include <pow.h>
#include <test/setup_common.h>
#include <script/standard.h>
+#include <util/time.h>
#include <validation.h>
#include <boost/test/unit_test.hpp>
@@ -125,9 +126,9 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
std::vector<BlockFilter> filters;
std::vector<uint256> filter_hashes;
- for (const CBlockIndex* block_index = chainActive.Genesis();
+ for (const CBlockIndex* block_index = ::ChainActive().Genesis();
block_index != nullptr;
- block_index = chainActive.Next(block_index)) {
+ block_index = ::ChainActive().Next(block_index)) {
BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
@@ -153,9 +154,9 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
{
LOCK(cs_main);
const CBlockIndex* block_index;
- for (block_index = chainActive.Genesis();
+ for (block_index = ::ChainActive().Genesis();
block_index != nullptr;
- block_index = chainActive.Next(block_index)) {
+ block_index = ::ChainActive().Next(block_index)) {
CheckFilterLookups(filter_index, block_index, last_header);
}
}
@@ -164,9 +165,9 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
const CBlockIndex* tip;
{
LOCK(cs_main);
- tip = chainActive.Tip();
+ tip = ::ChainActive().Tip();
}
- CScript coinbase_script_pub_key = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
+ CScript coinbase_script_pub_key = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));
std::vector<std::shared_ptr<CBlock>> chainA, chainB;
BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key, 10, chainA));
BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key, 10, chainB));
@@ -250,7 +251,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
{
LOCK(cs_main);
- tip = chainActive.Tip();
+ tip = ::ChainActive().Tip();
}
BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
@@ -267,8 +268,6 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(blockfilter_index_init_destroy, BasicTestingSetup)
{
- SetDataDir("tempdir");
-
BlockFilterIndex* filter_index;
filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 408a7fbda4..d796444419 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <util/memory.h>
#include <util/system.h>
#include <util/time.h>
#include <validation.h>
@@ -17,8 +18,6 @@
#include <condition_variable>
#include <unordered_set>
-#include <memory>
-#include <random.h>
// BasicTestingSetup not sufficient because nScriptCheckThreads is not set
// otherwise.
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 232c077c68..948591196c 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -3,14 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <attributes.h>
+#include <clientversion.h>
#include <coins.h>
-#include <consensus/validation.h>
#include <script/standard.h>
+#include <streams.h>
#include <test/setup_common.h>
#include <uint256.h>
#include <undo.h>
#include <util/strencodings.h>
-#include <validation.h>
#include <map>
#include <vector>
@@ -280,6 +280,7 @@ UtxoData::iterator FindRandomFrom(const std::set<COutPoint> &utxoSet) {
BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
{
SeedInsecureRand(/* deterministic */ true);
+ g_mock_deterministic_tests = true;
bool spent_a_duplicate_coinbase = false;
// A simple map to track what we expect the cache stack to represent.
@@ -474,6 +475,8 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
// Verify coverage.
BOOST_CHECK(spent_a_duplicate_coinbase);
+
+ g_mock_deterministic_tests = false;
}
BOOST_AUTO_TEST_CASE(ccoins_serialization)
@@ -485,7 +488,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
BOOST_CHECK_EQUAL(cc1.fCoinBase, false);
BOOST_CHECK_EQUAL(cc1.nHeight, 203998U);
BOOST_CHECK_EQUAL(cc1.out.nValue, CAmount{60000000000});
- BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))))));
+ BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))))));
// Good example
CDataStream ss2(ParseHex("8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"), SER_DISK, CLIENT_VERSION);
@@ -494,7 +497,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
BOOST_CHECK_EQUAL(cc2.fCoinBase, true);
BOOST_CHECK_EQUAL(cc2.nHeight, 120891U);
BOOST_CHECK_EQUAL(cc2.out.nValue, 110397);
- BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
+ BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
// Smallest possible example
CDataStream ss3(ParseHex("000006"), SER_DISK, CLIENT_VERSION);
diff --git a/src/test/compilerbug_tests.cpp b/src/test/compilerbug_tests.cpp
new file mode 100644
index 0000000000..74e1eac3ea
--- /dev/null
+++ b/src/test/compilerbug_tests.cpp
@@ -0,0 +1,43 @@
+// Copyright (c) 2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <test/setup_common.h>
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(compilerbug_tests, BasicTestingSetup)
+
+#if defined(__GNUC__)
+// This block will also be built under clang, which is fine (as it supports noinline)
+void __attribute__ ((noinline)) set_one(unsigned char* ptr)
+{
+ *ptr = 1;
+}
+
+int __attribute__ ((noinline)) check_zero(unsigned char const* in, unsigned int len)
+{
+ for (unsigned int i = 0; i < len; ++i) {
+ if (in[i] != 0) return 0;
+ }
+ return 1;
+}
+
+void set_one_on_stack() {
+ unsigned char buf[1];
+ set_one(buf);
+}
+
+BOOST_AUTO_TEST_CASE(gccbug_90348) {
+ // Test for GCC bug 90348. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90348
+ for (int i = 0; i <= 4; ++i) {
+ unsigned char in[4];
+ for (int j = 0; j < i; ++j) {
+ in[j] = 0;
+ set_one_on_stack(); // Apparently modifies in[0]
+ }
+ BOOST_CHECK(check_zero(in, i));
+ }
+}
+#endif
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 0d05b6514f..4e2acca4c3 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -5,12 +5,13 @@
#include <crypto/aes.h>
#include <crypto/chacha20.h>
#include <crypto/poly1305.h>
+#include <crypto/hkdf_sha256_32.h>
+#include <crypto/hmac_sha256.h>
+#include <crypto/hmac_sha512.h>
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
#include <crypto/sha512.h>
-#include <crypto/hmac_sha256.h>
-#include <crypto/hmac_sha512.h>
#include <random.h>
#include <util/strencodings.h>
#include <test/setup_common.h>
@@ -125,17 +126,36 @@ static void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, b
}
}
-static void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout)
+static void TestChaCha20(const std::string &hex_message, const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout)
{
std::vector<unsigned char> key = ParseHex(hexkey);
+ std::vector<unsigned char> m = ParseHex(hex_message);
ChaCha20 rng(key.data(), key.size());
rng.SetIV(nonce);
rng.Seek(seek);
std::vector<unsigned char> out = ParseHex(hexout);
std::vector<unsigned char> outres;
outres.resize(out.size());
- rng.Output(outres.data(), outres.size());
+ assert(hex_message.empty() || m.size() == out.size());
+
+ // perform the ChaCha20 round(s), if message is provided it will output the encrypted ciphertext otherwise the keystream
+ if (!hex_message.empty()) {
+ rng.Crypt(m.data(), outres.data(), outres.size());
+ } else {
+ rng.Keystream(outres.data(), outres.size());
+ }
BOOST_CHECK(out == outres);
+ if (!hex_message.empty()) {
+ // Manually XOR with the keystream and compare the output
+ rng.SetIV(nonce);
+ rng.Seek(seek);
+ std::vector<unsigned char> only_keystream(outres.size());
+ rng.Keystream(only_keystream.data(), only_keystream.size());
+ for (size_t i = 0; i != m.size(); i++) {
+ outres[i] = m[i] ^ only_keystream[i];
+ }
+ BOOST_CHECK(out == outres);
+ }
}
static void TestPoly1305(const std::string &hexmessage, const std::string &hexkey, const std::string& hextag)
@@ -149,6 +169,22 @@ static void TestPoly1305(const std::string &hexmessage, const std::string &hexke
BOOST_CHECK(tag == tagres);
}
+static void TestHKDF_SHA256_32(const std::string &ikm_hex, const std::string &salt_hex, const std::string &info_hex, const std::string &okm_check_hex) {
+ std::vector<unsigned char> initial_key_material = ParseHex(ikm_hex);
+ std::vector<unsigned char> salt = ParseHex(salt_hex);
+ std::vector<unsigned char> info = ParseHex(info_hex);
+
+
+ // our implementation only supports strings for the "info" and "salt", stringify them
+ std::string salt_stringified(reinterpret_cast<char*>(salt.data()), salt.size());
+ std::string info_stringified(reinterpret_cast<char*>(info.data()), info.size());
+
+ CHKDF_HMAC_SHA256_L32 hkdf32(initial_key_material.data(), initial_key_material.size(), salt_stringified);
+ unsigned char out[32];
+ hkdf32.Expand32(info_stringified, out);
+ BOOST_CHECK(HexStr(out, out + 32) == okm_check_hex);
+}
+
static std::string LongTestString() {
std::string ret;
for (int i=0; i<200000; i++) {
@@ -420,25 +456,37 @@ BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) {
BOOST_AUTO_TEST_CASE(chacha20_testvector)
{
// Test vector from RFC 7539
- TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1,
+
+ // test encryption
+ TestChaCha20("4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756"
+ "c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e"
+ "20776f756c642062652069742e",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1,
+ "6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d"
+ "624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74"
+ "a35be6b40b8eedf2785e42874d"
+ );
+
+ // test keystream output
+ TestChaCha20("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1,
"224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cb"
"a40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a"
"832c89c167eacd901d7e2bf363");
// Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
- TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0, 0,
+ TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", 0, 0,
"76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b"
"8f41518a11cc387b669b2ee6586");
- TestChaCha20("0000000000000000000000000000000000000000000000000000000000000001", 0, 0,
+ TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000001", 0, 0,
"4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d79"
"2b1c43fea817e9ad275ae546963");
- TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0,
+ TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0,
"de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b52770"
"62eb7a0433e445f41e3");
- TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 1, 0,
+ TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", 1, 0,
"ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc4"
"97a0b466e7d6bbdb0041b2f586b");
- TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0,
+ TestChaCha20("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0,
"f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3b"
"e59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc1"
"18be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5"
@@ -517,6 +565,26 @@ BOOST_AUTO_TEST_CASE(poly1305_testvector)
"13000000000000000000000000000000");
}
+BOOST_AUTO_TEST_CASE(hkdf_hmac_sha256_l32_tests)
+{
+ // Use rfc5869 test vectors but truncated to 32 bytes (our implementation only support length 32)
+ TestHKDF_SHA256_32(
+ /* IKM */ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ /* salt */ "000102030405060708090a0b0c",
+ /* info */ "f0f1f2f3f4f5f6f7f8f9",
+ /* expected OKM */ "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf");
+ TestHKDF_SHA256_32(
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f",
+ "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
+ "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+ "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c");
+ TestHKDF_SHA256_32(
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "",
+ "",
+ "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d");
+}
+
BOOST_AUTO_TEST_CASE(countbits_tests)
{
FastRandomContext ctx;
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 0bde92c18d..efcadd51fc 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -4,8 +4,8 @@
#include <dbwrapper.h>
#include <uint256.h>
-#include <random.h>
#include <test/setup_common.h>
+#include <util/memory.h>
#include <memory>
@@ -27,7 +27,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper)
{
// Perform tests both obfuscated and non-obfuscated.
for (const bool obfuscate : {false, true}) {
- fs::path ph = SetDataDir(std::string("dbwrapper").append(obfuscate ? "_true" : "_false"));
+ fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_obfuscate_true" : "dbwrapper_obfuscate_false");
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'k';
uint256 in = InsecureRand256();
@@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch)
{
// Perform tests both obfuscated and non-obfuscated.
for (const bool obfuscate : {false, true}) {
- fs::path ph = SetDataDir(std::string("dbwrapper_batch").append(obfuscate ? "_true" : "_false"));
+ fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_batch_obfuscate_true" : "dbwrapper_batch_obfuscate_false");
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'i';
@@ -83,7 +83,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
{
// Perform tests both obfuscated and non-obfuscated.
for (const bool obfuscate : {false, true}) {
- fs::path ph = SetDataDir(std::string("dbwrapper_iterator").append(obfuscate ? "_true" : "_false"));
+ fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_iterator_obfuscate_true" : "dbwrapper_iterator_obfuscate_false");
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
// The two keys are intentionally chosen for ordering
@@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
{
// We're going to share this fs::path between two wrappers
- fs::path ph = SetDataDir("existing_data_no_obfuscate");
+ fs::path ph = GetDataDir() / "existing_data_no_obfuscate";
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
@@ -164,7 +164,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
BOOST_AUTO_TEST_CASE(existing_data_reindex)
{
// We're going to share this fs::path between two wrappers
- fs::path ph = SetDataDir("existing_data_reindex");
+ fs::path ph = GetDataDir() / "existing_data_reindex";
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
@@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
BOOST_AUTO_TEST_CASE(iterator_ordering)
{
- fs::path ph = SetDataDir("iterator_ordering");
+ fs::path ph = GetDataDir() / "iterator_ordering";
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<256; ++x) {
uint8_t key = x;
@@ -277,7 +277,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering)
{
char buf[10];
- fs::path ph = SetDataDir("iterator_string_ordering");
+ fs::path ph = GetDataDir() / "iterator_string_ordering";
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<10; ++x) {
for (int y = 0; y < 10; y++) {
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index bcb9a7c181..93883d1d98 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -9,10 +9,11 @@
#include <keystore.h>
#include <net.h>
#include <net_processing.h>
-#include <pow.h>
#include <script/sign.h>
#include <serialize.h>
+#include <util/memory.h>
#include <util/system.h>
+#include <util/time.h>
#include <validation.h>
#include <test/setup_common.h>
@@ -90,8 +91,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
// This test requires that we have a chain with non-zero work.
{
LOCK(cs_main);
- BOOST_CHECK(chainActive.Tip() != nullptr);
- BOOST_CHECK(chainActive.Tip()->nChainWork > 0);
+ BOOST_CHECK(::ChainActive().Tip() != nullptr);
+ BOOST_CHECK(::ChainActive().Tip()->nChainWork > 0);
}
// Test starts here
@@ -381,7 +382,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vin[0].scriptSig << OP_1;
tx.vout.resize(1);
tx.vout[0].nValue = 1*CENT;
- tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
+ tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
AddOrphanTx(MakeTransactionRef(tx), i);
}
@@ -397,7 +398,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vin[0].prevout.hash = txPrev->GetHash();
tx.vout.resize(1);
tx.vout[0].nValue = 1*CENT;
- tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
+ tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL));
AddOrphanTx(MakeTransactionRef(tx), i);
@@ -411,7 +412,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
CMutableTransaction tx;
tx.vout.resize(1);
tx.vout[0].nValue = 1*CENT;
- tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
+ tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
tx.vin.resize(2777);
for (unsigned int j = 0; j < tx.vin.size(); j++)
{
diff --git a/src/test/flatfile_tests.cpp b/src/test/flatfile_tests.cpp
index 1db2f8054c..740d805cce 100644
--- a/src/test/flatfile_tests.cpp
+++ b/src/test/flatfile_tests.cpp
@@ -2,8 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <clientversion.h>
#include <flatfile.h>
+#include <streams.h>
#include <test/setup_common.h>
+#include <util/system.h>
#include <boost/test/unit_test.hpp>
@@ -11,7 +14,7 @@ BOOST_FIXTURE_TEST_SUITE(flatfile_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(flatfile_filename)
{
- auto data_dir = SetDataDir("flatfile_test");
+ const auto data_dir = GetDataDir();
FlatFilePos pos(456, 789);
@@ -24,7 +27,7 @@ BOOST_AUTO_TEST_CASE(flatfile_filename)
BOOST_AUTO_TEST_CASE(flatfile_open)
{
- auto data_dir = SetDataDir("flatfile_test");
+ const auto data_dir = GetDataDir();
FlatFileSeq seq(data_dir, "a", 16 * 1024);
std::string line1("A purely peer-to-peer version of electronic cash would allow online "
@@ -85,7 +88,7 @@ BOOST_AUTO_TEST_CASE(flatfile_open)
BOOST_AUTO_TEST_CASE(flatfile_allocate)
{
- auto data_dir = SetDataDir("flatfile_test");
+ const auto data_dir = GetDataDir();
FlatFileSeq seq(data_dir, "a", 100);
bool out_of_space;
@@ -105,7 +108,7 @@ BOOST_AUTO_TEST_CASE(flatfile_allocate)
BOOST_AUTO_TEST_CASE(flatfile_flush)
{
- auto data_dir = SetDataDir("flatfile_test");
+ const auto data_dir = GetDataDir();
FlatFileSeq seq(data_dir, "a", 100);
bool out_of_space;
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
index 6bd6bb1be3..6d5a6641f0 100644
--- a/src/test/fs_tests.cpp
+++ b/src/test/fs_tests.cpp
@@ -4,6 +4,7 @@
//
#include <fs.h>
#include <test/setup_common.h>
+#include <util/system.h>
#include <boost/test/unit_test.hpp>
@@ -11,7 +12,7 @@ BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(fsbridge_fstream)
{
- fs::path tmpfolder = SetDataDir("fsbridge_fstream");
+ fs::path tmpfolder = GetDataDir();
// tmpfile1 should be the same as tmpfile2
fs::path tmpfile1 = tmpfolder / "fs_tests_₿_🏃";
fs::path tmpfile2 = tmpfolder / L"fs_tests_₿_🏃";
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 97d7633715..9364ac4a32 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <addrdb.h>
#include <addrman.h>
#include <blockencodings.h>
#include <chain.h>
@@ -11,8 +12,6 @@
#include <net.h>
#include <primitives/block.h>
#include <protocol.h>
-#include <pubkey.h>
-#include <script/script.h>
#include <streams.h>
#include <undo.h>
#include <version.h>
@@ -20,8 +19,6 @@
#include <stdint.h>
#include <unistd.h>
-#include <algorithm>
-#include <memory>
#include <vector>
#include <test/fuzz/fuzz.h>
diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h
index 8b03a7e46e..4e009d9b54 100644
--- a/src/test/fuzz/fuzz.h
+++ b/src/test/fuzz/fuzz.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_TEST_FUZZ_FUZZ_H
#define BITCOIN_TEST_FUZZ_FUZZ_H
-#include <functional>
#include <stdint.h>
#include <vector>
diff --git a/src/test/fuzz/script_flags.cpp b/src/test/fuzz/script_flags.cpp
index 2c0bfa360c..9b90d66755 100644
--- a/src/test/fuzz/script_flags.cpp
+++ b/src/test/fuzz/script_flags.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <script/interpreter.h>
-#include <script/script.h>
#include <streams.h>
#include <version.h>
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index 325b7002f2..d91fcb0034 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -2,13 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <clientversion.h>
#include <crypto/siphash.h>
#include <hash.h>
#include <util/strencodings.h>
#include <test/setup_common.h>
-#include <vector>
-
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(hash_tests, BasicTestingSetup)
diff --git a/src/test/key_properties.cpp b/src/test/key_properties.cpp
index 8b508ed7f7..abcfc4547b 100644
--- a/src/test/key_properties.cpp
+++ b/src/test/key_properties.cpp
@@ -3,13 +3,9 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key.h>
-#include <base58.h>
-#include <script/script.h>
#include <uint256.h>
#include <util/system.h>
-#include <util/strencodings.h>
#include <test/setup_common.h>
-#include <string>
#include <vector>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index e816546e62..3e99dcaa40 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -5,7 +5,6 @@
#include <key.h>
#include <key_io.h>
-#include <script/script.h>
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
@@ -68,10 +67,10 @@ BOOST_AUTO_TEST_CASE(key_test1)
BOOST_CHECK(!key2C.VerifyPubKey(pubkey2));
BOOST_CHECK(key2C.VerifyPubKey(pubkey2C));
- BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(pubkey1.GetID()));
- BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(pubkey2.GetID()));
- BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(pubkey1C.GetID()));
- BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(pubkey2C.GetID()));
+ BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(PKHash(pubkey1)));
+ BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(PKHash(pubkey2)));
+ BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(PKHash(pubkey1C)));
+ BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(PKHash(pubkey2C)));
for (int n=0; n<16; n++)
{
@@ -188,4 +187,36 @@ BOOST_AUTO_TEST_CASE(key_signature_tests)
BOOST_CHECK(found_small);
}
+BOOST_AUTO_TEST_CASE(key_key_negation)
+{
+ // create a dummy hash for signature comparison
+ unsigned char rnd[8];
+ std::string str = "Bitcoin key verification\n";
+ GetRandBytes(rnd, sizeof(rnd));
+ uint256 hash;
+ CHash256().Write((unsigned char*)str.data(), str.size()).Write(rnd, sizeof(rnd)).Finalize(hash.begin());
+
+ // import the static test key
+ CKey key = DecodeSecret(strSecret1C);
+
+ // create a signature
+ std::vector<unsigned char> vch_sig;
+ std::vector<unsigned char> vch_sig_cmp;
+ key.Sign(hash, vch_sig);
+
+ // negate the key twice
+ BOOST_CHECK(key.GetPubKey().data()[0] == 0x03);
+ key.Negate();
+ // after the first negation, the signature must be different
+ key.Sign(hash, vch_sig_cmp);
+ BOOST_CHECK(vch_sig_cmp != vch_sig);
+ BOOST_CHECK(key.GetPubKey().data()[0] == 0x02);
+ key.Negate();
+ // after the second negation, we should have the original key and thus the
+ // same signature
+ key.Sign(hash, vch_sig_cmp);
+ BOOST_CHECK(vch_sig_cmp == vch_sig);
+ BOOST_CHECK(key.GetPubKey().data()[0] == 0x03);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 0f74b379c0..c6a3de2285 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -5,11 +5,11 @@
#include <policy/policy.h>
#include <txmempool.h>
#include <util/system.h>
+#include <util/time.h>
#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
-#include <list>
#include <vector>
BOOST_FIXTURE_TEST_SUITE(mempool_tests, TestingSetup)
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 6ed4359059..4bd40687a6 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -7,16 +7,15 @@
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <consensus/tx_verify.h>
-#include <consensus/validation.h>
-#include <validation.h>
#include <miner.h>
#include <policy/policy.h>
-#include <pubkey.h>
#include <script/standard.h>
#include <txmempool.h>
#include <uint256.h>
-#include <util/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
+#include <util/time.h>
+#include <validation.h>
#include <test/setup_common.h>
@@ -82,11 +81,11 @@ struct {
{2, 0xbbbeb305}, {2, 0xfe1c810a},
};
-static CBlockIndex CreateBlockIndex(int nHeight)
+static CBlockIndex CreateBlockIndex(int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
CBlockIndex index;
index.nHeight = nHeight;
- index.pprev = chainActive.Tip();
+ index.pprev = ::ChainActive().Tip();
return index;
}
@@ -231,17 +230,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
LOCK(cs_main);
pblock->nVersion = 1;
- pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1;
+ pblock->nTime = ::ChainActive().Tip()->GetMedianTimePast()+1;
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1;
txCoinbase.vin[0].scriptSig = CScript();
txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce);
- txCoinbase.vin[0].scriptSig.push_back(chainActive.Height());
+ txCoinbase.vin[0].scriptSig.push_back(::ChainActive().Height());
txCoinbase.vout.resize(1); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
txCoinbase.vout[0].scriptPubKey = CScript();
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
if (txFirst.size() == 0)
- baseheight = chainActive.Height();
+ baseheight = ::ChainActive().Height();
if (txFirst.size() < 4)
txFirst.push_back(pblock->vtx[0]);
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
@@ -367,29 +366,29 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
mempool.clear();
// subsidy changing
- int nHeight = chainActive.Height();
+ int nHeight = ::ChainActive().Height();
// Create an actual 209999-long block chain (without valid blocks).
- while (chainActive.Tip()->nHeight < 209999) {
- CBlockIndex* prev = chainActive.Tip();
+ while (::ChainActive().Tip()->nHeight < 209999) {
+ CBlockIndex* prev = ::ChainActive().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
pcoinsTip->SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
- chainActive.SetTip(next);
+ ::ChainActive().SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
// Extend to a 210000-long block chain.
- while (chainActive.Tip()->nHeight < 210000) {
- CBlockIndex* prev = chainActive.Tip();
+ while (::ChainActive().Tip()->nHeight < 210000) {
+ CBlockIndex* prev = ::ChainActive().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
pcoinsTip->SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
- chainActive.SetTip(next);
+ ::ChainActive().SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
@@ -399,7 +398,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE;
script = CScript() << OP_0;
- tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
+ tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script));
hash = tx.GetHash();
mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
@@ -412,16 +411,16 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
mempool.clear();
// Delete the dummy blocks again.
- while (chainActive.Tip()->nHeight > nHeight) {
- CBlockIndex* del = chainActive.Tip();
- chainActive.SetTip(del->pprev);
+ while (::ChainActive().Tip()->nHeight > nHeight) {
+ CBlockIndex* del = ::ChainActive().Tip();
+ ::ChainActive().SetTip(del->pprev);
pcoinsTip->SetBestBlock(del->pprev->GetBlockHash());
delete del->phashBlock;
delete del;
}
// non-final txs in mempool
- SetMockTime(chainActive.Tip()->GetMedianTimePast()+1);
+ SetMockTime(::ChainActive().Tip()->GetMedianTimePast()+1);
int flags = LOCKTIME_VERIFY_SEQUENCE|LOCKTIME_MEDIAN_TIME_PAST;
// height map
std::vector<int> prevheights;
@@ -433,7 +432,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].prevout.hash = txFirst[0]->GetHash(); // only 1 transaction
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
- tx.vin[0].nSequence = chainActive.Tip()->nHeight + 1; // txFirst[0] is the 2nd block
+ tx.vin[0].nSequence = ::ChainActive().Tip()->nHeight + 1; // txFirst[0] is the 2nd block
prevheights[0] = baseheight + 1;
tx.vout.resize(1);
tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
@@ -443,11 +442,11 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(chainActive.Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
// relative time locked
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
- tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((chainActive.Tip()->GetMedianTimePast()+1-chainActive[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
+ tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((::ChainActive().Tip()->GetMedianTimePast()+1-::ChainActive()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
@@ -455,36 +454,36 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(chainActive.Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
+ ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
+ ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
// absolute height locked
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
tx.vin[0].nSequence = CTxIn::SEQUENCE_FINAL - 1;
prevheights[0] = baseheight + 3;
- tx.nLockTime = chainActive.Tip()->nHeight + 1;
+ tx.nLockTime = ::ChainActive().Tip()->nHeight + 1;
hash = tx.GetHash();
mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(CTransaction(tx), chainActive.Tip()->nHeight + 2, chainActive.Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
// absolute time locked
tx.vin[0].prevout.hash = txFirst[3]->GetHash();
- tx.nLockTime = chainActive.Tip()->GetMedianTimePast();
+ tx.nLockTime = ::ChainActive().Tip()->GetMedianTimePast();
prevheights.resize(1);
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(CTransaction(tx), chainActive.Tip()->nHeight + 2, chainActive.Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
// mempool-dependent transactions (not added)
tx.vin[0].prevout.hash = hash;
- prevheights[0] = chainActive.Tip()->nHeight + 1;
+ prevheights[0] = ::ChainActive().Tip()->nHeight + 1;
tx.nLockTime = 0;
tx.vin[0].nSequence = 0;
BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
@@ -505,14 +504,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3U);
// However if we advance height by 1 and time by 512, all of them should be mined
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- chainActive.Tip()->nHeight++;
- SetMockTime(chainActive.Tip()->GetMedianTimePast() + 1);
+ ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+ ::ChainActive().Tip()->nHeight++;
+ SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1);
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U);
- chainActive.Tip()->nHeight--;
+ ::ChainActive().Tip()->nHeight--;
SetMockTime(0);
mempool.clear();
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index 682f1bee26..11e79937be 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -9,7 +9,7 @@
#include <script/script_error.h>
#include <script/interpreter.h>
#include <script/sign.h>
-#include <script/ismine.h>
+#include <tinyformat.h>
#include <uint256.h>
#include <test/setup_common.h>
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 54d18c0a1c..fed65afdbf 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -1,16 +1,19 @@
// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <addrdb.h>
#include <addrman.h>
+#include <clientversion.h>
#include <test/setup_common.h>
#include <string>
#include <boost/test/unit_test.hpp>
-#include <hash.h>
#include <serialize.h>
#include <streams.h>
#include <net.h>
#include <netbase.h>
#include <chainparams.h>
+#include <util/memory.h>
#include <util/system.h>
#include <memory>
@@ -89,7 +92,6 @@ BOOST_AUTO_TEST_CASE(cnode_listen_port)
BOOST_AUTO_TEST_CASE(caddrdb_read)
{
- SetDataDir("caddrdb_read");
CAddrManUncorrupted addrmanUncorrupted;
addrmanUncorrupted.MakeDeterministic();
@@ -135,7 +137,6 @@ BOOST_AUTO_TEST_CASE(caddrdb_read)
BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
{
- SetDataDir("caddrdb_read_corrupted");
CAddrManCorrupted addrmanCorrupted;
addrmanCorrupted.MakeDeterministic();
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index dd5e3eb6d5..86c0cecbf1 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -59,6 +59,7 @@ BOOST_AUTO_TEST_CASE(netbase_properties)
BOOST_CHECK(ResolveIP("FC00::").IsRFC4193());
BOOST_CHECK(ResolveIP("2001::2").IsRFC4380());
BOOST_CHECK(ResolveIP("2001:10::").IsRFC4843());
+ BOOST_CHECK(ResolveIP("2001:20::").IsRFC7343());
BOOST_CHECK(ResolveIP("FE80::").IsRFC4862());
BOOST_CHECK(ResolveIP("64:FF9B::").IsRFC6052());
BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor());
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 149094fc00..016a4f471b 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -7,6 +7,7 @@
#include <txmempool.h>
#include <uint256.h>
#include <util/system.h>
+#include <util/time.h>
#include <test/setup_common.h>
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index 653433bfce..1123d4202c 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -5,7 +5,6 @@
#include <chain.h>
#include <chainparams.h>
#include <pow.h>
-#include <random.h>
#include <util/system.h>
#include <test/setup_common.h>
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index 141be50f50..fc1f946bba 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -183,6 +183,26 @@ public:
pre_vector = pre_vector_alt;
}
+ void resize_uninitialized(realtype values) {
+ size_t r = values.size();
+ size_t s = real_vector.size() / 2;
+ if (real_vector.capacity() < s + r) {
+ real_vector.reserve(s + r);
+ }
+ real_vector.resize(s);
+ pre_vector.resize_uninitialized(s);
+ for (auto v : values) {
+ real_vector.push_back(v);
+ }
+ auto p = pre_vector.size();
+ pre_vector.resize_uninitialized(p + r);
+ for (auto v : values) {
+ pre_vector[p] = v;
+ ++p;
+ }
+ test();
+ }
+
~prevector_tester() {
BOOST_CHECK_MESSAGE(passed, "insecure_rand: " + rand_seed.ToString());
}
@@ -260,6 +280,14 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt)
if (InsecureRandBits(5) == 18) {
test.move();
}
+ if (InsecureRandBits(5) == 19) {
+ unsigned int num = 1 + (InsecureRandBits(4));
+ std::vector<int> values(num);
+ for (auto &v : values) {
+ v = InsecureRand32();
+ }
+ test.resize_uninitialized(values);
+ }
}
}
}
diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp
index 2b01acf7fa..41ca8029e5 100644
--- a/src/test/raii_event_tests.cpp
+++ b/src/test/raii_event_tests.cpp
@@ -14,8 +14,6 @@
#include <test/setup_common.h>
-#include <vector>
-
#include <boost/test/unit_test.hpp>
static std::map<void*, short> tags;
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 07d1326bcb..5ae0812243 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -9,10 +9,8 @@
#include <core_io.h>
#include <init.h>
#include <interfaces/chain.h>
-#include <key_io.h>
-#include <netbase.h>
-
#include <test/setup_common.h>
+#include <util/time.h>
#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
index 0ce5f09e42..735b67c06e 100644
--- a/src/test/script_p2sh_tests.cpp
+++ b/src/test/script_p2sh_tests.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/tx_verify.h>
-#include <core_io.h>
#include <key.h>
#include <keystore.h>
#include <validation.h>
@@ -12,7 +11,6 @@
#include <script/script_error.h>
#include <policy/settings.h>
#include <script/sign.h>
-#include <script/ismine.h>
#include <test/setup_common.h>
#include <vector>
@@ -69,14 +67,14 @@ BOOST_AUTO_TEST_CASE(sign)
// different keys, straight/P2SH, pubkey/pubkeyhash
CScript standardScripts[4];
standardScripts[0] << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
- standardScripts[1] = GetScriptForDestination(key[1].GetPubKey().GetID());
+ standardScripts[1] = GetScriptForDestination(PKHash(key[1].GetPubKey()));
standardScripts[2] << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
- standardScripts[3] = GetScriptForDestination(key[2].GetPubKey().GetID());
+ standardScripts[3] = GetScriptForDestination(PKHash(key[2].GetPubKey()));
CScript evalScripts[4];
for (int i = 0; i < 4; i++)
{
BOOST_CHECK(keystore.AddCScript(standardScripts[i]));
- evalScripts[i] = GetScriptForDestination(CScriptID(standardScripts[i]));
+ evalScripts[i] = GetScriptForDestination(ScriptHash(standardScripts[i]));
}
CMutableTransaction txFrom; // Funding transaction:
@@ -99,7 +97,6 @@ BOOST_AUTO_TEST_CASE(sign)
txTo[i].vin[0].prevout.n = i;
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
txTo[i].vout[0].nValue = 1;
- BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
}
for (int i = 0; i < 8; i++)
{
@@ -131,7 +128,7 @@ BOOST_AUTO_TEST_CASE(norecurse)
CScript invalidAsScript;
invalidAsScript << OP_INVALIDOPCODE << OP_INVALIDOPCODE;
- CScript p2sh = GetScriptForDestination(CScriptID(invalidAsScript));
+ CScript p2sh = GetScriptForDestination(ScriptHash(invalidAsScript));
CScript scriptSig;
scriptSig << Serialize(invalidAsScript);
@@ -142,7 +139,7 @@ BOOST_AUTO_TEST_CASE(norecurse)
// Try to recur, and verification should succeed because
// the inner HASH160 <> EQUAL should only check the hash:
- CScript p2sh2 = GetScriptForDestination(CScriptID(p2sh));
+ CScript p2sh2 = GetScriptForDestination(ScriptHash(p2sh));
CScript scriptSig2;
scriptSig2 << Serialize(invalidAsScript) << Serialize(p2sh);
@@ -165,7 +162,7 @@ BOOST_AUTO_TEST_CASE(set)
}
CScript inner[4];
- inner[0] = GetScriptForDestination(key[0].GetPubKey().GetID());
+ inner[0] = GetScriptForDestination(PKHash(key[0].GetPubKey()));
inner[1] = GetScriptForMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+2));
inner[2] = GetScriptForMultisig(1, std::vector<CPubKey>(keys.begin(), keys.begin()+2));
inner[3] = GetScriptForMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+3));
@@ -173,7 +170,7 @@ BOOST_AUTO_TEST_CASE(set)
CScript outer[4];
for (int i = 0; i < 4; i++)
{
- outer[i] = GetScriptForDestination(CScriptID(inner[i]));
+ outer[i] = GetScriptForDestination(ScriptHash(inner[i]));
BOOST_CHECK(keystore.AddCScript(inner[i]));
}
@@ -196,7 +193,6 @@ BOOST_AUTO_TEST_CASE(set)
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
txTo[i].vout[0].nValue = 1*CENT;
txTo[i].vout[0].scriptPubKey = inner[i];
- BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
}
for (int i = 0; i < 4; i++)
{
@@ -253,7 +249,7 @@ BOOST_AUTO_TEST_CASE(switchover)
CScript scriptSig;
scriptSig << Serialize(notValid);
- CScript fund = GetScriptForDestination(CScriptID(notValid));
+ CScript fund = GetScriptForDestination(ScriptHash(notValid));
// Validation should succeed under old rules (hash is correct):
@@ -284,11 +280,11 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
txFrom.vout.resize(7);
// First three are standard:
- CScript pay1 = GetScriptForDestination(key[0].GetPubKey().GetID());
+ CScript pay1 = GetScriptForDestination(PKHash(key[0].GetPubKey()));
BOOST_CHECK(keystore.AddCScript(pay1));
CScript pay1of3 = GetScriptForMultisig(1, keys);
- txFrom.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(pay1)); // P2SH (OP_CHECKSIG)
+ txFrom.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(pay1)); // P2SH (OP_CHECKSIG)
txFrom.vout[0].nValue = 1000;
txFrom.vout[1].scriptPubKey = pay1; // ordinary OP_CHECKSIG
txFrom.vout[1].nValue = 2000;
@@ -303,7 +299,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
oneAndTwo << OP_2 << ToByteVector(key[3].GetPubKey()) << ToByteVector(key[4].GetPubKey()) << ToByteVector(key[5].GetPubKey());
oneAndTwo << OP_3 << OP_CHECKMULTISIG;
BOOST_CHECK(keystore.AddCScript(oneAndTwo));
- txFrom.vout[3].scriptPubKey = GetScriptForDestination(CScriptID(oneAndTwo));
+ txFrom.vout[3].scriptPubKey = GetScriptForDestination(ScriptHash(oneAndTwo));
txFrom.vout[3].nValue = 4000;
// vout[4] is max sigops:
@@ -312,24 +308,24 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
fifteenSigops << ToByteVector(key[i%3].GetPubKey());
fifteenSigops << OP_15 << OP_CHECKMULTISIG;
BOOST_CHECK(keystore.AddCScript(fifteenSigops));
- txFrom.vout[4].scriptPubKey = GetScriptForDestination(CScriptID(fifteenSigops));
+ txFrom.vout[4].scriptPubKey = GetScriptForDestination(ScriptHash(fifteenSigops));
txFrom.vout[4].nValue = 5000;
// vout[5/6] are non-standard because they exceed MAX_P2SH_SIGOPS
CScript sixteenSigops; sixteenSigops << OP_16 << OP_CHECKMULTISIG;
BOOST_CHECK(keystore.AddCScript(sixteenSigops));
- txFrom.vout[5].scriptPubKey = GetScriptForDestination(CScriptID(sixteenSigops));
+ txFrom.vout[5].scriptPubKey = GetScriptForDestination(ScriptHash(sixteenSigops));
txFrom.vout[5].nValue = 5000;
CScript twentySigops; twentySigops << OP_CHECKMULTISIG;
BOOST_CHECK(keystore.AddCScript(twentySigops));
- txFrom.vout[6].scriptPubKey = GetScriptForDestination(CScriptID(twentySigops));
+ txFrom.vout[6].scriptPubKey = GetScriptForDestination(ScriptHash(twentySigops));
txFrom.vout[6].nValue = 6000;
AddCoins(coins, CTransaction(txFrom), 0);
CMutableTransaction txTo;
txTo.vout.resize(1);
- txTo.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID());
+ txTo.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
txTo.vin.resize(5);
for (int i = 0; i < 5; i++)
@@ -352,7 +348,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
CMutableTransaction txToNonStd1;
txToNonStd1.vout.resize(1);
- txToNonStd1.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID());
+ txToNonStd1.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
txToNonStd1.vout[0].nValue = 1000;
txToNonStd1.vin.resize(1);
txToNonStd1.vin[0].prevout.n = 5;
@@ -364,7 +360,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
CMutableTransaction txToNonStd2;
txToNonStd2.vout.resize(1);
- txToNonStd2.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID());
+ txToNonStd2.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
txToNonStd2.vout[0].nValue = 1000;
txToNonStd2.vin.resize(1);
txToNonStd2.vin[0].prevout.n = 6;
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 36a2b1bee5..046b220e3f 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -4,9 +4,7 @@
#include <key.h>
#include <keystore.h>
-#include <script/ismine.h>
#include <script/script.h>
-#include <script/script_error.h>
#include <script/standard.h>
#include <test/setup_common.h>
@@ -179,23 +177,23 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
s.clear();
s << ToByteVector(pubkey) << OP_CHECKSIG;
BOOST_CHECK(ExtractDestination(s, address));
- BOOST_CHECK(boost::get<CKeyID>(&address) &&
- *boost::get<CKeyID>(&address) == pubkey.GetID());
+ BOOST_CHECK(boost::get<PKHash>(&address) &&
+ *boost::get<PKHash>(&address) == PKHash(pubkey));
// TX_PUBKEYHASH
s.clear();
s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
BOOST_CHECK(ExtractDestination(s, address));
- BOOST_CHECK(boost::get<CKeyID>(&address) &&
- *boost::get<CKeyID>(&address) == pubkey.GetID());
+ BOOST_CHECK(boost::get<PKHash>(&address) &&
+ *boost::get<PKHash>(&address) == PKHash(pubkey));
// TX_SCRIPTHASH
CScript redeemScript(s); // initialize with leftover P2PKH script
s.clear();
s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
BOOST_CHECK(ExtractDestination(s, address));
- BOOST_CHECK(boost::get<CScriptID>(&address) &&
- *boost::get<CScriptID>(&address) == CScriptID(redeemScript));
+ BOOST_CHECK(boost::get<ScriptHash>(&address) &&
+ *boost::get<ScriptHash>(&address) == ScriptHash(redeemScript));
// TX_MULTISIG
s.clear();
@@ -255,8 +253,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK_EQUAL(whichType, TX_PUBKEY);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) &&
- *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID());
+ BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
+ *boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
// TX_PUBKEYHASH
s.clear();
@@ -265,8 +263,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) &&
- *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID());
+ BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
+ *boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
// TX_SCRIPTHASH
CScript redeemScript(s); // initialize with leftover P2PKH script
@@ -276,8 +274,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(boost::get<CScriptID>(&addresses[0]) &&
- *boost::get<CScriptID>(&addresses[0]) == CScriptID(redeemScript));
+ BOOST_CHECK(boost::get<ScriptHash>(&addresses[0]) &&
+ *boost::get<ScriptHash>(&addresses[0]) == ScriptHash(redeemScript));
// TX_MULTISIG
s.clear();
@@ -289,10 +287,10 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK_EQUAL(whichType, TX_MULTISIG);
BOOST_CHECK_EQUAL(addresses.size(), 2U);
BOOST_CHECK_EQUAL(nRequired, 2);
- BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) &&
- *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID());
- BOOST_CHECK(boost::get<CKeyID>(&addresses[1]) &&
- *boost::get<CKeyID>(&addresses[1]) == pubkeys[1].GetID());
+ BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
+ *boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
+ BOOST_CHECK(boost::get<PKHash>(&addresses[1]) &&
+ *boost::get<PKHash>(&addresses[1]) == PKHash(pubkeys[1]));
// TX_NULL_DATA
s.clear();
@@ -311,17 +309,17 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
CScript expected, result;
- // CKeyID
+ // PKHash
expected.clear();
expected << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
- result = GetScriptForDestination(pubkeys[0].GetID());
+ result = GetScriptForDestination(PKHash(pubkeys[0]));
BOOST_CHECK(result == expected);
// CScriptID
CScript redeemScript(result);
expected.clear();
expected << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
- result = GetScriptForDestination(CScriptID(redeemScript));
+ result = GetScriptForDestination(ScriptHash(redeemScript));
BOOST_CHECK(result == expected);
// CNoDestination
@@ -372,364 +370,4 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
BOOST_CHECK(result == expected);
}
-BOOST_AUTO_TEST_CASE(script_standard_IsMine)
-{
- CKey keys[2];
- CPubKey pubkeys[2];
- for (int i = 0; i < 2; i++) {
- keys[i].MakeNewKey(true);
- pubkeys[i] = keys[i].GetPubKey();
- }
-
- CKey uncompressedKey;
- uncompressedKey.MakeNewKey(false);
- CPubKey uncompressedPubkey = uncompressedKey.GetPubKey();
-
- CScript scriptPubKey;
- isminetype result;
-
- // P2PK compressed
- {
- CBasicKeyStore keystore;
- scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
-
- // Keystore does not have key
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has key
- BOOST_CHECK(keystore.AddKey(keys[0]));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- }
-
- // P2PK uncompressed
- {
- CBasicKeyStore keystore;
- scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
-
- // Keystore does not have key
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has key
- BOOST_CHECK(keystore.AddKey(uncompressedKey));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- }
-
- // P2PKH compressed
- {
- CBasicKeyStore keystore;
- scriptPubKey = GetScriptForDestination(pubkeys[0].GetID());
-
- // Keystore does not have key
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has key
- BOOST_CHECK(keystore.AddKey(keys[0]));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- }
-
- // P2PKH uncompressed
- {
- CBasicKeyStore keystore;
- scriptPubKey = GetScriptForDestination(uncompressedPubkey.GetID());
-
- // Keystore does not have key
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has key
- BOOST_CHECK(keystore.AddKey(uncompressedKey));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- }
-
- // P2SH
- {
- CBasicKeyStore keystore;
-
- CScript redeemScript = GetScriptForDestination(pubkeys[0].GetID());
- scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
-
- // Keystore does not have redeemScript or key
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has redeemScript but no key
- BOOST_CHECK(keystore.AddCScript(redeemScript));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has redeemScript and key
- BOOST_CHECK(keystore.AddKey(keys[0]));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- }
-
- // (P2PKH inside) P2SH inside P2SH (invalid)
- {
- CBasicKeyStore keystore;
-
- CScript redeemscript_inner = GetScriptForDestination(pubkeys[0].GetID());
- CScript redeemscript = GetScriptForDestination(CScriptID(redeemscript_inner));
- scriptPubKey = GetScriptForDestination(CScriptID(redeemscript));
-
- BOOST_CHECK(keystore.AddCScript(redeemscript));
- BOOST_CHECK(keystore.AddCScript(redeemscript_inner));
- BOOST_CHECK(keystore.AddCScript(scriptPubKey));
- BOOST_CHECK(keystore.AddKey(keys[0]));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
- }
-
- // (P2PKH inside) P2SH inside P2WSH (invalid)
- {
- CBasicKeyStore keystore;
-
- CScript redeemscript = GetScriptForDestination(pubkeys[0].GetID());
- CScript witnessscript = GetScriptForDestination(CScriptID(redeemscript));
- scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
-
- BOOST_CHECK(keystore.AddCScript(witnessscript));
- BOOST_CHECK(keystore.AddCScript(redeemscript));
- BOOST_CHECK(keystore.AddCScript(scriptPubKey));
- BOOST_CHECK(keystore.AddKey(keys[0]));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
- }
-
- // P2WPKH inside P2WSH (invalid)
- {
- CBasicKeyStore keystore;
-
- CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID()));
- scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
-
- BOOST_CHECK(keystore.AddCScript(witnessscript));
- BOOST_CHECK(keystore.AddCScript(scriptPubKey));
- BOOST_CHECK(keystore.AddKey(keys[0]));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
- }
-
- // (P2PKH inside) P2WSH inside P2WSH (invalid)
- {
- CBasicKeyStore keystore;
-
- CScript witnessscript_inner = GetScriptForDestination(pubkeys[0].GetID());
- CScript witnessscript = GetScriptForDestination(WitnessV0ScriptHash(witnessscript_inner));
- scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
-
- BOOST_CHECK(keystore.AddCScript(witnessscript_inner));
- BOOST_CHECK(keystore.AddCScript(witnessscript));
- BOOST_CHECK(keystore.AddCScript(scriptPubKey));
- BOOST_CHECK(keystore.AddKey(keys[0]));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
- }
-
- // P2WPKH compressed
- {
- CBasicKeyStore keystore;
- BOOST_CHECK(keystore.AddKey(keys[0]));
-
- scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID()));
-
- // Keystore implicitly has key and P2SH redeemScript
- BOOST_CHECK(keystore.AddCScript(scriptPubKey));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- }
-
- // P2WPKH uncompressed
- {
- CBasicKeyStore keystore;
- BOOST_CHECK(keystore.AddKey(uncompressedKey));
-
- scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(uncompressedPubkey.GetID()));
-
- // Keystore has key, but no P2SH redeemScript
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has key and P2SH redeemScript
- BOOST_CHECK(keystore.AddCScript(scriptPubKey));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
- }
-
- // scriptPubKey multisig
- {
- CBasicKeyStore keystore;
-
- scriptPubKey = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
-
- // Keystore does not have any keys
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has 1/2 keys
- BOOST_CHECK(keystore.AddKey(uncompressedKey));
-
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has 2/2 keys
- BOOST_CHECK(keystore.AddKey(keys[1]));
-
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has 2/2 keys and the script
- BOOST_CHECK(keystore.AddCScript(scriptPubKey));
-
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
- }
-
- // P2SH multisig
- {
- CBasicKeyStore keystore;
- BOOST_CHECK(keystore.AddKey(uncompressedKey));
- BOOST_CHECK(keystore.AddKey(keys[1]));
-
- CScript redeemScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
- scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
-
- // Keystore has no redeemScript
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has redeemScript
- BOOST_CHECK(keystore.AddCScript(redeemScript));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- }
-
- // P2WSH multisig with compressed keys
- {
- CBasicKeyStore keystore;
- BOOST_CHECK(keystore.AddKey(keys[0]));
- BOOST_CHECK(keystore.AddKey(keys[1]));
-
- CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
- scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
-
- // Keystore has keys, but no witnessScript or P2SH redeemScript
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has keys and witnessScript, but no P2SH redeemScript
- BOOST_CHECK(keystore.AddCScript(witnessScript));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has keys, witnessScript, P2SH redeemScript
- BOOST_CHECK(keystore.AddCScript(scriptPubKey));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- }
-
- // P2WSH multisig with uncompressed key
- {
- CBasicKeyStore keystore;
- BOOST_CHECK(keystore.AddKey(uncompressedKey));
- BOOST_CHECK(keystore.AddKey(keys[1]));
-
- CScript witnessScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
- scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
-
- // Keystore has keys, but no witnessScript or P2SH redeemScript
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has keys and witnessScript, but no P2SH redeemScript
- BOOST_CHECK(keystore.AddCScript(witnessScript));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has keys, witnessScript, P2SH redeemScript
- BOOST_CHECK(keystore.AddCScript(scriptPubKey));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
- }
-
- // P2WSH multisig wrapped in P2SH
- {
- CBasicKeyStore keystore;
-
- CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
- CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
- scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
-
- // Keystore has no witnessScript, P2SH redeemScript, or keys
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has witnessScript and P2SH redeemScript, but no keys
- BOOST_CHECK(keystore.AddCScript(redeemScript));
- BOOST_CHECK(keystore.AddCScript(witnessScript));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
-
- // Keystore has keys, witnessScript, P2SH redeemScript
- BOOST_CHECK(keystore.AddKey(keys[0]));
- BOOST_CHECK(keystore.AddKey(keys[1]));
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- }
-
- // OP_RETURN
- {
- CBasicKeyStore keystore;
- BOOST_CHECK(keystore.AddKey(keys[0]));
-
- scriptPubKey.clear();
- scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]);
-
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
- }
-
- // witness unspendable
- {
- CBasicKeyStore keystore;
- BOOST_CHECK(keystore.AddKey(keys[0]));
-
- scriptPubKey.clear();
- scriptPubKey << OP_0 << ToByteVector(ParseHex("aabb"));
-
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
- }
-
- // witness unknown
- {
- CBasicKeyStore keystore;
- BOOST_CHECK(keystore.AddKey(keys[0]));
-
- scriptPubKey.clear();
- scriptPubKey << OP_16 << ToByteVector(ParseHex("aabb"));
-
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
- }
-
- // Nonstandard
- {
- CBasicKeyStore keystore;
- BOOST_CHECK(keystore.AddKey(keys[0]));
-
- scriptPubKey.clear();
- scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
-
- result = IsMine(keystore, scriptPubKey);
- BOOST_CHECK_EQUAL(result, ISMINE_NO);
- }
-}
-
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 588ae55a69..ae903df0ad 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -14,12 +14,12 @@
#include <util/strencodings.h>
#include <test/setup_common.h>
#include <rpc/util.h>
+#include <streams.h>
#if defined(HAVE_CONSENSUS_LIB)
#include <script/bitcoinconsensus.h>
#endif
-#include <fstream>
#include <stdint.h>
#include <string>
#include <vector>
@@ -1211,7 +1211,7 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
BOOST_CHECK(keystore.AddKey(key));
}
- CMutableTransaction txFrom = BuildCreditingTransaction(GetScriptForDestination(keys[0].GetPubKey().GetID()));
+ CMutableTransaction txFrom = BuildCreditingTransaction(GetScriptForDestination(PKHash(keys[0].GetPubKey())));
CMutableTransaction txTo = BuildSpendingTransaction(CScript(), CScriptWitness(), CTransaction(txFrom));
CScript& scriptPubKey = txFrom.vout[0].scriptPubKey;
SignatureData scriptSig;
@@ -1237,7 +1237,7 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
// P2SH, single-signature case:
CScript pkSingle; pkSingle << ToByteVector(keys[0].GetPubKey()) << OP_CHECKSIG;
BOOST_CHECK(keystore.AddCScript(pkSingle));
- scriptPubKey = GetScriptForDestination(CScriptID(pkSingle));
+ scriptPubKey = GetScriptForDestination(ScriptHash(pkSingle));
BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 0, SIGHASH_ALL));
scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]);
combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty);
diff --git a/src/test/scriptnum10.h b/src/test/scriptnum10.h
index e763b64275..2c89a18331 100644
--- a/src/test/scriptnum10.h
+++ b/src/test/scriptnum10.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_TEST_SCRIPTNUM10_H
#define BITCOIN_TEST_SCRIPTNUM10_H
-#include <algorithm>
#include <limits>
#include <stdexcept>
#include <stdint.h>
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index 2fab309aa4..8a8620938e 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -6,6 +6,7 @@
#include <streams.h>
#include <hash.h>
#include <test/setup_common.h>
+#include <util/strencodings.h>
#include <stdint.h>
diff --git a/src/test/setup_common.cpp b/src/test/setup_common.cpp
index 6b671b2400..e3ba9cddb0 100644
--- a/src/test/setup_common.cpp
+++ b/src/test/setup_common.cpp
@@ -10,17 +10,22 @@
#include <consensus/params.h>
#include <consensus/validation.h>
#include <crypto/sha256.h>
+#include <init.h>
#include <miner.h>
-#include <net_processing.h>
+#include <net.h>
#include <noui.h>
#include <pow.h>
#include <rpc/register.h>
#include <rpc/server.h>
#include <script/sigcache.h>
#include <streams.h>
-#include <ui_interface.h>
+#include <txdb.h>
+#include <util/memory.h>
+#include <util/strencodings.h>
+#include <util/time.h>
#include <util/validation.h>
#include <validation.h>
+#include <validationinterface.h>
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
@@ -35,6 +40,13 @@ std::ostream& operator<<(std::ostream& os, const uint256& num)
BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
: m_path_root(fs::temp_directory_path() / "test_common_" PACKAGE_NAME / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{
+ fs::create_directories(m_path_root);
+ gArgs.ForceSetArg("-datadir", m_path_root.string());
+ ClearDatadirCache();
+ SelectParams(chainName);
+ gArgs.ForceSetArg("-printtoconsole", "0");
+ InitLogging();
+ LogInstance().StartLogging();
SHA256AutoDetect();
ECC_Start();
SetupEnvironment();
@@ -42,7 +54,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
InitSignatureCache();
InitScriptExecutionCache();
fCheckBlockIndex = true;
- SelectParams(chainName);
static bool noui_connected = false;
if (!noui_connected) {
noui_connect();
@@ -52,27 +63,18 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
BasicTestingSetup::~BasicTestingSetup()
{
+ LogInstance().DisconnectTestLogger();
fs::remove_all(m_path_root);
ECC_Stop();
}
-fs::path BasicTestingSetup::SetDataDir(const std::string& name)
-{
- fs::path ret = m_path_root / name;
- fs::create_directories(ret);
- gArgs.ForceSetArg("-datadir", ret.string());
- return ret;
-}
-
TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
- SetDataDir("tempdir");
const CChainParams& chainparams = Params();
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
RegisterAllCoreRPCCommands(tableRPC);
- ClearDatadirCache();
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
@@ -151,7 +153,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
{
LOCK(cs_main);
unsigned int extraNonce = 0;
- IncrementExtraNonce(&block, chainActive.Tip(), extraNonce);
+ IncrementExtraNonce(&block, ::ChainActive().Tip(), extraNonce);
}
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
diff --git a/src/test/setup_common.h b/src/test/setup_common.h
index 893eca216d..6c9494898c 100644
--- a/src/test/setup_common.h
+++ b/src/test/setup_common.h
@@ -11,10 +11,8 @@
#include <pubkey.h>
#include <random.h>
#include <scheduler.h>
-#include <txdb.h>
#include <txmempool.h>
-#include <memory>
#include <type_traits>
#include <boost/thread.hpp>
@@ -54,22 +52,19 @@ static inline bool InsecureRandBool() { return g_insecure_rand_ctx.randbool(); }
static constexpr CAmount CENT{1000000};
/** Basic testing setup.
- * This just configures logging and chain parameters.
+ * This just configures logging, data dir and chain parameters.
*/
struct BasicTestingSetup {
ECCVerifyHandle globalVerifyHandle;
explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~BasicTestingSetup();
-
- fs::path SetDataDir(const std::string& name);
-
private:
const fs::path m_path_root;
};
/** Testing setup that configures a complete environment.
- * Included are data directory, coins database, script check threads setup.
+ * Included are coins database, script check threads setup.
*/
struct TestingSetup : public BasicTestingSetup {
boost::thread_group threadGroup;
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index 4efa023fbb..a32f2cda92 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <consensus/consensus.h>
#include <consensus/tx_verify.h>
-#include <consensus/validation.h>
#include <pubkey.h>
#include <key.h>
#include <script/script.h>
@@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 3U);
BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 21U);
- CScript p2sh = GetScriptForDestination(CScriptID(s1));
+ CScript p2sh = GetScriptForDestination(ScriptHash(s1));
CScript scriptSig;
scriptSig << OP_0 << Serialize(s1);
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3U);
@@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
BOOST_CHECK_EQUAL(s2.GetSigOpCount(true), 3U);
BOOST_CHECK_EQUAL(s2.GetSigOpCount(false), 20U);
- p2sh = GetScriptForDestination(CScriptID(s2));
+ p2sh = GetScriptForDestination(ScriptHash(s2));
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(true), 0U);
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(false), 0U);
CScript scriptSig2;
@@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
// Multisig nested in P2SH
{
CScript redeemScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
- CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
+ CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
CScript scriptSig = CScript() << OP_0 << OP_0 << ToByteVector(redeemScript);
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, CScriptWitness());
@@ -185,7 +185,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
{
CScript p2pk = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
CScript scriptSig = GetScriptForWitness(p2pk);
- CScript scriptPubKey = GetScriptForDestination(CScriptID(scriptSig));
+ CScript scriptPubKey = GetScriptForDestination(ScriptHash(scriptSig));
scriptSig = CScript() << ToByteVector(scriptSig);
CScriptWitness scriptWitness;
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
@@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
{
CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
CScript redeemScript = GetScriptForWitness(witnessScript);
- CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
+ CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
CScript scriptSig = CScript() << ToByteVector(redeemScript);
CScriptWitness scriptWitness;
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index 4e37199c63..b812cef801 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <streams.h>
-#include <support/allocators/zeroafterfree.h>
#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/torcontrol_tests.cpp b/src/test/torcontrol_tests.cpp
index 6d8459f5b1..d846062d9b 100644
--- a/src/test/torcontrol_tests.cpp
+++ b/src/test/torcontrol_tests.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
#include <test/setup_common.h>
-#include <torcontrol.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 6242fdabd1..f77b77a972 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -20,6 +20,7 @@
#include <script/sign.h>
#include <script/script_error.h>
#include <script/standard.h>
+#include <streams.h>
#include <util/strencodings.h>
#include <map>
@@ -311,9 +312,9 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
dummyTransactions[1].vout.resize(2);
dummyTransactions[1].vout[0].nValue = 21*CENT;
- dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
+ dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[2].GetPubKey()));
dummyTransactions[1].vout[1].nValue = 22*CENT;
- dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
+ dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(PKHash(key[3].GetPubKey()));
AddCoins(coinsRet, CTransaction(dummyTransactions[1]), 0);
return dummyTransactions;
@@ -562,8 +563,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// P2SH pay-to-compressed-pubkey.
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey1)), output1, input1);
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey2)), output2, input2);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey1)), output1, input1);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey2)), output2, input2);
ReplaceRedeemScript(input2.vin[0].scriptSig, scriptPubkey1);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
@@ -587,8 +588,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// P2SH witness pay-to-compressed-pubkey (v0).
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1))), output1, input1);
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey2))), output2, input2);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey1))), output1, input1);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey2))), output2, input2);
ReplaceRedeemScript(input2.vin[0].scriptSig, GetScriptForWitness(scriptPubkey1));
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
@@ -612,8 +613,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// P2SH pay-to-uncompressed-pubkey.
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey1L)), output1, input1);
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey2L)), output2, input2);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey1L)), output1, input1);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey2L)), output2, input2);
ReplaceRedeemScript(input2.vin[0].scriptSig, scriptPubkey1L);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
@@ -629,8 +630,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey2L), output2, input2, false);
// Signing disabled for P2SH witness pay-to-uncompressed-pubkey (v1).
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1L))), output1, input1, false);
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey2L))), output2, input2, false);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey1L))), output1, input1, false);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey2L))), output2, input2, false);
// Normal 2-of-2 multisig
CreateCreditAndSpend(keystore, scriptMulti, output1, input1, false);
@@ -642,10 +643,10 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
// P2SH 2-of-2 multisig
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptMulti)), output1, input1, false);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptMulti)), output1, input1, false);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, false);
- CreateCreditAndSpend(keystore2, GetScriptForDestination(CScriptID(scriptMulti)), output2, input2, false);
+ CreateCreditAndSpend(keystore2, GetScriptForDestination(ScriptHash(scriptMulti)), output2, input2, false);
CheckWithFlag(output2, input2, 0, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false);
BOOST_CHECK(*output1 == *output2);
@@ -666,10 +667,10 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
// P2SH witness 2-of-2 multisig
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptMulti))), output1, input1, false);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptMulti))), output1, input1, false);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
- CreateCreditAndSpend(keystore2, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptMulti))), output2, input2, false);
+ CreateCreditAndSpend(keystore2, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptMulti))), output2, input2, false);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
BOOST_CHECK(*output1 == *output2);
@@ -695,7 +696,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].nValue = 90*CENT;
CKey key;
key.MakeNewKey(true);
- t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
+ t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
std::string reason;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index 9d62b471c1..d794d09d30 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -8,7 +8,6 @@
#include <test/setup_common.h>
#include <util/system.h>
#include <util/time.h>
-#include <validation.h>
#include <boost/test/unit_test.hpp>
@@ -56,7 +55,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
// Check that new transactions in new blocks make it into the index.
for (int i = 0; i < 10; i++) {
- CScript coinbase_script_pub_key = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
+ CScript coinbase_script_pub_key = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));
std::vector<CMutableTransaction> no_txns;
const CBlock& block = CreateAndProcessBlock(no_txns, coinbase_script_pub_key);
const CTransaction& txn = *block.vtx[0];
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 26ae7be202..2356e0ccdc 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -3,8 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <validation.h>
-#include <txmempool.h>
-#include <amount.h>
#include <consensus/validation.h>
#include <primitives/transaction.h>
#include <script/script.h>
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 01018043b1..45c97fa2aa 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -5,17 +5,11 @@
#include <consensus/validation.h>
#include <key.h>
#include <validation.h>
-#include <miner.h>
-#include <pubkey.h>
#include <txmempool.h>
-#include <random.h>
#include <script/standard.h>
#include <script/sign.h>
#include <test/setup_common.h>
-#include <util/time.h>
-#include <core_io.h>
#include <keystore.h>
-#include <policy/policy.h>
#include <boost/test/unit_test.hpp>
@@ -66,18 +60,27 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
// Test 1: block with both of those transactions should be rejected.
block = CreateAndProcessBlock(spends, scriptPubKey);
- BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
+ {
+ LOCK(cs_main);
+ BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ }
// Test 2: ... and should be rejected if spend1 is in the memory pool
BOOST_CHECK(ToMemPool(spends[0]));
block = CreateAndProcessBlock(spends, scriptPubKey);
- BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
+ {
+ LOCK(cs_main);
+ BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ }
mempool.clear();
// Test 3: ... and should be rejected if spend2 is in the memory pool
BOOST_CHECK(ToMemPool(spends[1]));
block = CreateAndProcessBlock(spends, scriptPubKey);
- BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
+ {
+ LOCK(cs_main);
+ BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ }
mempool.clear();
// Final sanity test: first spend in mempool, second in block, that's OK:
@@ -85,7 +88,10 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
oneSpend.push_back(spends[0]);
BOOST_CHECK(ToMemPool(spends[1]));
block = CreateAndProcessBlock(oneSpend, scriptPubKey);
- BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash());
+ {
+ LOCK(cs_main);
+ BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
+ }
// spends[1] should have been removed from the mempool when the
// block with spends[0] is accepted:
BOOST_CHECK_EQUAL(mempool.size(), 0U);
@@ -151,8 +157,8 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
}
CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
- CScript p2sh_scriptPubKey = GetScriptForDestination(CScriptID(p2pk_scriptPubKey));
- CScript p2pkh_scriptPubKey = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
+ CScript p2sh_scriptPubKey = GetScriptForDestination(ScriptHash(p2pk_scriptPubKey));
+ CScript p2pkh_scriptPubKey = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));
CScript p2wpkh_scriptPubKey = GetScriptForWitness(p2pkh_scriptPubKey);
CBasicKeyStore keystore;
@@ -220,7 +226,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey);
LOCK(cs_main);
- BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash());
+ BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash());
// Test P2SH: construct a transaction that is valid without P2SH, and
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index c1749fb856..33a118c2bb 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -1,19 +1,17 @@
// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
#include <arith_uint256.h>
+#include <streams.h>
#include <uint256.h>
#include <version.h>
#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
-#include <stdint.h>
#include <sstream>
#include <iomanip>
-#include <limits>
-#include <cmath>
#include <string>
-#include <stdio.h>
BOOST_FIXTURE_TEST_SUITE(uint256_tests, BasicTestingSetup)
diff --git a/src/test/util.cpp b/src/test/util.cpp
index 05d3a97a59..a2ea648324 100644
--- a/src/test/util.cpp
+++ b/src/test/util.cpp
@@ -6,22 +6,17 @@
#include <chainparams.h>
#include <consensus/merkle.h>
-#include <consensus/validation.h>
#include <key_io.h>
#include <miner.h>
#include <outputtype.h>
#include <pow.h>
-#include <scheduler.h>
#include <script/standard.h>
-#include <txdb.h>
#include <validation.h>
#include <validationinterface.h>
#ifdef ENABLE_WALLET
#include <wallet/wallet.h>
#endif
-#include <boost/thread.hpp>
-
const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj";
#ifdef ENABLE_WALLET
@@ -84,7 +79,8 @@ std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey)
.CreateNewBlock(coinbase_scriptPubKey)
->block);
- block->nTime = ::chainActive.Tip()->GetMedianTimePast() + 1;
+ LOCK(cs_main);
+ block->nTime = ::ChainActive().Tip()->GetMedianTimePast() + 1;
block->hashMerkleRoot = BlockMerkleRoot(*block);
return block;
diff --git a/src/test/util.h b/src/test/util.h
index 8ba647ec3f..f90cb0d623 100644
--- a/src/test/util.h
+++ b/src/test/util.h
@@ -34,5 +34,37 @@ std::string getnewaddress(CWallet& w);
/** Returns the generated coin */
CTxIn generatetoaddress(const std::string& address);
+/**
+ * Increment a string. Useful to enumerate all fixed length strings with
+ * characters in [min_char, max_char].
+ */
+template <typename CharType, size_t StringLength>
+bool NextString(CharType (&string)[StringLength], CharType min_char, CharType max_char)
+{
+ for (CharType& elem : string) {
+ bool has_next = elem != max_char;
+ elem = elem < min_char || elem >= max_char ? min_char : CharType(elem + 1);
+ if (has_next) return true;
+ }
+ return false;
+}
+
+/**
+ * Iterate over string values and call function for each string without
+ * successive duplicate characters.
+ */
+template <typename CharType, size_t StringLength, typename Fn>
+void ForEachNoDup(CharType (&string)[StringLength], CharType min_char, CharType max_char, Fn&& fn) {
+ for (bool has_next = true; has_next; has_next = NextString(string, min_char, max_char)) {
+ int prev = -1;
+ bool skip_string = false;
+ for (CharType c : string) {
+ if (c == prev) skip_string = true;
+ if (skip_string || c < min_char || c > max_char) break;
+ prev = c;
+ }
+ if (!skip_string) fn();
+ }
+}
#endif // BITCOIN_TEST_UTIL_H
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 0f1834240d..9960573b33 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -5,13 +5,15 @@
#include <util/system.h>
#include <clientversion.h>
-#include <primitives/transaction.h>
#include <sync.h>
+#include <test/util.h>
#include <util/strencodings.h>
#include <util/moneystr.h>
+#include <util/time.h>
#include <test/setup_common.h>
#include <stdint.h>
+#include <thread>
#include <vector>
#ifndef WIN32
#include <signal.h>
@@ -580,7 +582,7 @@ BOOST_AUTO_TEST_CASE(util_GetChainName)
// Test different ways settings can be merged, and verify results. This test can
// be used to confirm that updates to settings code don't change behavior
-// unintentially.
+// unintentionally.
//
// The test covers:
//
@@ -600,20 +602,22 @@ BOOST_AUTO_TEST_CASE(util_GetChainName)
// outside a network section, and non-network specific settings like "-server"
// that aren't sensitive to the network.
//
-struct SettingsMergeTestingSetup : public BasicTestingSetup {
+struct ArgsMergeTestingSetup : public BasicTestingSetup {
//! Max number of actions to sequence together. Can decrease this when
//! debugging to make test results easier to understand.
static constexpr int MAX_ACTIONS = 3;
- enum Action { SET = 0, NEGATE, SECTION_SET, SECTION_NEGATE, END };
+ enum Action { NONE, SET, NEGATE, SECTION_SET, SECTION_NEGATE };
using ActionList = Action[MAX_ACTIONS];
//! Enumerate all possible test configurations.
template <typename Fn>
void ForEachMergeSetup(Fn&& fn)
{
- ForEachActionList([&](const ActionList& arg_actions) {
- ForEachActionList([&](const ActionList& conf_actions) {
+ ActionList arg_actions = {};
+ ForEachNoDup(arg_actions, SET, SECTION_NEGATE, [&] {
+ ActionList conf_actions = {};
+ ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&] {
for (bool soft_set : {false, true}) {
for (bool force_set : {false, true}) {
for (const std::string& section : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) {
@@ -629,36 +633,6 @@ struct SettingsMergeTestingSetup : public BasicTestingSetup {
});
}
- //! Enumerate interesting combinations of actions.
- template <typename Fn>
- void ForEachActionList(Fn&& fn)
- {
- ActionList actions = {SET};
- for (bool done = false; !done;) {
- int prev_action = -1;
- bool skip_actions = false;
- for (Action action : actions) {
- if ((prev_action == END && action != END) || (prev_action != END && action == prev_action)) {
- // To cut down list of enumerated settings, skip enumerating
- // settings with ignored actions after an END, and settings that
- // repeat the same action twice in a row.
- skip_actions = true;
- break;
- }
- prev_action = action;
- }
- if (!skip_actions) fn(actions);
- done = true;
- for (Action& action : actions) {
- action = Action(action < END ? action + 1 : 0);
- if (action) {
- done = false;
- break;
- }
- }
- }
- }
-
//! Translate actions into a list of <key>=<value> setting strings.
std::vector<std::string> GetValues(const ActionList& actions,
const std::string& section,
@@ -668,7 +642,7 @@ struct SettingsMergeTestingSetup : public BasicTestingSetup {
std::vector<std::string> values;
int suffix = 0;
for (Action action : actions) {
- if (action == END) break;
+ if (action == NONE) break;
std::string prefix;
if (action == SECTION_SET || action == SECTION_NEGATE) prefix = section + ".";
if (action == SET || action == SECTION_SET) {
@@ -687,12 +661,12 @@ struct SettingsMergeTestingSetup : public BasicTestingSetup {
// Regression test covering different ways config settings can be merged. The
// test parses and merges settings, representing the results as strings that get
// compared against an expected hash. To debug, the result strings can be dumped
-// to a file (see below).
-BOOST_FIXTURE_TEST_CASE(util_SettingsMerge, SettingsMergeTestingSetup)
+// to a file (see comments below).
+BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup)
{
CHash256 out_sha;
FILE* out_file = nullptr;
- if (const char* out_path = getenv("SETTINGS_MERGE_TEST_OUT")) {
+ if (const char* out_path = getenv("ARGS_MERGE_TEST_OUT")) {
out_file = fsbridge::fopen(out_path, "w");
if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed");
}
@@ -706,7 +680,7 @@ BOOST_FIXTURE_TEST_CASE(util_SettingsMerge, SettingsMergeTestingSetup)
desc += network;
parser.m_network = network;
- const std::string& name = net_specific ? "server" : "wallet";
+ const std::string& name = net_specific ? "wallet" : "server";
const std::string key = "-" + name;
parser.AddArg(key, name, false, OptionsCategory::OPTIONS);
if (net_specific) parser.SetNetworkOnlyArg(key);
@@ -794,14 +768,116 @@ BOOST_FIXTURE_TEST_CASE(util_SettingsMerge, SettingsMergeTestingSetup)
// If check below fails, should manually dump the results with:
//
- // SETTINGS_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_SettingsMerge
+ // ARGS_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_ArgsMerge
//
// And verify diff against previous results to make sure the changes are expected.
//
// Results file is formatted like:
//
// <input> || <IsArgSet/IsArgNegated/GetArg output> | <GetArgs output> | <GetUnsuitable output>
- BOOST_CHECK_EQUAL(out_sha_hex, "80964e17fbd3c5569d3c824d032e28e2d319ef57494735b0e76eb7aad9957f2c");
+ BOOST_CHECK_EQUAL(out_sha_hex, "b835eef5977d69114eb039a976201f8c7121f34fe2b7ea2b73cafb516e5c9dc8");
+}
+
+// Similar test as above, but for ArgsManager::GetChainName function.
+struct ChainMergeTestingSetup : public BasicTestingSetup {
+ static constexpr int MAX_ACTIONS = 2;
+
+ enum Action { NONE, ENABLE_TEST, DISABLE_TEST, NEGATE_TEST, ENABLE_REG, DISABLE_REG, NEGATE_REG };
+ using ActionList = Action[MAX_ACTIONS];
+
+ //! Enumerate all possible test configurations.
+ template <typename Fn>
+ void ForEachMergeSetup(Fn&& fn)
+ {
+ ActionList arg_actions = {};
+ ForEachNoDup(arg_actions, ENABLE_TEST, NEGATE_REG, [&] {
+ ActionList conf_actions = {};
+ ForEachNoDup(conf_actions, ENABLE_TEST, NEGATE_REG, [&] { fn(arg_actions, conf_actions); });
+ });
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
+{
+ CHash256 out_sha;
+ FILE* out_file = nullptr;
+ if (const char* out_path = getenv("CHAIN_MERGE_TEST_OUT")) {
+ out_file = fsbridge::fopen(out_path, "w");
+ if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed");
+ }
+
+ ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions) {
+ TestArgsManager parser;
+ LOCK(parser.cs_args);
+ parser.AddArg("-regtest", "regtest", false, OptionsCategory::OPTIONS);
+ parser.AddArg("-testnet", "testnet", false, OptionsCategory::OPTIONS);
+
+ auto arg = [](Action action) { return action == ENABLE_TEST ? "-testnet=1" :
+ action == DISABLE_TEST ? "-testnet=0" :
+ action == NEGATE_TEST ? "-notestnet=1" :
+ action == ENABLE_REG ? "-regtest=1" :
+ action == DISABLE_REG ? "-regtest=0" :
+ action == NEGATE_REG ? "-noregtest=1" : nullptr; };
+
+ std::string desc;
+ std::vector<const char*> argv = {"ignored"};
+ for (Action action : arg_actions) {
+ const char* argstr = arg(action);
+ if (!argstr) break;
+ argv.push_back(argstr);
+ desc += " ";
+ desc += argv.back();
+ }
+ std::string error;
+ BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error));
+ BOOST_CHECK_EQUAL(error, "");
+
+ std::string conf;
+ for (Action action : conf_actions) {
+ const char* argstr = arg(action);
+ if (!argstr) break;
+ desc += " ";
+ desc += argstr + 1;
+ conf += argstr + 1;
+ }
+ std::istringstream conf_stream(conf);
+ BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error));
+ BOOST_CHECK_EQUAL(error, "");
+
+ desc += " || ";
+ try {
+ desc += parser.GetChainName();
+ } catch (const std::runtime_error& e) {
+ desc += "error: ";
+ desc += e.what();
+ }
+ desc += "\n";
+
+ out_sha.Write((const unsigned char*)desc.data(), desc.size());
+ if (out_file) {
+ BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) == desc.size());
+ }
+ });
+
+ if (out_file) {
+ if (fclose(out_file)) throw std::system_error(errno, std::generic_category(), "fclose failed");
+ out_file = nullptr;
+ }
+
+ unsigned char out_sha_bytes[CSHA256::OUTPUT_SIZE];
+ out_sha.Finalize(out_sha_bytes);
+ std::string out_sha_hex = HexStr(std::begin(out_sha_bytes), std::end(out_sha_bytes));
+
+ // If check below fails, should manually dump the results with:
+ //
+ // CHAIN_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_ChainMerge
+ //
+ // And verify diff against previous results to make sure the changes are expected.
+ //
+ // Results file is formatted like:
+ //
+ // <input> || <output>
+ BOOST_CHECK_EQUAL(out_sha_hex, "b284f4b4a15dd6bf8c06213a69a004b1960388e1d9917173927db52ac220927f");
}
BOOST_AUTO_TEST_CASE(util_FormatMoney)
@@ -993,6 +1069,27 @@ BOOST_AUTO_TEST_CASE(gettime)
BOOST_CHECK((GetTime() & ~0xFFFFFFFFLL) == 0);
}
+BOOST_AUTO_TEST_CASE(util_time_GetTime)
+{
+ SetMockTime(111);
+ // Check that mock time does not change after a sleep
+ for (const auto& num_sleep : {0, 1}) {
+ MilliSleep(num_sleep);
+ BOOST_CHECK_EQUAL(111, GetTime()); // Deprecated time getter
+ BOOST_CHECK_EQUAL(111, GetTime<std::chrono::seconds>().count());
+ BOOST_CHECK_EQUAL(111000, GetTime<std::chrono::milliseconds>().count());
+ BOOST_CHECK_EQUAL(111000000, GetTime<std::chrono::microseconds>().count());
+ }
+
+ SetMockTime(0);
+ // Check that system time changes after a sleep
+ const auto ms_0 = GetTime<std::chrono::milliseconds>();
+ const auto us_0 = GetTime<std::chrono::microseconds>();
+ MilliSleep(1);
+ BOOST_CHECK(ms_0 < GetTime<std::chrono::milliseconds>());
+ BOOST_CHECK(us_0 < GetTime<std::chrono::microseconds>());
+}
+
BOOST_AUTO_TEST_CASE(test_IsDigit)
{
BOOST_CHECK_EQUAL(IsDigit('0'), true);
@@ -1302,7 +1399,7 @@ static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
BOOST_AUTO_TEST_CASE(test_LockDirectory)
{
- fs::path dirname = SetDataDir("test_LockDirectory") / fs::unique_path();
+ fs::path dirname = GetDataDir() / "lock_dir";
const std::string lockname = ".lock";
#ifndef WIN32
// Revert SIGCHLD to default, otherwise boost.test will catch and fail on
@@ -1391,7 +1488,7 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory)
BOOST_AUTO_TEST_CASE(test_DirIsWritable)
{
// Should be able to write to the data dir.
- fs::path tmpdirname = SetDataDir("test_DirIsWritable");
+ fs::path tmpdirname = GetDataDir();
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true);
// Should not be able to write to a non-existent dir.
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 4d54aa9b2c..6a9813442b 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -11,9 +11,12 @@
#include <pow.h>
#include <random.h>
#include <test/setup_common.h>
+#include <util/time.h>
#include <validation.h>
#include <validationinterface.h>
+#include <thread>
+
struct RegtestingSetup : public TestingSetup {
RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {}
};
@@ -144,7 +147,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
const CBlockIndex* initial_tip = nullptr;
{
LOCK(cs_main);
- initial_tip = chainActive.Tip();
+ initial_tip = ::ChainActive().Tip();
}
TestSubscriber sub(initial_tip->GetBlockHash());
RegisterValidationInterface(&sub);
@@ -181,7 +184,8 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
UnregisterValidationInterface(&sub);
- BOOST_CHECK_EQUAL(sub.m_expected_tip, chainActive.Tip()->GetBlockHash());
+ LOCK(cs_main);
+ BOOST_CHECK_EQUAL(sub.m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 38d91b6647..0ca3a17974 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -271,12 +271,12 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Before MedianTimePast of the chain has crossed nStartTime, the bit
// should not be set.
CBlockIndex *lastBlock = nullptr;
- lastBlock = firstChain.Mine(2016, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
- // Mine 2011 more blocks at the old time, and check that CBV isn't setting the bit yet.
- for (int i=1; i<2012; i++) {
- lastBlock = firstChain.Mine(2016+i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ // Mine more blocks (4 less than the adjustment period) at the old time, and check that CBV isn't setting the bit yet.
+ for (uint32_t i = 1; i < mainnetParams.nMinerConfirmationWindow - 4; i++) {
+ lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// This works because VERSIONBITS_LAST_OLD_BLOCK_VERSION happens
// to be 4, and the bit we're testing happens to be bit 28.
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
@@ -284,13 +284,13 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Now mine 5 more blocks at the start time -- MTP should not have passed yet, so
// CBV should still not yet set the bit.
nTime = nStartTime;
- for (int i=2012; i<=2016; i++) {
- lastBlock = firstChain.Mine(2016+i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ for (uint32_t i = mainnetParams.nMinerConfirmationWindow - 4; i <= mainnetParams.nMinerConfirmationWindow; i++) {
+ lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
}
// Advance to the next period and transition to STARTED,
- lastBlock = firstChain.Mine(6048, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// so ComputeBlockVersion should now set the bit,
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// and should also be using the VERSIONBITS_TOP_BITS.
@@ -298,8 +298,8 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Check that ComputeBlockVersion will set the bit until nTimeout
nTime += 600;
- int blocksToMine = 4032; // test blocks for up to 2 time periods
- int nHeight = 6048;
+ uint32_t blocksToMine = mainnetParams.nMinerConfirmationWindow * 2; // test blocks for up to 2 time periods
+ uint32_t nHeight = mainnetParams.nMinerConfirmationWindow * 3;
// These blocks are all before nTimeout is reached.
while (nTime < nTimeout && blocksToMine > 0) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
@@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
nTime = nTimeout;
// FAILED is only triggered at the end of a period, so CBV should be setting
// the bit until the period transition.
- for (int i=0; i<2015; i++) {
+ for (uint32_t i = 0; i < mainnetParams.nMinerConfirmationWindow - 1; i++) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
nHeight += 1;
@@ -329,20 +329,20 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Mine one period worth of blocks, and check that the bit will be on for the
// next period.
- lastBlock = secondChain.Mine(2016, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// Mine another period worth of blocks, signaling the new bit.
- lastBlock = secondChain.Mine(4032, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
+ lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow * 2, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
// After one period of setting the bit on each block, it should have locked in.
// We keep setting the bit for one more period though, until activation.
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// Now check that we keep mining the block until the end of this period, and
// then stop at the beginning of the next period.
- lastBlock = secondChain.Mine(6047, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
- lastBlock = secondChain.Mine(6048, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = secondChain.Mine((mainnetParams.nMinerConfirmationWindow * 3) - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit)) != 0);
+ lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
// Finally, verify that after a soft fork has activated, CBV no longer uses
diff --git a/src/threadsafety.h b/src/threadsafety.h
index 33acddc65c..47e6b2ea38 100644
--- a/src/threadsafety.h
+++ b/src/threadsafety.h
@@ -54,15 +54,4 @@
#define ASSERT_EXCLUSIVE_LOCK(...)
#endif // __GNUC__
-// Utility class for indicating to compiler thread analysis that a mutex is
-// locked (when it couldn't be determined otherwise).
-struct SCOPED_LOCKABLE LockAnnotation
-{
- template <typename Mutex>
- explicit LockAnnotation(Mutex& mutex) EXCLUSIVE_LOCK_FUNCTION(mutex)
- {
- }
- ~LockAnnotation() UNLOCK_FUNCTION() {}
-};
-
#endif // BITCOIN_THREADSAFETY_H
diff --git a/src/timedata.cpp b/src/timedata.cpp
index 9c022c9ad1..b43639d729 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -12,7 +12,6 @@
#include <sync.h>
#include <ui_interface.h>
#include <util/system.h>
-#include <util/strencodings.h>
#include <warnings.h>
diff --git a/src/tinyformat.h b/src/tinyformat.h
index 14b7cd3026..182f518a0b 100644
--- a/src/tinyformat.h
+++ b/src/tinyformat.h
@@ -1063,6 +1063,7 @@ std::string format(const std::string &fmt, const Args&... args)
} // namespace tinyformat
+/** Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for details) */
#define strprintf tfm::format
#endif // TINYFORMAT_H_INCLUDED
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 550e23b222..a1c730ba08 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.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 <chainparams.h>
#include <torcontrol.h>
#include <util/strencodings.h>
#include <netbase.h>
@@ -412,7 +413,7 @@ public:
TorController(struct event_base* base, const std::string& target);
~TorController();
- /** Get name fo file to store private key in */
+ /** Get name of file to store private key in */
fs::path GetPrivateKeyFile();
/** Reconnect, after getting disconnected */
@@ -500,7 +501,7 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe
}
return;
}
- service = LookupNumeric(std::string(service_id+".onion").c_str(), GetListenPort());
+ service = LookupNumeric(std::string(service_id+".onion").c_str(), Params().GetDefaultPort());
LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString());
if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) {
LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", GetPrivateKeyFile().string());
@@ -534,9 +535,8 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply&
if (private_key.empty()) // No private key, generate one
private_key = "NEW:RSA1024"; // Explicitly request RSA1024 - see issue #9214
// Request hidden service, redirect port.
- // Note that the 'virtual' port doesn't have to be the same as our internal port, but this is just a convenient
- // choice. TODO; refactor the shutdown sequence some day.
- _conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, GetListenPort(), GetListenPort()),
+ // Note that the 'virtual' port is always the default port to avoid decloaking nodes using other ports.
+ _conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, Params().GetDefaultPort(), GetListenPort()),
std::bind(&TorController::add_onion_cb, this, std::placeholders::_1, std::placeholders::_2));
} else {
LogPrintf("tor: Authentication failed\n");
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 8447352c54..73fe2a8ee4 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -5,8 +5,6 @@
#include <txdb.h>
-#include <chainparams.h>
-#include <hash.h>
#include <random.h>
#include <pow.h>
#include <shutdown.h>
@@ -255,6 +253,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
// Load mapBlockIndex
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
+ if (ShutdownRequested()) return false;
std::pair<char, uint256> key;
if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
CDiskBlockIndex diskindex;
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 90b28227a0..cac7beb6a1 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -13,8 +13,6 @@
#include <policy/fees.h>
#include <policy/settings.h>
#include <reverse_iterator.h>
-#include <streams.h>
-#include <timedata.h>
#include <util/system.h>
#include <util/moneystr.h>
#include <util/time.h>
@@ -601,7 +599,7 @@ static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& m
CAmount txfee = 0;
bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee);
assert(fCheckResult);
- UpdateCoins(tx, mempoolDuplicate, 1000000);
+ UpdateCoins(tx, mempoolDuplicate, std::numeric_limits<int>::max());
}
void CTxMemPool::check(const CCoinsViewCache *pcoins) const
diff --git a/src/txmempool.h b/src/txmempool.h
index 3ada47a28e..ce0b762336 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -496,7 +496,7 @@ public:
* By design, it is guaranteed that:
*
* 1. Locking both `cs_main` and `mempool.cs` will give a view of mempool
- * that is consistent with current chain tip (`chainActive` and
+ * that is consistent with current chain tip (`::ChainActive()` and
* `pcoinsTip`) and is fully populated. Fully populated means that if the
* current active chain is missing transactions that were present in a
* previously active chain, all the missing transactions will have been
diff --git a/src/ui_interface.cpp b/src/ui_interface.cpp
index 31a95486d7..d310637145 100644
--- a/src/ui_interface.cpp
+++ b/src/ui_interface.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <ui_interface.h>
-#include <util/system.h>
#include <boost/signals2/last_value.hpp>
#include <boost/signals2/signal.hpp>
@@ -22,7 +21,8 @@ struct UISignals {
boost::signals2::signal<CClientUIInterface::NotifyBlockTipSig> NotifyBlockTip;
boost::signals2::signal<CClientUIInterface::NotifyHeaderTipSig> NotifyHeaderTip;
boost::signals2::signal<CClientUIInterface::BannedListChangedSig> BannedListChanged;
-} g_ui_signals;
+};
+static UISignals g_ui_signals;
#define ADD_SIGNALS_IMPL_WRAPPER(signal_name) \
boost::signals2::connection CClientUIInterface::signal_name##_connect(std::function<signal_name##Sig> fn) \
diff --git a/src/ui_interface.h b/src/ui_interface.h
index d408f6f889..5e0380dc45 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -69,6 +69,9 @@ 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,
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 6925bda4ef..87ff6e62ba 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -1,13 +1,11 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/system.h>
#include <chainparamsbase.h>
-#include <random.h>
-#include <serialize.h>
#include <util/strencodings.h>
#include <stdarg.h>
@@ -675,7 +673,7 @@ void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
{
std::string message = FormatException(pex, pszThread);
LogPrintf("\n\n************************\n%s\n", message);
- fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
+ tfm::format(std::cerr, "\n\n************************\n%s\n", message.c_str());
}
fs::path GetDefaultDataDir()
@@ -707,19 +705,16 @@ fs::path GetDefaultDataDir()
static fs::path g_blocks_path_cache_net_specific;
static fs::path pathCached;
static fs::path pathCachedNetSpecific;
-static CCriticalSection csPathCached;
+static RecursiveMutex csPathCached;
const fs::path &GetBlocksDir()
{
-
LOCK(csPathCached);
-
fs::path &path = g_blocks_path_cache_net_specific;
- // This can be called during exceptions by LogPrintf(), so we cache the
- // value so we don't have to do memory allocations after that.
- if (!path.empty())
- return path;
+ // Cache the path to avoid calling fs::create_directories on every call of
+ // this function
+ if (!path.empty()) return path;
if (gArgs.IsArgSet("-blocksdir")) {
path = fs::system_complete(gArgs.GetArg("-blocksdir", ""));
@@ -739,15 +734,12 @@ const fs::path &GetBlocksDir()
const fs::path &GetDataDir(bool fNetSpecific)
{
-
LOCK(csPathCached);
-
fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached;
- // This can be called during exceptions by LogPrintf(), so we cache the
- // value so we don't have to do memory allocations after that.
- if (!path.empty())
- return path;
+ // Cache the path to avoid calling fs::create_directories on every call of
+ // this function
+ if (!path.empty()) return path;
if (gArgs.IsArgSet("-datadir")) {
path = fs::system_complete(gArgs.GetArg("-datadir", ""));
@@ -935,7 +927,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
}
}
for (const std::string& to_include : includeconf) {
- fprintf(stderr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", to_include.c_str());
+ tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", to_include.c_str());
}
}
}
diff --git a/src/util/system.h b/src/util/system.h
index 1a83cb67b1..15d7b1b402 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -20,18 +20,16 @@
#include <fs.h>
#include <logging.h>
#include <sync.h>
-#include <util/threadnames.h>
#include <tinyformat.h>
#include <util/memory.h>
+#include <util/threadnames.h>
#include <util/time.h>
-#include <atomic>
#include <exception>
#include <map>
#include <set>
#include <stdint.h>
#include <string>
-#include <unordered_set>
#include <utility>
#include <vector>
@@ -85,6 +83,7 @@ fs::path GetDefaultDataDir();
// The blocks directory is always net specific.
const fs::path &GetBlocksDir();
const fs::path &GetDataDir(bool fNetSpecific = true);
+/** Tests only */
void ClearDatadirCache();
fs::path GetConfigFile(const std::string& confPath);
#ifdef WIN32
diff --git a/src/util/threadnames.cpp b/src/util/threadnames.cpp
index 7b0d744aec..b221b0c975 100644
--- a/src/util/threadnames.cpp
+++ b/src/util/threadnames.cpp
@@ -9,6 +9,11 @@
#include <atomic>
#include <thread>
+#if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
+#include <pthread.h>
+#include <pthread_np.h>
+#endif
+
#include <util/threadnames.h>
#ifdef HAVE_SYS_PRCTL_H
diff --git a/src/util/time.cpp b/src/util/time.cpp
index c0ede98701..2b202ae95f 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -27,6 +27,20 @@ int64_t GetTime()
return now;
}
+template <typename T>
+T GetTime()
+{
+ const std::chrono::seconds mocktime{nMockTime.load(std::memory_order_relaxed)};
+
+ return std::chrono::duration_cast<T>(
+ mocktime.count() ?
+ mocktime :
+ std::chrono::microseconds{GetTimeMicros()});
+}
+template std::chrono::seconds GetTime();
+template std::chrono::milliseconds GetTime();
+template std::chrono::microseconds GetTime();
+
void SetMockTime(int64_t nMockTimeIn)
{
nMockTime.store(nMockTimeIn, std::memory_order_relaxed);
diff --git a/src/util/time.h b/src/util/time.h
index 68de1c156e..e4f9996777 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -8,27 +8,34 @@
#include <stdint.h>
#include <string>
+#include <chrono>
/**
- * GetTimeMicros() and GetTimeMillis() both return the system time, but in
- * different units. GetTime() returns the system time in seconds, but also
- * supports mocktime, where the time can be specified by the user, eg for
- * testing (eg with the setmocktime rpc, or -mocktime argument).
- *
- * TODO: Rework these functions to be type-safe (so that we don't inadvertently
- * compare numbers with different units, or compare a mocktime to system time).
+ * DEPRECATED
+ * Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable)
*/
-
int64_t GetTime();
+
+/** Returns the system time (not mockable) */
int64_t GetTimeMillis();
+/** Returns the system time (not mockable) */
int64_t GetTimeMicros();
+/** Returns the system time (not mockable) */
int64_t GetSystemTimeInSeconds(); // Like GetTime(), but not mockable
+
+/** For testing. Set e.g. with the setmocktime rpc, or -mocktime argument */
void SetMockTime(int64_t nMockTimeIn);
+/** For testing */
int64_t GetMockTime();
+
void MilliSleep(int64_t n);
+/** Return system time (or mocked time, if set) */
+template <typename T>
+T GetTime();
+
/**
- * ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date,Time}
+ * ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date}
* helper functions if possible.
*/
std::string FormatISO8601DateTime(int64_t nTime);
diff --git a/src/validation.cpp b/src/validation.cpp
index ba9d6184ee..d39b78614c 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -20,7 +20,6 @@
#include <index/txindex.h>
#include <policy/fees.h>
#include <policy/policy.h>
-#include <policy/rbf.h>
#include <policy/settings.h>
#include <pow.h>
#include <primitives/block.h>
@@ -60,163 +59,29 @@
#define MICRO 0.000001
#define MILLI 0.001
-/**
- * Global state
- */
-namespace {
- struct CBlockIndexWorkComparator
- {
- bool operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
- // First sort by most total work, ...
- if (pa->nChainWork > pb->nChainWork) return false;
- if (pa->nChainWork < pb->nChainWork) return true;
-
- // ... then by earliest time received, ...
- if (pa->nSequenceId < pb->nSequenceId) return false;
- if (pa->nSequenceId > pb->nSequenceId) return true;
-
- // Use pointer address as tie breaker (should only happen with blocks
- // loaded from disk, as those all have id 0).
- if (pa < pb) return false;
- if (pa > pb) return true;
-
- // Identical blocks.
- return false;
- }
- };
-} // anon namespace
-
-enum DisconnectResult
-{
- DISCONNECT_OK, // All good.
- DISCONNECT_UNCLEAN, // Rolled back, but UTXO set was inconsistent with block.
- DISCONNECT_FAILED // Something else went wrong.
-};
-
-class ConnectTrace;
-
-/**
- * CChainState stores and provides an API to update our local knowledge of the
- * current best chain and header tree.
- *
- * It generally provides access to the current block tree, as well as functions
- * to provide new data, which it will appropriately validate and incorporate in
- * its state as necessary.
- *
- * Eventually, the API here is targeted at being exposed externally as a
- * consumable libconsensus library, so any functions added must only call
- * other class member functions, pure functions in other parts of the consensus
- * library, callbacks via the validation interface, or read/write-to-disk
- * functions (eventually this will also be via callbacks).
- */
-class CChainState {
-private:
- /**
- * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
- * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
- * missing the data for the block.
- */
- std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
-
- /**
- * Every received block is assigned a unique and increasing identifier, so we
- * know which one to give priority in case of a fork.
- */
- CCriticalSection cs_nBlockSequenceId;
- /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */
- int32_t nBlockSequenceId = 1;
- /** Decreasing counter (used by subsequent preciousblock calls). */
- int32_t nBlockReverseSequenceId = -1;
- /** chainwork for the last block that preciousblock has been applied to. */
- arith_uint256 nLastPreciousChainwork = 0;
-
- /** In order to efficiently track invalidity of headers, we keep the set of
- * blocks which we tried to connect and found to be invalid here (ie which
- * were set to BLOCK_FAILED_VALID since the last restart). We can then
- * walk this set and check if a new header is a descendant of something in
- * this set, preventing us from having to walk mapBlockIndex when we try
- * to connect a bad block and fail.
- *
- * While this is more complicated than marking everything which descends
- * from an invalid block as invalid at the time we discover it to be
- * invalid, doing so would require walking all of mapBlockIndex to find all
- * descendants. Since this case should be very rare, keeping track of all
- * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
- * well.
- *
- * Because we already walk mapBlockIndex in height-order at startup, we go
- * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
- * instead of putting things in this set.
- */
- std::set<CBlockIndex*> m_failed_blocks;
-
- /**
- * the ChainState CriticalSection
- * A lock that must be held when modifying this ChainState - held in ActivateBestChain()
- */
- CCriticalSection m_cs_chainstate;
-
-public:
- CChain chainActive;
- BlockMap mapBlockIndex GUARDED_BY(cs_main);
- std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
- CBlockIndex *pindexBestInvalid = nullptr;
-
- bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock);
-
- /**
- * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
- * that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
- */
- bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- // Block (dis)connection on a given view:
- DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view);
- bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
- CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- // Block disconnection on our pcoinsTip:
- bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- // Manual block validity manipulation:
- bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
- bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex);
- void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
+ // First sort by most total work, ...
+ if (pa->nChainWork > pb->nChainWork) return false;
+ if (pa->nChainWork < pb->nChainWork) return true;
- bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
- bool RewindBlockIndex(const CChainParams& params);
- bool LoadGenesisBlock(const CChainParams& chainparams);
+ // ... then by earliest time received, ...
+ if (pa->nSequenceId < pb->nSequenceId) return false;
+ if (pa->nSequenceId > pb->nSequenceId) return true;
- void PruneBlockIndexCandidates();
+ // Use pointer address as tie breaker (should only happen with blocks
+ // loaded from disk, as those all have id 0).
+ if (pa < pb) return false;
+ if (pa > pb) return true;
- void UnloadBlockIndex();
-
-private:
- bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Create a new block index entry for a given block hash */
- CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /**
- * Make various assertions about the state of the block index.
- *
- * By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex.
- */
- void CheckBlockIndex(const Consensus::Params& consensusParams);
+ // Identical blocks.
+ return false;
+}
- void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+static CChainState g_chainstate;
- bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+CChainState& ChainstateActive() { return g_chainstate; }
- //! Mark a block as not having block data
- void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-} g_chainstate;
+CChain& ChainActive() { return g_chainstate.m_chain; }
/**
* Mutex to guard access to validation specific variables, such as reading
@@ -230,8 +95,7 @@ private:
*/
RecursiveMutex cs_main;
-BlockMap& mapBlockIndex = g_chainstate.mapBlockIndex;
-CChain& chainActive = g_chainstate.chainActive;
+BlockMap& mapBlockIndex = ::ChainstateActive().mapBlockIndex;
CBlockIndex *pindexBestHeader = nullptr;
Mutex g_best_block_mutex;
std::condition_variable g_best_block_cv;
@@ -247,7 +111,6 @@ bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
size_t nCoinCacheUsage = 5000 * 300;
uint64_t nPruneTarget = 0;
int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
-bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT;
uint256 hashAssumeValid;
arith_uint256 nMinimumChainWork;
@@ -262,12 +125,12 @@ CScript COINBASE_FLAGS;
// Internal stuff
namespace {
- CBlockIndex *&pindexBestInvalid = g_chainstate.pindexBestInvalid;
+ CBlockIndex *&pindexBestInvalid = ::ChainstateActive().pindexBestInvalid;
/** All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
* Pruned nodes may have entries where B is missing data.
*/
- std::multimap<CBlockIndex*, CBlockIndex*>& mapBlocksUnlinked = g_chainstate.mapBlocksUnlinked;
+ std::multimap<CBlockIndex*, CBlockIndex*>& mapBlocksUnlinked = ::ChainstateActive().mapBlocksUnlinked;
CCriticalSection cs_LastBlockFile;
std::vector<CBlockFileInfo> vinfoBlockFile;
@@ -308,15 +171,7 @@ std::unique_ptr<CCoinsViewDB> pcoinsdbview;
std::unique_ptr<CCoinsViewCache> pcoinsTip;
std::unique_ptr<CBlockTreeDB> pblocktree;
-enum class FlushStateMode {
- NONE,
- IF_NEEDED,
- PERIODIC,
- ALWAYS
-};
-
// See definition for documentation
-static bool FlushStateToDisk(const CChainParams& chainParams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0);
static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
@@ -336,13 +191,13 @@ bool CheckFinalTx(const CTransaction &tx, int flags)
// scheduled, so no flags are set.
flags = std::max(flags, 0);
- // CheckFinalTx() uses chainActive.Height()+1 to evaluate
+ // CheckFinalTx() uses ::ChainActive().Height()+1 to evaluate
// nLockTime because when IsFinalTx() is called within
// CBlock::AcceptBlock(), the height of the block *being*
// evaluated is what is used. Thus if we want to know if a
// transaction can be part of the *next* block, we need to call
- // IsFinalTx() with one more than chainActive.Height().
- const int nBlockHeight = chainActive.Height() + 1;
+ // IsFinalTx() with one more than ::ChainActive().Height().
+ const int nBlockHeight = ::ChainActive().Height() + 1;
// BIP113 requires that time-locked transactions have nLockTime set to
// less than the median time of the previous block they're contained in.
@@ -350,7 +205,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags)
// chain tip, so we use that to calculate the median time passed to
// IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set.
const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST)
- ? chainActive.Tip()->GetMedianTimePast()
+ ? ::ChainActive().Tip()->GetMedianTimePast()
: GetAdjustedTime();
return IsFinalTx(tx, nBlockHeight, nBlockTime);
@@ -363,9 +218,9 @@ bool TestLockPointValidity(const LockPoints* lp)
// If there are relative lock times then the maxInputBlock will be set
// If there are no relative lock times, the LockPoints don't depend on the chain
if (lp->maxInputBlock) {
- // Check whether chainActive is an extension of the block at which the LockPoints
+ // Check whether ::ChainActive() is an extension of the block at which the LockPoints
// calculation was valid. If not LockPoints are no longer valid
- if (!chainActive.Contains(lp->maxInputBlock)) {
+ if (!::ChainActive().Contains(lp->maxInputBlock)) {
return false;
}
}
@@ -379,17 +234,17 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
AssertLockHeld(cs_main);
AssertLockHeld(pool.cs);
- CBlockIndex* tip = chainActive.Tip();
+ CBlockIndex* tip = ::ChainActive().Tip();
assert(tip != nullptr);
CBlockIndex index;
index.pprev = tip;
- // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate
+ // CheckSequenceLocks() uses ::ChainActive().Height()+1 to evaluate
// height based locks because when SequenceLocks() is called within
// ConnectBlock(), the height of the block *being*
// evaluated is what is used.
// Thus if we want to know if a transaction can be part of the
- // *next* block, we need to use one more than chainActive.Height()
+ // *next* block, we need to use one more than ::ChainActive().Height()
index.nHeight = tip->nHeight + 1;
std::pair<int, int64_t> lockPair;
@@ -399,7 +254,7 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
lockPair.second = lp->time;
}
else {
- // pcoinsTip contains the UTXO set for chainActive.Tip()
+ // pcoinsTip contains the UTXO set for ::ChainActive().Tip()
CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
std::vector<int> prevheights;
prevheights.resize(tx.vin.size());
@@ -464,11 +319,11 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
- if (IsInitialBlockDownload())
+ if (::ChainstateActive().IsInitialBlockDownload())
return false;
- if (chainActive.Tip()->GetBlockTime() < (GetTime() - MAX_FEE_ESTIMATION_TIP_AGE))
+ if (::ChainActive().Tip()->GetBlockTime() < (GetTime() - MAX_FEE_ESTIMATION_TIP_AGE))
return false;
- if (chainActive.Height() < pindexBestHeader->nHeight - 1)
+ if (::ChainActive().Height() < pindexBestHeader->nHeight - 1)
return false;
return true;
}
@@ -520,7 +375,7 @@ static void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool,
mempool.UpdateTransactionsFromBlock(vHashUpdate);
// We also need to remove any now-immature transactions
- mempool.removeForReorg(pcoinsTip.get(), chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
+ mempool.removeForReorg(pcoinsTip.get(), ::ChainActive().Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
// Re-limit mempool size, in case we added any transactions
LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
}
@@ -630,15 +485,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// unconfirmed ancestors anyway; doing otherwise is hopelessly
// insecure.
bool fReplacementOptOut = true;
- if (fEnableReplacement)
+ for (const CTxIn &_txin : ptxConflicting->vin)
{
- for (const CTxIn &_txin : ptxConflicting->vin)
+ if (_txin.nSequence <= MAX_BIP125_RBF_SEQUENCE)
{
- if (_txin.nSequence <= MAX_BIP125_RBF_SEQUENCE)
- {
- fReplacementOptOut = false;
- break;
- }
+ fReplacementOptOut = false;
+ break;
}
}
if (fReplacementOptOut) {
@@ -727,7 +579,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}
}
- CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, chainActive.Height(),
+ CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, ::ChainActive().Height(),
fSpendsCoinbase, nSigOpsCost, lp);
unsigned int nSize = entry.GetTxSize();
@@ -924,7 +776,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack.
- unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), chainparams.GetConsensus());
+ unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(::ChainActive().Tip(), chainparams.GetConsensus());
if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) {
return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s",
__func__, hash.ToString(), FormatStateMessage(state));
@@ -989,7 +841,7 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
}
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
CValidationState stateDummy;
- FlushStateToDisk(chainparams, stateDummy, FlushStateMode::PERIODIC);
+ ::ChainstateActive().FlushStateToDisk(chainparams, stateDummy, FlushStateMode::PERIODIC);
return res;
}
@@ -1165,31 +1017,34 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
return nSubsidy;
}
-bool IsInitialBlockDownload()
+// Note that though this is marked const, we may end up modifying `m_cached_finished_ibd`, which
+// is a performance-related implementation detail. This function must be marked
+// `const` so that `CValidationInterface` clients (which are given a `const CChainState*`)
+// can call it.
+//
+bool CChainState::IsInitialBlockDownload() const
{
- // Once this function has returned false, it must remain false.
- static std::atomic<bool> latchToFalse{false};
// Optimization: pre-test latch before taking the lock.
- if (latchToFalse.load(std::memory_order_relaxed))
+ if (m_cached_finished_ibd.load(std::memory_order_relaxed))
return false;
LOCK(cs_main);
- if (latchToFalse.load(std::memory_order_relaxed))
+ if (m_cached_finished_ibd.load(std::memory_order_relaxed))
return false;
if (fImporting || fReindex)
return true;
- if (chainActive.Tip() == nullptr)
+ if (m_chain.Tip() == nullptr)
return true;
- if (chainActive.Tip()->nChainWork < nMinimumChainWork)
+ if (m_chain.Tip()->nChainWork < nMinimumChainWork)
return true;
- if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
+ if (m_chain.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
return true;
LogPrintf("Leaving InitialBlockDownload (latching to false)\n");
- latchToFalse.store(true, std::memory_order_relaxed);
+ m_cached_finished_ibd.store(true, std::memory_order_relaxed);
return false;
}
-CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr;
+static CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr;
static void AlertNotify(const std::string& strMessage)
{
@@ -1214,15 +1069,15 @@ static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
AssertLockHeld(cs_main);
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before finishing our initial sync)
- if (IsInitialBlockDownload())
+ if (::ChainstateActive().IsInitialBlockDownload())
return;
// If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it)
// of our head, drop it
- if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72)
+ if (pindexBestForkTip && ::ChainActive().Height() - pindexBestForkTip->nHeight >= 72)
pindexBestForkTip = nullptr;
- if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6)))
+ if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > ::ChainActive().Tip()->nChainWork + (GetBlockProof(*::ChainActive().Tip()) * 6)))
{
if (!GetfLargeWorkForkFound() && pindexBestForkBase)
{
@@ -1255,7 +1110,7 @@ static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) E
AssertLockHeld(cs_main);
// If we are on a fork that is sufficiently large, set a warning flag
CBlockIndex* pfork = pindexNewForkTip;
- CBlockIndex* plonger = chainActive.Tip();
+ CBlockIndex* plonger = ::ChainActive().Tip();
while (pfork && pfork != plonger)
{
while (plonger && plonger->nHeight > pfork->nHeight)
@@ -1274,7 +1129,7 @@ static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) E
// the 7-block condition and from this always have the most-likely-to-cause-warning fork
if (pfork && (!pindexBestForkTip || pindexNewForkTip->nHeight > pindexBestForkTip->nHeight) &&
pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) &&
- chainActive.Height() - pindexNewForkTip->nHeight < 72)
+ ::ChainActive().Height() - pindexNewForkTip->nHeight < 72)
{
pindexBestForkTip = pindexNewForkTip;
pindexBestForkBase = pfork;
@@ -1291,10 +1146,10 @@ void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(c
LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime()));
- CBlockIndex *tip = chainActive.Tip();
+ CBlockIndex *tip = ::ChainActive().Tip();
assert (tip);
LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__,
- tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0),
+ tip->GetBlockHash().ToString(), ::ChainActive().Height(), log(tip->nChainWork.getdouble())/log(2.0),
FormatISO8601DateTime(tip->GetBlockTime()));
CheckForkWarningConditions();
}
@@ -1519,20 +1374,22 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
}
/** Abort with a message */
-static bool AbortNode(const std::string& strMessage, const std::string& userMessage="")
+static bool AbortNode(const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0)
{
SetMiscWarning(strMessage);
LogPrintf("*** %s\n", strMessage);
- uiInterface.ThreadSafeMessageBox(
- userMessage.empty() ? _("Error: A fatal internal error occurred, see debug.log for details") : userMessage,
- "", CClientUIInterface::MSG_ERROR);
+ if (!userMessage.empty()) {
+ uiInterface.ThreadSafeMessageBox(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);
+ }
StartShutdown();
return false;
}
-static bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="")
+static bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0)
{
- AbortNode(strMessage, userMessage);
+ AbortNode(strMessage, userMessage, prefix);
return state.Error(strMessage);
}
@@ -2086,16 +1943,12 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
return true;
}
-/**
- * Update the on-disk chain state.
- * The caches and indexes are flushed depending on the mode we're called with
- * if they're too large, if it's been a while since the last write,
- * or always and in all cases if we're in prune mode and are deleting files.
- *
- * If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything
- * besides checking if we need to prune.
- */
-bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight) {
+bool CChainState::FlushStateToDisk(
+ const CChainParams& chainparams,
+ CValidationState &state,
+ FlushStateMode mode,
+ int nManualPruneHeight)
+{
int64_t nMempoolUsage = mempool.DynamicMemoryUsage();
LOCK(cs_main);
static int64_t nLastWrite = 0;
@@ -2147,7 +2000,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
if (fDoFullFlush || fPeriodicWrite) {
// Depend on nMinDiskSpace to ensure we can write block index
if (!CheckDiskSpace(GetBlocksDir())) {
- return AbortNode(state, "Disk space is low!", _("Error: Disk space is low!"));
+ return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!"), CClientUIInterface::MSG_NOPREFIX);
}
// First make sure all block and undo data is flushed to disk.
FlushBlockFile();
@@ -2182,7 +2035,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
// 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 * pcoinsTip->GetCacheSize())) {
- return AbortNode(state, "Disk space is low!", _("Error: Disk space is low!"));
+ return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!"), CClientUIInterface::MSG_NOPREFIX);
}
// Flush the chainstate (which may refer to block index entries).
if (!pcoinsTip->Flush())
@@ -2193,7 +2046,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
}
if (full_flush_completed) {
// Update best block in wallet (so we can detect restored wallets).
- GetMainSignals().ChainStateFlushed(chainActive.GetLocator());
+ GetMainSignals().ChainStateFlushed(m_chain.GetLocator());
}
} catch (const std::runtime_error& e) {
return AbortNode(state, std::string("System error while flushing: ") + e.what());
@@ -2201,19 +2054,20 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
return true;
}
-void FlushStateToDisk() {
+void CChainState::ForceFlushStateToDisk() {
CValidationState state;
const CChainParams& chainparams = Params();
- if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) {
+ if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
}
}
-void PruneAndFlush() {
+void CChainState::PruneAndFlush() {
CValidationState state;
fCheckForPruning = true;
const CChainParams& chainparams = Params();
- if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) {
+
+ if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
}
}
@@ -2247,7 +2101,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
}
std::string warningMessages;
- if (!IsInitialBlockDownload())
+ if (!::ChainstateActive().IsInitialBlockDownload())
{
int nUpgraded = 0;
const CBlockIndex* pindex = pindexNew;
@@ -2285,7 +2139,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
}
-/** Disconnect chainActive's tip.
+/** Disconnect m_chain's tip.
* After calling, the mempool will be in an inconsistent state, with
* transactions from disconnected blocks being added to disconnectpool. You
* should make the mempool consistent again by calling UpdateMempoolForReorg.
@@ -2297,7 +2151,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
*/
bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool)
{
- CBlockIndex *pindexDelete = chainActive.Tip();
+ CBlockIndex *pindexDelete = m_chain.Tip();
assert(pindexDelete);
// Read block from disk.
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
@@ -2332,7 +2186,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha
}
}
- chainActive.SetTip(pindexDelete->pprev);
+ m_chain.SetTip(pindexDelete->pprev);
UpdateTip(pindexDelete->pprev, chainparams);
// Let wallets know transactions went from 1-confirmed to
@@ -2410,14 +2264,14 @@ public:
};
/**
- * Connect a new block to chainActive. pblock is either nullptr or a pointer to a CBlock
+ * Connect a new block to m_chain. pblock is either nullptr or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*
* The block is added to connectTrace if connection succeeds.
*/
bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool)
{
- assert(pindexNew->pprev == chainActive.Tip());
+ assert(pindexNew->pprev == m_chain.Tip());
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
std::shared_ptr<const CBlock> pthisBlock;
@@ -2458,8 +2312,8 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
// Remove conflicting transactions from the mempool.;
mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
disconnectpool.removeForBlock(blockConnecting.vtx);
- // Update chainActive & related variables.
- chainActive.SetTip(pindexNew);
+ // Update m_chain & related variables.
+ m_chain.SetTip(pindexNew);
UpdateTip(pindexNew, chainparams);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
@@ -2490,7 +2344,7 @@ CBlockIndex* CChainState::FindMostWorkChain() {
// Just going until the active chain is an optimization, as we know all blocks in it are valid already.
CBlockIndex *pindexTest = pindexNew;
bool fInvalidAncestor = false;
- while (pindexTest && !chainActive.Contains(pindexTest)) {
+ while (pindexTest && !m_chain.Contains(pindexTest)) {
assert(pindexTest->HaveTxsDownloaded() || pindexTest->nHeight == 0);
// Pruned nodes may have entries in setBlockIndexCandidates for
@@ -2533,7 +2387,7 @@ void CChainState::PruneBlockIndexCandidates() {
// Note that we can't delete the current block itself, as we may need to return to it later in case a
// reorganization to a better block fails.
std::set<CBlockIndex*, CBlockIndexWorkComparator>::iterator it = setBlockIndexCandidates.begin();
- while (it != setBlockIndexCandidates.end() && setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) {
+ while (it != setBlockIndexCandidates.end() && setBlockIndexCandidates.value_comp()(*it, m_chain.Tip())) {
setBlockIndexCandidates.erase(it++);
}
// Either the current tip or a successor of it we're working towards is left in setBlockIndexCandidates.
@@ -2548,13 +2402,13 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
{
AssertLockHeld(cs_main);
- const CBlockIndex *pindexOldTip = chainActive.Tip();
- const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork);
+ const CBlockIndex *pindexOldTip = m_chain.Tip();
+ const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork);
// Disconnect active blocks which are no longer in the best chain.
bool fBlocksDisconnected = false;
DisconnectedBlockTransactions disconnectpool;
- while (chainActive.Tip() && chainActive.Tip() != pindexFork) {
+ while (m_chain.Tip() && m_chain.Tip() != pindexFork) {
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
// This is likely a fatal error, but keep the mempool consistent,
// just in case. Only remove from the mempool in this case.
@@ -2602,7 +2456,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
}
} else {
PruneBlockIndexCandidates();
- if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) {
+ if (!pindexOldTip || m_chain.Tip()->nChainWork > pindexOldTip->nChainWork) {
// We're in a better position than we were. Return temporarily to release the lock.
fContinue = false;
break;
@@ -2638,7 +2492,7 @@ static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
if (pindexHeader != pindexHeaderOld) {
fNotify = true;
- fInitialBlockDownload = IsInitialBlockDownload();
+ fInitialBlockDownload = ::ChainstateActive().IsInitialBlockDownload();
pindexHeaderOld = pindexHeader;
}
}
@@ -2648,7 +2502,7 @@ static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
}
}
-static void LimitValidationInterfaceQueue() {
+static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
AssertLockNotHeld(cs_main);
if (GetMainSignals().CallbacksPending() > 10) {
@@ -2694,7 +2548,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
{
LOCK(cs_main);
- CBlockIndex* starting_tip = chainActive.Tip();
+ CBlockIndex* starting_tip = m_chain.Tip();
bool blocks_connected = false;
do {
// We absolutely may not unlock cs_main until we've made forward progress
@@ -2706,7 +2560,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
}
// Whether we have anything to do at all.
- if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip()) {
+ if (pindexMostWork == nullptr || pindexMostWork == m_chain.Tip()) {
break;
}
@@ -2720,16 +2574,16 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
// Wipe cache, we may need another branch now.
pindexMostWork = nullptr;
}
- pindexNewTip = chainActive.Tip();
+ pindexNewTip = m_chain.Tip();
for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
assert(trace.pblock && trace.pindex);
GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
}
- } while (!chainActive.Tip() || (starting_tip && CBlockIndexWorkComparator()(chainActive.Tip(), starting_tip)));
+ } while (!m_chain.Tip() || (starting_tip && CBlockIndexWorkComparator()(m_chain.Tip(), starting_tip)));
if (!blocks_connected) return true;
- const CBlockIndex* pindexFork = chainActive.FindFork(starting_tip);
+ const CBlockIndex* pindexFork = m_chain.FindFork(starting_tip);
bool fInitialDownload = IsInitialBlockDownload();
// Notify external listeners about the new tip.
@@ -2764,22 +2618,22 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
}
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
- return g_chainstate.ActivateBestChain(state, chainparams, std::move(pblock));
+ return ::ChainstateActive().ActivateBestChain(state, chainparams, std::move(pblock));
}
bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex)
{
{
LOCK(cs_main);
- if (pindex->nChainWork < chainActive.Tip()->nChainWork) {
+ if (pindex->nChainWork < m_chain.Tip()->nChainWork) {
// Nothing to do, this block is not at the tip.
return true;
}
- if (chainActive.Tip()->nChainWork > nLastPreciousChainwork) {
+ if (m_chain.Tip()->nChainWork > nLastPreciousChainwork) {
// The chain has been extended since the last call, reset the counter.
nBlockReverseSequenceId = -1;
}
- nLastPreciousChainwork = chainActive.Tip()->nChainWork;
+ nLastPreciousChainwork = m_chain.Tip()->nChainWork;
setBlockIndexCandidates.erase(pindex);
pindex->nSequenceId = nBlockReverseSequenceId;
if (nBlockReverseSequenceId > std::numeric_limits<int32_t>::min()) {
@@ -2796,7 +2650,7 @@ bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& par
return ActivateBestChain(state, params, std::shared_ptr<const CBlock>());
}
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) {
- return g_chainstate.PreciousBlock(state, params, pindex);
+ return ::ChainstateActive().PreciousBlock(state, params, pindex);
}
bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
@@ -2813,11 +2667,11 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
LimitValidationInterfaceQueue();
LOCK(cs_main);
- if (!chainActive.Contains(pindex)) break;
+ if (!m_chain.Contains(pindex)) break;
pindex_was_in_chain = true;
- CBlockIndex *invalid_walk_tip = chainActive.Tip();
+ CBlockIndex *invalid_walk_tip = m_chain.Tip();
- // ActivateBestChain considers blocks already in chainActive
+ // ActivateBestChain considers blocks already in m_chain
// unconditionally valid already, so force disconnect away from it.
DisconnectedBlockTransactions disconnectpool;
bool ret = DisconnectTip(state, chainparams, &disconnectpool);
@@ -2828,7 +2682,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
// keeping the mempool up to date is probably futile anyway).
UpdateMempoolForReorg(disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
if (!ret) return false;
- assert(invalid_walk_tip->pprev == chainActive.Tip());
+ assert(invalid_walk_tip->pprev == m_chain.Tip());
// We immediately mark the disconnected blocks as invalid.
// This prevents a case where pruned nodes may fail to invalidateblock
@@ -2853,7 +2707,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
{
LOCK(cs_main);
- if (chainActive.Contains(to_mark_failed)) {
+ if (m_chain.Contains(to_mark_failed)) {
// If the to-be-marked invalid block is in the active chain, something is interfering and we can't proceed.
return false;
}
@@ -2868,7 +2722,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
// add it again.
BlockMap::iterator it = mapBlockIndex.begin();
while (it != mapBlockIndex.end()) {
- if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, m_chain.Tip())) {
setBlockIndexCandidates.insert(it->second);
}
it++;
@@ -2885,7 +2739,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
}
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) {
- return g_chainstate.InvalidateBlock(state, chainparams, pindex);
+ return ::ChainstateActive().InvalidateBlock(state, chainparams, pindex);
}
void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
@@ -2899,7 +2753,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
it->second->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(it->second);
- if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), it->second)) {
setBlockIndexCandidates.insert(it->second);
}
if (it->second == pindexBestInvalid) {
@@ -2923,7 +2777,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
}
void ResetBlockFailureFlags(CBlockIndex *pindex) {
- return g_chainstate.ResetBlockFailureFlags(pindex);
+ return ::ChainstateActive().ResetBlockFailureFlags(pindex);
}
CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block)
@@ -2991,7 +2845,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
LOCK(cs_nBlockSequenceId);
pindex->nSequenceId = nBlockSequenceId++;
}
- if (chainActive.Tip() == nullptr || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) {
+ if (m_chain.Tip() == nullptr || !setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) {
setBlockIndexCandidates.insert(pindex);
}
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex);
@@ -3047,7 +2901,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 low!", _("Error: Disk space is low!"));
+ return AbortNode("Disk space is too low!", _("Error: Disk space is too low!"), CClientUIInterface::MSG_NOPREFIX);
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
@@ -3071,7 +2925,7 @@ static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &pos, un
bool out_of_space;
size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space);
if (out_of_space) {
- return AbortNode(state, "Disk space is low!", _("Error: Disk space is low!"));
+ return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!"), CClientUIInterface::MSG_NOPREFIX);
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
@@ -3221,7 +3075,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
}
//! Returns last CBlockIndex* that is a checkpoint
-static CBlockIndex* GetLastCheckpoint(const CCheckpointData& data)
+static CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
const MapCheckpoints& checkpoints = data.mapCheckpoints;
@@ -3245,7 +3099,7 @@ static CBlockIndex* GetLastCheckpoint(const CCheckpointData& data)
* in ConnectBlock().
* Note that -reindex-chainstate skips the validation that happens here!
*/
-static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime)
+static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
assert(pindexPrev != nullptr);
const int nHeight = pindexPrev->nHeight + 1;
@@ -3461,7 +3315,7 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
LOCK(cs_main);
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
- if (!g_chainstate.AcceptBlockHeader(header, state, chainparams, &pindex)) {
+ if (!::ChainstateActive().AcceptBlockHeader(header, state, chainparams, &pindex)) {
if (first_invalid) *first_invalid = header;
return false;
}
@@ -3511,13 +3365,13 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
// process an unrequested block if it's new and has enough work to
// advance our tip, and isn't too many blocks ahead.
bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA;
- bool fHasMoreOrSameWork = (chainActive.Tip() ? pindex->nChainWork >= chainActive.Tip()->nChainWork : true);
+ bool fHasMoreOrSameWork = (m_chain.Tip() ? pindex->nChainWork >= m_chain.Tip()->nChainWork : true);
// Blocks that are too out-of-order needlessly limit the effectiveness of
// pruning, because pruning will not delete block files that contain any
// blocks which are too close in height to the tip. Apply this test
// regardless of whether pruning is enabled; it should generally be safe to
// not process unrequested blocks.
- bool fTooFarAhead = (pindex->nHeight > int(chainActive.Height() + MIN_BLOCKS_TO_KEEP));
+ bool fTooFarAhead = (pindex->nHeight > int(m_chain.Height() + MIN_BLOCKS_TO_KEEP));
// TODO: Decouple this function from the block download logic by removing fRequested
// This requires some new chain data structure to efficiently look up if a
@@ -3551,7 +3405,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
// Header is valid/has work, merkle tree and segwit merkle tree are good...RELAY NOW
// (but if it does not build on our best tip, let the SendMessages loop relay it)
- if (!IsInitialBlockDownload() && chainActive.Tip() == pindex->pprev)
+ if (!IsInitialBlockDownload() && m_chain.Tip() == pindex->pprev)
GetMainSignals().NewPoWValidBlock(pindex, pblock);
// Write block to history file
@@ -3592,7 +3446,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
if (ret) {
// Store to disk
- ret = g_chainstate.AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
+ ret = ::ChainstateActive().AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
}
if (!ret) {
GetMainSignals().BlockChecked(*pblock, state);
@@ -3603,7 +3457,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
NotifyHeaderTip();
CValidationState state; // Only used to report errors, not invalidity - ignore it
- if (!g_chainstate.ActivateBestChain(state, chainparams, pblock))
+ if (!::ChainstateActive().ActivateBestChain(state, chainparams, pblock))
return error("%s: ActivateBestChain failed (%s)", __func__, FormatStateMessage(state));
return true;
@@ -3612,7 +3466,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot)
{
AssertLockHeld(cs_main);
- assert(pindexPrev && pindexPrev == chainActive.Tip());
+ assert(pindexPrev && pindexPrev == ::ChainActive().Tip());
CCoinsViewCache viewNew(pcoinsTip.get());
uint256 block_hash(block.GetHash());
CBlockIndex indexDummy(block);
@@ -3627,7 +3481,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev))
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state));
- if (!g_chainstate.ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
+ if (!::ChainstateActive().ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
return false;
assert(state.IsValid());
@@ -3701,11 +3555,11 @@ static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPr
assert(fPruneMode && nManualPruneHeight > 0);
LOCK2(cs_main, cs_LastBlockFile);
- if (chainActive.Tip() == nullptr)
+ if (::ChainActive().Tip() == nullptr)
return;
// last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip)
- unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP);
+ unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, ::ChainActive().Tip()->nHeight - MIN_BLOCKS_TO_KEEP);
int count=0;
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune)
@@ -3722,7 +3576,8 @@ void PruneBlockFilesManual(int nManualPruneHeight)
{
CValidationState state;
const CChainParams& chainparams = Params();
- if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
+ if (!::ChainstateActive().FlushStateToDisk(
+ chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
}
}
@@ -3745,14 +3600,14 @@ void PruneBlockFilesManual(int nManualPruneHeight)
static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
{
LOCK2(cs_main, cs_LastBlockFile);
- if (chainActive.Tip() == nullptr || nPruneTarget == 0) {
+ if (::ChainActive().Tip() == nullptr || nPruneTarget == 0) {
return;
}
- if ((uint64_t)chainActive.Tip()->nHeight <= nPruneAfterHeight) {
+ if ((uint64_t)::ChainActive().Tip()->nHeight <= nPruneAfterHeight) {
return;
}
- unsigned int nLastBlockWeCanPrune = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP;
+ unsigned int nLastBlockWeCanPrune = ::ChainActive().Tip()->nHeight - MIN_BLOCKS_TO_KEEP;
uint64_t nCurrentUsage = CalculateCurrentUsage();
// We don't check to prune until after we've allocated new space for files
// So we should leave a buffer under our target to account for another allocation
@@ -3766,7 +3621,7 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
// To avoid excessive prune events negating the benefit of high dbcache
// values, we should not prune too rapidly.
// So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
- if (IsInitialBlockDownload()) {
+ if (::ChainstateActive().IsInitialBlockDownload()) {
// Since this is only relevant during IBD, we use a fixed 10%
nBuffer += nPruneTarget / 10;
}
@@ -3858,6 +3713,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
sort(vSortedByHeight.begin(), vSortedByHeight.end());
for (const std::pair<int, CBlockIndex*>& item : vSortedByHeight)
{
+ if (ShutdownRequested()) return false;
CBlockIndex* pindex = item.second;
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
@@ -3894,7 +3750,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- if (!g_chainstate.LoadBlockIndex(chainparams.GetConsensus(), *pblocktree))
+ if (!::ChainstateActive().LoadBlockIndex(chainparams.GetConsensus(), *pblocktree))
return false;
// Load block file info
@@ -3948,33 +3804,23 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
bool LoadChainTip(const CChainParams& chainparams)
{
AssertLockHeld(cs_main);
+ assert(!pcoinsTip->GetBestBlock().IsNull()); // Never called when the coins view is empty
- if (chainActive.Tip() && chainActive.Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) return true;
-
- if (pcoinsTip->GetBestBlock().IsNull() && mapBlockIndex.size() == 1) {
- // In case we just added the genesis block, connect it now, so
- // that we always have a chainActive.Tip() when we return.
- LogPrintf("%s: Connecting genesis block...\n", __func__);
- CValidationState state;
- if (!ActivateBestChain(state, chainparams)) {
- LogPrintf("%s: failed to activate chain (%s)\n", __func__, FormatStateMessage(state));
- return false;
- }
- }
+ if (::ChainActive().Tip() && ::ChainActive().Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) return true;
// Load pointer to end of best chain
CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock());
if (!pindex) {
return false;
}
- chainActive.SetTip(pindex);
+ ::ChainActive().SetTip(pindex);
- g_chainstate.PruneBlockIndexCandidates();
+ ::ChainstateActive().PruneBlockIndexCandidates();
LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n",
- chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(),
- FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()),
- GuessVerificationProgress(chainparams.TxData(), chainActive.Tip()));
+ ::ChainActive().Tip()->GetBlockHash().ToString(), ::ChainActive().Height(),
+ FormatISO8601DateTime(::ChainActive().Tip()->GetBlockTime()),
+ GuessVerificationProgress(chainparams.TxData(), ::ChainActive().Tip()));
return true;
}
@@ -3991,12 +3837,12 @@ CVerifyDB::~CVerifyDB()
bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
{
LOCK(cs_main);
- if (chainActive.Tip() == nullptr || chainActive.Tip()->pprev == nullptr)
+ if (::ChainActive().Tip() == nullptr || ::ChainActive().Tip()->pprev == nullptr)
return true;
// Verify blocks in the best chain
- if (nCheckDepth <= 0 || nCheckDepth > chainActive.Height())
- nCheckDepth = chainActive.Height();
+ if (nCheckDepth <= 0 || nCheckDepth > ::ChainActive().Height())
+ nCheckDepth = ::ChainActive().Height();
nCheckLevel = std::max(0, std::min(4, nCheckLevel));
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
CCoinsViewCache coins(coinsview);
@@ -4006,16 +3852,16 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
CValidationState state;
int reportDone = 0;
LogPrintf("[0%%]..."); /* Continued */
- for (pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
+ for (pindex = ::ChainActive().Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
boost::this_thread::interruption_point();
- const int percentageDone = std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
+ const int percentageDone = std::max(1, std::min(99, (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
if (reportDone < percentageDone/10) {
// report every 10% step
LogPrintf("[%d%%]...", percentageDone); /* Continued */
reportDone = percentageDone/10;
}
uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false);
- if (pindex->nHeight <= chainActive.Height()-nCheckDepth)
+ if (pindex->nHeight <= ::ChainActive().Height()-nCheckDepth)
break;
if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
// If pruning, only go back as far as we have data.
@@ -4042,7 +3888,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
- DisconnectResult res = g_chainstate.DisconnectBlock(block, pindex, coins);
+ DisconnectResult res = ::ChainstateActive().DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
@@ -4057,27 +3903,27 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
return true;
}
if (pindexFailure)
- return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
+ return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", ::ChainActive().Height() - pindexFailure->nHeight + 1, nGoodTransactions);
// store block count as we move pindex at check level >= 4
- int block_count = chainActive.Height() - pindex->nHeight;
+ int block_count = ::ChainActive().Height() - pindex->nHeight;
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
- while (pindex != chainActive.Tip()) {
+ while (pindex != ::ChainActive().Tip()) {
boost::this_thread::interruption_point();
- const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
+ const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
if (reportDone < percentageDone/10) {
// report every 10% step
LogPrintf("[%d%%]...", percentageDone); /* Continued */
reportDone = percentageDone/10;
}
uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false);
- pindex = chainActive.Next(pindex);
+ pindex = ::ChainActive().Next(pindex);
CBlock block;
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
- if (!g_chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
+ if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, chainparams))
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state));
}
}
@@ -4176,14 +4022,14 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
}
bool ReplayBlocks(const CChainParams& params, CCoinsView* view) {
- return g_chainstate.ReplayBlocks(params, view);
+ return ::ChainstateActive().ReplayBlocks(params, view);
}
//! Helper for CChainState::RewindBlockIndex
void CChainState::EraseBlockData(CBlockIndex* index)
{
AssertLockHeld(cs_main);
- assert(!chainActive.Contains(index)); // Make sure this block isn't active
+ assert(!m_chain.Contains(index)); // Make sure this block isn't active
// Reduce validity
index->nStatus = std::min<unsigned int>(index->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (index->nStatus & ~BLOCK_VALID_MASK);
@@ -4217,7 +4063,7 @@ void CChainState::EraseBlockData(CBlockIndex* index)
bool CChainState::RewindBlockIndex(const CChainParams& params)
{
- // Note that during -reindex-chainstate we are called with an empty chainActive!
+ // Note that during -reindex-chainstate we are called with an empty m_chain!
// First erase all post-segwit blocks without witness not in the main chain,
// as this can we done without costly DisconnectTip calls. Active
@@ -4225,7 +4071,7 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
{
LOCK(cs_main);
for (const auto& entry : mapBlockIndex) {
- if (IsWitnessEnabled(entry.second->pprev, params.GetConsensus()) && !(entry.second->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(entry.second)) {
+ if (IsWitnessEnabled(entry.second->pprev, params.GetConsensus()) && !(entry.second->nStatus & BLOCK_OPT_WITNESS) && !m_chain.Contains(entry.second)) {
EraseBlockData(entry.second);
}
}
@@ -4236,17 +4082,17 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
int nHeight = 1;
{
LOCK(cs_main);
- while (nHeight <= chainActive.Height()) {
+ while (nHeight <= m_chain.Height()) {
// Although SCRIPT_VERIFY_WITNESS is now generally enforced on all
// blocks in ConnectBlock, we don't need to go back and
// re-download/re-verify blocks from before segwit actually activated.
- if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
+ if (IsWitnessEnabled(m_chain[nHeight - 1], params.GetConsensus()) && !(m_chain[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
break;
}
nHeight++;
}
- tip = chainActive.Tip();
+ tip = m_chain.Tip();
}
// nHeight is now the height of the first insufficiently-validated block, or tipheight + 1
@@ -4256,7 +4102,7 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
{
LOCK(cs_main);
// Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active)
- assert(tip == chainActive.Tip());
+ assert(tip == m_chain.Tip());
if (tip == nullptr || tip->nHeight < nHeight) break;
if (fPruneMode && !(tip->nStatus & BLOCK_HAVE_DATA)) {
// If pruning, don't try rewinding past the HAVE_DATA point;
@@ -4276,9 +4122,9 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
// We do this after actual disconnecting, otherwise we'll end up writing the lack of data
// to disk before writing the chainstate, resulting in a failure to continue if interrupted.
// Note: If we encounter an insufficiently validated block that
- // is on chainActive, it must be because we are a pruning node, and
+ // is on m_chain, it must be because we are a pruning node, and
// this block or some successor doesn't HAVE_DATA, so we were unable to
- // rewind all the way. Blocks remaining on chainActive at this point
+ // rewind all the way. Blocks remaining on m_chain at this point
// must not have their validity reduced.
EraseBlockData(tip);
@@ -4296,9 +4142,9 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
{
LOCK(cs_main);
- if (chainActive.Tip() != nullptr) {
+ if (m_chain.Tip() != nullptr) {
// We can't prune block index candidates based on our tip if we have
- // no tip due to chainActive being empty!
+ // no tip due to m_chain being empty!
PruneBlockIndexCandidates();
CheckBlockIndex(params.GetConsensus());
@@ -4309,16 +4155,17 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
}
bool RewindBlockIndex(const CChainParams& params) {
- if (!g_chainstate.RewindBlockIndex(params)) {
+ if (!::ChainstateActive().RewindBlockIndex(params)) {
return false;
}
- if (chainActive.Tip() != nullptr) {
- // FlushStateToDisk can possibly read chainActive. Be conservative
+ LOCK(cs_main);
+ if (::ChainActive().Tip() != nullptr) {
+ // FlushStateToDisk can possibly read ::ChainActive(). Be conservative
// and skip it here, we're about to -reindex-chainstate anyway, so
// it'll get called a bunch real soon.
CValidationState state;
- if (!FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) {
+ if (!::ChainstateActive().FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) {
LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state));
return false;
}
@@ -4339,7 +4186,7 @@ void CChainState::UnloadBlockIndex() {
void UnloadBlockIndex()
{
LOCK(cs_main);
- chainActive.SetTip(nullptr);
+ ::ChainActive().SetTip(nullptr);
pindexBestInvalid = nullptr;
pindexBestHeader = nullptr;
mempool.clear();
@@ -4359,7 +4206,7 @@ void UnloadBlockIndex()
mapBlockIndex.clear();
fHavePruned = false;
- g_chainstate.UnloadBlockIndex();
+ ::ChainstateActive().UnloadBlockIndex();
}
bool LoadBlockIndex(const CChainParams& chainparams)
@@ -4389,7 +4236,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
LOCK(cs_main);
// Check whether we're already initialized by checking for genesis in
- // mapBlockIndex. Note that we can't use chainActive here, since it is
+ // mapBlockIndex. Note that we can't use m_chain here, since it is
// set based on the coins db, not the block index db, which is the only
// thing loaded at this point.
if (mapBlockIndex.count(chainparams.GenesisBlock().GetHash()))
@@ -4411,7 +4258,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
bool LoadGenesisBlock(const CChainParams& chainparams)
{
- return g_chainstate.LoadGenesisBlock(chainparams);
+ return ::ChainstateActive().LoadGenesisBlock(chainparams);
}
bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos *dbp)
@@ -4476,7 +4323,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
CBlockIndex* pindex = LookupBlockIndex(hash);
if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) {
CValidationState state;
- if (g_chainstate.AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) {
+ if (::ChainstateActive().AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) {
nLoaded++;
}
if (state.IsError()) {
@@ -4513,7 +4360,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
head.ToString());
LOCK(cs_main);
CValidationState dummy;
- if (g_chainstate.AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
+ if (::ChainstateActive().AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
{
nLoaded++;
queue.push_back(pblockrecursive->GetHash());
@@ -4546,8 +4393,8 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
// During a reindex, we read the genesis block and call CheckBlockIndex before ActivateBestChain,
// so we have the genesis block in mapBlockIndex but no active chain. (A few of the tests when
- // iterating the block tree require that chainActive has been initialized.)
- if (chainActive.Height() < 0) {
+ // iterating the block tree require that m_chain has been initialized.)
+ if (m_chain.Height() < 0) {
assert(mapBlockIndex.size() <= 1);
return;
}
@@ -4591,7 +4438,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
if (pindex->pprev == nullptr) {
// Genesis block checks.
assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match.
- assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block.
+ assert(pindex == m_chain.Genesis()); // The current active chain's genesis block must be this block.
}
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
@@ -4620,13 +4467,13 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
// Checks for not-invalid blocks.
assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents.
}
- if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstNeverProcessed == nullptr) {
+ if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && pindexFirstNeverProcessed == nullptr) {
if (pindexFirstInvalid == nullptr) {
// If this block sorts at least as good as the current tip and
// is valid and we have all data for its parents, it must be in
- // setBlockIndexCandidates. chainActive.Tip() must also be there
+ // setBlockIndexCandidates. m_chain.Tip() must also be there
// even if some data has been pruned.
- if (pindexFirstMissing == nullptr || pindex == chainActive.Tip()) {
+ if (pindexFirstMissing == nullptr || pindex == m_chain.Tip()) {
assert(setBlockIndexCandidates.count(pindex));
}
// If some parent is missing, then it could be that this block was in
@@ -4660,11 +4507,11 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
// - it has a descendant that at some point had more work than the
// tip, and
// - we tried switching to that descendant but were missing
- // data for some intermediate block between chainActive and the
+ // data for some intermediate block between m_chain and the
// tip.
- // So if this block is itself better than chainActive.Tip() and it wasn't in
+ // So if this block is itself better than m_chain.Tip() and it wasn't in
// setBlockIndexCandidates, then it must be in mapBlocksUnlinked.
- if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && setBlockIndexCandidates.count(pindex) == 0) {
+ if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && setBlockIndexCandidates.count(pindex) == 0) {
if (pindexFirstInvalid == nullptr) {
assert(foundInUnlinked);
}
@@ -4735,19 +4582,19 @@ CBlockFileInfo* GetBlockFileInfo(size_t n)
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
LOCK(cs_main);
- return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache);
+ return VersionBitsState(::ChainActive().Tip(), params, pos, versionbitscache);
}
BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
LOCK(cs_main);
- return VersionBitsStatistics(chainActive.Tip(), params, pos);
+ return VersionBitsStatistics(::ChainActive().Tip(), params, pos);
}
int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
LOCK(cs_main);
- return VersionBitsStateSinceHeight(chainActive.Tip(), params, pos, versionbitscache);
+ return VersionBitsStateSinceHeight(::ChainActive().Tip(), params, pos, versionbitscache);
}
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
@@ -4912,4 +4759,5 @@ public:
delete (*it1).second;
mapBlockIndex.clear();
}
-} instance_of_cmaincleanup;
+};
+static CMainCleanup instance_of_cmaincleanup;
diff --git a/src/validation.h b/src/validation.h
index 7ab6adaf33..638229952d 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -31,6 +31,7 @@
#include <utility>
#include <vector>
+class CChainState;
class CBlockIndex;
class CBlockTreeDB;
class CBlockUndo;
@@ -44,6 +45,7 @@ class CTxMemPool;
class CValidationState;
struct ChainTxData;
+struct DisconnectedBlockTransactions;
struct PrecomputedTransactionData;
struct LockPoints;
@@ -114,8 +116,6 @@ static const char* const DEFAULT_BLOCKFILTERINDEX = "0";
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
/** Default for -persistmempool */
static const bool DEFAULT_PERSIST_MEMPOOL = true;
-/** Default for -mempoolreplacement */
-static const bool DEFAULT_ENABLE_REPLACEMENT = true;
/** Default for using fee filter */
static const bool DEFAULT_FEEFILTER = true;
@@ -158,7 +158,6 @@ extern size_t nCoinCacheUsage;
extern CFeeRate minRelayTxFee;
/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */
extern int64_t nMaxTipAge;
-extern bool fEnableReplacement;
/** Block hash whose ancestors we will assume to have valid scripts without checking them. */
extern uint256 hashAssumeValid;
@@ -176,7 +175,7 @@ extern bool fHavePruned;
extern bool fPruneMode;
/** Number of MiB of block files that we're trying to stay below. */
extern uint64_t nPruneTarget;
-/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */
+/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of ::ChainActive().Tip() will not be pruned. */
static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
/** Minimum blocks required to signal NODE_NETWORK_LIMITED */
static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288;
@@ -247,8 +246,6 @@ bool LoadChainTip(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_m
void UnloadBlockIndex();
/** Run an instance of the script checking thread */
void ThreadScriptCheck(int worker_num);
-/** Check whether we are doing an initial block download (synchronizing from disk or network) */
-bool IsInitialBlockDownload();
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256& hash, CTransactionRef& tx, const Consensus::Params& params, uint256& hashBlock, const CBlockIndex* const blockIndex = nullptr);
/**
@@ -276,10 +273,6 @@ void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
*/
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
-/** Flush all state, indexes and buffers to disk. */
-void FlushStateToDisk();
-/** Prune block files and flush state to disk. */
-void PruneAndFlush();
/** Prune block files up to a given height */
void PruneBlockFilesManual(int nManualPruneHeight);
@@ -393,7 +386,7 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa
bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);
/** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */
-bool RewindBlockIndex(const CChainParams& params);
+bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main);
/** Update uncommitted block structures (currently: only the witness reserved value). This is safe for submitted blocks. */
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams);
@@ -412,7 +405,7 @@ public:
/** Replay blocks that aren't fully applied to the database. */
bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
-inline CBlockIndex* LookupBlockIndex(const uint256& hash)
+inline CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
BlockMap::const_iterator it = mapBlockIndex.find(hash);
@@ -422,6 +415,186 @@ inline CBlockIndex* LookupBlockIndex(const uint256& hash)
/** Find the last common block between the parameter chain and a locator. */
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+enum DisconnectResult
+{
+ DISCONNECT_OK, // All good.
+ DISCONNECT_UNCLEAN, // Rolled back, but UTXO set was inconsistent with block.
+ DISCONNECT_FAILED // Something else went wrong.
+};
+
+class ConnectTrace;
+
+/** @see CChainState::FlushStateToDisk */
+enum class FlushStateMode {
+ NONE,
+ IF_NEEDED,
+ PERIODIC,
+ ALWAYS
+};
+
+struct CBlockIndexWorkComparator
+{
+ bool operator()(const CBlockIndex *pa, const CBlockIndex *pb) const;
+};
+
+/**
+ * CChainState stores and provides an API to update our local knowledge of the
+ * current best chain and header tree.
+ *
+ * It generally provides access to the current block tree, as well as functions
+ * to provide new data, which it will appropriately validate and incorporate in
+ * its state as necessary.
+ *
+ * Eventually, the API here is targeted at being exposed externally as a
+ * consumable libconsensus library, so any functions added must only call
+ * other class member functions, pure functions in other parts of the consensus
+ * library, callbacks via the validation interface, or read/write-to-disk
+ * functions (eventually this will also be via callbacks).
+ */
+class CChainState {
+private:
+ /**
+ * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
+ * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
+ * missing the data for the block.
+ */
+ std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
+
+ /**
+ * Every received block is assigned a unique and increasing identifier, so we
+ * know which one to give priority in case of a fork.
+ */
+ CCriticalSection cs_nBlockSequenceId;
+ /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */
+ int32_t nBlockSequenceId = 1;
+ /** Decreasing counter (used by subsequent preciousblock calls). */
+ int32_t nBlockReverseSequenceId = -1;
+ /** chainwork for the last block that preciousblock has been applied to. */
+ arith_uint256 nLastPreciousChainwork = 0;
+
+ /** In order to efficiently track invalidity of headers, we keep the set of
+ * blocks which we tried to connect and found to be invalid here (ie which
+ * were set to BLOCK_FAILED_VALID since the last restart). We can then
+ * walk this set and check if a new header is a descendant of something in
+ * this set, preventing us from having to walk mapBlockIndex when we try
+ * to connect a bad block and fail.
+ *
+ * While this is more complicated than marking everything which descends
+ * from an invalid block as invalid at the time we discover it to be
+ * invalid, doing so would require walking all of mapBlockIndex to find all
+ * descendants. Since this case should be very rare, keeping track of all
+ * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
+ * well.
+ *
+ * Because we already walk mapBlockIndex in height-order at startup, we go
+ * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
+ * instead of putting things in this set.
+ */
+ std::set<CBlockIndex*> m_failed_blocks;
+
+ /**
+ * the ChainState CriticalSection
+ * A lock that must be held when modifying this ChainState - held in ActivateBestChain()
+ */
+ CCriticalSection m_cs_chainstate;
+
+ /**
+ * Whether this chainstate is undergoing initial block download.
+ *
+ * Mutable because we need to be able to mark IsInitialBlockDownload()
+ * const, which latches this for caching purposes.
+ */
+ mutable std::atomic<bool> m_cached_finished_ibd{false};
+
+public:
+ //! The current chain of blockheaders we consult and build on.
+ //! @see CChain, CBlockIndex.
+ CChain m_chain;
+ BlockMap mapBlockIndex GUARDED_BY(cs_main);
+ std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
+ CBlockIndex *pindexBestInvalid = nullptr;
+
+ bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ /**
+ * Update the on-disk chain state.
+ * The caches and indexes are flushed depending on the mode we're called with
+ * if they're too large, if it's been a while since the last write,
+ * or always and in all cases if we're in prune mode and are deleting files.
+ *
+ * If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything
+ * besides checking if we need to prune.
+ */
+ bool FlushStateToDisk(
+ const CChainParams& chainparams,
+ CValidationState &state,
+ FlushStateMode mode,
+ int nManualPruneHeight = 0);
+
+ //! Unconditionally flush all changes to disk.
+ void ForceFlushStateToDisk();
+
+ //! Prune blockfiles from the disk if necessary and then flush chainstate changes
+ //! if we pruned.
+ void PruneAndFlush();
+
+ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main);
+
+ /**
+ * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
+ * that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
+ */
+ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ // Block (dis)connection on a given view:
+ DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view);
+ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
+ CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ // Block disconnection on our pcoinsTip:
+ bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ // Manual block validity manipulation:
+ bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
+ bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main);
+ bool LoadGenesisBlock(const CChainParams& chainparams);
+
+ void PruneBlockIndexCandidates();
+
+ void UnloadBlockIndex();
+
+ /** Check whether we are doing an initial block download (synchronizing from disk or network) */
+ bool IsInitialBlockDownload() const;
+
+private:
+ bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /** Create a new block index entry for a given block hash */
+ CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /**
+ * Make various assertions about the state of the block index.
+ *
+ * By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex.
+ */
+ void CheckBlockIndex(const Consensus::Params& consensusParams);
+
+ void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ //! Mark a block as not having block data
+ void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+};
+
/** Mark a block as precious and reorganize.
*
* May not be called in a
@@ -430,13 +603,16 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main);
/** Mark a block as invalid. */
-bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex);
+bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
/** Remove invalidity status from a block and its descendants. */
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** The currently-connected chain of blocks (protected by cs_main). */
-extern CChain& chainActive;
+/** @returns the most-work valid chainstate. */
+CChainState& ChainstateActive();
+
+/** @returns the most-work chain. */
+CChain& ChainActive();
/** Global variable that points to the coins database (protected by cs_main) */
extern std::unique_ptr<CCoinsViewDB> pcoinsdbview;
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 5d0ee1d1fc..59a620ab95 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -8,8 +8,6 @@
#include <primitives/block.h>
#include <scheduler.h>
#include <txmempool.h>
-#include <util/system.h>
-#include <validation.h>
#include <list>
#include <atomic>
diff --git a/src/validationinterface.h b/src/validationinterface.h
index ea1b2e7e76..3ce617b827 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -16,7 +16,6 @@ extern CCriticalSection cs_main;
class CBlock;
class CBlockIndex;
struct CBlockLocator;
-class CBlockIndex;
class CConnman;
class CValidationInterface;
class CValidationState;
diff --git a/src/wallet/coincontrol.cpp b/src/wallet/coincontrol.cpp
index 87d2c4f06e..60bce66839 100644
--- a/src/wallet/coincontrol.cpp
+++ b/src/wallet/coincontrol.cpp
@@ -13,6 +13,7 @@ void CCoinControl::SetNull()
fAllowOtherInputs = false;
fAllowWatchOnly = false;
m_avoid_partial_spends = gArgs.GetBoolArg("-avoidpartialspends", DEFAULT_AVOIDPARTIALSPENDS);
+ m_avoid_address_reuse = false;
setSelected.clear();
m_feerate.reset();
fOverrideFeeRate = false;
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 9257b272bc..249c402e4d 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -34,6 +34,8 @@ public:
boost::optional<bool> m_signal_bip125_rbf;
//! Avoid partial use of funds sent to a given address
bool m_avoid_partial_spends;
+ //! Forbids inclusion of dirty (previously used) addresses
+ bool m_avoid_address_reuse;
//! Fee estimation mode to control arguments to estimateSmartFee
FeeEstimateMode m_fee_mode;
//! Minimum chain depth value for coin availability
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index a255177e36..dd56ea10ab 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 6a326bfd97..b5f90deabd 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -1,15 +1,11 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <wallet/db.h>
-#include <addrman.h>
-#include <hash.h>
-#include <protocol.h>
#include <util/strencodings.h>
-#include <wallet/walletutil.h>
#include <stdint.h>
@@ -407,13 +403,6 @@ bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, std::string& er
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(nullptr, nullptr, nullptr));
LogPrintf("Using wallet %s\n", file_path.string());
- // Wallet file must be a plain filename without a directory
- if (walletFile != fs::basename(walletFile) + fs::extension(walletFile))
- {
- errorStr = strprintf(_("Wallet %s resides outside wallet directory %s"), walletFile, walletDir.string());
- return false;
- }
-
if (!env->Open(true /* retry */)) {
errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
return false;
@@ -614,7 +603,9 @@ void BerkeleyBatch::Flush()
if (fReadOnly)
nMinutes = 1;
- env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
+ if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault
+ env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
+ }
}
void BerkeleyDatabase::IncrementUpdateCounter()
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 762fb83a2f..b3856fbaf9 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 78db4df5e5..46cf6b7616 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -10,14 +10,10 @@
#include <wallet/wallet.h>
#include <policy/fees.h>
#include <policy/policy.h>
-#include <policy/rbf.h>
-#include <validation.h> //for mempool access
-#include <txmempool.h>
#include <util/moneystr.h>
#include <util/rbf.h>
#include <util/system.h>
#include <util/validation.h>
-#include <net.h>
//! Check whether transaction has descendant in wallet or mempool, or has been
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index f9cbfc5f68..0c4e1cb7dd 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp
index d9ae18ed60..ad69e84358 100644
--- a/src/wallet/fees.cpp
+++ b/src/wallet/fees.cpp
@@ -1,13 +1,11 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <wallet/fees.h>
-#include <policy/policy.h>
#include <util/system.h>
-#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/wallet.h>
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 47ef01bfd1..0265433863 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -1,20 +1,15 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <chainparams.h>
#include <init.h>
#include <interfaces/chain.h>
#include <net.h>
-#include <scheduler.h>
#include <outputtype.h>
-#include <util/error.h>
#include <util/system.h>
#include <util/moneystr.h>
-#include <validation.h>
#include <walletinitinterface.h>
-#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
@@ -39,7 +34,7 @@ const WalletInitInterface& g_wallet_init_interface = WalletInit();
void WalletInit::AddWalletOptions() const
{
gArgs.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), false, OptionsCategory::WALLET);
- gArgs.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)", DEFAULT_AVOIDPARTIALSPENDS), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u (always enabled for wallets with \"avoid_reuse\" enabled))", DEFAULT_AVOIDPARTIALSPENDS), false, OptionsCategory::WALLET);
gArgs.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", false, OptionsCategory::WALLET);
gArgs.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", false, OptionsCategory::WALLET);
gArgs.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
diff --git a/src/script/ismine.cpp b/src/wallet/ismine.cpp
index 51bd2d6e9f..6138d4ae44 100644
--- a/src/script/ismine.cpp
+++ b/src/wallet/ismine.cpp
@@ -3,13 +3,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <script/ismine.h>
+#include <wallet/ismine.h>
#include <key.h>
-#include <keystore.h>
#include <script/script.h>
#include <script/sign.h>
-
+#include <wallet/wallet.h>
typedef std::vector<unsigned char> valtype;
@@ -46,7 +45,7 @@ bool PermitsUncompressed(IsMineSigVersion sigversion)
return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH;
}
-bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore)
+bool HaveKeys(const std::vector<valtype>& pubkeys, const CWallet& keystore)
{
for (const valtype& pubkey : pubkeys) {
CKeyID keyID = CPubKey(pubkey).GetID();
@@ -55,7 +54,7 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore)
return true;
}
-IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion)
+IsMineResult IsMineInner(const CWallet& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion)
{
IsMineResult ret = IsMineResult::NO;
@@ -90,7 +89,7 @@ IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey,
// This also applies to the P2WSH case.
break;
}
- ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
+ ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(PKHash(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
break;
}
case TX_PUBKEYHASH:
@@ -172,7 +171,7 @@ IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey,
} // namespace
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey)
+isminetype IsMine(const CWallet& keystore, const CScript& scriptPubKey)
{
switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) {
case IsMineResult::INVALID:
@@ -186,7 +185,7 @@ isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey)
assert(false);
}
-isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest)
+isminetype IsMine(const CWallet& keystore, const CTxDestination& dest)
{
CScript script = GetScriptForDestination(dest);
return IsMine(keystore, script);
diff --git a/src/script/ismine.h b/src/wallet/ismine.h
index 55e28e225a..41555fcb93 100644
--- a/src/script/ismine.h
+++ b/src/wallet/ismine.h
@@ -3,31 +3,33 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_SCRIPT_ISMINE_H
-#define BITCOIN_SCRIPT_ISMINE_H
+#ifndef BITCOIN_WALLET_ISMINE_H
+#define BITCOIN_WALLET_ISMINE_H
#include <script/standard.h>
#include <stdint.h>
#include <bitset>
-class CKeyStore;
+class CWallet;
class CScript;
/** IsMine() return codes */
-enum isminetype
+enum isminetype : unsigned int
{
ISMINE_NO = 0,
ISMINE_WATCH_ONLY = 1 << 0,
ISMINE_SPENDABLE = 1 << 1,
+ ISMINE_USED = 1 << 2,
ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE,
+ ISMINE_ALL_USED = ISMINE_ALL | ISMINE_USED,
ISMINE_ENUM_ELEMENTS,
};
/** used for bitflags of isminetype */
typedef uint8_t isminefilter;
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
-isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
+isminetype IsMine(const CWallet& wallet, const CScript& scriptPubKey);
+isminetype IsMine(const CWallet& wallet, const CTxDestination& dest);
/**
* Cachable amount subdivided into watchonly and spendable parts.
@@ -48,4 +50,4 @@ struct CachableAmount
}
};
-#endif // BITCOIN_SCRIPT_ISMINE_H
+#endif // BITCOIN_WALLET_ISMINE_H
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 79c5f439df..54aa12dba8 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/load.h b/src/wallet/load.h
index 9bb6f2166e..81f078fd10 100644
--- a/src/wallet/load.h
+++ b/src/wallet/load.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/psbtwallet.cpp b/src/wallet/psbtwallet.cpp
index 1b17b09763..ce4788dee1 100644
--- a/src/wallet/psbtwallet.cpp
+++ b/src/wallet/psbtwallet.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index c339e111ba..3112dca9f5 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -16,12 +16,12 @@
#include <util/bip32.h>
#include <util/system.h>
#include <util/time.h>
-#include <validation.h>
#include <wallet/wallet.h>
#include <wallet/rpcwallet.h>
#include <stdint.h>
+#include <tuple>
#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
@@ -115,7 +115,8 @@ UniValue importprivkey(const JSONRPCRequest& request)
"\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one private key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
- "may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n",
+ "may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key (see dumpprivkey)"},
{"label", RPCArg::Type::STR, /* default */ "current label if address exists, otherwise \"\"", "An optional label"},
@@ -157,8 +158,11 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (!request.params[2].isNull())
fRescan = request.params[2].get_bool();
- if (fRescan && pwallet->chain().getPruneMode()) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
+ if (fRescan && pwallet->chain().havePruned()) {
+ // Exit early and print an error.
+ // If a block is pruned after this check, we will import the key(s),
+ // but fail the rescan with a generic error.
+ throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
}
if (fRescan && !reserver.reserve()) {
@@ -216,7 +220,8 @@ UniValue abortrescan(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
RPCHelpMan{"abortrescan",
- "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n",
+ "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{},
RPCResults{},
RPCExamples{
@@ -252,7 +257,7 @@ static void ImportScript(CWallet* const pwallet, const CScript& script, const st
if (!pwallet->HaveCScript(id) && !pwallet->AddCScript(script)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
}
- ImportAddress(pwallet, id, strLabel);
+ ImportAddress(pwallet, ScriptHash(id), strLabel);
} else {
CTxDestination destination;
if (ExtractDestination(script, destination)) {
@@ -285,8 +290,10 @@ UniValue importaddress(const JSONRPCRequest& request)
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
"If you have the full public key, you should call importpubkey instead of this.\n"
+ "Hint: use importmulti to import more than one address.\n"
"\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
- "as change, and not show up in many RPCs.\n",
+ "as change, and not show up in many RPCs.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Bitcoin address (or hex-encoded script)"},
{"label", RPCArg::Type::STR, /* default */ "\"\"", "An optional label"},
@@ -314,8 +321,11 @@ UniValue importaddress(const JSONRPCRequest& request)
if (!request.params[2].isNull())
fRescan = request.params[2].get_bool();
- if (fRescan && pwallet->chain().getPruneMode()) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
+ if (fRescan && pwallet->chain().havePruned()) {
+ // Exit early and print an error.
+ // If a block is pruned after this check, we will import the key(s),
+ // but fail the rescan with a generic error.
+ throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
}
WalletRescanReserver reserver(pwallet);
@@ -479,8 +489,10 @@ UniValue importpubkey(const JSONRPCRequest& request)
throw std::runtime_error(
RPCHelpMan{"importpubkey",
"\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
+ "Hint: use importmulti to import more than one public key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
- "may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n",
+ "may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"pubkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The hex-encoded public key"},
{"label", RPCArg::Type::STR, /* default */ "\"\"", "An optional label"},
@@ -507,8 +519,11 @@ UniValue importpubkey(const JSONRPCRequest& request)
if (!request.params[2].isNull())
fRescan = request.params[2].get_bool();
- if (fRescan && pwallet->chain().getPruneMode()) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
+ if (fRescan && pwallet->chain().havePruned()) {
+ // Exit early and print an error.
+ // If a block is pruned after this check, we will import the key(s),
+ // but fail the rescan with a generic error.
+ throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
}
WalletRescanReserver reserver(pwallet);
@@ -558,7 +573,8 @@ UniValue importwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
RPCHelpMan{"importwallet",
- "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n",
+ "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet file"},
},
@@ -573,8 +589,11 @@ UniValue importwallet(const JSONRPCRequest& request)
},
}.ToString());
- if (pwallet->chain().getPruneMode()) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");
+ if (pwallet->chain().havePruned()) {
+ // Exit early and print an error.
+ // If a block is pruned after this check, we will import the key(s),
+ // but fail the rescan with a generic error.
+ throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled when blocks are pruned");
}
WalletRescanReserver reserver(pwallet);
@@ -661,17 +680,17 @@ UniValue importwallet(const JSONRPCRequest& request)
assert(key.VerifyPubKey(pubkey));
CKeyID keyid = pubkey.GetID();
if (pwallet->HaveKey(keyid)) {
- pwallet->WalletLogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid));
+ pwallet->WalletLogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(PKHash(keyid)));
continue;
}
- pwallet->WalletLogPrintf("Importing %s...\n", EncodeDestination(keyid));
+ pwallet->WalletLogPrintf("Importing %s...\n", EncodeDestination(PKHash(keyid)));
if (!pwallet->AddKeyPubKey(key, pubkey)) {
fGood = false;
continue;
}
pwallet->mapKeyMetadata[keyid].nCreateTime = time;
if (has_label)
- pwallet->SetAddressBook(keyid, label, "receive");
+ pwallet->SetAddressBook(PKHash(keyid), label, "receive");
nTimeBegin = std::min(nTimeBegin, time);
progress++;
}
@@ -807,19 +826,16 @@ UniValue dumpwallet(const JSONRPCRequest& request)
if (!file.is_open())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
- std::map<CTxDestination, int64_t> mapKeyBirth;
+ std::map<CKeyID, int64_t> mapKeyBirth;
const std::map<CKeyID, int64_t>& mapKeyPool = pwallet->GetAllReserveKeys();
pwallet->GetKeyBirthTimes(*locked_chain, mapKeyBirth);
std::set<CScriptID> scripts = pwallet->GetCScripts();
- // TODO: include scripts in GetKeyBirthTimes() output instead of separate
// sort time/key pairs
std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
for (const auto& entry : mapKeyBirth) {
- if (const CKeyID* keyID = boost::get<CKeyID>(&entry.first)) { // set and test
- vKeyBirth.push_back(std::make_pair(entry.second, *keyID));
- }
+ vKeyBirth.push_back(std::make_pair(entry.second, entry.first));
}
mapKeyBirth.clear();
std::sort(vKeyBirth.begin(), vKeyBirth.end());
@@ -870,7 +886,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
for (const CScriptID &scriptid : scripts) {
CScript script;
std::string create_time = "0";
- std::string address = EncodeDestination(scriptid);
+ std::string address = EncodeDestination(ScriptHash(scriptid));
// get birth times for scripts with metadata
auto it = pwallet->m_script_metadata.find(scriptid);
if (it != pwallet->m_script_metadata.end()) {
@@ -1144,18 +1160,12 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
if (!data.exists("range")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range");
}
- auto range = ParseRange(data["range"]);
- range_start = range.first;
- range_end = range.second;
- if (range_start < 0 || (range_end >> 31) != 0 || range_end - range_start >= 1000000) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid descriptor range specified");
- }
+ std::tie(range_start, range_end) = ParseDescriptorRange(data["range"]);
}
const UniValue& priv_keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
- // Expand all descriptors to get public keys and scripts.
- // TODO: get private keys from descriptors too
+ // Expand all descriptors to get public keys and scripts, and private keys if available.
for (int i = range_start; i <= range_end; ++i) {
FlatSigningProvider out_keys;
std::vector<CScript> scripts_temp;
@@ -1169,7 +1179,10 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
import_data.import_scripts.emplace(x.second);
}
+ parsed_desc->ExpandPrivate(i, keys, out_keys);
+
std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end()));
+ std::copy(out_keys.keys.begin(), out_keys.keys.end(), std::inserter(privkey_map, privkey_map.end()));
import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end());
}
@@ -1261,55 +1274,17 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
// All good, time to import
pwallet->MarkDirty();
- for (const auto& entry : import_data.import_scripts) {
- if (!pwallet->HaveCScript(CScriptID(entry)) && !pwallet->AddCScript(entry)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
- }
- }
- for (const auto& entry : privkey_map) {
- const CKey& key = entry.second;
- CPubKey pubkey = key.GetPubKey();
- const CKeyID& id = entry.first;
- assert(key.VerifyPubKey(pubkey));
- pwallet->mapKeyMetadata[id].nCreateTime = timestamp;
- // If the private key is not present in the wallet, insert it.
- if (!pwallet->HaveKey(id) && !pwallet->AddKeyPubKey(key, pubkey)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
- }
- pwallet->UpdateTimeFirstKey(timestamp);
+ if (!pwallet->ImportScripts(import_data.import_scripts)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
}
- for (const auto& entry : import_data.key_origins) {
- pwallet->AddKeyOrigin(entry.second.first, entry.second.second);
+ if (!pwallet->ImportPrivKeys(privkey_map, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
}
- for (const CKeyID& id : ordered_pubkeys) {
- auto entry = pubkey_map.find(id);
- if (entry == pubkey_map.end()) {
- continue;
- }
- const CPubKey& pubkey = entry->second;
- CPubKey temp;
- if (!pwallet->GetPubKey(id, temp) && !pwallet->AddWatchOnly(GetScriptForRawPubKey(pubkey), timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
- }
- pwallet->mapKeyMetadata[id].nCreateTime = timestamp;
-
- // Add to keypool only works with pubkeys
- if (add_keypool) {
- pwallet->AddKeypoolPubkey(pubkey, internal);
- }
+ if (!pwallet->ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.key_origins, add_keypool, internal, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
-
- for (const CScript& script : script_pub_keys) {
- if (!have_solving_data || !::IsMine(*pwallet, script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated
- if (!pwallet->AddWatchOnly(script, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
- }
- }
- CTxDestination dest;
- ExtractDestination(script, dest);
- if (!internal && IsValidDestination(dest)) {
- pwallet->SetAddressBook(dest, label, "receive");
- }
+ if (!pwallet->ImportScriptPubKeys(label, script_pub_keys, have_solving_data, internal, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
result.pushKV("success", UniValue(true));
@@ -1354,7 +1329,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
"If an address/script is imported without all of the private keys required to spend from that address, it will be watchonly. The 'watchonly' option must be set to true in this case or a warning will be returned.\n"
"Conversely, if all the private keys are provided and the address/script is spendable, the watchonly option must be set to false, or a warning will be returned.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
- "may report that the imported keys, addresses or scripts exists but related transactions are still missing.\n",
+ "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
{
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 626cdccfee..eae5f876ea 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -4,33 +4,27 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <amount.h>
-#include <chain.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <init.h>
#include <interfaces/chain.h>
#include <key_io.h>
-#include <net.h>
#include <node/transaction.h>
#include <outputtype.h>
#include <policy/feerate.h>
#include <policy/fees.h>
-#include <policy/policy.h>
#include <policy/rbf.h>
#include <rpc/rawtransaction_util.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/descriptor.h>
#include <script/sign.h>
-#include <shutdown.h>
-#include <timedata.h>
#include <util/bip32.h>
#include <util/fees.h>
#include <util/moneystr.h>
#include <util/system.h>
#include <util/url.h>
#include <util/validation.h>
-#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
#include <wallet/psbtwallet.h>
@@ -47,6 +41,17 @@
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
+static inline bool GetAvoidReuseFlag(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();
+
+ if (avoid_reuse && !can_avoid_reuse) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "wallet does not have the \"avoid reuse\" feature enabled");
+ }
+
+ return avoid_reuse;
+}
+
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
{
if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
@@ -310,7 +315,7 @@ static UniValue setlabel(const JSONRPCRequest& request)
static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
{
- CAmount curBalance = pwallet->GetBalance().m_mine_trusted;
+ CAmount curBalance = pwallet->GetBalance(0, coin_control.m_avoid_address_reuse).m_mine_trusted;
// Check amount
if (nValue <= 0)
@@ -357,7 +362,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 9)
throw std::runtime_error(
RPCHelpMan{"sendtoaddress",
"\nSend an amount to a given address." +
@@ -378,6 +383,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
" \"CONSERVATIVE\""},
+ {"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."},
},
RPCResult{
"\"txid\" (string) The transaction id.\n"
@@ -434,6 +441,9 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
}
}
+ 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;
EnsureWalletIsUnlocked(pwallet);
@@ -550,13 +560,14 @@ static UniValue signmessage(const JSONRPCRequest& request)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
}
- const CKeyID *keyID = boost::get<CKeyID>(&dest);
- if (!keyID) {
+ const PKHash *pkhash = boost::get<PKHash>(&dest);
+ if (!pkhash) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
CKey key;
- if (!pwallet->GetKey(*keyID, key)) {
+ CKeyID keyID(*pkhash);
+ if (!pwallet->GetKey(keyID, key)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
}
@@ -722,7 +733,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || (request.params.size() > 3 ))
+ if (request.fHelp || request.params.size() > 4)
throw std::runtime_error(
RPCHelpMan{"getbalance",
"\nReturns the total available balance.\n"
@@ -732,6 +743,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
{"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
{"minconf", RPCArg::Type::NUM, /* default */ "0", "Only include transactions confirmed at least this many times."},
{"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Also include balance in watch-only addresses (see 'importaddress')"},
+ {"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
},
RPCResult{
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n"
@@ -768,7 +780,9 @@ static UniValue getbalance(const JSONRPCRequest& request)
include_watchonly = true;
}
- const auto bal = pwallet->GetBalance(min_depth);
+ bool avoid_reuse = GetAvoidReuseFlag(pwallet, request.params[3]);
+
+ const auto bal = pwallet->GetBalance(min_depth, avoid_reuse);
return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
}
@@ -1022,8 +1036,8 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
}
// Construct using pay-to-script-hash:
- CScript inner = CreateMultisigRedeemscript(required, pubkeys);
- CTxDestination dest = AddAndGetDestinationForScript(*pwallet, inner, output_type);
+ CScript inner;
+ CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, *pwallet, inner);
pwallet->SetAddressBook(dest, label, "send");
UniValue result(UniValue::VOBJ);
@@ -2123,6 +2137,10 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
+ if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet does not contain private keys, nothing to encrypt.");
+ }
+
if (pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
}
@@ -2391,6 +2409,7 @@ static UniValue getbalances(const JSONRPCRequest& request)
" \"trusted\": xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n"
" \"untrusted_pending\": xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n"
" \"immature\": xxx (numeric) balance from immature coinbase outputs\n"
+ " \"used\": xxx (numeric) (only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)\n"
" },\n"
" \"watchonly\": { (object) watchonly balances (not present if wallet does not watch anything)\n"
" \"trusted\": xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n"
@@ -2423,6 +2442,12 @@ static UniValue getbalances(const JSONRPCRequest& request)
balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending));
balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature));
+ if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
+ // If the AVOID_REUSE flag is set, bal has been set to just the un-reused address balance. Get
+ // the total balance, and then subtract bal to get the reused address balance.
+ const auto full_bal = wallet.GetBalance(0, false);
+ balances_mine.pushKV("used", ValueFromAmount(full_bal.m_mine_trusted + full_bal.m_mine_untrusted_pending - bal.m_mine_trusted - bal.m_mine_untrusted_pending));
+ }
balances.pushKV("mine", balances_mine);
}
if (wallet.HaveWatchOnly()) {
@@ -2462,6 +2487,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
" \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n"
" \"private_keys_enabled\": true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n"
+ " \"avoid_reuse\": true|false (boolean) whether this wallet tracks clean/dirty coins in terms of reuse\n"
" \"scanning\": (json object) current scanning details, or false if no scan is in progress\n"
" {\n"
" \"duration\" : xxxx (numeric) elapsed seconds since scan start\n"
@@ -2510,6 +2536,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
obj.pushKV("hdseedid", seed_id.GetHex());
}
obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
+ obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE));
if (pwallet->IsScanning()) {
UniValue scanning(UniValue::VOBJ);
scanning.pushKV("duration", pwallet->ScanningDuration() / 1000);
@@ -2638,29 +2665,103 @@ static UniValue loadwallet(const JSONRPCRequest& request)
return obj;
}
-static UniValue createwallet(const JSONRPCRequest& request)
+static UniValue setwalletflag(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) {
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ CWallet* const pwallet = wallet.get();
+
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
+ std::string flags = "";
+ for (auto& it : WALLET_FLAG_MAP)
+ if (it.second & MUTABLE_WALLET_FLAGS)
+ flags += (flags == "" ? "" : ", ") + it.first;
throw std::runtime_error(
- RPCHelpMan{"createwallet",
- "\nCreates and loads a new wallet.\n",
+ RPCHelpMan{"setwalletflag",
+ "\nChange the state of the given wallet flag for a wallet.\n",
{
- {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
- {"disable_private_keys", RPCArg::Type::BOOL, /* default */ "false", "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
- {"blank", RPCArg::Type::BOOL, /* default */ "false", "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
+ {"flag", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the flag to change. Current available flags: " + flags},
+ {"value", RPCArg::Type::BOOL, /* default */ "true", "The new state."},
},
RPCResult{
"{\n"
- " \"name\" : <wallet_name>, (string) The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path.\n"
- " \"warning\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n"
+ " \"flag_name\": string (string) The name of the flag that was modified\n"
+ " \"flag_state\": bool (bool) The new state of the flag\n"
+ " \"warnings\": string (string) Any warnings associated with the change\n"
"}\n"
},
RPCExamples{
- HelpExampleCli("createwallet", "\"testwallet\"")
- + HelpExampleRpc("createwallet", "\"testwallet\"")
+ HelpExampleCli("setwalletflag", "avoid_reuse")
+ + HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
},
}.ToString());
}
+
+ std::string flag_str = request.params[0].get_str();
+ bool value = request.params[1].isNull() || request.params[1].get_bool();
+
+ if (!WALLET_FLAG_MAP.count(flag_str)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unknown wallet flag: %s", flag_str));
+ }
+
+ auto flag = WALLET_FLAG_MAP.at(flag_str);
+
+ if (!(flag & MUTABLE_WALLET_FLAGS)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is immutable: %s", flag_str));
+ }
+
+ UniValue res(UniValue::VOBJ);
+
+ if (pwallet->IsWalletFlagSet(flag) == value) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is already set to %s: %s", value ? "true" : "false", flag_str));
+ }
+
+ res.pushKV("flag_name", flag_str);
+ res.pushKV("flag_state", value);
+
+ if (value) {
+ pwallet->SetWalletFlag(flag);
+ } else {
+ pwallet->UnsetWalletFlag(flag);
+ }
+
+ if (flag && value && WALLET_FLAG_CAVEATS.count(flag)) {
+ res.pushKV("warnings", WALLET_FLAG_CAVEATS.at(flag));
+ }
+
+ return res;
+}
+
+static UniValue createwallet(const JSONRPCRequest& request)
+{
+ const RPCHelpMan help{
+ "createwallet",
+ "\nCreates and loads a new wallet.\n",
+ {
+ {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
+ {"disable_private_keys", RPCArg::Type::BOOL, /* default */ "false", "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
+ {"blank", RPCArg::Type::BOOL, /* default */ "false", "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
+ {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."},
+ {"avoid_reuse", RPCArg::Type::BOOL, /* default */ "false", "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
+ },
+ RPCResult{
+ "{\n"
+ " \"name\" : <wallet_name>, (string) The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path.\n"
+ " \"warning\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n"
+ "}\n"
+ },
+ RPCExamples{
+ HelpExampleCli("createwallet", "\"testwallet\"")
+ + HelpExampleRpc("createwallet", "\"testwallet\"")
+ },
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
std::string error;
std::string warning;
@@ -2669,10 +2770,27 @@ static UniValue createwallet(const JSONRPCRequest& request)
flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
}
+ bool create_blank = false; // Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
if (!request.params[2].isNull() && request.params[2].get_bool()) {
+ create_blank = true;
+ flags |= WALLET_FLAG_BLANK_WALLET;
+ }
+ SecureString passphrase;
+ passphrase.reserve(100);
+ if (!request.params[3].isNull()) {
+ passphrase = request.params[3].get_str().c_str();
+ if (passphrase.empty()) {
+ // Empty string is invalid
+ throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Cannot encrypt a wallet with a blank password");
+ }
+ // Born encrypted wallets need to be blank first so that wallet creation doesn't make any unencrypted keys
flags |= WALLET_FLAG_BLANK_WALLET;
}
+ if (!request.params[4].isNull() && request.params[4].get_bool()) {
+ flags |= WALLET_FLAG_AVOID_REUSE;
+ }
+
WalletLocation location(request.params[0].get_str());
if (location.Exists()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + location.GetName() + " already exists.");
@@ -2687,6 +2805,29 @@ static UniValue createwallet(const JSONRPCRequest& request)
if (!wallet) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
}
+
+ // Encrypt the wallet if there's a passphrase
+ if (!passphrase.empty() && !(flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (!wallet->EncryptWallet(passphrase)) {
+ throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Wallet created but failed to encrypt.");
+ }
+
+ if (!create_blank) {
+ // Unlock the wallet
+ if (!wallet->Unlock(passphrase)) {
+ throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Wallet was encrypted but could not be unlocked");
+ }
+
+ // Set a seed for the wallet
+ CPubKey master_pub_key = wallet->GenerateNewSeed();
+ wallet->SetHDSeed(master_pub_key);
+ wallet->NewKeyPool();
+
+ // Relock the wallet
+ wallet->Lock();
+ }
+ }
+
AddWallet(wallet);
wallet->postInitProcess();
@@ -2751,9 +2892,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 5)
- throw std::runtime_error(
- RPCHelpMan{"listunspent",
+ const RPCHelpMan help{
+ "listunspent",
"\nReturns array of unspent transaction outputs\n"
"with between minconf and maxconf (inclusive) confirmations.\n"
"Optionally filter to only include txouts paid to specified addresses.\n",
@@ -2790,6 +2930,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
" \"witnessScript\" : \"script\" (string) witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH\n"
" \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n"
" \"solvable\" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys\n"
+ " \"reused\" : xxx, (bool) (only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)\n"
" \"desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n"
" \"safe\" : xxx (bool) Whether this output is considered safe to spend. Unconfirmed transactions\n"
" from outside keys and unconfirmed replacement transactions are considered unsafe\n"
@@ -2805,7 +2946,11 @@ static UniValue listunspent(const JSONRPCRequest& request)
+ HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
+ HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
int nMinDepth = 1;
if (!request.params[0].isNull()) {
@@ -2869,17 +3014,22 @@ static UniValue listunspent(const JSONRPCRequest& request)
UniValue results(UniValue::VARR);
std::vector<COutput> vecOutputs;
{
+ CCoinControl cctl;
+ cctl.m_avoid_address_reuse = false;
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- pwallet->AvailableCoins(*locked_chain, vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
+ pwallet->AvailableCoins(*locked_chain, vecOutputs, !include_unsafe, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
}
LOCK(pwallet->cs_wallet);
+ const bool avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
+
for (const COutput& out : vecOutputs) {
CTxDestination address;
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
bool fValidAddress = ExtractDestination(scriptPubKey, address);
+ bool reused = avoid_reuse && pwallet->IsUsedDestination(address);
if (destinations.size() && (!fValidAddress || !destinations.count(address)))
continue;
@@ -2897,7 +3047,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
}
if (scriptPubKey.IsPayToScriptHash()) {
- const CScriptID& hash = boost::get<CScriptID>(address);
+ const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address));
CScript redeemScript;
if (pwallet->GetCScript(hash, redeemScript)) {
entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()));
@@ -2936,6 +3086,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
auto descriptor = InferDescriptor(scriptPubKey, *pwallet);
entry.pushKV("desc", descriptor->ToString());
}
+ if (avoid_reuse) entry.pushKV("reused", reused);
entry.pushKV("safe", out.fSafe);
results.push_back(entry);
}
@@ -3068,9 +3219,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
- throw std::runtime_error(
- RPCHelpMan{"fundrawtransaction",
+ const RPCHelpMan help{"fundrawtransaction",
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
"This will not modify existing inputs, and will add at most one change output to the outputs.\n"
"No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
@@ -3109,8 +3258,13 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
" \"CONSERVATIVE\""},
},
"options"},
- {"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction \n"
- " If iswitness is not present, heuristic tests will be used in decoding"},
+ {"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
+ "If iswitness is not present, heuristic tests will be used in decoding.\n"
+ "If true, only witness deserialization will be tried.\n"
+ "If false, only non-witness deserialization will be tried.\n"
+ "This boolean should reflect whether the transaction has inputs\n"
+ "(e.g. fully valid, or on-chain transactions), if known by the caller."
+ },
},
RPCResult{
"{\n"
@@ -3129,7 +3283,11 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
"\nSend the transaction\n"
+ HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
@@ -3412,7 +3570,8 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
RPCHelpMan{"rescanblockchain",
- "\nRescan the local blockchain for wallet related transactions.\n",
+ "\nRescan the local blockchain for wallet related transactions.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"start_height", RPCArg::Type::NUM, /* default */ "0", "block height where the rescan should start"},
{"stop_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."},
@@ -3537,8 +3696,9 @@ public:
UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); }
- UniValue operator()(const CKeyID& keyID) const
+ UniValue operator()(const PKHash& pkhash) const
{
+ CKeyID keyID(pkhash);
UniValue obj(UniValue::VOBJ);
CPubKey vchPubKey;
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
@@ -3548,8 +3708,9 @@ public:
return obj;
}
- UniValue operator()(const CScriptID& scriptID) const
+ UniValue operator()(const ScriptHash& scripthash) const
{
+ CScriptID scriptID(scripthash);
UniValue obj(UniValue::VOBJ);
CScript subscript;
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
@@ -4137,13 +4298,13 @@ static const CRPCCommand commands[] =
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label","address_type"} },
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
- { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank"} },
+ { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse"} },
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
{ "wallet", "getaddressesbylabel", &getaddressesbylabel, {"label"} },
{ "wallet", "getaddressinfo", &getaddressinfo, {"address"} },
- { "wallet", "getbalance", &getbalance, {"dummy","minconf","include_watchonly"} },
+ { "wallet", "getbalance", &getbalance, {"dummy","minconf","include_watchonly","avoid_reuse"} },
{ "wallet", "getnewaddress", &getnewaddress, {"label","address_type"} },
{ "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
@@ -4174,10 +4335,11 @@ static const CRPCCommand commands[] =
{ "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
{ "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
{ "wallet", "sendmany", &sendmany, {"dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} },
- { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} },
+ { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode","avoid_reuse"} },
{ "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} },
{ "wallet", "setlabel", &setlabel, {"address","label"} },
{ "wallet", "settxfee", &settxfee, {"amount"} },
+ { "wallet", "setwalletflag", &setwalletflag, {"flag","value"} },
{ "wallet", "signmessage", &signmessage, {"address","message"} },
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
{ "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} },
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index 90617472cc..1c0523c90b 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 34b9770e8b..9e7f0ed773 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index d9b07af329..c961456572 100644
--- a/src/wallet/test/db_tests.cpp
+++ b/src/wallet/test/db_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -16,7 +16,7 @@ BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(getwalletenv_file)
{
std::string test_name = "test_name.dat";
- fs::path datadir = SetDataDir("tempdir");
+ const fs::path datadir = GetDataDir();
fs::path file_path = datadir / test_name;
std::ofstream f(file_path.BOOST_FILESYSTEM_C_STR);
f.close();
@@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file)
BOOST_AUTO_TEST_CASE(getwalletenv_directory)
{
std::string expected_name = "wallet.dat";
- fs::path datadir = SetDataDir("tempdir");
+ const fs::path datadir = GetDataDir();
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
@@ -40,8 +40,8 @@ BOOST_AUTO_TEST_CASE(getwalletenv_directory)
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
{
- fs::path datadir = SetDataDir("tempdir");
- fs::path datadir_2 = SetDataDir("tempdir_2");
+ fs::path datadir = GetDataDir() / "1";
+ fs::path datadir_2 = GetDataDir() / "2";
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);
@@ -54,8 +54,8 @@ BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
{
- fs::path datadir = SetDataDir("tempdir");
- fs::path datadir_2 = SetDataDir("tempdir_2");
+ fs::path datadir = GetDataDir() / "1";
+ fs::path datadir_2 = GetDataDir() / "2";
std::string filename;
std::shared_ptr <BerkeleyEnvironment> env_1_a = GetWalletEnv(datadir, filename);
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
index 3b828d57f9..86ba0013fe 100644
--- a/src/wallet/test/init_test_fixture.cpp
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <fs.h>
+#include <util/system.h>
#include <wallet/test/init_test_fixture.h>
@@ -13,7 +14,7 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam
std::string sep;
sep += fs::path::preferred_separator;
- m_datadir = SetDataDir("tempdir");
+ m_datadir = GetDataDir();
m_cwd = fs::current_path();
m_walletdir_path_cases["default"] = m_datadir / "wallets";
@@ -41,4 +42,4 @@ InitWalletDirTestingSetup::~InitWalletDirTestingSetup()
void InitWalletDirTestingSetup::SetWalletDir(const fs::path& walletdir_path)
{
gArgs.ForceSetArg("-walletdir", walletdir_path.string());
-} \ No newline at end of file
+}
diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h
index 0f2d9fbd3d..e2b7075085 100644
--- a/src/wallet/test/init_test_fixture.h
+++ b/src/wallet/test/init_test_fixture.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index e1c53c83e2..1816fca257 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -1,17 +1,13 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/test/unit_test.hpp>
#include <test/setup_common.h>
+#include <util/system.h>
#include <wallet/test/init_test_fixture.h>
-#include <init.h>
-#include <walletinitinterface.h>
-#include <wallet/wallet.h>
-
-
BOOST_FIXTURE_TEST_SUITE(init_tests, InitWalletDirTestingSetup)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp
new file mode 100644
index 0000000000..062fef7748
--- /dev/null
+++ b/src/wallet/test/ismine_tests.cpp
@@ -0,0 +1,398 @@
+// Copyright (c) 2017-2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <key.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <test/setup_common.h>
+#include <wallet/ismine.h>
+#include <wallet/wallet.h>
+
+#include <boost/test/unit_test.hpp>
+
+
+BOOST_FIXTURE_TEST_SUITE(ismine_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(ismine_standard)
+{
+ CKey keys[2];
+ CPubKey pubkeys[2];
+ for (int i = 0; i < 2; i++) {
+ keys[i].MakeNewKey(true);
+ pubkeys[i] = keys[i].GetPubKey();
+ }
+
+ CKey uncompressedKey;
+ uncompressedKey.MakeNewKey(false);
+ CPubKey uncompressedPubkey = uncompressedKey.GetPubKey();
+ std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain();
+
+ CScript scriptPubKey;
+ isminetype result;
+
+ // P2PK compressed
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
+
+ // Keystore does not have key
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has key
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ }
+
+ // P2PK uncompressed
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
+
+ // Keystore does not have key
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has key
+ BOOST_CHECK(keystore.AddKey(uncompressedKey));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ }
+
+ // P2PKH compressed
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0]));
+
+ // Keystore does not have key
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has key
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ }
+
+ // P2PKH uncompressed
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey));
+
+ // Keystore does not have key
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has key
+ BOOST_CHECK(keystore.AddKey(uncompressedKey));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ }
+
+ // P2SH
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+
+ CScript redeemScript = GetScriptForDestination(PKHash(pubkeys[0]));
+ scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
+
+ // Keystore does not have redeemScript or key
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has redeemScript but no key
+ BOOST_CHECK(keystore.AddCScript(redeemScript));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has redeemScript and key
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ }
+
+ // (P2PKH inside) P2SH inside P2SH (invalid)
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+
+ CScript redeemscript_inner = GetScriptForDestination(PKHash(pubkeys[0]));
+ CScript redeemscript = GetScriptForDestination(ScriptHash(redeemscript_inner));
+ scriptPubKey = GetScriptForDestination(ScriptHash(redeemscript));
+
+ BOOST_CHECK(keystore.AddCScript(redeemscript));
+ BOOST_CHECK(keystore.AddCScript(redeemscript_inner));
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // (P2PKH inside) P2SH inside P2WSH (invalid)
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+
+ CScript redeemscript = GetScriptForDestination(PKHash(pubkeys[0]));
+ CScript witnessscript = GetScriptForDestination(ScriptHash(redeemscript));
+ scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
+
+ BOOST_CHECK(keystore.AddCScript(witnessscript));
+ BOOST_CHECK(keystore.AddCScript(redeemscript));
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // P2WPKH inside P2WSH (invalid)
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+
+ CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(PKHash(pubkeys[0])));
+ scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
+
+ BOOST_CHECK(keystore.AddCScript(witnessscript));
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // (P2PKH inside) P2WSH inside P2WSH (invalid)
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+
+ CScript witnessscript_inner = GetScriptForDestination(PKHash(pubkeys[0]));
+ CScript witnessscript = GetScriptForDestination(WitnessV0ScriptHash(witnessscript_inner));
+ scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
+
+ BOOST_CHECK(keystore.AddCScript(witnessscript_inner));
+ BOOST_CHECK(keystore.AddCScript(witnessscript));
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // P2WPKH compressed
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+
+ scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(PKHash(pubkeys[0])));
+
+ // Keystore implicitly has key and P2SH redeemScript
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ }
+
+ // P2WPKH uncompressed
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ BOOST_CHECK(keystore.AddKey(uncompressedKey));
+
+ scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(PKHash(uncompressedPubkey)));
+
+ // Keystore has key, but no P2SH redeemScript
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has key and P2SH redeemScript
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // scriptPubKey multisig
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+
+ scriptPubKey = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
+
+ // Keystore does not have any keys
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has 1/2 keys
+ BOOST_CHECK(keystore.AddKey(uncompressedKey));
+
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has 2/2 keys
+ BOOST_CHECK(keystore.AddKey(keys[1]));
+
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has 2/2 keys and the script
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // P2SH multisig
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ BOOST_CHECK(keystore.AddKey(uncompressedKey));
+ BOOST_CHECK(keystore.AddKey(keys[1]));
+
+ CScript redeemScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
+ scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
+
+ // Keystore has no redeemScript
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has redeemScript
+ BOOST_CHECK(keystore.AddCScript(redeemScript));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ }
+
+ // P2WSH multisig with compressed keys
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+ BOOST_CHECK(keystore.AddKey(keys[1]));
+
+ CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
+ scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
+
+ // Keystore has keys, but no witnessScript or P2SH redeemScript
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has keys and witnessScript, but no P2SH redeemScript
+ BOOST_CHECK(keystore.AddCScript(witnessScript));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has keys, witnessScript, P2SH redeemScript
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ }
+
+ // P2WSH multisig with uncompressed key
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ BOOST_CHECK(keystore.AddKey(uncompressedKey));
+ BOOST_CHECK(keystore.AddKey(keys[1]));
+
+ CScript witnessScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
+ scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
+
+ // Keystore has keys, but no witnessScript or P2SH redeemScript
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has keys and witnessScript, but no P2SH redeemScript
+ BOOST_CHECK(keystore.AddCScript(witnessScript));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has keys, witnessScript, P2SH redeemScript
+ BOOST_CHECK(keystore.AddCScript(scriptPubKey));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // P2WSH multisig wrapped in P2SH
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+
+ CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
+ CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
+ scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
+
+ // Keystore has no witnessScript, P2SH redeemScript, or keys
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has witnessScript and P2SH redeemScript, but no keys
+ BOOST_CHECK(keystore.AddCScript(redeemScript));
+ BOOST_CHECK(keystore.AddCScript(witnessScript));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+
+ // Keystore has keys, witnessScript, P2SH redeemScript
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+ BOOST_CHECK(keystore.AddKey(keys[1]));
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ }
+
+ // OP_RETURN
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]);
+
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // witness unspendable
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_0 << ToByteVector(ParseHex("aabb"));
+
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // witness unknown
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_16 << ToByteVector(ParseHex("aabb"));
+
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // Nonstandard
+ {
+ CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(keystore.cs_wallet);
+ BOOST_CHECK(keystore.AddKey(keys[0]));
+
+ scriptPubKey.clear();
+ scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
+
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index f774cb4ad1..0400f1207c 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -1,15 +1,12 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key_io.h>
-#include <script/sign.h>
#include <util/bip32.h>
#include <util/strencodings.h>
#include <wallet/psbtwallet.h>
-#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
-#include <univalue.h>
#include <boost/test/unit_test.hpp>
#include <test/setup_common.h>
diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp
index acc61c984f..2f41813234 100644
--- a/src/wallet/test/wallet_crypto_tests.cpp
+++ b/src/wallet/test/wallet_crypto_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Copyright (c) 2014-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index 6526e69eea..ba0843f352 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -1,13 +1,9 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <wallet/test/wallet_test_fixture.h>
-#include <rpc/server.h>
-#include <wallet/db.h>
-#include <wallet/rpcwallet.h>
-
WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
: TestingSetup(chainName),
m_wallet(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock())
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index 1017e61700..c1dbecdf8c 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 3cdbde33c3..ef95d0544f 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -5,19 +5,17 @@
#include <wallet/wallet.h>
#include <memory>
-#include <set>
#include <stdint.h>
-#include <utility>
#include <vector>
#include <consensus/validation.h>
#include <interfaces/chain.h>
+#include <policy/policy.h>
#include <rpc/server.h>
#include <test/setup_common.h>
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/test/wallet_test_fixture.h>
-#include <policy/policy.h>
#include <boost/test/unit_test.hpp>
#include <univalue.h>
@@ -36,16 +34,15 @@ static void AddKey(CWallet& wallet, const CKey& key)
BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
{
- auto chain = interfaces::MakeChain();
-
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* oldTip = chainActive.Tip();
+ CBlockIndex* oldTip = ::ChainActive().Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CBlockIndex* newTip = chainActive.Tip();
+ CBlockIndex* newTip = ::ChainActive().Tip();
- LockAnnotation lock(::cs_main);
+ auto chain = interfaces::MakeChain();
auto locked_chain = chain->lock();
+ LockAssertion lock(::cs_main);
// Verify ScanForWalletTransactions accommodates a null start block.
{
@@ -116,16 +113,15 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
{
- auto chain = interfaces::MakeChain();
-
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* oldTip = chainActive.Tip();
+ CBlockIndex* oldTip = ::ChainActive().Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CBlockIndex* newTip = chainActive.Tip();
+ CBlockIndex* newTip = ::ChainActive().Tip();
- LockAnnotation lock(::cs_main);
+ auto chain = interfaces::MakeChain();
auto locked_chain = chain->lock();
+ LockAssertion lock(::cs_main);
// Prune the older block file.
PruneOneBlockFile(oldTip->GetBlockPos().nFile);
@@ -177,11 +173,9 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// than or equal to key birthday.
BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{
- auto chain = interfaces::MakeChain();
-
// Create two blocks with same timestamp to verify that importwallet rescan
// will pick up both blocks, not just the first.
- const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5;
+ const int64_t BLOCK_TIME = ::ChainActive().Tip()->GetBlockTimeMax() + 5;
SetMockTime(BLOCK_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
@@ -192,9 +186,11 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
SetMockTime(KEY_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
+ auto chain = interfaces::MakeChain();
auto locked_chain = chain->lock();
+ LockAssertion lock(::cs_main);
- std::string backup_file = (SetDataDir("importwallet_rescan") / "wallet.backup").string();
+ std::string backup_file = (GetDataDir() / "wallet.backup").string();
// Import key into wallet and call dumpwallet to create backup file.
{
@@ -245,11 +241,15 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{
auto chain = interfaces::MakeChain();
+
CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
CWalletTx wtx(&wallet, m_coinbase_txns.back());
+
auto locked_chain = chain->lock();
+ LockAssertion lock(::cs_main);
LOCK(wallet.cs_wallet);
- wtx.hashBlock = chainActive.Tip()->GetBlockHash();
+
+ wtx.hashBlock = ::ChainActive().Tip()->GetBlockHash();
wtx.nIndex = 0;
// Call GetImmatureCredit() once before adding the key to the wallet to
@@ -270,8 +270,8 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
SetMockTime(mockTime);
CBlockIndex* block = nullptr;
if (blockTime > 0) {
- LockAnnotation lock(::cs_main);
auto locked_chain = wallet.chain().lock();
+ LockAssertion lock(::cs_main);
auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second);
const uint256& hash = inserted.first->first;
@@ -322,7 +322,7 @@ BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
{
- CTxDestination dest = CKeyID();
+ CTxDestination dest = PKHash();
LOCK(m_wallet.cs_wallet);
m_wallet.AddDestData(dest, "misc", "val_misc");
m_wallet.AddDestData(dest, "rr0", "val_rr0");
@@ -346,10 +346,10 @@ public:
AddKey(*wallet, coinbaseKey);
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
- CWallet::ScanResult result = wallet->ScanForWalletTransactions(chainActive.Genesis()->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
+ CWallet::ScanResult result = wallet->ScanForWalletTransactions(::ChainActive().Genesis()->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK_EQUAL(result.last_scanned_block, chainActive.Tip()->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.last_scanned_height, chainActive.Height());
+ BOOST_CHECK_EQUAL(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, ::ChainActive().Height());
BOOST_CHECK(result.last_failed_block.IsNull());
}
@@ -366,7 +366,10 @@ public:
int changePos = -1;
std::string error;
CCoinControl dummy;
- BOOST_CHECK(wallet->CreateTransaction(*m_locked_chain, {recipient}, tx, reservekey, fee, changePos, error, dummy));
+ {
+ auto locked_chain = m_chain->lock();
+ BOOST_CHECK(wallet->CreateTransaction(*locked_chain, {recipient}, tx, reservekey, fee, changePos, error, dummy));
+ }
CValidationState state;
BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekey, state));
CMutableTransaction blocktx;
@@ -375,15 +378,16 @@ public:
blocktx = CMutableTransaction(*wallet->mapWallet.at(tx->GetHash()).tx);
}
CreateAndProcessBlock({CMutableTransaction(blocktx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
+
+ LOCK(cs_main);
LOCK(wallet->cs_wallet);
auto it = wallet->mapWallet.find(tx->GetHash());
BOOST_CHECK(it != wallet->mapWallet.end());
- it->second.SetMerkleBranch(chainActive.Tip()->GetBlockHash(), 1);
+ it->second.SetMerkleBranch(::ChainActive().Tip()->GetBlockHash(), 1);
return it->second;
}
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain();
- std::unique_ptr<interfaces::Chain::Lock> m_locked_chain = m_chain->assumeLocked(); // Temporary. Removed in upcoming lock cleanup
std::unique_ptr<CWallet> wallet;
};
@@ -395,11 +399,12 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
// address.
std::map<CTxDestination, std::vector<COutput>> list;
{
- LOCK2(cs_main, wallet->cs_wallet);
- list = wallet->ListCoins(*m_locked_chain);
+ auto locked_chain = m_chain->lock();
+ LOCK(wallet->cs_wallet);
+ list = wallet->ListCoins(*locked_chain);
}
BOOST_CHECK_EQUAL(list.size(), 1U);
- BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
+ BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U);
// Check initial balance from one mature coinbase transaction.
@@ -411,18 +416,20 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
// pubkey.
AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */});
{
- LOCK2(cs_main, wallet->cs_wallet);
- list = wallet->ListCoins(*m_locked_chain);
+ auto locked_chain = m_chain->lock();
+ LOCK(wallet->cs_wallet);
+ list = wallet->ListCoins(*locked_chain);
}
BOOST_CHECK_EQUAL(list.size(), 1U);
- BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
+ BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U);
// Lock both coins. Confirm number of available coins drops to 0.
{
- LOCK2(cs_main, wallet->cs_wallet);
+ auto locked_chain = m_chain->lock();
+ LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- wallet->AvailableCoins(*m_locked_chain, available);
+ wallet->AvailableCoins(*locked_chain, available);
BOOST_CHECK_EQUAL(available.size(), 2U);
}
for (const auto& group : list) {
@@ -432,19 +439,21 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
}
}
{
- LOCK2(cs_main, wallet->cs_wallet);
+ auto locked_chain = m_chain->lock();
+ LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- wallet->AvailableCoins(*m_locked_chain, available);
+ wallet->AvailableCoins(*locked_chain, available);
BOOST_CHECK_EQUAL(available.size(), 0U);
}
// Confirm ListCoins still returns same result as before, despite coins
// being locked.
{
- LOCK2(cs_main, wallet->cs_wallet);
- list = wallet->ListCoins(*m_locked_chain);
+ auto locked_chain = m_chain->lock();
+ LOCK(wallet->cs_wallet);
+ list = wallet->ListCoins(*locked_chain);
}
BOOST_CHECK_EQUAL(list.size(), 1U);
- BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
+ BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U);
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 1a9e640a8a..16366a0c23 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -14,17 +14,12 @@
#include <key.h>
#include <key_io.h>
#include <keystore.h>
-#include <net.h>
#include <policy/fees.h>
#include <policy/policy.h>
-#include <policy/rbf.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/descriptor.h>
#include <script/script.h>
-#include <shutdown.h>
-#include <timedata.h>
-#include <txmempool.h>
#include <util/bip32.h>
#include <util/error.h>
#include <util/fees.h>
@@ -41,6 +36,14 @@
#include <boost/algorithm/string/replace.hpp>
+const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS{
+ {WALLET_FLAG_AVOID_REUSE,
+ "You need to rescan the blockchain in order to correctly mark used "
+ "destinations in the past. Until this is done, some destinations may "
+ "be considered unused, even if the opposite is the case."
+ },
+};
+
static const size_t OUTPUT_GROUP_MAX_ENTRIES = 10;
static CCriticalSection cs_wallets;
@@ -306,7 +309,7 @@ bool CWallet::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const C
// check if we need to remove from watch-only
CScript script;
- script = GetScriptForDestination(pubkey.GetID());
+ script = GetScriptForDestination(PKHash(pubkey));
if (HaveWatchOnly(script)) {
RemoveWatchOnly(script);
}
@@ -320,7 +323,7 @@ bool CWallet::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const C
secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]);
}
- UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET);
+ UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
return true;
}
@@ -362,12 +365,6 @@ void CWallet::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata&
m_script_metadata[script_id] = meta;
}
-// Writes a keymetadata for a public key. overwrite specifies whether to overwrite an existing metadata for that key if there exists one.
-bool CWallet::WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, const bool overwrite)
-{
- return WalletBatch(*database).WriteKeyMetadata(meta, pubkey, overwrite);
-}
-
void CWallet::UpgradeKeyMetadata()
{
AssertLockHeld(cs_wallet);
@@ -376,7 +373,6 @@ void CWallet::UpgradeKeyMetadata()
}
std::unique_ptr<WalletBatch> batch = MakeUnique<WalletBatch>(*database);
- size_t cnt = 0;
for (auto& meta_pair : mapKeyMetadata) {
CKeyMetadata& meta = meta_pair.second;
if (!meta.hd_seed_id.IsNull() && !meta.has_key_origin && meta.hdKeypath != "s") { // If the hdKeypath is "s", that's the seed and it doesn't have a key origin
@@ -399,10 +395,6 @@ void CWallet::UpgradeKeyMetadata()
CPubKey pubkey;
if (GetPubKey(meta_pair.first, pubkey)) {
batch->WriteKeyMetadata(meta, pubkey, true);
- if (++cnt % 1000 == 0) {
- // avoid creating overlarge in-memory batches in case the wallet contains large amounts of keys
- batch.reset(new WalletBatch(*database));
- }
}
}
}
@@ -433,10 +425,16 @@ void CWallet::UpdateTimeFirstKey(int64_t nCreateTime)
bool CWallet::AddCScript(const CScript& redeemScript)
{
+ WalletBatch batch(*database);
+ return AddCScriptWithDB(batch, redeemScript);
+}
+
+bool CWallet::AddCScriptWithDB(WalletBatch& batch, const CScript& redeemScript)
+{
if (!CCryptoKeyStore::AddCScript(redeemScript))
return false;
- if (WalletBatch(*database).WriteCScript(Hash160(redeemScript), redeemScript)) {
- UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET);
+ if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) {
+ UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
return true;
}
return false;
@@ -449,7 +447,7 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
* these. Do not add them to the wallet and warn. */
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
{
- std::string strAddr = EncodeDestination(CScriptID(redeemScript));
+ std::string strAddr = EncodeDestination(ScriptHash(redeemScript));
WalletLogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr);
return true;
}
@@ -457,20 +455,32 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
return CCryptoKeyStore::AddCScript(redeemScript);
}
-bool CWallet::AddWatchOnly(const CScript& dest)
+bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest)
{
if (!CCryptoKeyStore::AddWatchOnly(dest))
return false;
const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)];
UpdateTimeFirstKey(meta.nCreateTime);
NotifyWatchonlyChanged(true);
- if (WalletBatch(*database).WriteWatchOnly(dest, meta)) {
- UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET);
+ if (batch.WriteWatchOnly(dest, meta)) {
+ UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
return true;
}
return false;
}
+bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time)
+{
+ m_script_metadata[CScriptID(dest)].nCreateTime = create_time;
+ return AddWatchOnlyWithDB(batch, dest);
+}
+
+bool CWallet::AddWatchOnly(const CScript& dest)
+{
+ WalletBatch batch(*database);
+ return AddWatchOnlyWithDB(batch, dest);
+}
+
bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime)
{
m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime;
@@ -930,6 +940,37 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
return success;
}
+void CWallet::SetUsedDestinationState(const uint256& hash, unsigned int n, bool used)
+{
+ const CWalletTx* srctx = GetWalletTx(hash);
+ if (!srctx) return;
+
+ CTxDestination dst;
+ if (ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst)) {
+ if (::IsMine(*this, dst)) {
+ LOCK(cs_wallet);
+ if (used && !GetDestData(dst, "used", nullptr)) {
+ AddDestData(dst, "used", "p"); // p for "present", opposite of absent (null)
+ } else if (!used && GetDestData(dst, "used", nullptr)) {
+ EraseDestData(dst, "used");
+ }
+ }
+ }
+}
+
+bool CWallet::IsUsedDestination(const CTxDestination& dst) const
+{
+ LOCK(cs_wallet);
+ return ::IsMine(*this, dst) && GetDestData(dst, "used", nullptr);
+}
+
+bool CWallet::IsUsedDestination(const uint256& hash, unsigned int n) const
+{
+ CTxDestination dst;
+ const CWalletTx* srctx = GetWalletTx(hash);
+ return srctx && ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst) && IsUsedDestination(dst);
+}
+
bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
{
LOCK(cs_wallet);
@@ -938,6 +979,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
uint256 hash = wtxIn.GetHash();
+ if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
+ // Mark used destinations
+ for (const CTxIn& txin : wtxIn.tx->vin) {
+ const COutPoint& op = txin.prevout;
+ SetUsedDestinationState(op.hash, op.n, true);
+ }
+ }
+
// Inserts only if not already there, returns tx inserted or tx found
std::pair<std::map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(std::make_pair(hash, wtxIn));
CWalletTx& wtx = (*ret.first).second;
@@ -1288,7 +1337,7 @@ void CWallet::UpdatedBlockTip()
void CWallet::BlockUntilSyncedToCurrentChain() {
AssertLockNotHeld(cs_wallet);
// Skip the queue-draining stuff if we know we're caught up with
- // chainActive.Tip(), otherwise put a callback in the validation interface queue and wait
+ // ::ChainActive().Tip(), otherwise put a callback in the validation interface queue and wait
// for the queue to drain enough to execute it (indicating we are caught up
// at least with the time we entered this function).
uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed);
@@ -1543,13 +1592,19 @@ void CWallet::SetWalletFlag(uint64_t flags)
void CWallet::UnsetWalletFlag(uint64_t flag)
{
+ WalletBatch batch(*database);
+ UnsetWalletFlagWithDB(batch, flag);
+}
+
+void CWallet::UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag)
+{
LOCK(cs_wallet);
m_wallet_flags &= ~flag;
- if (!WalletBatch(*database).WriteWalletFlags(m_wallet_flags))
+ if (!batch.WriteWalletFlags(m_wallet_flags))
throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
}
-bool CWallet::IsWalletFlagSet(uint64_t flag)
+bool CWallet::IsWalletFlagSet(uint64_t flag) const
{
return (m_wallet_flags & flag);
}
@@ -1558,7 +1613,7 @@ bool CWallet::SetWalletFlags(uint64_t overwriteFlags, bool memonly)
{
LOCK(cs_wallet);
m_wallet_flags = overwriteFlags;
- if (((overwriteFlags & g_known_wallet_flags) >> 32) ^ (overwriteFlags >> 32)) {
+ if (((overwriteFlags & KNOWN_WALLET_FLAGS) >> 32) ^ (overwriteFlags >> 32)) {
// contains unknown non-tolerable wallet flags
return false;
}
@@ -1606,6 +1661,80 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut>
return true;
}
+bool CWallet::ImportScripts(const std::set<CScript> scripts)
+{
+ WalletBatch batch(*database);
+ for (const auto& entry : scripts) {
+ if (!HaveCScript(CScriptID(entry)) && !AddCScriptWithDB(batch, entry)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool CWallet::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp)
+{
+ WalletBatch batch(*database);
+ for (const auto& entry : privkey_map) {
+ const CKey& key = entry.second;
+ CPubKey pubkey = key.GetPubKey();
+ const CKeyID& id = entry.first;
+ assert(key.VerifyPubKey(pubkey));
+ mapKeyMetadata[id].nCreateTime = timestamp;
+ // If the private key is not present in the wallet, insert it.
+ if (!HaveKey(id) && !AddKeyPubKeyWithDB(batch, key, pubkey)) {
+ return false;
+ }
+ UpdateTimeFirstKey(timestamp);
+ }
+ return true;
+}
+
+bool CWallet::ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp)
+{
+ WalletBatch batch(*database);
+ for (const auto& entry : key_origins) {
+ AddKeyOriginWithDB(batch, entry.second.first, entry.second.second);
+ }
+ for (const CKeyID& id : ordered_pubkeys) {
+ auto entry = pubkey_map.find(id);
+ if (entry == pubkey_map.end()) {
+ continue;
+ }
+ const CPubKey& pubkey = entry->second;
+ CPubKey temp;
+ if (!GetPubKey(id, temp) && !AddWatchOnlyWithDB(batch, GetScriptForRawPubKey(pubkey), timestamp)) {
+ return false;
+ }
+ mapKeyMetadata[id].nCreateTime = timestamp;
+
+ // Add to keypool only works with pubkeys
+ if (add_keypool) {
+ AddKeypoolPubkeyWithDB(pubkey, internal, batch);
+ NotifyCanGetAddressesChanged();
+ }
+ }
+ return true;
+}
+
+bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool internal, const int64_t timestamp)
+{
+ WalletBatch batch(*database);
+ for (const CScript& script : script_pub_keys) {
+ if (!have_solving_data || !::IsMine(*this, script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated
+ if (!AddWatchOnlyWithDB(batch, script, timestamp)) {
+ return false;
+ }
+ }
+ CTxDestination dest;
+ ExtractDestination(script, dest);
+ if (!internal && IsValidDestination(dest)) {
+ SetAddressBookWithDB(batch, dest, label, "receive");
+ }
+ }
+ return true;
+}
+
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
{
std::vector<CTxOut> txouts;
@@ -1977,7 +2106,7 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
return 0;
// Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future).
- bool allow_cache = filter == ISMINE_SPENDABLE || filter == ISMINE_WATCH_ONLY;
+ bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL;
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsImmatureCoinBase(locked_chain))
@@ -1987,12 +2116,12 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
return m_amounts[AVAILABLE_CREDIT].m_value[filter];
}
+ bool allow_used_addresses = (filter & ISMINE_USED) || !pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
CAmount nCredit = 0;
uint256 hashTx = GetHash();
for (unsigned int i = 0; i < tx->vout.size(); i++)
{
- if (!pwallet->IsSpent(locked_chain, hashTx, i))
- {
+ if (!pwallet->IsSpent(locked_chain, hashTx, i) && (allow_used_addresses || !pwallet->IsUsedDestination(hashTx, i))) {
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, filter);
if (!MoneyRange(nCredit))
@@ -2134,9 +2263,10 @@ void MaybeResendWalletTxs()
*/
-CWallet::Balance CWallet::GetBalance(const int min_depth) const
+CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) const
{
Balance ret;
+ isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED;
{
auto locked_chain = chain().lock();
LOCK(cs_wallet);
@@ -2145,8 +2275,8 @@ CWallet::Balance CWallet::GetBalance(const int min_depth) const
const CWalletTx& wtx = entry.second;
const bool is_trusted{wtx.IsTrusted(*locked_chain)};
const int tx_depth{wtx.GetDepthInMainChain(*locked_chain)};
- const CAmount tx_credit_mine{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_SPENDABLE)};
- const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_WATCH_ONLY)};
+ const CAmount tx_credit_mine{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)};
+ const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)};
if (is_trusted && tx_depth >= min_depth) {
ret.m_mine_trusted += tx_credit_mine;
ret.m_watchonly_trusted += tx_credit_watchonly;
@@ -2184,6 +2314,9 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
vCoins.clear();
CAmount nTotal = 0;
+ // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where
+ // a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses
+ bool allow_used_addresses = !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse);
for (const auto& entry : mapWallet)
{
@@ -2265,6 +2398,10 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
continue;
}
+ if (!allow_used_addresses && IsUsedDestination(wtxid, i)) {
+ continue;
+ }
+
bool solvable = IsSolvable(*this, wtx.tx->vout[i].scriptPubKey);
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
@@ -3149,8 +3286,7 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
return DBErrors::LOAD_OK;
}
-
-bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
+bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
{
bool fUpdated = false;
{
@@ -3163,9 +3299,15 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
}
NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
- if (!strPurpose.empty() && !WalletBatch(*database).WritePurpose(EncodeDestination(address), strPurpose))
+ if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
return false;
- return WalletBatch(*database).WriteName(EncodeDestination(address), strName);
+ return batch.WriteName(EncodeDestination(address), strName);
+}
+
+bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
+{
+ WalletBatch batch(*database);
+ return SetAddressBookWithDB(batch, address, strName, strPurpose);
}
bool CWallet::DelAddressBook(const CTxDestination& address)
@@ -3315,13 +3457,6 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
return true;
}
-void CWallet::AddKeypoolPubkey(const CPubKey& pubkey, const bool internal)
-{
- WalletBatch batch(*database);
- AddKeypoolPubkeyWithDB(pubkey, internal, batch);
- NotifyCanGetAddressesChanged();
-}
-
void CWallet::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch)
{
LOCK(cs_wallet);
@@ -3711,7 +3846,7 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
/** @} */ // end of Actions
-void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CTxDestination, int64_t>& mapKeyBirth) const {
+void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CKeyID, int64_t>& mapKeyBirth) const {
AssertLockHeld(cs_wallet);
mapKeyBirth.clear();
@@ -4057,16 +4192,12 @@ 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);
- if ((wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
- //selective allow to set flags
- walletInstance->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
- } else if (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET) {
- walletInstance->SetWalletFlag(WALLET_FLAG_BLANK_WALLET);
- } else {
+ walletInstance->SetWalletFlags(wallet_creation_flags, false);
+ if (!(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) {
// generate a new seed
CPubKey seed = walletInstance->GenerateNewSeed();
walletInstance->SetHDSeed(seed);
- } // Otherwise, do not generate a new seed
+ }
// Top up the keypool
if (walletInstance->CanGenerateKeys() && !walletInstance->TopUpKeyPool()) {
@@ -4074,7 +4205,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
return nullptr;
}
- auto locked_chain = chain.assumeLocked(); // Temporary. Removed in upcoming lock cleanup
+ auto locked_chain = chain.lock();
walletInstance->ChainStateFlushed(locked_chain->getTipLocator());
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
// Make it impossible to disable private keys after creation
@@ -4209,10 +4340,13 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (tip_height && *tip_height != rescan_height)
{
- //We can't rescan beyond non-pruned blocks, stop and throw an error
- //this might happen if a user uses an old wallet within a pruned node
- // or if he ran -disablewallet for a longer time, then decided to re-enable
- if (chain.getPruneMode()) {
+ // We can't rescan beyond non-pruned blocks, stop and throw an error.
+ // This might happen if a user uses an old wallet within a pruned node
+ // or if they ran -disablewallet for a longer time, then decided to re-enable
+ if (chain.havePruned()) {
+ // Exit early and print an error.
+ // If a block is pruned after this check, we will load the wallet,
+ // but fail the rescan with a generic error.
int block_height = *tip_height;
while (block_height > 0 && locked_chain->haveBlockOnDisk(block_height - 1) && rescan_height != block_height) {
--block_height;
@@ -4440,12 +4574,12 @@ bool CWallet::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const
return true;
}
-bool CWallet::AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info)
+bool CWallet::AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info)
{
LOCK(cs_wallet);
std::copy(info.fingerprint, info.fingerprint + 4, mapKeyMetadata[pubkey.GetID()].key_origin.fingerprint);
mapKeyMetadata[pubkey.GetID()].key_origin.path = info.path;
mapKeyMetadata[pubkey.GetID()].has_key_origin = true;
mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path);
- return WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true);
+ return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true);
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 30ea51c8a2..7b5465c219 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -11,7 +11,6 @@
#include <interfaces/handler.h>
#include <outputtype.h>
#include <policy/feerate.h>
-#include <script/ismine.h>
#include <script/sign.h>
#include <streams.h>
#include <tinyformat.h>
@@ -21,6 +20,7 @@
#include <validationinterface.h>
#include <wallet/coinselection.h>
#include <wallet/crypter.h>
+#include <wallet/ismine.h>
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
@@ -120,6 +120,10 @@ enum WalletFlags : uint64_t {
// wallet flags in the upper section (> 1 << 31) will lead to not opening the wallet if flag is unknown
// unknown wallet flags in the lower section <= (1 << 31) will be tolerated
+ // will categorize coins as clean (not reused) and dirty (reused), and handle
+ // them with privacy considerations in mind
+ WALLET_FLAG_AVOID_REUSE = (1ULL << 0),
+
// Indicates that the metadata has already been upgraded to contain key origins
WALLET_FLAG_KEY_ORIGIN_METADATA = (1ULL << 1),
@@ -139,16 +143,79 @@ enum WalletFlags : uint64_t {
WALLET_FLAG_BLANK_WALLET = (1ULL << 33),
};
-static constexpr uint64_t g_known_wallet_flags = WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET | WALLET_FLAG_KEY_ORIGIN_METADATA;
+static constexpr uint64_t KNOWN_WALLET_FLAGS =
+ WALLET_FLAG_AVOID_REUSE
+ | WALLET_FLAG_BLANK_WALLET
+ | WALLET_FLAG_KEY_ORIGIN_METADATA
+ | WALLET_FLAG_DISABLE_PRIVATE_KEYS;
+
+static constexpr uint64_t MUTABLE_WALLET_FLAGS =
+ WALLET_FLAG_AVOID_REUSE;
-/** A key pool entry */
+static const std::map<std::string,WalletFlags> WALLET_FLAG_MAP{
+ {"avoid_reuse", WALLET_FLAG_AVOID_REUSE},
+ {"blank", WALLET_FLAG_BLANK_WALLET},
+ {"key_origin_metadata", WALLET_FLAG_KEY_ORIGIN_METADATA},
+ {"disable_private_keys", WALLET_FLAG_DISABLE_PRIVATE_KEYS},
+};
+
+extern const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS;
+
+/** A key from a CWallet's keypool
+ *
+ * The wallet holds one (for pre HD-split wallets) or several keypools. These
+ * are sets of keys that have not yet been used to provide addresses or receive
+ * change.
+ *
+ * The Bitcoin Core wallet was originally a collection of unrelated private
+ * keys with their associated addresses. If a non-HD wallet generated a
+ * key/address, gave that address out and then restored a backup from before
+ * that key's generation, then any funds sent to that address would be
+ * lost definitively.
+ *
+ * The keypool was implemented to avoid this scenario (commit: 10384941). The
+ * wallet would generate a set of keys (100 by default). When a new public key
+ * was required, either to give out as an address or to use in a change output,
+ * it would be drawn from the keypool. The keypool would then be topped up to
+ * maintain 100 keys. This ensured that as long as the wallet hadn't used more
+ * than 100 keys since the previous backup, all funds would be safe, since a
+ * restored wallet would be able to scan for all owned addresses.
+ *
+ * A keypool also allowed encrypted wallets to give out addresses without
+ * having to be decrypted to generate a new private key.
+ *
+ * With the introduction of HD wallets (commit: f1902510), the keypool
+ * essentially became an address look-ahead pool. Restoring old backups can no
+ * longer definitively lose funds as long as the addresses used were from the
+ * wallet's HD seed (since all private keys can be rederived from the seed).
+ * However, if many addresses were used since the backup, then the wallet may
+ * not know how far ahead in the HD chain to look for its addresses. The
+ * keypool is used to implement a 'gap limit'. The keypool maintains a set of
+ * keys (by default 1000) ahead of the last used key and scans for the
+ * addresses of those keys. This avoids the risk of not seeing transactions
+ * involving the wallet's addresses, or of re-using the same address.
+ *
+ * The HD-split wallet feature added a second keypool (commit: 02592f4c). There
+ * is an external keypool (for addresses to hand out) and an internal keypool
+ * (for change addresses).
+ *
+ * Keypool keys are stored in the wallet/keystore's keymap. The keypool data is
+ * stored as sets of indexes in the wallet (setInternalKeyPool,
+ * setExternalKeyPool and set_pre_split_keypool), and a map from the key to the
+ * index (m_pool_key_to_index). The CKeyPool object is used to
+ * serialize/deserialize the pool data to/from the database.
+ */
class CKeyPool
{
public:
+ //! The time at which the key was generated. Set in AddKeypoolPubKeyWithDB
int64_t nTime;
+ //! The public key
CPubKey vchPubKey;
- bool fInternal; // for change outputs
- bool m_pre_split; // For keys generated before keypool split upgrade
+ //! Whether this keypool entry is in the internal keypool (for change outputs)
+ bool fInternal;
+ //! Whether this key was generated for a keypool before the wallet was upgraded to HD-split
+ bool m_pre_split;
CKeyPool();
CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
@@ -187,6 +254,57 @@ public:
}
};
+/** A wrapper to reserve a key from a wallet keypool
+ *
+ * CReserveKey is used to reserve a key from the keypool. It is passed around
+ * during the CreateTransaction/CommitTransaction procedure.
+ *
+ * Instantiating a CReserveKey does not reserve a keypool key. To do so,
+ * GetReservedKey() needs to be called on the object. Once a key has been
+ * reserved, call KeepKey() on the CReserveKey object to make sure it is not
+ * returned to the keypool. Call ReturnKey() to return the key to the keypool
+ * so it can be re-used (for example, if the key was used in a new transaction
+ * and that transaction was not completed and needed to be aborted).
+ *
+ * If a key is reserved and KeepKey() is not called, then the key will be
+ * returned to the keypool when the CReserveObject goes out of scope.
+ */
+class CReserveKey
+{
+protected:
+ //! The wallet to reserve the keypool key from
+ CWallet* pwallet;
+ //! The index of the key in the keypool
+ int64_t nIndex{-1};
+ //! The public key
+ CPubKey vchPubKey;
+ //! Whether this is from the internal (change output) keypool
+ bool fInternal{false};
+
+public:
+ //! Construct a CReserveKey object. This does NOT reserve a key from the keypool yet
+ explicit CReserveKey(CWallet* pwalletIn)
+ {
+ pwallet = pwalletIn;
+ }
+
+ CReserveKey(const CReserveKey&) = delete;
+ CReserveKey& operator=(const CReserveKey&) = delete;
+
+ //! Destructor. If a key has been reserved and not KeepKey'ed, it will be returned to the keypool
+ ~CReserveKey()
+ {
+ ReturnKey();
+ }
+
+ //! Reserve a key from the keypool
+ bool GetReservedKey(CPubKey &pubkey, bool internal = false);
+ //! Return a key to the keypool
+ void ReturnKey();
+ //! Keep the key. Do not return it to the keypool when this object goes out of scope
+ void KeepKey();
+};
+
/** Address book data */
class CAddressBookData
{
@@ -677,6 +795,26 @@ private:
* nTimeFirstKey more intelligently for more efficient rescans.
*/
bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
+ /** Add a KeyOriginInfo to the wallet */
+ bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info);
+
+ //! Adds a key to the store, and saves it to disk.
+ bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
+ //! Adds a watch-only address to the store, and saves it to disk.
+ bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
+ void AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch);
+
+ bool SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose);
+
+ //! Adds a script to the store and saves it to disk
+ bool AddCScriptWithDB(WalletBatch& batch, const CScript& script);
+
+ //! Unsets a wallet flag and saves it to disk
+ void UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag);
/** Interface for accessing chain state. */
interfaces::Chain* m_chain;
@@ -735,8 +873,6 @@ public:
// Map from Script ID to key metadata (for watch-only keys).
std::map<CScriptID, CKeyMetadata> m_script_metadata GUARDED_BY(cs_wallet);
- bool WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, bool overwrite);
-
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID = 0;
@@ -808,6 +944,12 @@ public:
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
bool IsSpent(interfaces::Chain::Lock& locked_chain, const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
+ // Whether this or any UTXO with the same CTxDestination has been spent.
+ bool IsUsedDestination(const CTxDestination& dst) const;
+ bool IsUsedDestination(const uint256& hash, unsigned int n) const;
+ void SetUsedDestinationState(const uint256& hash, unsigned int n, bool used);
+
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const;
bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -832,7 +974,6 @@ public:
CPubKey GenerateNewKey(WalletBatch& batch, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
//! Load metadata (used by LoadWallet)
@@ -875,7 +1016,7 @@ public:
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
- void GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CTxDestination, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CKeyID, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
unsigned int ComputeTimeSmart(const CWalletTx& wtx) const;
/**
@@ -921,7 +1062,7 @@ public:
CAmount m_watchonly_untrusted_pending{0};
CAmount m_watchonly_immature{0};
};
- Balance GetBalance(int min_depth = 0) const;
+ 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);
@@ -951,6 +1092,11 @@ public:
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig = false) const;
bool DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig = false) const;
+ bool ImportScripts(const std::set<CScript> scripts) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET};
bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE};
@@ -972,8 +1118,6 @@ public:
bool NewKeyPool();
size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool TopUpKeyPool(unsigned int kpSize = 0);
- void AddKeypoolPubkey(const CPubKey& pubkey, const bool internal);
- void AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch);
/**
* Reserves a key from the keypool and sets nIndex to its index
@@ -1170,7 +1314,7 @@ public:
void UnsetWalletFlag(uint64_t flag);
/** check if a certain wallet flag is set */
- bool IsWalletFlagSet(uint64_t flag);
+ bool IsWalletFlagSet(uint64_t flag) const;
/** overwrite all flags by the given uint64_t
returns false if unknown, non-tolerable flags are present */
@@ -1190,9 +1334,6 @@ public:
/** Implement lookup of key origin information through wallet key metadata. */
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
-
- /** Add a KeyOriginInfo to the wallet */
- bool AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info);
};
/**
@@ -1201,34 +1342,6 @@ public:
*/
void MaybeResendWalletTxs();
-/** A key allocated from the key pool. */
-class CReserveKey
-{
-protected:
- CWallet* pwallet;
- int64_t nIndex{-1};
- CPubKey vchPubKey;
- bool fInternal{false};
-
-public:
- explicit CReserveKey(CWallet* pwalletIn)
- {
- pwallet = pwalletIn;
- }
-
- CReserveKey(const CReserveKey&) = delete;
- CReserveKey& operator=(const CReserveKey&) = delete;
-
- ~CReserveKey()
- {
- ReturnKey();
- }
-
- void ReturnKey();
- bool GetReservedKey(CPubKey &pubkey, bool internal = false);
- void KeepKey();
-};
-
/** RAII object to check and reserve a wallet rescan */
class WalletRescanReserver
{
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 3122cd6fa4..ece97e2a75 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 0532a55ff5..d4a3bba97a 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -143,9 +143,11 @@ public:
};
/** Access to the wallet database.
- * This represents a single transaction at the
- * database. It will be committed when the object goes out of scope.
- * Optionally (on by default) it will flush to disk as well.
+ * Opens the database and provides read and write access to it. Each read and write is its own transaction.
+ * Multiple operation transactions can be started using TxnBegin() and committed using TxnCommit()
+ * Otherwise the transaction will be committed when the object goes out of scope.
+ * Optionally (on by default) it will flush to disk on close.
+ * Every 1000 writes will automatically trigger a flush to disk.
*/
class WalletBatch
{
@@ -157,6 +159,9 @@ private:
return false;
}
m_database.IncrementUpdateCounter();
+ if (m_database.nUpdateCounter % 1000 == 0) {
+ m_batch.Flush();
+ }
return true;
}
@@ -167,6 +172,9 @@ private:
return false;
}
m_database.IncrementUpdateCounter();
+ if (m_database.nUpdateCounter % 1000 == 0) {
+ m_batch.Flush();
+ }
return true;
}
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 1ff1e8b840..0843194511 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -1,8 +1,7 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <base58.h>
#include <fs.h>
#include <util/system.h>
#include <wallet/wallet.h>
@@ -23,7 +22,7 @@ static void WalletToolReleaseWallet(CWallet* wallet)
static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path)
{
if (fs::exists(path)) {
- fprintf(stderr, "Error: File exists already\n");
+ tfm::format(std::cerr, "Error: File exists already\n");
return nullptr;
}
// dummy chain interface
@@ -31,7 +30,7 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
bool first_run = true;
DBErrors load_wallet_ret = wallet_instance->LoadWallet(first_run);
if (load_wallet_ret != DBErrors::LOAD_OK) {
- fprintf(stderr, "Error creating %s", name.c_str());
+ tfm::format(std::cerr, "Error creating %s", name.c_str());
return nullptr;
}
@@ -41,7 +40,7 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
CPubKey seed = wallet_instance->GenerateNewSeed();
wallet_instance->SetHDSeed(seed);
- fprintf(stdout, "Topping up keypool...\n");
+ tfm::format(std::cout, "Topping up keypool...\n");
wallet_instance->TopUpKeyPool();
return wallet_instance;
}
@@ -49,7 +48,7 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path)
{
if (!fs::exists(path)) {
- fprintf(stderr, "Error: Wallet files does not exist\n");
+ tfm::format(std::cerr, "Error: Wallet files does not exist\n");
return nullptr;
}
@@ -59,29 +58,29 @@ static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::pa
try {
bool first_run;
load_wallet_ret = wallet_instance->LoadWallet(first_run);
- } catch (const std::runtime_error) {
- fprintf(stderr, "Error loading %s. Is wallet being used by another process?\n", name.c_str());
+ } catch (const std::runtime_error&) {
+ tfm::format(std::cerr, "Error loading %s. Is wallet being used by another process?\n", name.c_str());
return nullptr;
}
if (load_wallet_ret != DBErrors::LOAD_OK) {
wallet_instance = nullptr;
if (load_wallet_ret == DBErrors::CORRUPT) {
- fprintf(stderr, "Error loading %s: Wallet corrupted", name.c_str());
+ tfm::format(std::cerr, "Error loading %s: Wallet corrupted", name.c_str());
return nullptr;
} else if (load_wallet_ret == DBErrors::NONCRITICAL_ERROR) {
- fprintf(stderr, "Error reading %s! All keys read correctly, but transaction data"
+ tfm::format(std::cerr, "Error reading %s! All keys read correctly, but transaction data"
" or address book entries might be missing or incorrect.",
name.c_str());
} else if (load_wallet_ret == DBErrors::TOO_NEW) {
- fprintf(stderr, "Error loading %s: Wallet requires newer version of %s",
+ tfm::format(std::cerr, "Error loading %s: Wallet requires newer version of %s",
name.c_str(), PACKAGE_NAME);
return nullptr;
} else if (load_wallet_ret == DBErrors::NEED_REWRITE) {
- fprintf(stderr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME);
+ tfm::format(std::cerr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME);
return nullptr;
} else {
- fprintf(stderr, "Error loading %s", name.c_str());
+ tfm::format(std::cerr, "Error loading %s", name.c_str());
return nullptr;
}
}
@@ -93,12 +92,12 @@ static void WalletShowInfo(CWallet* wallet_instance)
{
LOCK(wallet_instance->cs_wallet);
- fprintf(stdout, "Wallet info\n===========\n");
- fprintf(stdout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no");
- fprintf(stdout, "HD (hd seed available): %s\n", wallet_instance->GetHDChain().seed_id.IsNull() ? "no" : "yes");
- fprintf(stdout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());
- fprintf(stdout, "Transactions: %zu\n", wallet_instance->mapWallet.size());
- fprintf(stdout, "Address Book: %zu\n", wallet_instance->mapAddressBook.size());
+ tfm::format(std::cout, "Wallet info\n===========\n");
+ tfm::format(std::cout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no");
+ tfm::format(std::cout, "HD (hd seed available): %s\n", wallet_instance->GetHDChain().seed_id.IsNull() ? "no" : "yes");
+ tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());
+ tfm::format(std::cout, "Transactions: %zu\n", wallet_instance->mapWallet.size());
+ tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->mapAddressBook.size());
}
bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
@@ -113,12 +112,12 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
}
} else if (command == "info") {
if (!fs::exists(path)) {
- fprintf(stderr, "Error: no wallet file at %s\n", name.c_str());
+ tfm::format(std::cerr, "Error: no wallet file at %s\n", name.c_str());
return false;
}
std::string error;
if (!WalletBatch::VerifyEnvironment(path, error)) {
- fprintf(stderr, "Error loading %s. Is wallet being used by other process?\n", name.c_str());
+ tfm::format(std::cerr, "Error loading %s. Is wallet being used by other process?\n", name.c_str());
return false;
}
std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
@@ -126,7 +125,7 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
WalletShowInfo(wallet_instance.get());
wallet_instance->Flush(true);
} else {
- fprintf(stderr, "Invalid command: %s\n", command.c_str());
+ tfm::format(std::cerr, "Invalid command: %s\n", command.c_str());
return false;
}
diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h
index 5b06fd1792..7ee2505631 100644
--- a/src/wallet/wallettool.h
+++ b/src/wallet/wallettool.h
@@ -1,11 +1,11 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_WALLET_WALLETTOOL_H
#define BITCOIN_WALLET_WALLETTOOL_H
-#include <script/ismine.h>
+#include <wallet/ismine.h>
#include <wallet/wallet.h>
namespace WalletTool {
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
index b227a15556..04c2407a89 100644
--- a/src/wallet/walletutil.cpp
+++ b/src/wallet/walletutil.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Copyright (c) 2017-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/warnings.cpp b/src/warnings.cpp
index 1c6ba13f60..5542412a7f 100644
--- a/src/warnings.cpp
+++ b/src/warnings.cpp
@@ -4,14 +4,13 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <sync.h>
-#include <clientversion.h>
#include <util/system.h>
#include <warnings.h>
-CCriticalSection cs_warnings;
-std::string strMiscWarning GUARDED_BY(cs_warnings);
-bool fLargeWorkForkFound GUARDED_BY(cs_warnings) = false;
-bool fLargeWorkInvalidChainFound GUARDED_BY(cs_warnings) = false;
+static RecursiveMutex cs_warnings;
+static std::string strMiscWarning GUARDED_BY(cs_warnings);
+static bool fLargeWorkForkFound GUARDED_BY(cs_warnings) = false;
+static bool fLargeWorkInvalidChainFound GUARDED_BY(cs_warnings) = false;
void SetMiscWarning(const std::string& strWarning)
{
diff --git a/src/zmq/zmqabstractnotifier.cpp b/src/zmq/zmqabstractnotifier.cpp
index 6a9661e3e8..a5f3be8f5b 100644
--- a/src/zmq/zmqabstractnotifier.cpp
+++ b/src/zmq/zmqabstractnotifier.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <zmq/zmqabstractnotifier.h>
-#include <util/system.h>
const int CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM;
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index 6826cf62d6..de59b71b8f 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -7,7 +7,6 @@
#include <version.h>
#include <validation.h>
-#include <streams.h>
#include <util/system.h>
void zmqError(const char *str)