diff options
-rw-r--r-- | src/init.cpp | 8 | ||||
-rw-r--r--[-rwxr-xr-x] | src/net_processing.cpp | 0 | ||||
-rw-r--r-- | src/qt/clientmodel.cpp | 2 | ||||
-rw-r--r-- | src/script/descriptor.cpp | 189 | ||||
-rw-r--r-- | src/test/dbwrapper_tests.cpp | 18 | ||||
-rw-r--r-- | src/test/denialofservice_tests.cpp | 4 | ||||
-rw-r--r-- | src/test/descriptor_tests.cpp | 12 | ||||
-rw-r--r-- | src/test/flatfile_tests.cpp | 8 | ||||
-rw-r--r-- | src/test/fs_tests.cpp | 2 | ||||
-rw-r--r-- | src/test/getarg_tests.cpp | 120 | ||||
-rw-r--r-- | src/test/settings_tests.cpp | 2 | ||||
-rw-r--r-- | src/test/util/setup_common.cpp | 8 | ||||
-rw-r--r-- | src/test/util/setup_common.h | 2 | ||||
-rw-r--r-- | src/test/util_tests.cpp | 43 | ||||
-rw-r--r-- | src/test/versionbits_tests.cpp | 64 | ||||
-rw-r--r-- | src/util/system.cpp | 164 | ||||
-rw-r--r-- | src/util/system.h | 28 | ||||
-rw-r--r-- | src/validation.cpp | 6 |
18 files changed, 359 insertions, 321 deletions
diff --git a/src/init.cpp b/src/init.cpp index 07e882c9df..741e70f748 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -637,7 +637,7 @@ static void CleanupBlockRevFiles() // Remove the rev files immediately and insert the blk file paths into an // ordered map keyed by block file index. LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n"); - fs::path blocksdir = GetBlocksDir(); + fs::path blocksdir = gArgs.GetBlocksDirPath(); for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) { if (fs::is_regular_file(*it) && it->path().filename().string().length() == 12 && @@ -919,7 +919,7 @@ bool AppInitParameterInteraction(const ArgsManager& args) InitWarning(warnings); } - if (!fs::is_directory(GetBlocksDir())) { + if (!fs::is_directory(gArgs.GetBlocksDirPath())) { return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), args.GetArg("-blocksdir", ""))); } @@ -1759,8 +1759,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) InitError(strprintf(_("Error: Disk space is low for %s"), GetDataDir())); return false; } - if (!CheckDiskSpace(GetBlocksDir())) { - InitError(strprintf(_("Error: Disk space is low for %s"), GetBlocksDir())); + if (!CheckDiskSpace(gArgs.GetBlocksDirPath())) { + InitError(strprintf(_("Error: Disk space is low for %s"), gArgs.GetBlocksDirPath())); return false; } diff --git a/src/net_processing.cpp b/src/net_processing.cpp index b3927b5ec5..b3927b5ec5 100755..100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index b244bc94f2..f2c555de52 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -216,7 +216,7 @@ QString ClientModel::dataDir() const QString ClientModel::blocksDir() const { - return GUIUtil::boostPathToQString(GetBlocksDir()); + return GUIUtil::boostPathToQString(gArgs.GetBlocksDirPath()); } void ClientModel::updateBanlist() diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 30399dca51..f1433553bc 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -481,34 +481,35 @@ class DescriptorImpl : public Descriptor const std::string m_name; protected: - //! The sub-descriptor argument (nullptr for everything but SH and WSH). + //! The sub-descriptor arguments (empty for everything but SH and WSH). //! In doc/descriptors.m this is referred to as SCRIPT expressions sh(SCRIPT) //! and wsh(SCRIPT), and distinct from KEY expressions and ADDR expressions. - const std::unique_ptr<DescriptorImpl> m_subdescriptor_arg; + //! Subdescriptors can only ever generate a single script. + const std::vector<std::unique_ptr<DescriptorImpl>> m_subdescriptor_args; //! Return a serialization of anything except pubkey and script arguments, to be prepended to those. virtual std::string ToStringExtra() const { return ""; } /** A helper function to construct the scripts for this descriptor. * - * This function is invoked once for every CScript produced by evaluating - * m_subdescriptor_arg, or just once in case m_subdescriptor_arg is nullptr. - + * This function is invoked once by ExpandHelper. + * * @param pubkeys The evaluations of the m_pubkey_args field. - * @param script The evaluation of m_subdescriptor_arg (or nullptr when m_subdescriptor_arg is nullptr). + * @param scripts The evaluations of m_subdescriptor_args (one for each m_subdescriptor_args element). * @param out A FlatSigningProvider to put scripts or public keys in that are necessary to the solver. - * The script arguments to this function are automatically added, as is the origin info of the provided pubkeys. + * The origin info of the provided pubkeys is automatically added. * @return A vector with scriptPubKeys for this descriptor. */ - virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, const CScript* script, FlatSigningProvider& out) const = 0; + virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, Span<const CScript> scripts, FlatSigningProvider& out) const = 0; public: - DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_arg(std::move(script)) {} + DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args() {} + DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args(Vector(std::move(script))) {} bool IsSolvable() const override { - if (m_subdescriptor_arg) { - if (!m_subdescriptor_arg->IsSolvable()) return false; + for (const auto& arg : m_subdescriptor_args) { + if (!arg->IsSolvable()) return false; } return true; } @@ -518,12 +519,24 @@ public: for (const auto& pubkey : m_pubkey_args) { if (pubkey->IsRange()) return true; } - if (m_subdescriptor_arg) { - if (m_subdescriptor_arg->IsRange()) return true; + for (const auto& arg : m_subdescriptor_args) { + if (arg->IsRange()) return true; } return false; } + virtual bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, bool priv, bool normalized) const + { + size_t pos = 0; + for (const auto& scriptarg : m_subdescriptor_args) { + if (pos++) ret += ","; + std::string tmp; + if (!scriptarg->ToStringHelper(arg, tmp, priv, normalized)) return false; + ret += std::move(tmp); + } + return true; + } + bool ToStringHelper(const SigningProvider* arg, std::string& out, bool priv, bool normalized) const { std::string extra = ToStringExtra(); @@ -541,13 +554,10 @@ public: } ret += std::move(tmp); } - if (m_subdescriptor_arg) { - if (pos++) ret += ","; - std::string tmp; - if (!m_subdescriptor_arg->ToStringHelper(arg, tmp, priv, normalized)) return false; - ret += std::move(tmp); - } - out = std::move(ret) + ")"; + std::string subscript; + if (!ToStringSubScriptHelper(arg, subscript, priv, normalized)) return false; + if (pos && subscript.size()) ret += ','; + out = std::move(ret) + std::move(subscript) + ")"; return true; } @@ -577,17 +587,20 @@ public: std::vector<std::pair<CPubKey, KeyOriginInfo>> entries; entries.reserve(m_pubkey_args.size()); - // Construct temporary data in `entries` and `subscripts`, to avoid producing output in case of failure. + // Construct temporary data in `entries`, `subscripts`, and `subprovider` to avoid producing output in case of failure. for (const auto& p : m_pubkey_args) { entries.emplace_back(); if (!p->GetPubKey(pos, arg, entries.back().first, entries.back().second, read_cache, write_cache)) return false; } std::vector<CScript> subscripts; - if (m_subdescriptor_arg) { - FlatSigningProvider subprovider; - if (!m_subdescriptor_arg->ExpandHelper(pos, arg, read_cache, subscripts, subprovider, write_cache)) return false; - out = Merge(out, subprovider); + FlatSigningProvider subprovider; + for (const auto& subarg : m_subdescriptor_args) { + std::vector<CScript> outscripts; + if (!subarg->ExpandHelper(pos, arg, read_cache, outscripts, subprovider, write_cache)) return false; + assert(outscripts.size() == 1); + subscripts.emplace_back(std::move(outscripts[0])); } + out = Merge(std::move(out), std::move(subprovider)); std::vector<CPubKey> pubkeys; pubkeys.reserve(entries.size()); @@ -595,17 +608,8 @@ public: pubkeys.push_back(entry.first); out.origins.emplace(entry.first.GetID(), std::make_pair<CPubKey, KeyOriginInfo>(CPubKey(entry.first), std::move(entry.second))); } - if (m_subdescriptor_arg) { - for (const auto& subscript : subscripts) { - out.scripts.emplace(CScriptID(subscript), subscript); - std::vector<CScript> addscripts = MakeScripts(pubkeys, &subscript, out); - for (auto& addscript : addscripts) { - output_scripts.push_back(std::move(addscript)); - } - } - } else { - output_scripts = MakeScripts(pubkeys, nullptr, out); - } + + output_scripts = MakeScripts(pubkeys, MakeSpan(subscripts), out); return true; } @@ -626,10 +630,8 @@ public: if (!p->GetPrivKey(pos, provider, key)) continue; out.keys.emplace(key.GetPubKey().GetID(), key); } - if (m_subdescriptor_arg) { - FlatSigningProvider subprovider; - m_subdescriptor_arg->ExpandPrivate(pos, provider, subprovider); - out = Merge(out, subprovider); + for (const auto& arg : m_subdescriptor_args) { + arg->ExpandPrivate(pos, provider, out); } } @@ -642,9 +644,9 @@ class AddressDescriptor final : public DescriptorImpl const CTxDestination m_destination; protected: std::string ToStringExtra() const override { return EncodeDestination(m_destination); } - std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(m_destination)); } + std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript>, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(m_destination)); } public: - AddressDescriptor(CTxDestination destination) : DescriptorImpl({}, {}, "addr"), m_destination(std::move(destination)) {} + AddressDescriptor(CTxDestination destination) : DescriptorImpl({}, "addr"), m_destination(std::move(destination)) {} bool IsSolvable() const final { return false; } std::optional<OutputType> GetOutputType() const override @@ -668,9 +670,9 @@ class RawDescriptor final : public DescriptorImpl const CScript m_script; protected: std::string ToStringExtra() const override { return HexStr(m_script); } - std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Vector(m_script); } + std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript>, FlatSigningProvider&) const override { return Vector(m_script); } public: - RawDescriptor(CScript script) : DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {} + RawDescriptor(CScript script) : DescriptorImpl({}, "raw"), m_script(std::move(script)) {} bool IsSolvable() const final { return false; } std::optional<OutputType> GetOutputType() const override @@ -694,9 +696,9 @@ public: class PKDescriptor final : public DescriptorImpl { protected: - std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Vector(GetScriptForRawPubKey(keys[0])); } + std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider&) const override { return Vector(GetScriptForRawPubKey(keys[0])); } public: - PKDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "pk") {} + PKDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "pk") {} bool IsSingleType() const final { return true; } }; @@ -704,14 +706,14 @@ public: class PKHDescriptor final : public DescriptorImpl { protected: - std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override + std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider& out) const override { CKeyID id = keys[0].GetID(); out.pubkeys.emplace(id, keys[0]); return Vector(GetScriptForDestination(PKHash(id))); } public: - PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "pkh") {} + PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "pkh") {} std::optional<OutputType> GetOutputType() const override { return OutputType::LEGACY; } bool IsSingleType() const final { return true; } }; @@ -720,14 +722,14 @@ public: class WPKHDescriptor final : public DescriptorImpl { protected: - std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override + std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider& out) const override { CKeyID id = keys[0].GetID(); out.pubkeys.emplace(id, keys[0]); return Vector(GetScriptForDestination(WitnessV0KeyHash(id))); } public: - WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "wpkh") {} + WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "wpkh") {} std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; } bool IsSingleType() const final { return true; } }; @@ -736,7 +738,7 @@ public: class ComboDescriptor final : public DescriptorImpl { protected: - std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override + std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider& out) const override { std::vector<CScript> ret; CKeyID id = keys[0].GetID(); @@ -752,7 +754,7 @@ protected: return ret; } public: - ComboDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "combo") {} + ComboDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "combo") {} bool IsSingleType() const final { return false; } }; @@ -763,7 +765,7 @@ class MultisigDescriptor final : public DescriptorImpl const bool m_sorted; protected: std::string ToStringExtra() const override { return strprintf("%i", m_threshold); } - std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { + std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider&) const override { if (m_sorted) { std::vector<CPubKey> sorted_keys(keys); std::sort(sorted_keys.begin(), sorted_keys.end()); @@ -772,7 +774,7 @@ protected: return Vector(GetScriptForMultisig(m_threshold, keys)); } public: - MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), {}, sorted ? "sortedmulti" : "multi"), m_threshold(threshold), m_sorted(sorted) {} + MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), sorted ? "sortedmulti" : "multi"), m_threshold(threshold), m_sorted(sorted) {} bool IsSingleType() const final { return true; } }; @@ -780,14 +782,19 @@ public: class SHDescriptor final : public DescriptorImpl { protected: - std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(ScriptHash(*script))); } + std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript> scripts, FlatSigningProvider& out) const override + { + auto ret = Vector(GetScriptForDestination(ScriptHash(scripts[0]))); + if (ret.size()) out.scripts.emplace(CScriptID(scripts[0]), scripts[0]); + return ret; + } public: SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {} std::optional<OutputType> GetOutputType() const override { - assert(m_subdescriptor_arg); - if (m_subdescriptor_arg->GetOutputType() == OutputType::BECH32) return OutputType::P2SH_SEGWIT; + assert(m_subdescriptor_args.size() == 1); + if (m_subdescriptor_args[0]->GetOutputType() == OutputType::BECH32) return OutputType::P2SH_SEGWIT; return OutputType::LEGACY; } bool IsSingleType() const final { return true; } @@ -797,7 +804,12 @@ public: class WSHDescriptor final : public DescriptorImpl { protected: - std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(WitnessV0ScriptHash(*script))); } + std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript> scripts, FlatSigningProvider& out) const override + { + auto ret = Vector(GetScriptForDestination(WitnessV0ScriptHash(scripts[0]))); + if (ret.size()) out.scripts.emplace(CScriptID(scripts[0]), scripts[0]); + return ret; + } public: WSHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "wsh") {} std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; } @@ -809,9 +821,10 @@ public: //////////////////////////////////////////////////////////////////////////// enum class ParseScriptContext { - TOP, - P2SH, - P2WSH, + TOP, //!< Top-level context (script goes directly in scriptPubKey) + P2SH, //!< Inside sh() (script becomes P2SH redeemScript) + P2WPKH, //!< Inside wpkh() (no script, pubkey only) + P2WSH, //!< Inside wsh() (script becomes v0 witness script) }; /** Parse a key path, being passed a split list of elements (the first element is ignored). */ @@ -838,10 +851,11 @@ enum class ParseScriptContext { } /** Parse a public key that excludes origin information. */ -std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out, std::string& error) +std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error) { using namespace spanparsing; + bool permit_uncompressed = ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH; auto split = Split(sp, '/'); std::string str(split[0].begin(), split[0].end()); if (str.size() == 0) { @@ -899,7 +913,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S } /** Parse a public key including origin information (if enabled). */ -std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out, std::string& error) +std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error) { using namespace spanparsing; @@ -908,7 +922,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<c error = "Multiple ']' characters found for a single pubkey"; return nullptr; } - if (origin_split.size() == 1) return ParsePubkeyInner(key_exp_index, origin_split[0], permit_uncompressed, out, error); + if (origin_split.size() == 1) return ParsePubkeyInner(key_exp_index, origin_split[0], ctx, out, error); if (origin_split[0].empty() || origin_split[0][0] != '[') { error = strprintf("Key origin start '[ character expected but not found, got '%c' instead", origin_split[0].empty() ? /** empty, implies split char */ ']' : origin_split[0][0]); @@ -930,34 +944,37 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<c assert(fpr_bytes.size() == 4); std::copy(fpr_bytes.begin(), fpr_bytes.end(), info.fingerprint); if (!ParseKeyPath(slash_split, info.path, error)) return nullptr; - auto provider = ParsePubkeyInner(key_exp_index, origin_split[1], permit_uncompressed, out, error); + auto provider = ParsePubkeyInner(key_exp_index, origin_split[1], ctx, out, error); if (!provider) return nullptr; return std::make_unique<OriginPubkeyProvider>(key_exp_index, std::move(info), std::move(provider)); } /** Parse a script in a particular context. */ -std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error) +std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error) { using namespace spanparsing; auto expr = Expr(sp); bool sorted_multi = false; if (Func("pk", expr)) { - auto pubkey = ParsePubkey(key_exp_index, expr, ctx != ParseScriptContext::P2WSH, out, error); + auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error); if (!pubkey) return nullptr; + ++key_exp_index; return std::make_unique<PKDescriptor>(std::move(pubkey)); } if (Func("pkh", expr)) { - auto pubkey = ParsePubkey(key_exp_index, expr, ctx != ParseScriptContext::P2WSH, out, error); + auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error); if (!pubkey) return nullptr; + ++key_exp_index; return std::make_unique<PKHDescriptor>(std::move(pubkey)); } if (ctx == ParseScriptContext::TOP && Func("combo", expr)) { - auto pubkey = ParsePubkey(key_exp_index, expr, true, out, error); + auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error); if (!pubkey) return nullptr; + ++key_exp_index; return std::make_unique<ComboDescriptor>(std::move(pubkey)); - } else if (ctx != ParseScriptContext::TOP && Func("combo", expr)) { - error = "Cannot have combo in non-top level"; + } else if (Func("combo", expr)) { + error = "Can only have combo() at top level"; return nullptr; } if ((sorted_multi = Func("sortedmulti", expr)) || Func("multi", expr)) { @@ -975,7 +992,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c return nullptr; } auto arg = Expr(expr); - auto pk = ParsePubkey(key_exp_index, arg, ctx != ParseScriptContext::P2WSH, out, error); + auto pk = ParsePubkey(key_exp_index, arg, ctx, out, error); if (!pk) return nullptr; script_size += pk->GetSize() + 1; providers.emplace_back(std::move(pk)); @@ -1005,28 +1022,29 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c } return std::make_unique<MultisigDescriptor>(thres, std::move(providers), sorted_multi); } - if (ctx != ParseScriptContext::P2WSH && Func("wpkh", expr)) { - auto pubkey = ParsePubkey(key_exp_index, expr, false, out, error); + if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH) && Func("wpkh", expr)) { + auto pubkey = ParsePubkey(key_exp_index, expr, ParseScriptContext::P2WPKH, out, error); if (!pubkey) return nullptr; + key_exp_index++; return std::make_unique<WPKHDescriptor>(std::move(pubkey)); - } else if (ctx == ParseScriptContext::P2WSH && Func("wpkh", expr)) { - error = "Cannot have wpkh within wsh"; + } else if (Func("wpkh", expr)) { + error = "Can only have wpkh() at top level or inside sh()"; return nullptr; } if (ctx == ParseScriptContext::TOP && Func("sh", expr)) { auto desc = ParseScript(key_exp_index, expr, ParseScriptContext::P2SH, out, error); if (!desc || expr.size()) return nullptr; return std::make_unique<SHDescriptor>(std::move(desc)); - } else if (ctx != ParseScriptContext::TOP && Func("sh", expr)) { - error = "Cannot have sh in non-top level"; + } else if (Func("sh", expr)) { + error = "Can only have sh() at top level"; return nullptr; } - if (ctx != ParseScriptContext::P2WSH && Func("wsh", expr)) { + if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH) && Func("wsh", expr)) { auto desc = ParseScript(key_exp_index, expr, ParseScriptContext::P2WSH, out, error); if (!desc || expr.size()) return nullptr; return std::make_unique<WSHDescriptor>(std::move(desc)); - } else if (ctx == ParseScriptContext::P2WSH && Func("wsh", expr)) { - error = "Cannot have wsh within wsh"; + } else if (Func("wsh", expr)) { + error = "Can only have wsh() at top level or inside sh()"; return nullptr; } if (ctx == ParseScriptContext::TOP && Func("addr", expr)) { @@ -1036,6 +1054,9 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c return nullptr; } return std::make_unique<AddressDescriptor>(std::move(dest)); + } else if (Func("addr", expr)) { + error = "Can only have addr() at top level"; + return nullptr; } if (ctx == ParseScriptContext::TOP && Func("raw", expr)) { std::string str(expr.begin(), expr.end()); @@ -1045,6 +1066,9 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c } auto bytes = ParseHex(str); return std::make_unique<RawDescriptor>(CScript(bytes.begin(), bytes.end())); + } else if (Func("raw", expr)) { + error = "Can only have raw() at top level"; + return nullptr; } if (ctx == ParseScriptContext::P2SH) { error = "A function is needed within P2SH"; @@ -1174,7 +1198,8 @@ std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProv { Span<const char> sp{descriptor}; if (!CheckChecksum(sp, require_checksum, error)) return nullptr; - auto ret = ParseScript(0, sp, ParseScriptContext::TOP, out, error); + uint32_t key_exp_index = 0; + auto ret = ParseScript(key_exp_index, sp, ParseScriptContext::TOP, out, error); if (sp.size() == 0 && ret) return std::unique_ptr<Descriptor>(std::move(ret)); return nullptr; } diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index a6080ad3dd..b5f3bb2fa4 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -26,7 +26,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper) { // Perform tests both obfuscated and non-obfuscated. for (const bool obfuscate : {false, true}) { - fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_obfuscate_true" : "dbwrapper_obfuscate_false"); + fs::path ph = m_args.GetDataDirPath() / (obfuscate ? "dbwrapper_obfuscate_true" : "dbwrapper_obfuscate_false"); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); char key = 'k'; uint256 in = InsecureRand256(); @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_basic_data) { // Perform tests both obfuscated and non-obfuscated. for (bool obfuscate : {false, true}) { - fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_1_obfuscate_true" : "dbwrapper_1_obfuscate_false"); + fs::path ph = m_args.GetDataDirPath() / (obfuscate ? "dbwrapper_1_obfuscate_true" : "dbwrapper_1_obfuscate_false"); CDBWrapper dbw(ph, (1 << 20), false, true, obfuscate); uint256 res; @@ -126,7 +126,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch) { // Perform tests both obfuscated and non-obfuscated. for (const bool obfuscate : {false, true}) { - fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_batch_obfuscate_true" : "dbwrapper_batch_obfuscate_false"); + fs::path ph = m_args.GetDataDirPath() / (obfuscate ? "dbwrapper_batch_obfuscate_true" : "dbwrapper_batch_obfuscate_false"); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); char key = 'i'; @@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator) { // Perform tests both obfuscated and non-obfuscated. for (const bool obfuscate : {false, true}) { - fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_iterator_obfuscate_true" : "dbwrapper_iterator_obfuscate_false"); + fs::path ph = m_args.GetDataDirPath() / (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 @@ -202,7 +202,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 = GetDataDir() / "existing_data_no_obfuscate"; + fs::path ph = m_args.GetDataDirPath() / "existing_data_no_obfuscate"; create_directories(ph); // Set up a non-obfuscated wrapper to write some initial data. @@ -243,7 +243,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 = GetDataDir() / "existing_data_reindex"; + fs::path ph = m_args.GetDataDirPath() / "existing_data_reindex"; create_directories(ph); // Set up a non-obfuscated wrapper to write some initial data. @@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex) BOOST_AUTO_TEST_CASE(iterator_ordering) { - fs::path ph = GetDataDir() / "iterator_ordering"; + fs::path ph = m_args.GetDataDirPath() / "iterator_ordering"; CDBWrapper dbw(ph, (1 << 20), true, false, false); for (int x=0x00; x<256; ++x) { uint8_t key = x; @@ -358,7 +358,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering) { char buf[10]; - fs::path ph = GetDataDir() / "iterator_string_ordering"; + fs::path ph = m_args.GetDataDirPath() / "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++) { @@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(unicodepath) // On Windows this test will fail if the directory is created using // the ANSI CreateDirectoryA call and the code page isn't UTF8. // It will succeed if created with CreateDirectoryW. - fs::path ph = GetDataDir() / "test_runner_₿_🏃_20191128_104644"; + fs::path ph = m_args.GetDataDirPath() / "test_runner_₿_🏃_20191128_104644"; CDBWrapper dbw(ph, (1 << 20)); fs::path lockPath = ph / "LOCK"; diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index 599e628685..ddf0e0ca90 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -208,7 +208,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management) BOOST_AUTO_TEST_CASE(peer_discouragement) { const CChainParams& chainparams = Params(); - auto banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); + auto banman = std::make_unique<BanMan>(m_args.GetDataDirPath() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = std::make_unique<CConnmanTest>(0x1337, 0x1337, *m_node.addrman); auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool, false); @@ -302,7 +302,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement) BOOST_AUTO_TEST_CASE(DoS_bantime) { const CChainParams& chainparams = Params(); - auto banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); + auto banman = std::make_unique<BanMan>(m_args.GetDataDirPath() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman); auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool, false); diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index aecf955fee..ea41a03728 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -24,7 +24,7 @@ void CheckUnparsable(const std::string& prv, const std::string& pub, const std:: auto parse_pub = Parse(pub, keys_pub, error); BOOST_CHECK_MESSAGE(!parse_priv, prv); BOOST_CHECK_MESSAGE(!parse_pub, pub); - BOOST_CHECK(error == expected_error); + BOOST_CHECK_EQUAL(error, expected_error); } constexpr int DEFAULT = 0; @@ -355,12 +355,12 @@ BOOST_AUTO_TEST_CASE(descriptor_test) // Check for invalid nesting of structures CheckUnparsable("sh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "A function is needed within P2SH"); // P2SH needs a script, not a key - CheckUnparsable("sh(combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Cannot have combo in non-top level"); // Old must be top level + CheckUnparsable("sh(combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Can only have combo() at top level"); // Old must be top level CheckUnparsable("wsh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wsh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "A function is needed within P2WSH"); // P2WSH needs a script, not a key - CheckUnparsable("wsh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Cannot have wpkh within wsh"); // Cannot embed witness inside witness - CheckUnparsable("wsh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have sh in non-top level"); // Cannot embed P2SH inside P2WSH - CheckUnparsable("sh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have sh in non-top level"); // Cannot embed P2SH inside P2SH - CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have wsh within wsh"); // Cannot embed P2WSH inside P2WSH + CheckUnparsable("wsh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Can only have wpkh() at top level or inside sh()"); // Cannot embed witness inside witness + CheckUnparsable("wsh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Can only have sh() at top level"); // Cannot embed P2SH inside P2WSH + CheckUnparsable("sh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Can only have sh() at top level"); // Cannot embed P2SH inside P2SH + CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Can only have wsh() at top level or inside sh()"); // Cannot embed P2WSH inside P2WSH // Checksums Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", "sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}}); diff --git a/src/test/flatfile_tests.cpp b/src/test/flatfile_tests.cpp index 0c5c19113d..9194ed8130 100644 --- a/src/test/flatfile_tests.cpp +++ b/src/test/flatfile_tests.cpp @@ -14,7 +14,7 @@ BOOST_FIXTURE_TEST_SUITE(flatfile_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(flatfile_filename) { - const auto data_dir = GetDataDir(); + const auto data_dir = m_args.GetDataDirPath(); FlatFilePos pos(456, 789); @@ -27,7 +27,7 @@ BOOST_AUTO_TEST_CASE(flatfile_filename) BOOST_AUTO_TEST_CASE(flatfile_open) { - const auto data_dir = GetDataDir(); + const auto data_dir = m_args.GetDataDirPath(); FlatFileSeq seq(data_dir, "a", 16 * 1024); std::string line1("A purely peer-to-peer version of electronic cash would allow online " @@ -88,7 +88,7 @@ BOOST_AUTO_TEST_CASE(flatfile_open) BOOST_AUTO_TEST_CASE(flatfile_allocate) { - const auto data_dir = GetDataDir(); + const auto data_dir = m_args.GetDataDirPath(); FlatFileSeq seq(data_dir, "a", 100); bool out_of_space; @@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE(flatfile_allocate) BOOST_AUTO_TEST_CASE(flatfile_flush) { - const auto data_dir = GetDataDir(); + const auto data_dir = m_args.GetDataDirPath(); 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 e52cd5230c..452bc06bbb 100644 --- a/src/test/fs_tests.cpp +++ b/src/test/fs_tests.cpp @@ -13,7 +13,7 @@ BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(fsbridge_fstream) { - fs::path tmpfolder = GetDataDir(); + fs::path tmpfolder = m_args.GetDataDirPath(); // tmpfile1 should be the same as tmpfile2 fs::path tmpfile1 = tmpfolder / "fs_tests_₿_🏃"; fs::path tmpfile2 = tmpfolder / "fs_tests_₿_🏃"; diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index 45c9b90ee9..2a217f3455 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -18,7 +18,7 @@ namespace getarg_tests{ protected: void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args); void ResetArgs(const std::string& strArg); - ArgsManager m_args; + ArgsManager m_local_args; }; } @@ -39,14 +39,14 @@ void LocalTestingSetup :: ResetArgs(const std::string& strArg) vecChar.push_back(s.c_str()); std::string error; - BOOST_CHECK(m_args.ParseParameters(vecChar.size(), vecChar.data(), error)); + BOOST_CHECK(m_local_args.ParseParameters(vecChar.size(), vecChar.data(), error)); } void LocalTestingSetup :: SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args) { - m_args.ClearArgs(); + m_local_args.ClearArgs(); for (const auto& arg : args) { - m_args.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS); + m_local_args.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS); } } @@ -55,52 +55,52 @@ BOOST_AUTO_TEST_CASE(boolarg) const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY); SetupArgs({foo}); ResetArgs("-foo"); - BOOST_CHECK(m_args.GetBoolArg("-foo", false)); - BOOST_CHECK(m_args.GetBoolArg("-foo", true)); + BOOST_CHECK(m_local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(m_local_args.GetBoolArg("-foo", true)); - BOOST_CHECK(!m_args.GetBoolArg("-fo", false)); - BOOST_CHECK(m_args.GetBoolArg("-fo", true)); + BOOST_CHECK(!m_local_args.GetBoolArg("-fo", false)); + BOOST_CHECK(m_local_args.GetBoolArg("-fo", true)); - BOOST_CHECK(!m_args.GetBoolArg("-fooo", false)); - BOOST_CHECK(m_args.GetBoolArg("-fooo", true)); + BOOST_CHECK(!m_local_args.GetBoolArg("-fooo", false)); + BOOST_CHECK(m_local_args.GetBoolArg("-fooo", true)); ResetArgs("-foo=0"); - BOOST_CHECK(!m_args.GetBoolArg("-foo", false)); - BOOST_CHECK(!m_args.GetBoolArg("-foo", true)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); ResetArgs("-foo=1"); - BOOST_CHECK(m_args.GetBoolArg("-foo", false)); - BOOST_CHECK(m_args.GetBoolArg("-foo", true)); + BOOST_CHECK(m_local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(m_local_args.GetBoolArg("-foo", true)); // New 0.6 feature: auto-map -nosomething to !-something: ResetArgs("-nofoo"); - BOOST_CHECK(!m_args.GetBoolArg("-foo", false)); - BOOST_CHECK(!m_args.GetBoolArg("-foo", true)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); ResetArgs("-nofoo=1"); - BOOST_CHECK(!m_args.GetBoolArg("-foo", false)); - BOOST_CHECK(!m_args.GetBoolArg("-foo", true)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); ResetArgs("-foo -nofoo"); // -nofoo should win - BOOST_CHECK(!m_args.GetBoolArg("-foo", false)); - BOOST_CHECK(!m_args.GetBoolArg("-foo", true)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); ResetArgs("-foo=1 -nofoo=1"); // -nofoo should win - BOOST_CHECK(!m_args.GetBoolArg("-foo", false)); - BOOST_CHECK(!m_args.GetBoolArg("-foo", true)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); ResetArgs("-foo=0 -nofoo=0"); // -nofoo=0 should win - BOOST_CHECK(m_args.GetBoolArg("-foo", false)); - BOOST_CHECK(m_args.GetBoolArg("-foo", true)); + BOOST_CHECK(m_local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(m_local_args.GetBoolArg("-foo", true)); // New 0.6 feature: treat -- same as -: ResetArgs("--foo=1"); - BOOST_CHECK(m_args.GetBoolArg("-foo", false)); - BOOST_CHECK(m_args.GetBoolArg("-foo", true)); + BOOST_CHECK(m_local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(m_local_args.GetBoolArg("-foo", true)); ResetArgs("--nofoo=1"); - BOOST_CHECK(!m_args.GetBoolArg("-foo", false)); - BOOST_CHECK(!m_args.GetBoolArg("-foo", true)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); } @@ -110,24 +110,24 @@ BOOST_AUTO_TEST_CASE(stringarg) const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY); SetupArgs({foo, bar}); ResetArgs(""); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), ""); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "eleven"); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), "eleven"); ResetArgs("-foo -bar"); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), ""); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), ""); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), ""); ResetArgs("-foo="); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), ""); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), ""); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), ""); ResetArgs("-foo=11"); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "11"); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "11"); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "11"); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), "11"); ResetArgs("-foo=eleven"); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "eleven"); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "eleven"); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "eleven"); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), "eleven"); } @@ -137,20 +137,20 @@ BOOST_AUTO_TEST_CASE(intarg) const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY); SetupArgs({foo, bar}); ResetArgs(""); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 11), 11); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 0), 0); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 11), 11); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), 0); ResetArgs("-foo -bar"); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 11), 0); - BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 11), 0); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 11), 0); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 0); ResetArgs("-foo=11 -bar=12"); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 0), 11); - BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 11), 12); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), 11); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 12); ResetArgs("-foo=NaN -bar=NotANumber"); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 1), 0); - BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 11), 0); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 1), 0); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 0); } BOOST_AUTO_TEST_CASE(doubledash) @@ -159,11 +159,11 @@ BOOST_AUTO_TEST_CASE(doubledash) const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY); SetupArgs({foo, bar}); ResetArgs("--foo"); - BOOST_CHECK_EQUAL(m_args.GetBoolArg("-foo", false), true); + BOOST_CHECK_EQUAL(m_local_args.GetBoolArg("-foo", false), true); ResetArgs("--foo=verbose --bar=1"); - BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "verbose"); - BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 0), 1); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "verbose"); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 0), 1); } BOOST_AUTO_TEST_CASE(boolargno) @@ -172,24 +172,24 @@ BOOST_AUTO_TEST_CASE(boolargno) const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY); SetupArgs({foo, bar}); ResetArgs("-nofoo"); - BOOST_CHECK(!m_args.GetBoolArg("-foo", true)); - BOOST_CHECK(!m_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); ResetArgs("-nofoo=1"); - BOOST_CHECK(!m_args.GetBoolArg("-foo", true)); - BOOST_CHECK(!m_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); ResetArgs("-nofoo=0"); - BOOST_CHECK(m_args.GetBoolArg("-foo", true)); - BOOST_CHECK(m_args.GetBoolArg("-foo", false)); + BOOST_CHECK(m_local_args.GetBoolArg("-foo", true)); + BOOST_CHECK(m_local_args.GetBoolArg("-foo", false)); ResetArgs("-foo --nofoo"); // --nofoo should win - BOOST_CHECK(!m_args.GetBoolArg("-foo", true)); - BOOST_CHECK(!m_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); + BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); ResetArgs("-nofoo -foo"); // foo always wins: - BOOST_CHECK(m_args.GetBoolArg("-foo", true)); - BOOST_CHECK(m_args.GetBoolArg("-foo", false)); + BOOST_CHECK(m_local_args.GetBoolArg("-foo", true)); + BOOST_CHECK(m_local_args.GetBoolArg("-foo", false)); } BOOST_AUTO_TEST_CASE(logargs) @@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(logargs) }); // Log the arguments - m_args.LogArgs(); + m_local_args.LogArgs(); LogInstance().DeleteCallback(print_connection); // Check that what should appear does, and what shouldn't doesn't. diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp index 548fd020a6..f5ae9f86d1 100644 --- a/src/test/settings_tests.cpp +++ b/src/test/settings_tests.cpp @@ -45,7 +45,7 @@ BOOST_FIXTURE_TEST_SUITE(settings_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(ReadWrite) { - fs::path path = GetDataDir() / "settings.json"; + fs::path path = m_args.GetDataDirPath() / "settings.json"; WriteText(path, R"({ "string": "string", diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 2e7f64a444..ffc5115145 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -71,7 +71,8 @@ std::ostream& operator<<(std::ostream& os, const uint256& num) } BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args) - : m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()} + : m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()}, + m_args{} { const std::vector<const char*> arguments = Cat( { @@ -87,8 +88,9 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve extra_args); util::ThreadRename("test"); fs::create_directories(m_path_root); + m_args.ForceSetArg("-datadir", m_path_root.string()); gArgs.ForceSetArg("-datadir", m_path_root.string()); - ClearDatadirCache(); + gArgs.ClearPathCache(); { SetupServerArgs(m_node); std::string error; @@ -191,7 +193,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const } m_node.addrman = std::make_unique<CAddrMan>(); - m_node.banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); + m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirPath() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); m_node.connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman); // Deterministic randomness for tests. m_node.peerman = PeerManager::make(chainparams, *m_node.connman, *m_node.addrman, m_node.banman.get(), *m_node.scheduler, *m_node.chainman, diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index a32f1f3805..b19dd75765 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -8,6 +8,7 @@ #include <chainparamsbase.h> #include <fs.h> #include <key.h> +#include <util/system.h> #include <node/context.h> #include <pubkey.h> #include <random.h> @@ -80,6 +81,7 @@ struct BasicTestingSetup { ~BasicTestingSetup(); const fs::path m_path_root; + ArgsManager m_args; }; /** Testing setup that performs all steps up until right before diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index a1ac9ead69..04b908829b 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -49,24 +49,27 @@ BOOST_FIXTURE_TEST_SUITE(util_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(util_datadir) { - ClearDatadirCache(); - const fs::path dd_norm = GetDataDir(); + // Use local args variable instead of m_args to avoid making assumptions about test setup + ArgsManager args; + args.ForceSetArg("-datadir", m_path_root.string()); - gArgs.ForceSetArg("-datadir", dd_norm.string() + "/"); - ClearDatadirCache(); - BOOST_CHECK_EQUAL(dd_norm, GetDataDir()); + const fs::path dd_norm = args.GetDataDirPath(); - gArgs.ForceSetArg("-datadir", dd_norm.string() + "/."); - ClearDatadirCache(); - BOOST_CHECK_EQUAL(dd_norm, GetDataDir()); + args.ForceSetArg("-datadir", dd_norm.string() + "/"); + args.ClearPathCache(); + BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirPath()); - gArgs.ForceSetArg("-datadir", dd_norm.string() + "/./"); - ClearDatadirCache(); - BOOST_CHECK_EQUAL(dd_norm, GetDataDir()); + args.ForceSetArg("-datadir", dd_norm.string() + "/."); + args.ClearPathCache(); + BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirPath()); - gArgs.ForceSetArg("-datadir", dd_norm.string() + "/.//"); - ClearDatadirCache(); - BOOST_CHECK_EQUAL(dd_norm, GetDataDir()); + args.ForceSetArg("-datadir", dd_norm.string() + "/./"); + args.ClearPathCache(); + BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirPath()); + + args.ForceSetArg("-datadir", dd_norm.string() + "/.//"); + args.ClearPathCache(); + BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirPath()); } BOOST_AUTO_TEST_CASE(util_check) @@ -1143,21 +1146,23 @@ BOOST_AUTO_TEST_CASE(util_ReadWriteSettings) { // Test writing setting. TestArgsManager args1; + args1.ForceSetArg("-datadir", m_path_root.string()); args1.LockSettings([&](util::Settings& settings) { settings.rw_settings["name"] = "value"; }); args1.WriteSettingsFile(); // Test reading setting. TestArgsManager args2; + args2.ForceSetArg("-datadir", m_path_root.string()); args2.ReadSettingsFile(); args2.LockSettings([&](util::Settings& settings) { BOOST_CHECK_EQUAL(settings.rw_settings["name"].get_str(), "value"); }); // Test error logging, and remove previously written setting. { ASSERT_DEBUG_LOG("Failed renaming settings file"); - fs::remove(GetDataDir() / "settings.json"); - fs::create_directory(GetDataDir() / "settings.json"); + fs::remove(args1.GetDataDirPath() / "settings.json"); + fs::create_directory(args1.GetDataDirPath() / "settings.json"); args2.WriteSettingsFile(); - fs::remove(GetDataDir() / "settings.json"); + fs::remove(args1.GetDataDirPath() / "settings.json"); } } @@ -1796,7 +1801,7 @@ static constexpr char ExitCommand = 'X'; BOOST_AUTO_TEST_CASE(test_LockDirectory) { - fs::path dirname = GetDataDir() / "lock_dir"; + fs::path dirname = m_args.GetDataDirPath() / "lock_dir"; const std::string lockname = ".lock"; #ifndef WIN32 // Revert SIGCHLD to default, otherwise boost.test will catch and fail on @@ -1885,7 +1890,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 = GetDataDir(); + fs::path tmpdirname = m_args.GetDataDirPath(); BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true); // Should not be able to write to a non-existent dir. diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index 05838ec19a..304cd8feb0 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -81,11 +81,9 @@ class VersionBitsTester TestNeverActiveConditionChecker checker_never[CHECKERS]; // Test counter (to identify failures) - int num; + int num{1000}; public: - VersionBitsTester() : num(1000) {} - VersionBitsTester& Reset() { // Have each group of tests be counted by the 1000s part, starting at 1000 num = num - (num % 1000) + 1000; @@ -257,39 +255,6 @@ BOOST_AUTO_TEST_CASE(versionbits_test) } } -BOOST_AUTO_TEST_CASE(versionbits_sanity) -{ - // Sanity checks of version bit deployments - const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN); - const Consensus::Params &mainnetParams = chainParams->GetConsensus(); - for (int i=0; i<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { - uint32_t bitmask = VersionBitsMask(mainnetParams, static_cast<Consensus::DeploymentPos>(i)); - // Make sure that no deployment tries to set an invalid bit. - BOOST_CHECK_EQUAL(bitmask & ~(uint32_t)VERSIONBITS_TOP_MASK, bitmask); - - // Check min_activation_height is on a retarget boundary - BOOST_CHECK_EQUAL(mainnetParams.vDeployments[i].min_activation_height % mainnetParams.nMinerConfirmationWindow, 0); - // Check min_activation_height is 0 for ALWAYS_ACTIVE and never active deployments - if (mainnetParams.vDeployments[i].nStartTime == Consensus::BIP9Deployment::ALWAYS_ACTIVE || mainnetParams.vDeployments[i].nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) { - BOOST_CHECK_EQUAL(mainnetParams.vDeployments[i].min_activation_height, 0); - } - - // Verify that the deployment windows of different deployment using the - // same bit are disjoint. - // This test may need modification at such time as a new deployment - // is proposed that reuses the bit of an activated soft fork, before the - // end time of that soft fork. (Alternatively, the end time of that - // activated soft fork could be later changed to be earlier to avoid - // overlap.) - for (int j=i+1; j<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; j++) { - if (VersionBitsMask(mainnetParams, static_cast<Consensus::DeploymentPos>(j)) == bitmask) { - BOOST_CHECK(mainnetParams.vDeployments[j].nStartTime > mainnetParams.vDeployments[i].nTimeout || - mainnetParams.vDeployments[i].nStartTime > mainnetParams.vDeployments[j].nTimeout); - } - } - } -} - /** Check that ComputeBlockVersion will set the appropriate bit correctly */ static void check_computeblockversion(const Consensus::Params& params, Consensus::DeploymentPos dep) { @@ -305,16 +270,25 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus BOOST_CHECK_EQUAL(ComputeBlockVersion(nullptr, params), VERSIONBITS_TOP_BITS); // always/never active deployments shouldn't need to be tested further - if (nStartTime == Consensus::BIP9Deployment::ALWAYS_ACTIVE) return; - if (nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) return; + if (nStartTime == Consensus::BIP9Deployment::ALWAYS_ACTIVE || + nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) + { + BOOST_CHECK_EQUAL(min_activation_height, 0); + return; + } BOOST_REQUIRE(nStartTime < nTimeout); BOOST_REQUIRE(nStartTime >= 0); BOOST_REQUIRE(nTimeout <= std::numeric_limits<uint32_t>::max() || nTimeout == Consensus::BIP9Deployment::NO_TIMEOUT); BOOST_REQUIRE(0 <= bit && bit < 32); + // Make sure that no deployment tries to set an invalid bit. BOOST_REQUIRE(((1 << bit) & VERSIONBITS_TOP_MASK) == 0); BOOST_REQUIRE(min_activation_height >= 0); - BOOST_REQUIRE_EQUAL(min_activation_height % params.nMinerConfirmationWindow, 0); + // Check min_activation_height is on a retarget boundary + BOOST_REQUIRE_EQUAL(min_activation_height % params.nMinerConfirmationWindow, 0U); + + const uint32_t bitmask{VersionBitsMask(params, dep)}; + BOOST_CHECK_EQUAL(bitmask, uint32_t{1} << bit); // In the first chain, test that the bit is set by CBV until it has failed. // In the second chain, test the bit is set by CBV while STARTED and @@ -443,8 +417,18 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion) // ACTIVE and FAILED states in roughly the way we expect for (const auto& chain_name : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET, CBaseChainParams::REGTEST}) { const auto chainParams = CreateChainParams(*m_node.args, chain_name); + uint32_t chain_all_vbits{0}; for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) { - check_computeblockversion(chainParams->GetConsensus(), static_cast<Consensus::DeploymentPos>(i)); + const auto dep = static_cast<Consensus::DeploymentPos>(i); + // Check that no bits are re-used (within the same chain). This is + // disallowed because the transition to FAILED (on timeout) does + // not take precedence over STARTED/LOCKED_IN. So all softforks on + // the same bit might overlap, even when non-overlapping start-end + // times are picked. + const uint32_t dep_mask{VersionBitsMask(chainParams->GetConsensus(), dep)}; + BOOST_CHECK(!(chain_all_vbits & dep_mask)); + chain_all_vbits |= dep_mask; + check_computeblockversion(chainParams->GetConsensus(), dep); } } diff --git a/src/util/system.cpp b/src/util/system.cpp index 0b83a76504..9b3bd46b38 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -235,6 +235,19 @@ static bool CheckValid(const std::string& key, const util::SettingsValue& val, u return true; } +namespace { +fs::path StripRedundantLastElementsOfPath(const fs::path& path) +{ + auto result = path; + while (result.filename().string() == ".") { + result = result.parent_path(); + } + + assert(fs::equivalent(result, path)); + return result; +} +} // namespace + // Define default constructor and destructor that are not inline, so code instantiating this class doesn't need to // #include class definitions for all members. // For example, m_settings has an internal dependency on univalue. @@ -375,6 +388,72 @@ std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) co return std::nullopt; } +const fs::path& ArgsManager::GetBlocksDirPath() +{ + LOCK(cs_args); + fs::path& path = m_cached_blocks_path; + + // Cache the path to avoid calling fs::create_directories on every call of + // this function + if (!path.empty()) return path; + + if (IsArgSet("-blocksdir")) { + path = fs::system_complete(GetArg("-blocksdir", "")); + if (!fs::is_directory(path)) { + path = ""; + return path; + } + } else { + path = GetDataDirPath(false); + } + + path /= BaseParams().DataDir(); + path /= "blocks"; + fs::create_directories(path); + path = StripRedundantLastElementsOfPath(path); + return path; +} + +const fs::path& ArgsManager::GetDataDirPath(bool net_specific) const +{ + LOCK(cs_args); + fs::path& path = net_specific ? m_cached_network_datadir_path : m_cached_datadir_path; + + // Cache the path to avoid calling fs::create_directories on every call of + // this function + if (!path.empty()) return path; + + std::string datadir = GetArg("-datadir", ""); + if (!datadir.empty()) { + path = fs::system_complete(datadir); + if (!fs::is_directory(path)) { + path = ""; + return path; + } + } else { + path = GetDefaultDataDir(); + } + if (net_specific) + path /= BaseParams().DataDir(); + + if (fs::create_directories(path)) { + // This is the first run, create wallets subdirectory too + fs::create_directories(path / "wallets"); + } + + path = StripRedundantLastElementsOfPath(path); + return path; +} + +void ArgsManager::ClearPathCache() +{ + LOCK(cs_args); + + m_cached_datadir_path = fs::path(); + m_cached_network_datadir_path = fs::path(); + m_cached_blocks_path = fs::path(); +} + std::optional<const ArgsManager::Command> ArgsManager::GetCommand() const { Command ret; @@ -434,7 +513,7 @@ bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp) const } if (filepath) { std::string settings = GetArg("-settings", BITCOIN_SETTINGS_FILENAME); - *filepath = fsbridge::AbsPathJoin(GetDataDir(/* net_specific= */ true), temp ? settings + ".tmp" : settings); + *filepath = fsbridge::AbsPathJoin(GetDataDirPath(/* net_specific= */ true), temp ? settings + ".tmp" : settings); } return true; } @@ -723,79 +802,9 @@ fs::path GetDefaultDataDir() #endif } -namespace { -fs::path StripRedundantLastElementsOfPath(const fs::path& path) -{ - auto result = path; - while (result.filename().string() == ".") { - result = result.parent_path(); - } - - assert(fs::equivalent(result, path)); - return result; -} -} // namespace - -static fs::path g_blocks_path_cache_net_specific; -static fs::path pathCached; -static fs::path pathCachedNetSpecific; -static RecursiveMutex csPathCached; - -const fs::path &GetBlocksDir() -{ - LOCK(csPathCached); - fs::path &path = g_blocks_path_cache_net_specific; - - // 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", "")); - if (!fs::is_directory(path)) { - path = ""; - return path; - } - } else { - path = GetDataDir(false); - } - - path /= BaseParams().DataDir(); - path /= "blocks"; - fs::create_directories(path); - path = StripRedundantLastElementsOfPath(path); - return path; -} - const fs::path &GetDataDir(bool fNetSpecific) { - LOCK(csPathCached); - fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached; - - // Cache the path to avoid calling fs::create_directories on every call of - // this function - if (!path.empty()) return path; - - std::string datadir = gArgs.GetArg("-datadir", ""); - if (!datadir.empty()) { - path = fs::system_complete(datadir); - if (!fs::is_directory(path)) { - path = ""; - return path; - } - } else { - path = GetDefaultDataDir(); - } - if (fNetSpecific) - path /= BaseParams().DataDir(); - - if (fs::create_directories(path)) { - // This is the first run, create wallets subdirectory too - fs::create_directories(path / "wallets"); - } - - path = StripRedundantLastElementsOfPath(path); - return path; + return gArgs.GetDataDirPath(fNetSpecific); } bool CheckDataDirOption() @@ -804,15 +813,6 @@ bool CheckDataDirOption() return datadir.empty() || fs::is_directory(fs::system_complete(datadir)); } -void ClearDatadirCache() -{ - LOCK(csPathCached); - - pathCached = fs::path(); - pathCachedNetSpecific = fs::path(); - g_blocks_path_cache_net_specific = fs::path(); -} - fs::path GetConfigFile(const std::string& confPath) { return AbsPathForConfigVal(fs::path(confPath), false); @@ -971,7 +971,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) } // If datadir is changed in .conf file: - ClearDatadirCache(); + gArgs.ClearPathCache(); if (!CheckDataDirOption()) { error = strprintf("specified data directory \"%s\" does not exist.", GetArg("-datadir", "")); return false; diff --git a/src/util/system.h b/src/util/system.h index 29657e56e2..61f862c93a 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -91,13 +91,9 @@ void ReleaseDirectoryLocks(); bool TryCreateDirectories(const fs::path& p); fs::path GetDefaultDataDir(); -// The blocks directory is always net specific. -const fs::path &GetBlocksDir(); const fs::path &GetDataDir(bool fNetSpecific = true); // Return true if -datadir option points to a valid directory or is not specified. bool CheckDataDirOption(); -/** Tests only */ -void ClearDatadirCache(); fs::path GetConfigFile(const std::string& confPath); #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true); @@ -200,6 +196,9 @@ protected: std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args); bool m_accept_any_command GUARDED_BY(cs_args){true}; std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args); + fs::path m_cached_blocks_path GUARDED_BY(cs_args); + mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args); + mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args); [[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false); @@ -264,6 +263,27 @@ public: std::optional<const Command> GetCommand() const; /** + * Get blocks directory path + * + * @return Blocks path which is network specific + */ + const fs::path& GetBlocksDirPath(); + + /** + * Get data directory path + * + * @param net_specific Append network identifier to the returned path + * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned + * @post Returned directory path is created unless it is empty + */ + const fs::path& GetDataDirPath(bool net_specific = true) const; + + /** + * Clear cached directory paths + */ + void ClearPathCache(); + + /** * Return a vector of strings of the given argument * * @param strArg Argument to get (e.g. "-foo") diff --git a/src/validation.cpp b/src/validation.cpp index 332cb581b8..2bf505e26b 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2204,7 +2204,7 @@ bool CChainState::FlushStateToDisk( // Write blocks and block index to disk. if (fDoFullFlush || fPeriodicWrite) { // Depend on nMinDiskSpace to ensure we can write block index - if (!CheckDiskSpace(GetBlocksDir())) { + if (!CheckDiskSpace(gArgs.GetBlocksDirPath())) { return AbortNode(state, "Disk space is too low!", _("Disk space is too low!")); } { @@ -3890,12 +3890,12 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr static FlatFileSeq BlockFileSeq() { - return FlatFileSeq(GetBlocksDir(), "blk", gArgs.GetBoolArg("-fastprune", false) ? 0x4000 /* 16kb */ : BLOCKFILE_CHUNK_SIZE); + return FlatFileSeq(gArgs.GetBlocksDirPath(), "blk", gArgs.GetBoolArg("-fastprune", false) ? 0x4000 /* 16kb */ : BLOCKFILE_CHUNK_SIZE); } static FlatFileSeq UndoFileSeq() { - return FlatFileSeq(GetBlocksDir(), "rev", UNDOFILE_CHUNK_SIZE); + return FlatFileSeq(gArgs.GetBlocksDirPath(), "rev", UNDOFILE_CHUNK_SIZE); } FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly) { |