diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/Makefile.test.include | 1 | ||||
-rw-r--r-- | src/base58.cpp | 184 | ||||
-rw-r--r-- | src/base58.h | 1 | ||||
-rw-r--r-- | src/bech32.cpp | 191 | ||||
-rw-r--r-- | src/bech32.h | 25 | ||||
-rw-r--r-- | src/chainparams.cpp | 6 | ||||
-rw-r--r-- | src/chainparams.h | 2 | ||||
-rw-r--r-- | src/policy/policy.cpp | 2 | ||||
-rw-r--r-- | src/qt/bitcoinaddressvalidator.cpp | 2 | ||||
-rw-r--r-- | src/rpc/client.cpp | 1 | ||||
-rw-r--r-- | src/rpc/misc.cpp | 44 | ||||
-rw-r--r-- | src/script/ismine.cpp | 1 | ||||
-rw-r--r-- | src/script/sign.cpp | 2 | ||||
-rw-r--r-- | src/script/standard.cpp | 45 | ||||
-rw-r--r-- | src/script/standard.h | 37 | ||||
-rw-r--r-- | src/test/base58_tests.cpp | 135 | ||||
-rw-r--r-- | src/test/bech32_tests.cpp | 67 | ||||
-rw-r--r-- | src/test/data/base58_keys_invalid.json | 30 | ||||
-rw-r--r-- | src/test/data/base58_keys_valid.json | 679 | ||||
-rw-r--r-- | src/test/script_standard_tests.cpp | 39 | ||||
-rw-r--r-- | src/utilstrencodings.h | 24 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 64 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 21 |
24 files changed, 1090 insertions, 515 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index ebae53a8c1..c71e457ebe 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -78,6 +78,7 @@ BITCOIN_CORE_H = \ addrdb.h \ addrman.h \ base58.h \ + bech32.h \ bloom.h \ blockencodings.h \ chain.h \ @@ -316,6 +317,7 @@ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ base58.cpp \ + bech32.cpp \ chainparams.cpp \ coins.cpp \ compressor.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 3a932f460d..ed95f345b1 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -31,6 +31,7 @@ BITCOIN_TESTS =\ test/base32_tests.cpp \ test/base58_tests.cpp \ test/base64_tests.cpp \ + test/bech32_tests.cpp \ test/bip32_tests.cpp \ test/blockencodings_tests.cpp \ test/bloom_tests.cpp \ diff --git a/src/base58.cpp b/src/base58.cpp index 3b907c20d6..c2cc5d979f 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -4,17 +4,20 @@ #include "base58.h" +#include "bech32.h" #include "hash.h" +#include "script/script.h" #include "uint256.h" +#include "utilstrencodings.h" -#include <assert.h> -#include <stdint.h> -#include <string.h> -#include <vector> -#include <string> #include <boost/variant/apply_visitor.hpp> #include <boost/variant/static_visitor.hpp> +#include <algorithm> +#include <assert.h> +#include <string.h> + + /** All alphanumeric characters except for "0", "I", "O", and "l" */ static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; @@ -212,86 +215,113 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const namespace { -/** base58-encoded Bitcoin addresses. - * Public-key-hash-addresses have version 0 (or 111 testnet). - * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. - * Script-hash-addresses have version 5 (or 196 testnet). - * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. - */ -class CBitcoinAddress : public CBase58Data { -public: - bool Set(const CKeyID &id); - bool Set(const CScriptID &id); - bool Set(const CTxDestination &dest); - bool IsValid() const; - bool IsValid(const CChainParams ¶ms) const; - - CBitcoinAddress() {} - CBitcoinAddress(const CTxDestination &dest) { Set(dest); } - CBitcoinAddress(const std::string& strAddress) { SetString(strAddress); } - CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); } - - CTxDestination Get() const; -}; - -class CBitcoinAddressVisitor : public boost::static_visitor<bool> +class DestinationEncoder : public boost::static_visitor<std::string> { private: - CBitcoinAddress* addr; + const CChainParams& m_params; public: - explicit CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {} + DestinationEncoder(const CChainParams& params) : m_params(params) {} - bool operator()(const CKeyID& id) const { return addr->Set(id); } - bool operator()(const CScriptID& id) const { return addr->Set(id); } - bool operator()(const CNoDestination& no) const { return false; } -}; - -} // namespace + std::string operator()(const CKeyID& id) const + { + std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } -bool CBitcoinAddress::Set(const CKeyID& id) -{ - SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20); - return true; -} + std::string operator()(const CScriptID& id) const + { + std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } -bool CBitcoinAddress::Set(const CScriptID& id) -{ - SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20); - return true; -} + std::string operator()(const WitnessV0KeyHash& id) const + { + std::vector<unsigned char> data = {0}; + ConvertBits<8, 5, true>(data, id.begin(), id.end()); + return bech32::Encode(m_params.Bech32HRP(), data); + } -bool CBitcoinAddress::Set(const CTxDestination& dest) -{ - return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); -} + std::string operator()(const WitnessV0ScriptHash& id) const + { + std::vector<unsigned char> data = {0}; + ConvertBits<8, 5, true>(data, id.begin(), id.end()); + return bech32::Encode(m_params.Bech32HRP(), data); + } -bool CBitcoinAddress::IsValid() const -{ - return IsValid(Params()); -} + std::string operator()(const WitnessUnknown& id) const + { + if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) { + return {}; + } + std::vector<unsigned char> data = {(unsigned char)id.version}; + ConvertBits<8, 5, true>(data, id.program, id.program + id.length); + return bech32::Encode(m_params.Bech32HRP(), data); + } -bool CBitcoinAddress::IsValid(const CChainParams& params) const -{ - bool fCorrectSize = vchData.size() == 20; - bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) || - vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); - return fCorrectSize && fKnownVersion; -} + std::string operator()(const CNoDestination& no) const { return {}; } +}; -CTxDestination CBitcoinAddress::Get() const +CTxDestination DecodeDestination(const std::string& str, const CChainParams& params) { - if (!IsValid()) - return CNoDestination(); - uint160 id; - memcpy(&id, vchData.data(), 20); - if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) - return CKeyID(id); - else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) - return CScriptID(id); - else - return CNoDestination(); + std::vector<unsigned char> data; + uint160 hash; + if (DecodeBase58Check(str, data)) { + // base58-encoded Bitcoin addresses. + // Public-key-hash-addresses have version 0 (or 111 testnet). + // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. + 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); + } + // 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); + } + } + data.clear(); + auto bech = bech32::Decode(str); + if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) { + // Bech32 decoding + int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16) + // The rest of the symbols are converted witness program bytes. + if (ConvertBits<5, 8, false>(data, bech.second.begin() + 1, bech.second.end())) { + if (version == 0) { + { + WitnessV0KeyHash keyid; + if (data.size() == keyid.size()) { + std::copy(data.begin(), data.end(), keyid.begin()); + return keyid; + } + } + { + WitnessV0ScriptHash scriptid; + if (data.size() == scriptid.size()) { + std::copy(data.begin(), data.end(), scriptid.begin()); + return scriptid; + } + } + return CNoDestination(); + } + if (version > 16 || data.size() < 2 || data.size() > 40) { + return CNoDestination(); + } + WitnessUnknown unk; + unk.version = version; + std::copy(data.begin(), data.end(), unk.program); + unk.length = data.size(); + return unk; + } + } + return CNoDestination(); } +} // namespace void CBitcoinSecret::SetKey(const CKey& vchSecret) { @@ -328,22 +358,20 @@ bool CBitcoinSecret::SetString(const std::string& strSecret) std::string EncodeDestination(const CTxDestination& dest) { - CBitcoinAddress addr(dest); - if (!addr.IsValid()) return ""; - return addr.ToString(); + return boost::apply_visitor(DestinationEncoder(Params()), dest); } CTxDestination DecodeDestination(const std::string& str) { - return CBitcoinAddress(str).Get(); + return DecodeDestination(str, Params()); } bool IsValidDestinationString(const std::string& str, const CChainParams& params) { - return CBitcoinAddress(str).IsValid(params); + return IsValidDestination(DecodeDestination(str, params)); } bool IsValidDestinationString(const std::string& str) { - return CBitcoinAddress(str).IsValid(); + return IsValidDestinationString(str, Params()); } diff --git a/src/base58.h b/src/base58.h index 4b895ca022..9dc4234248 100644 --- a/src/base58.h +++ b/src/base58.h @@ -17,7 +17,6 @@ #include "chainparams.h" #include "key.h" #include "pubkey.h" -#include "script/script.h" #include "script/standard.h" #include "support/allocators/zeroafterfree.h" diff --git a/src/bech32.cpp b/src/bech32.cpp new file mode 100644 index 0000000000..573eac58bb --- /dev/null +++ b/src/bech32.cpp @@ -0,0 +1,191 @@ +// 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 "bech32.h" + +namespace +{ + +typedef std::vector<uint8_t> data; + +/** The Bech32 character set for encoding. */ +const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + +/** The Bech32 character set for decoding. */ +const int8_t CHARSET_REV[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 +}; + +/** Concatenate two byte arrays. */ +data Cat(data x, const data& y) +{ + x.insert(x.end(), y.begin(), y.end()); + return x; +} + +/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to + * make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher + * bits correspond to earlier values. */ +uint32_t PolyMod(const data& v) +{ + // The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an + // implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) = + // 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that + // [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...]. + + // The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of + // v(x) mod g(x), where g(x) is the Bech32 generator, + // x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way + // that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a + // window of 1023 characters. Among the various possible BCH codes, one was selected to in + // fact guarantee detection of up to 4 errors within a window of 89 characters. + + // Note that the coefficients are elements of GF(32), here represented as decimal numbers + // between {}. In this finite field, addition is just XOR of the corresponding numbers. For + // example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires + // treating the bits of values themselves as coefficients of a polynomial over a smaller field, + // GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} = + // (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a + // = a^3 + 1 (mod a^5 + a^3 + 1) = {9}. + + // During the course of the loop below, `c` contains the bitpacked coefficients of the + // polynomial constructed from just the values of v that were processed so far, mod g(x). In + // the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of + // v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value + // for `c`. + uint32_t c = 1; + for (auto v_i : v) { + // We want to update `c` to correspond to a polynomial with one extra term. If the initial + // value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to + // correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to + // process. Simplifying: + // c'(x) = (f(x) * x + v_i) mod g(x) + // ((f(x) mod g(x)) * x + v_i) mod g(x) + // (c(x) * x + v_i) mod g(x) + // If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute + // c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x) + // = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x) + // = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i + // If we call (x^6 mod g(x)) = k(x), this can be written as + // c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x) + + // First, determine the value of c0: + uint8_t c0 = c >> 25; + + // Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i: + c = ((c & 0x1ffffff) << 5) ^ v_i; + + // Finally, for each set bit n in c0, conditionally add {2^n}k(x): + if (c0 & 1) c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18} + if (c0 & 2) c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13} + if (c0 & 4) c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26} + if (c0 & 8) c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29} + if (c0 & 16) c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19} + } + return c; +} + +/** Convert to lower case. */ +inline unsigned char LowerCase(unsigned char c) +{ + return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c; +} + +/** Expand a HRP for use in checksum computation. */ +data ExpandHRP(const std::string& hrp) +{ + data ret; + ret.reserve(hrp.size() + 90); + ret.resize(hrp.size() * 2 + 1); + for (size_t i = 0; i < hrp.size(); ++i) { + unsigned char c = hrp[i]; + ret[i] = c >> 5; + ret[i + hrp.size() + 1] = c & 0x1f; + } + ret[hrp.size()] = 0; + return ret; +} + +/** Verify a checksum. */ +bool VerifyChecksum(const std::string& hrp, const data& values) +{ + // PolyMod computes what value to xor into the final values to make the checksum 0. However, + // if we required that the checksum was 0, it would be the case that appending a 0 to a valid + // list of values would result in a new valid list. For that reason, Bech32 requires the + // resulting checksum to be 1 instead. + return PolyMod(Cat(ExpandHRP(hrp), values)) == 1; +} + +/** Create a checksum. */ +data CreateChecksum(const std::string& hrp, const data& values) +{ + data enc = Cat(ExpandHRP(hrp), values); + enc.resize(enc.size() + 6); // Append 6 zeroes + uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes. + data ret(6); + for (size_t i = 0; i < 6; ++i) { + // Convert the 5-bit groups in mod to checksum values. + ret[i] = (mod >> (5 * (5 - i))) & 31; + } + return ret; +} + +} // namespace + +namespace bech32 +{ + +/** Encode a Bech32 string. */ +std::string Encode(const std::string& hrp, const data& values) { + data checksum = CreateChecksum(hrp, values); + data combined = Cat(values, checksum); + std::string ret = hrp + '1'; + ret.reserve(ret.size() + combined.size()); + for (auto c : combined) { + ret += CHARSET[c]; + } + return ret; +} + +/** Decode a Bech32 string. */ +std::pair<std::string, data> Decode(const std::string& str) { + bool lower = false, upper = false; + for (size_t i = 0; i < str.size(); ++i) { + unsigned char c = str[i]; + if (c < 33 || c > 126) return {}; + if (c >= 'a' && c <= 'z') lower = true; + if (c >= 'A' && c <= 'Z') upper = true; + } + if (lower && upper) return {}; + size_t pos = str.rfind('1'); + if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) { + return {}; + } + data values(str.size() - 1 - pos); + for (size_t i = 0; i < str.size() - 1 - pos; ++i) { + unsigned char c = str[i + pos + 1]; + int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c]; + if (rev == -1) { + return {}; + } + values[i] = rev; + } + std::string hrp; + for (size_t i = 0; i < pos; ++i) { + hrp += LowerCase(str[i]); + } + if (!VerifyChecksum(hrp, values)) { + return {}; + } + return {hrp, data(values.begin(), values.end() - 6)}; +} + +} // namespace bech32 diff --git a/src/bech32.h b/src/bech32.h new file mode 100644 index 0000000000..7ef7b22213 --- /dev/null +++ b/src/bech32.h @@ -0,0 +1,25 @@ +// 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. + +// Bech32 is a string encoding format used in newer address types. +// The output consists of a human-readable part (alphanumeric), a +// separator character (1), and a base32 data section, the last +// 6 characters of which are a checksum. +// +// For more information, see BIP 173. + +#include <stdint.h> +#include <string> +#include <vector> + +namespace bech32 +{ + +/** Encode a Bech32 string. Returns the empty string in case of failure. */ +std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values); + +/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */ +std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str); + +} // namespace bech32 diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 2021ec51db..85c9cd6934 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -137,6 +137,8 @@ public: base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; + bech32_hrp = "bc"; + vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); fDefaultConsistencyChecks = false; @@ -236,6 +238,8 @@ public: base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; + bech32_hrp = "tb"; + vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); fDefaultConsistencyChecks = false; @@ -330,6 +334,8 @@ 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}; + + bech32_hrp = "bcrt"; } }; diff --git a/src/chainparams.h b/src/chainparams.h index f55ae4cf7f..3948c9163f 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -73,6 +73,7 @@ public: std::string NetworkIDString() const { return strNetworkID; } const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; } const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } + const std::string& Bech32HRP() const { return bech32_hrp; } const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; } const CCheckpointData& Checkpoints() const { return checkpointData; } const ChainTxData& TxData() const { return chainTxData; } @@ -86,6 +87,7 @@ protected: uint64_t nPruneAfterHeight; std::vector<CDNSSeedData> vSeeds; std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES]; + std::string bech32_hrp; std::string strNetworkID; CBlock genesis; std::vector<SeedSpec6> vFixedSeeds; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 56912d0375..b2fb284508 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -76,7 +76,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool w else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH)) return false; - return whichType != TX_NONSTANDARD; + return whichType != TX_NONSTANDARD && whichType != TX_WITNESS_UNKNOWN; } bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled) diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index 4dd1092806..362a71f04d 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -67,7 +67,7 @@ QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &po if (((ch >= '0' && ch<='9') || (ch >= 'a' && ch<='z') || (ch >= 'A' && ch<='Z')) && - ch != 'l' && ch != 'I' && ch != '0' && ch != 'O') + ch != 'I' && ch != 'O') // Characters invalid in both Base58 and Bech32 { // Alphanumeric and not a 'forbidden' character } diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 406ad2f6ec..f54f24e2a7 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -129,6 +129,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "logging", 0, "include" }, { "logging", 1, "exclude" }, { "disconnectnode", 1, "nodeid" }, + { "addwitnessaddress", 1, "p2sh" }, // Echo with conversion (For testing only) { "echojson", 0, "arg0" }, { "echojson", 1, "arg1" }, diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index f8cdf57d14..521b49e2a7 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -7,6 +7,7 @@ #include "chain.h" #include "clientversion.h" #include "core_io.h" +#include "crypto/ripemd160.h" #include "init.h" #include "validation.h" #include "httpserver.h" @@ -45,6 +46,7 @@ public: UniValue obj(UniValue::VOBJ); CPubKey vchPubKey; obj.push_back(Pair("isscript", false)); + obj.push_back(Pair("iswitness", false)); if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { obj.push_back(Pair("pubkey", HexStr(vchPubKey))); obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); @@ -56,6 +58,7 @@ public: UniValue obj(UniValue::VOBJ); CScript subscript; obj.push_back(Pair("isscript", true)); + obj.push_back(Pair("iswitness", false)); if (pwallet && pwallet->GetCScript(scriptID, subscript)) { std::vector<CTxDestination> addresses; txnouttype whichType; @@ -73,6 +76,47 @@ public: } return obj; } + + UniValue operator()(const WitnessV0KeyHash& id) const + { + UniValue obj(UniValue::VOBJ); + CPubKey pubkey; + obj.push_back(Pair("isscript", false)); + obj.push_back(Pair("iswitness", true)); + obj.push_back(Pair("witness_version", 0)); + obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end()))); + if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) { + obj.push_back(Pair("pubkey", HexStr(pubkey))); + } + return obj; + } + + UniValue operator()(const WitnessV0ScriptHash& id) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + obj.push_back(Pair("isscript", true)); + obj.push_back(Pair("iswitness", true)); + obj.push_back(Pair("witness_version", 0)); + obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end()))); + CRIPEMD160 hasher; + uint160 hash; + hasher.Write(id.begin(), 32).Finalize(hash.begin()); + if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { + obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); + } + return obj; + } + + UniValue operator()(const WitnessUnknown& id) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + obj.push_back(Pair("iswitness", true)); + obj.push_back(Pair("witness_version", (int)id.version)); + obj.push_back(Pair("witness_program", HexStr(id.program, id.program + id.length))); + return obj; + } }; #endif diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index c3aade1774..6b68f0679e 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -61,6 +61,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& { case TX_NONSTANDARD: case TX_NULL_DATA: + case TX_WITNESS_UNKNOWN: break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); diff --git a/src/script/sign.cpp b/src/script/sign.cpp index dc50467d3f..ac58b690a2 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -79,6 +79,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP { case TX_NONSTANDARD: case TX_NULL_DATA: + case TX_WITNESS_UNKNOWN: return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); @@ -309,6 +310,7 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature { case TX_NONSTANDARD: case TX_NULL_DATA: + case TX_WITNESS_UNKNOWN: // Don't know anything about this, assume bigger one is correct: if (sigs1.script.size() >= sigs2.script.size()) return sigs1; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index b6e2232ab4..f57f1f61b4 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -30,6 +30,7 @@ const char* GetTxnOutputType(txnouttype t) case TX_NULL_DATA: return "nulldata"; case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; + case TX_WITNESS_UNKNOWN: return "witness_unknown"; } return nullptr; } @@ -75,6 +76,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v vSolutionsRet.push_back(witnessprogram); return true; } + if (witnessversion != 0) { + typeRet = TX_WITNESS_UNKNOWN; + vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion}); + vSolutionsRet.push_back(std::move(witnessprogram)); + return true; + } return false; } @@ -198,6 +205,23 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) { addressRet = CScriptID(uint160(vSolutions[0])); return true; + } else if (whichType == TX_WITNESS_V0_KEYHASH) { + WitnessV0KeyHash hash; + std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin()); + addressRet = hash; + return true; + } else if (whichType == TX_WITNESS_V0_SCRIPTHASH) { + WitnessV0ScriptHash hash; + std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin()); + addressRet = hash; + return true; + } else if (whichType == TX_WITNESS_UNKNOWN) { + WitnessUnknown unk; + unk.version = vSolutions[0][0]; + std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program); + unk.length = vSolutions[1].size(); + addressRet = unk; + return true; } // Multisig txns have more than one address... return false; @@ -268,6 +292,27 @@ public: *script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; return true; } + + bool operator()(const WitnessV0KeyHash& id) const + { + script->clear(); + *script << OP_0 << ToByteVector(id); + return true; + } + + bool operator()(const WitnessV0ScriptHash& id) const + { + script->clear(); + *script << OP_0 << ToByteVector(id); + return true; + } + + bool operator()(const WitnessUnknown& id) const + { + script->clear(); + *script << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length); + return true; + } }; } // namespace diff --git a/src/script/standard.h b/src/script/standard.h index 8df143a3a3..fa07ea88c1 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -64,6 +64,7 @@ enum txnouttype TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data TX_WITNESS_V0_SCRIPTHASH, TX_WITNESS_V0_KEYHASH, + TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above }; class CNoDestination { @@ -72,14 +73,42 @@ public: friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } }; +struct WitnessV0ScriptHash : public uint256 {}; +struct WitnessV0KeyHash : public uint160 {}; + +//! CTxDestination subtype to encode any future Witness version +struct WitnessUnknown +{ + unsigned int version; + unsigned int length; + unsigned char program[40]; + + friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) { + if (w1.version != w2.version) return false; + if (w1.length != w2.length) return false; + return std::equal(w1.program, w1.program + w1.length, w2.program); + } + + friend bool operator<(const WitnessUnknown& w1, const WitnessUnknown& w2) { + if (w1.version < w2.version) return true; + if (w1.version > w2.version) return false; + if (w1.length < w2.length) return true; + if (w1.length > w2.length) return false; + return std::lexicographical_compare(w1.program, w1.program + w1.length, w2.program, w2.program + w2.length); + } +}; + /** * A txout script template with a specific destination. It is either: * * CNoDestination: no destination set - * * CKeyID: TX_PUBKEYHASH destination - * * CScriptID: TX_SCRIPTHASH destination + * * CKeyID: TX_PUBKEYHASH destination (P2PKH) + * * CScriptID: 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> CTxDestination; +typedef boost::variant<CNoDestination, CKeyID, CScriptID, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination; /** Check whether a CTxDestination is a CNoDestination. */ bool IsValidDestination(const CTxDestination& dest); @@ -104,7 +133,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v * Parse a standard scriptPubKey for the destination address. Assigns result to * the addressRet parameter and returns true if successful. For multisig * scripts, instead use ExtractDestinations. Currently only works for P2PK, - * P2PKH, and P2SH scripts. + * P2PKH, P2SH, P2WPKH, and P2WSH scripts. */ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 4829590c54..6bc6dd5187 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -10,14 +10,15 @@ #include "key.h" #include "script/script.h" +#include "test/test_bitcoin.h" #include "uint256.h" #include "util.h" #include "utilstrencodings.h" -#include "test/test_bitcoin.h" + +#include <univalue.h> #include <boost/test/unit_test.hpp> -#include <univalue.h> extern UniValue read_json(const std::string& jsondata); @@ -72,50 +73,6 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58) BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); } -// Visitor to check address type -class TestAddrTypeVisitor : public boost::static_visitor<bool> -{ -private: - std::string exp_addrType; -public: - explicit TestAddrTypeVisitor(const std::string &_exp_addrType) : exp_addrType(_exp_addrType) { } - bool operator()(const CKeyID &id) const - { - return (exp_addrType == "pubkey"); - } - bool operator()(const CScriptID &id) const - { - return (exp_addrType == "script"); - } - bool operator()(const CNoDestination &no) const - { - return (exp_addrType == "none"); - } -}; - -// Visitor to check address payload -class TestPayloadVisitor : public boost::static_visitor<bool> -{ -private: - std::vector<unsigned char> exp_payload; -public: - explicit TestPayloadVisitor(std::vector<unsigned char> &_exp_payload) : exp_payload(_exp_payload) { } - bool operator()(const CKeyID &id) const - { - uint160 exp_key(exp_payload); - return exp_key == id; - } - bool operator()(const CScriptID &id) const - { - uint160 exp_key(exp_payload); - return exp_key == id; - } - bool operator()(const CNoDestination &no) const - { - return exp_payload.size() == 0; - } -}; - // Goal: check that parsed keys match test payload BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) { @@ -127,8 +84,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) for (unsigned int idx = 0; idx < tests.size(); idx++) { UniValue test = tests[idx]; std::string strTest = test.write(); - if (test.size() < 3) // Allow for extra stuff (useful for comments) - { + if (test.size() < 3) { // Allow for extra stuff (useful for comments) BOOST_ERROR("Bad test: " << strTest); continue; } @@ -136,13 +92,9 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - bool isTestnet = find_value(metadata, "isTestnet").get_bool(); - if (isTestnet) - SelectParams(CBaseChainParams::TESTNET); - else - SelectParams(CBaseChainParams::MAIN); - if(isPrivkey) - { + SelectParams(find_value(metadata, "chain").get_str()); + bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool(); + if (isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); // Must be valid private key BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest); @@ -154,15 +106,27 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) // Private key must be invalid public key destination = DecodeDestination(exp_base58string); BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest); - } - else - { - std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey" + } else { // Must be valid public key destination = DecodeDestination(exp_base58string); + CScript script = GetScriptForDestination(destination); BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest); - BOOST_CHECK_MESSAGE((boost::get<CScriptID>(&destination) != nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest); - BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), destination), "addrType mismatch" + strTest); + BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); + + // Try flipped case version + for (char& c : exp_base58string) { + if (c >= 'a' && c <= 'z') { + c = (c - 'a') + 'A'; + } else if (c >= 'A' && c <= 'Z') { + c = (c - 'A') + 'a'; + } + } + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest); + if (IsValidDestination(destination)) { + script = GetScriptForDestination(destination); + BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); + } // Public key must be invalid private key secret.SetString(exp_base58string); @@ -188,13 +152,8 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - bool isTestnet = find_value(metadata, "isTestnet").get_bool(); - if (isTestnet) - SelectParams(CBaseChainParams::TESTNET); - else - SelectParams(CBaseChainParams::MAIN); - if(isPrivkey) - { + SelectParams(find_value(metadata, "chain").get_str()); + if (isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); CKey key; key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); @@ -202,36 +161,20 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) CBitcoinSecret secret; secret.SetKey(key); BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest); - } - else - { - std::string exp_addrType = find_value(metadata, "addrType").get_str(); + } else { CTxDestination dest; - if(exp_addrType == "pubkey") - { - dest = CKeyID(uint160(exp_payload)); - } - else if(exp_addrType == "script") - { - dest = CScriptID(uint160(exp_payload)); - } - else if(exp_addrType == "none") - { - dest = CNoDestination(); - } - else - { - BOOST_ERROR("Bad addrtype: " << strTest); - continue; - } + CScript exp_script(exp_payload.begin(), exp_payload.end()); + ExtractDestination(exp_script, dest); std::string address = EncodeDestination(dest); - BOOST_CHECK_MESSAGE(address == exp_base58string, "mismatch: " + strTest); + + BOOST_CHECK_EQUAL(address, exp_base58string); } } SelectParams(CBaseChainParams::MAIN); } + // Goal: check that base58 parsing code is robust against a variety of corrupted data BOOST_AUTO_TEST_CASE(base58_keys_invalid) { @@ -250,13 +193,15 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid) std::string exp_base58string = test[0].get_str(); // must be invalid as public and as private key - destination = DecodeDestination(exp_base58string); - BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest); - secret.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest); + for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) { + SelectParams(chain); + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest); + secret.SetString(exp_base58string); + BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey in mainnet:" + strTest); + } } } BOOST_AUTO_TEST_SUITE_END() - diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp new file mode 100644 index 0000000000..ce4cddd64b --- /dev/null +++ b/src/test/bech32_tests.cpp @@ -0,0 +1,67 @@ +// 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 "bech32.h" +#include "test/test_bitcoin.h" + +#include <boost/test/unit_test.hpp> + +BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup) + +bool CaseInsensitiveEqual(const std::string &s1, const std::string &s2) +{ + if (s1.size() != s2.size()) return false; + for (size_t i = 0; i < s1.size(); ++i) { + char c1 = s1[i]; + if (c1 >= 'A' && c1 <= 'Z') c1 -= ('A' - 'a'); + char c2 = s2[i]; + if (c2 >= 'A' && c2 <= 'Z') c2 -= ('A' - 'a'); + if (c1 != c2) return false; + } + return true; +} + +BOOST_AUTO_TEST_CASE(bip173_testvectors_valid) +{ + static const std::string CASES[] = { + "A12UEL5L", + "a12uel5l", + "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", + "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", + "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + "?1ezyfcl", + }; + for (const std::string& str : CASES) { + auto ret = bech32::Decode(str); + BOOST_CHECK(!ret.first.empty()); + std::string recode = bech32::Encode(ret.first, ret.second); + BOOST_CHECK(!recode.empty()); + BOOST_CHECK(CaseInsensitiveEqual(str, recode)); + } +} + +BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid) +{ + static const std::string CASES[] = { + " 1nwldj5", + "\x7f""1axkwrx", + "\x80""1eym55h", + "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", + "pzry9x0s0muk", + "1pzry9x0s0muk", + "x1b4n0q5v", + "li1dgmt3", + "de1lg7wt\xff", + "A1G7SGD8", + "10a06t8", + "1qzzfhee", + }; + for (const std::string& str : CASES) { + auto ret = bech32::Decode(str); + BOOST_CHECK(ret.first.empty()); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/data/base58_keys_invalid.json b/src/test/data/base58_keys_invalid.json index a088620f1b..2056c7491c 100644 --- a/src/test/data/base58_keys_invalid.json +++ b/src/test/data/base58_keys_invalid.json @@ -148,5 +148,35 @@ ], [ "2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED" + ], + [ + "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty" + ], + [ + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5" + ], + [ + "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2" + ], + [ + "bc1rw5uspcuh" + ], + [ + "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90" + ], + [ + "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P" + ], + [ + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7" + ], + [ + "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du" + ], + [ + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv" + ], + [ + "bc1gmk9yu" ] ] diff --git a/src/test/data/base58_keys_valid.json b/src/test/data/base58_keys_valid.json index e1e252e22d..8418a6002d 100644 --- a/src/test/data/base58_keys_valid.json +++ b/src/test/data/base58_keys_valid.json @@ -1,452 +1,533 @@ [ [ - "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", - "65a16059864a2fdbc7c99a4723a8395bc6f188eb", + "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", + "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", - "74f209f6ea907e2ea48f74fae05782ae8a665257", + "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", + "a91474f209f6ea907e2ea48f74fae05782ae8a66525787", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", - "53c0307d6851aa0ce7825ba883c6bd9ad242b486", + "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", + "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", - "6349a418fc4578d10a372b54b45c280cc8c4382f", + "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", + "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "regtest" } - ], + ], [ - "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr", - "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19", + "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", + "a9146349a418fc4578d10a372b54b45c280cc8c4382f87", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD", - "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4", + "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr", + "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", - "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", + "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD", + "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", - "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", + "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", + "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", - "6d23156cbbdcc82a5a47eee4c2c7c583c18b6bf4", + "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", + "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "regtest" } - ], + ], [ - "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", - "fcc5460dd6e2487c7d75b1963625da0e8f4c5975", + "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", + "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", - "f1d470f9b02370fdec2e6b708b08ac431bf7a5f7", + "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", + "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "regtest" } - ], + ], [ - "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", - "c579342c2c4c9220205e2cdc285617040c924a0a", + "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", + "76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc", - "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e", + "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", + "a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi", - "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4", + "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", + "76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj", - "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203", + "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", + "a914c579342c2c4c9220205e2cdc285617040c924a0a87", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN", - "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9", + "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc", + "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", - "7987ccaa53d02c8873487ef919677cd3db7a6912", + "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi", + "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", - "63bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb", + "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj", + "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", - "ef66444b5b17f14e8fae6e7e19b045a78c54fd79", + "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN", + "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", - "c3e55fceceaa4391ed2a9677f4a4d34eacd021a0", + "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", + "76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9", - "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252", + "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", + "a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT", - "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c", + "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", + "76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo", - "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52", + "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", + "a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7", - "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69", + "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9", + "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", - "adc1cc2081a27206fae25792f28bbc55b831549d", + "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT", + "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", - "188f91a931947eddd7432d6e614387e32b244709", + "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo", + "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", - "1694f5bc1a7295b600f40018a618a6ea48eeb498", + "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7", + "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", - "3b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f3", + "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", + "76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR", - "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0", + "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", + "a914188f91a931947eddd7432d6e614387e32b24470987", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8", - "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af", + "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", + "76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", - "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", + "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", + "a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA", - "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef", + "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR", + "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", - "c4c1b72491ede1eedaca00618407ee0b772cad0d", + "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8", + "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", - "f6fe69bcb548a829cce4c57bf6fff8af3a5981f9", + "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", + "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", - "261f83568a098a8638844bd7aeca039d5f2352c0", + "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", + "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "regtest" } - ], + ], [ - "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", - "e930e1834a4d234702773951d627cce82fbb5d2e", + "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA", + "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg", - "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0", + "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", + "76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi", - "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3", + "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", + "a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys", - "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb", + "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", + "76a914261f83568a098a8638844bd7aeca039d5f2352c088ac", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw", - "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de", + "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", + "a914e930e1834a4d234702773951d627cce82fbb5d2e87", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", - "5eadaf9bb7121f0f192561a5a62f5e5f54210292", + "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg", + "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", - "3f210e7277c899c3a155cc1c90f4106cbddeec6e", + "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi", + "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "myoqcgYiehufrsnnkqdqbp69dddVDMopJu", - "c8a3c2a09a298592c3e180f02487cd91ba3400b5", + "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys", + "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", - "99b31df7c9068d1481b596578ddbb4d3bd90baeb", + "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw", + "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4", - "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae", + "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", + "76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2", - "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd", + "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", + "a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV", - "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801", + "myoqcgYiehufrsnnkqdqbp69dddVDMopJu", + "76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h", - "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c", + "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", + "a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", - "1ed467017f043e91ed4c44b4e8dd674db211c4e6", + "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4", + "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", - "5ece0cadddc415b1980f001785947120acdb36fc", + "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2", + "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" + } + ], + [ + "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV", + "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801", + { + "isCompressed": false, + "isPrivkey": true, + "chain": "test" + } + ], + [ + "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h", + "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c", + { + "isCompressed": true, + "isPrivkey": true, + "chain": "test" + } + ], + [ + "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", + "76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac", + { + "isPrivkey": false, + "chain": "main" + } + ], + [ + "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", + "a9145ece0cadddc415b1980f001785947120acdb36fc87", + { + "isPrivkey": false, + "chain": "main" + } + ], + [ + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", + "0014751e76e8199196d454941c45d1b3a323f1433bd6", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080", + "0014751e76e8199196d454941c45d1b3a323f1433bd6", + { + "isPrivkey": false, + "chain": "regtest", + "tryCaseFlip": true + } + ], + [ + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", + { + "isPrivkey": false, + "chain": "test", + "tryCaseFlip": true + } + ], + [ + "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", + "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "bc1sw50qa3jx3s", + "6002751e", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", + "5210751e76e8199196d454941c45d1b3a323", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", + "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + { + "isPrivkey": false, + "chain": "test", + "tryCaseFlip": true + } + ], + [ + "bcrt1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvseswlauz7", + "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + { + "isPrivkey": false, + "chain": "regtest", + "tryCaseFlip": true } ] ] diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index 3d17a0dbb6..bd2d9ed115 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -170,11 +170,6 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure) s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD; BOOST_CHECK(!Solver(s, whichType, solutions)); - // TX_WITNESS with unknown version - s.clear(); - s << OP_1 << ToByteVector(pubkey); - BOOST_CHECK(!Solver(s, whichType, solutions)); - // TX_WITNESS with incorrect program size s.clear(); s << OP_0 << std::vector<unsigned char>(19, 0x01); @@ -225,13 +220,29 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) // TX_WITNESS_V0_KEYHASH s.clear(); - s << OP_0 << ToByteVector(pubkey); - BOOST_CHECK(!ExtractDestination(s, address)); + s << OP_0 << ToByteVector(pubkey.GetID()); + BOOST_CHECK(ExtractDestination(s, address)); + WitnessV0KeyHash keyhash; + CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(keyhash.begin()); + BOOST_CHECK(boost::get<WitnessV0KeyHash>(&address) && *boost::get<WitnessV0KeyHash>(&address) == keyhash); // TX_WITNESS_V0_SCRIPTHASH s.clear(); - s << OP_0 << ToByteVector(CScriptID(redeemScript)); - BOOST_CHECK(!ExtractDestination(s, address)); + WitnessV0ScriptHash scripthash; + CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin()); + s << OP_0 << ToByteVector(scripthash); + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get<WitnessV0ScriptHash>(&address) && *boost::get<WitnessV0ScriptHash>(&address) == scripthash); + + // TX_WITNESS with unknown version + s.clear(); + s << OP_1 << ToByteVector(pubkey); + BOOST_CHECK(ExtractDestination(s, address)); + WitnessUnknown unk; + unk.length = 33; + unk.version = 1; + std::copy(pubkey.begin(), pubkey.end(), unk.program); + BOOST_CHECK(boost::get<WitnessUnknown>(&address) && *boost::get<WitnessUnknown>(&address) == unk); } BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) @@ -298,16 +309,6 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) s.clear(); s << OP_RETURN << std::vector<unsigned char>({75}); BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); - - // TX_WITNESS_V0_KEYHASH - s.clear(); - s << OP_0 << ToByteVector(pubkeys[0].GetID()); - BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); - - // TX_WITNESS_V0_SCRIPTHASH - s.clear(); - s << OP_0 << ToByteVector(CScriptID(redeemScript)); - BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); } BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_) diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 192f33fb29..af33f0e5f8 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -149,4 +149,28 @@ bool TimingResistantEqual(const T& a, const T& b) */ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); +/** Convert from one power-of-2 number base to another. */ +template<int frombits, int tobits, bool pad, typename O, typename I> +bool ConvertBits(O& out, I it, I end) { + size_t acc = 0; + size_t bits = 0; + constexpr size_t maxv = (1 << tobits) - 1; + constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1; + while (it != end) { + acc = ((acc << frombits) | *it) & max_acc; + bits += frombits; + while (bits >= tobits) { + bits -= tobits; + out.push_back((acc >> bits) & maxv); + } + ++it; + } + if (pad) { + if (bits) out.push_back((acc << (tobits - bits)) & maxv); + } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { + return false; + } + return true; +} + #endif // BITCOIN_UTILSTRENCODINGS_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ae89b3c0a1..5d98498a4b 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1154,11 +1154,10 @@ class Witnessifier : public boost::static_visitor<bool> { public: CWallet * const pwallet; - CScriptID result; + CTxDestination result; + bool already_witness; - explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {} - - bool operator()(const CNoDestination &dest) const { return false; } + explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {} bool operator()(const CKeyID &keyID) { if (pwallet) { @@ -1172,9 +1171,7 @@ public: !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { return false; } - pwallet->AddCScript(witscript); - result = CScriptID(witscript); - return true; + return ExtractDestination(witscript, result); } return false; } @@ -1185,7 +1182,8 @@ public: int witnessversion; std::vector<unsigned char> witprog; if (subscript.IsWitnessProgram(witnessversion, witprog)) { - result = scriptID; + ExtractDestination(subscript, result); + already_witness = true; return true; } CScript witscript = GetScriptForWitness(subscript); @@ -1197,12 +1195,27 @@ public: !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { return false; } - pwallet->AddCScript(witscript); - result = CScriptID(witscript); - return true; + return ExtractDestination(witscript, result); } return false; } + + bool operator()(const WitnessV0KeyHash& id) + { + already_witness = true; + result = id; + return true; + } + + bool operator()(const WitnessV0ScriptHash& id) + { + already_witness = true; + result = id; + return true; + } + + template<typename T> + bool operator()(const T& dest) { return false; } }; UniValue addwitnessaddress(const JSONRPCRequest& request) @@ -1212,17 +1225,18 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { - std::string msg = "addwitnessaddress \"address\"\n" + std::string msg = "addwitnessaddress \"address\" ( p2sh )\n" "\nAdd a witness address for a script (with pubkey or redeemscript known).\n" "It returns the witness script.\n" "\nArguments:\n" "1. \"address\" (string, required) An address known to the wallet\n" + "2. p2sh (bool, optional, default=true) Embed inside P2SH\n" "\nResult:\n" - "\"witnessaddress\", (string) The value of the new address (P2SH of witness script).\n" + "\"witnessaddress\", (string) The value of the new address (P2SH or BIP173).\n" "}\n" ; throw std::runtime_error(msg); @@ -1240,13 +1254,31 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } + bool p2sh = true; + if (!request.params[1].isNull()) { + p2sh = request.params[1].get_bool(); + } + Witnessifier w(pwallet); bool ret = boost::apply_visitor(w, dest); if (!ret) { throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed"); } - pwallet->SetAddressBook(w.result, "", "receive"); + CScript witprogram = GetScriptForDestination(w.result); + + if (p2sh) { + w.result = CScriptID(witprogram); + } + + if (w.already_witness) { + if (!(dest == w.result)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types"); + } + } else { + pwallet->AddCScript(witprogram); + pwallet->SetAddressBook(w.result, "", "receive"); + } return EncodeDestination(w.result); } @@ -3199,7 +3231,7 @@ static const CRPCCommand commands[] = { "wallet", "abandontransaction", &abandontransaction, {"txid"} }, { "wallet", "abortrescan", &abortrescan, {} }, { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} }, - { "wallet", "addwitnessaddress", &addwitnessaddress, {"address"} }, + { "wallet", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, { "wallet", "backupwallet", &backupwallet, {"destination"} }, { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d83203f409..6760d6ff84 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -111,7 +111,26 @@ public: Process(script); } - void operator()(const CNoDestination &none) {} + void operator()(const WitnessV0ScriptHash& scriptID) + { + CScriptID id; + CRIPEMD160().Write(scriptID.begin(), 32).Finalize(id.begin()); + CScript script; + if (keystore.GetCScript(id, script)) { + Process(script); + } + } + + void operator()(const WitnessV0KeyHash& keyid) + { + CKeyID id(keyid); + if (keystore.HaveKey(id)) { + vKeys.push_back(id); + } + } + + template<typename X> + void operator()(const X &none) {} }; const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const |