95256a0 Include Base58 encoded prefixes in chainparams:
base58Prefixes populates every role, seems incomplete to only populate PUBKEY_ADDRESS and SCRIPT_ADDRESS - especially since this change is extracted to a new commit.
And to avoid having to go back and forth between commits during review, can you please help us explain how these changes will fit into the following commits in the commit message, maybe something like:
chainparams: add base58 display prefix data
Expose the human-visible leading characters (e.g. "1", "3", "xpub") of base58 strings via CChainParams::Base58PrefixText(), mirroring the raw-byte base58Prefixes[] table already populated for every Base58Type.
Follow-up commits consume this from DecodeDestination to build network-aware error messages such as "Invalid Base58 address. Expected prefix 3, or 1".
Populating every role keeps the table symmetric with base58Prefixes.
And to make sure each step is fully functional (and doesn't break in future steps), we could cover each commit's changes with some simple tests (also serving as live documentation):
diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp
index 02c7ae9d22..b03f242e78 100644
--- a/src/kernel/chainparams.cpp
+++ b/src/kernel/chainparams.cpp
@@ -147,6 +147,11 @@ public:
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,128);
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4};
+ base58PrefixText[PUBKEY_ADDRESS] = {"1"};
+ base58PrefixText[SCRIPT_ADDRESS] = {"3"};
+ base58PrefixText[SECRET_KEY] = {"5", "K", "L"};
+ base58PrefixText[EXT_PUBLIC_KEY] = {"xpub"};
+ base58PrefixText[EXT_SECRET_KEY] = {"xprv"};
bech32_hrp = "bc";
@@ -260,6 +265,11 @@ public:
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
+ base58PrefixText[PUBKEY_ADDRESS] = {"m", "n"};
+ base58PrefixText[SCRIPT_ADDRESS] = {"2"};
+ base58PrefixText[SECRET_KEY] = {"9", "c"};
+ base58PrefixText[EXT_PUBLIC_KEY] = {"tpub"};
+ base58PrefixText[EXT_SECRET_KEY] = {"tprv"};
bech32_hrp = "tb";
@@ -365,6 +375,11 @@ public:
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
+ base58PrefixText[PUBKEY_ADDRESS] = {"m", "n"};
+ base58PrefixText[SCRIPT_ADDRESS] = {"2"};
+ base58PrefixText[SECRET_KEY] = {"9", "c"};
+ base58PrefixText[EXT_PUBLIC_KEY] = {"tpub"};
+ base58PrefixText[EXT_SECRET_KEY] = {"tprv"};
bech32_hrp = "tb";
@@ -506,6 +521,11 @@ public:
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
+ base58PrefixText[PUBKEY_ADDRESS] = {"m", "n"};
+ base58PrefixText[SCRIPT_ADDRESS] = {"2"};
+ base58PrefixText[SECRET_KEY] = {"9", "c"};
+ base58PrefixText[EXT_PUBLIC_KEY] = {"tpub"};
+ base58PrefixText[EXT_SECRET_KEY] = {"tprv"};
bech32_hrp = "tb";
@@ -638,6 +658,11 @@ public:
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
+ base58PrefixText[PUBKEY_ADDRESS] = {"m", "n"};
+ base58PrefixText[SCRIPT_ADDRESS] = {"2"};
+ base58PrefixText[SECRET_KEY] = {"9", "c"};
+ base58PrefixText[EXT_PUBLIC_KEY] = {"tpub"};
+ base58PrefixText[EXT_SECRET_KEY] = {"tprv"};
bech32_hrp = "bcrt";
diff --git a/src/kernel/chainparams.h b/src/kernel/chainparams.h
index f7209bee1b..ff2811d428 100644
--- a/src/kernel/chainparams.h
+++ b/src/kernel/chainparams.h
@@ -112,6 +112,8 @@ public:
/** Return the list of hostnames to look up for DNS seeds */
const std::vector<std::string>& DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
+ /** Display prefixes (e.g. "1", "xpub") for this base58 role on this network. */
+ const std::vector<std::string>& Base58PrefixText(Base58Type type) const { return base58PrefixText[type]; }
const std::string& Bech32HRP() const { return bech32_hrp; }
const std::vector<uint8_t>& FixedSeeds() const { return vFixedSeeds; }
const HeadersSyncParams& HeadersSync() const { return m_headers_sync_params; }
@@ -171,6 +173,7 @@ protected:
uint64_t m_assumed_chain_state_size;
std::vector<std::string> vSeeds;
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
+ std::vector<std::string> base58PrefixText[MAX_BASE58_TYPES];
std::string bech32_hrp;
ChainType m_chain_type;
CBlock genesis;
diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp
index 2ea9c0b8ef..1cacdb398b 100644
--- a/src/test/key_io_tests.cpp
+++ b/src/test/key_io_tests.cpp
@@ -17,6 +17,8 @@
#include <boost/test/unit_test.hpp>
#include <algorithm>
+#include <array>
+#include <utility>
BOOST_FIXTURE_TEST_SUITE(key_io_tests, BasicTestingSetup)
@@ -147,4 +149,29 @@ BOOST_AUTO_TEST_CASE(key_io_invalid)
}
}
+// Goal: check that each network's base58 display prefixes match what user-facing
+// error messages (e.g. in DecodeDestination) will reference.
+BOOST_AUTO_TEST_CASE(base58_prefix_text)
+{
+ using Entry = std::pair<CChainParams::Base58Type, std::vector<std::string>>;
+ using Prefixes = std::array<Entry, CChainParams::MAX_BASE58_TYPES>;
+ using enum Entry::first_type;
+
+ const Prefixes mainnet{{{PUBKEY_ADDRESS, {"1"}}, {SCRIPT_ADDRESS, {"3"}}, {SECRET_KEY, {"5", "K", "L"}}, {EXT_PUBLIC_KEY, {"xpub"}}, {EXT_SECRET_KEY, {"xprv"}}}};
+ const Prefixes testnet{{{PUBKEY_ADDRESS, {"m", "n"}}, {SCRIPT_ADDRESS, {"2"}}, {SECRET_KEY, {"9", "c"}}, {EXT_PUBLIC_KEY, {"tpub"}}, {EXT_SECRET_KEY, {"tprv"}}}};
+
+ const auto check{[](ChainType chain, const Prefixes& expected) {
+ const auto params{CreateChainParams(ArgsManager{}, chain)};
+ for (const auto& [type, values] : expected) {
+ const auto& actual{params->Base58PrefixText(type)};
+ BOOST_CHECK_EQUAL_COLLECTIONS(actual.begin(), actual.end(), values.begin(), values.end());
+ }
+ }};
+
+ check(ChainType::MAIN, mainnet);
+ for (const auto chain : {ChainType::TESTNET, ChainType::TESTNET4, ChainType::SIGNET, ChainType::REGTEST}) {
+ check(chain, testnet);
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()