Browse Source

Merge #11167: Full BIP173 (Bech32) support

8213838 [Qt] tolerate BIP173/bech32 addresses during input validation (Jonas Schnelli)
06eaca6 [RPC] Wallet: test importing of native witness scripts (NicolasDorier)
fd0041a Use BIP173 addresses in segwit.py test (Pieter Wuille)
e278f12 Support BIP173 in addwitnessaddress (Pieter Wuille)
c091b99 Implement BIP173 addresses and tests (Pieter Wuille)
bd355b8 Add regtest testing to base58_tests (Pieter Wuille)
6565c55 Convert base58_tests from type/payload to scriptPubKey comparison (Pieter Wuille)
8fd2267 Import Bech32 C++ reference code & tests (Pieter Wuille)
1e46ebd Implement {Encode,Decode}Destination without CBitcoinAddress (Pieter Wuille)

Pull request description:

  Builds on top of #11117.

  This adds support for:
  * Creating BIP173 addresses for testing (through `addwitnessaddress`, though by default it still produces P2SH versions)
  * Sending to BIP173 addresses (including non-v0 ones)
  * Analysing BIP173 addresses (through `validateaddress`)

  It includes a reformatted version of the [C++ Bech32 reference code](https://github.com/sipa/bech32/tree/master/ref/c%2B%2B) and an independent implementation of the address encoding/decoding logic (integrated with CTxDestination). All BIP173 test vectors are included.

  Not included (and intended for other PRs):
  * Full wallet support for SegWit (which would include automatically adding witness scripts to the wallet during automatic keypool topup, SegWit change outputs, ...) [see #11403]
  * Splitting base58.cpp and tests/base58_tests.cpp up into base58-specific code, and "address encoding"-code [see #11372]
  * Error locating in UI for BIP173 addresses.

Tree-SHA512: 238031185fd07f3ac873c586043970cc2db91bf7735c3c168cb33a3db39a7bda81d4891b649685bb17ef90dc63af0328e7705d8cd3e8dafd6c4d3c08fb230341
0.16
Wladimir J. van der Laan 7 years ago
parent
commit
aa624b61c9
No known key found for this signature in database
GPG Key ID: 1E4AED62986CD25D
  1. 2
      src/Makefile.am
  2. 1
      src/Makefile.test.include
  3. 184
      src/base58.cpp
  4. 1
      src/base58.h
  5. 191
      src/bech32.cpp
  6. 25
      src/bech32.h
  7. 6
      src/chainparams.cpp
  8. 2
      src/chainparams.h
  9. 2
      src/policy/policy.cpp
  10. 2
      src/qt/bitcoinaddressvalidator.cpp
  11. 1
      src/rpc/client.cpp
  12. 44
      src/rpc/misc.cpp
  13. 1
      src/script/ismine.cpp
  14. 2
      src/script/sign.cpp
  15. 45
      src/script/standard.cpp
  16. 37
      src/script/standard.h
  17. 135
      src/test/base58_tests.cpp
  18. 67
      src/test/bech32_tests.cpp
  19. 30
      src/test/data/base58_keys_invalid.json
  20. 679
      src/test/data/base58_keys_valid.json
  21. 39
      src/test/script_standard_tests.cpp
  22. 24
      src/utilstrencodings.h
  23. 64
      src/wallet/rpcwallet.cpp
  24. 21
      src/wallet/wallet.cpp
  25. 63
      test/functional/segwit.py
  26. 28
      test/functional/test_framework/address.py
  27. 107
      test/functional/test_framework/segwit_addr.py
  28. 6
      test/util/data/txcreatemultisig3.json
  29. 6
      test/util/data/txcreateoutpubkey2.json
  30. 6
      test/util/data/txcreatescript3.json

2
src/Makefile.am

@ -78,6 +78,7 @@ BITCOIN_CORE_H = \ @@ -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) @@ -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 \

1
src/Makefile.test.include

@ -31,6 +31,7 @@ BITCOIN_TESTS =\ @@ -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 \

184
src/base58.cpp

@ -4,17 +4,20 @@ @@ -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 @@ -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 &params) 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) @@ -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());
}

1
src/base58.h

@ -17,7 +17,6 @@ @@ -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"

191
src/bech32.cpp

@ -0,0 +1,191 @@ @@ -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

25
src/bech32.h

@ -0,0 +1,25 @@ @@ -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

6
src/chainparams.cpp

@ -137,6 +137,8 @@ public: @@ -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: @@ -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: @@ -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";
}
};

2
src/chainparams.h

@ -73,6 +73,7 @@ public: @@ -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: @@ -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;

2
src/policy/policy.cpp

@ -76,7 +76,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool w @@ -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)

2
src/qt/bitcoinaddressvalidator.cpp

@ -67,7 +67,7 @@ QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &po @@ -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
}

1
src/rpc/client.cpp

@ -129,6 +129,7 @@ static const CRPCConvertParam vRPCConvertParams[] = @@ -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" },

44
src/rpc/misc.cpp

@ -7,6 +7,7 @@ @@ -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: @@ -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: @@ -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: @@ -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

1
src/script/ismine.cpp

@ -61,6 +61,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& @@ -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();

2
src/script/sign.cpp

@ -79,6 +79,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP @@ -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 @@ -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;

45
src/script/standard.cpp

@ -30,6 +30,7 @@ const char* GetTxnOutputType(txnouttype t) @@ -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 @@ -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) @@ -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: @@ -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

37
src/script/standard.h

@ -64,6 +64,7 @@ enum txnouttype @@ -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: @@ -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 @@ -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);

135
src/test/base58_tests.cpp

@ -10,14 +10,15 @@ @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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()

67
src/test/bech32_tests.cpp

@ -0,0 +1,67 @@ @@ -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()

30
src/test/data/base58_keys_invalid.json

@ -148,5 +148,35 @@ @@ -148,5 +148,35 @@
],
[
"2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED"
],
[
"tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"
],
[
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"
],
[
"BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"
],
[
"bc1rw5uspcuh"
],
[
"bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"
],
[
"BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"
],
[
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"
],
[
"bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"
],
[
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"
],
[
"bc1gmk9yu"
]
]

679
src/test/data/base58_keys_valid.json

@ -1,452 +1,533 @@ @@ -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
}
]
]

39
src/test/script_standard_tests.cpp

@ -170,11 +170,6 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure) @@ -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) @@ -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) @@ -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_)

24
src/utilstrencodings.h

@ -149,4 +149,28 @@ bool TimingResistantEqual(const T& a, const T& b) @@ -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

64
src/wallet/rpcwallet.cpp

@ -1154,11 +1154,10 @@ class Witnessifier : public boost::static_visitor<bool> @@ -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: @@ -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: @@ -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: @@ -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) @@ -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) @@ -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[] = @@ -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"} },

21
src/wallet/wallet.cpp

@ -111,7 +111,26 @@ public: @@ -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

63
test/functional/segwit.py

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex
from test_framework.address import script_to_p2sh, key_to_p2pkh
from test_framework.address import script_to_p2sh, key_to_p2pkh, key_to_p2sh_p2wpkh, key_to_p2wpkh, script_to_p2sh_p2wsh, script_to_p2wsh, program_to_witness
from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE
from io import BytesIO
@ -33,15 +33,15 @@ def witness_script(use_p2wsh, pubkey): @@ -33,15 +33,15 @@ def witness_script(use_p2wsh, pubkey):
# Return a transaction (in hex) that spends the given utxo to a segwit output,
# optionally wrapping the segwit output using P2SH.
def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount):
pkscript = hex_str_to_bytes(witness_script(use_p2wsh, pubkey))
if (encode_p2sh):
p2sh_hash = hash160(pkscript)
pkscript = CScript([OP_HASH160, p2sh_hash, OP_EQUAL])
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), b""))
tx.vout.append(CTxOut(int(amount*COIN), pkscript))
return ToHex(tx)
def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
if use_p2wsh:
program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
else:
addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey)
if not encode_p2sh:
assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey))
return node.createrawtransaction([utxo], {addr: amount})
# Create a transaction spending a given utxo to a segwit output corresponding
# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH;
@ -49,7 +49,7 @@ def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount): @@ -49,7 +49,7 @@ def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount):
# sign=True will have the given node sign the transaction.
# insert_redeem_script will be added to the scriptSig, if given.
def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""):
tx_to_witness = create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount)
tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount)
if (sign):
signed = node.signrawtransaction(tx_to_witness)
assert("errors" not in signed or len(["errors"]) == 0)
@ -133,8 +133,15 @@ class SegWitTest(BitcoinTestFramework): @@ -133,8 +133,15 @@ class SegWitTest(BitcoinTestFramework):
newaddress = self.nodes[i].getnewaddress()
self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"])
multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]])
self.nodes[i].addwitnessaddress(newaddress)
self.nodes[i].addwitnessaddress(multiaddress)
multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG])
p2sh_addr = self.nodes[i].addwitnessaddress(newaddress, True)
bip173_addr = self.nodes[i].addwitnessaddress(newaddress, False)
p2sh_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, True)
bip173_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, False)
assert_equal(p2sh_addr, key_to_p2sh_p2wpkh(self.pubkey[-1]))
assert_equal(bip173_addr, key_to_p2wpkh(self.pubkey[-1]))
assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript))
assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript))
p2sh_ids.append([])
wit_ids.append([])
for v in range(2):
@ -558,6 +565,13 @@ class SegWitTest(BitcoinTestFramework): @@ -558,6 +565,13 @@ class SegWitTest(BitcoinTestFramework):
solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1))
self.mine_and_test_listunspent(unseen_anytime, 0)
# Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works
v1_addr = program_to_witness(1, [3,5])
v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])],{v1_addr: 1})
v1_decoded = self.nodes[1].decoderawtransaction(v1_tx)
assert_equal(v1_decoded['vout'][0]['scriptPubKey']['addresses'][0], v1_addr)
assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305")
# Check that spendable outputs are really spendable
self.create_and_mine_tx_from_txids(spendable_txid)
@ -570,6 +584,29 @@ class SegWitTest(BitcoinTestFramework): @@ -570,6 +584,29 @@ class SegWitTest(BitcoinTestFramework):
self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K")
self.create_and_mine_tx_from_txids(solvable_txid)
# Test that importing native P2WPKH/P2WSH scripts works
for use_p2wsh in [False, True]:
if use_p2wsh:
scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a"
transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000"
else:
scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87"
transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000"
self.nodes[1].importaddress(scriptPubKey, "", False)
rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex']
rawtxfund = self.nodes[1].signrawtransaction(rawtxfund)["hex"]
txid = self.nodes[1].sendrawtransaction(rawtxfund)
assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
# Assert it is properly saved
self.stop_node(1)
self.start_node(1)
assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
def mine_and_test_listunspent(self, script_list, ismine):
utxo = find_unspent(self.nodes[0], 50)
tx = CTransaction()

28
test/functional/test_framework/address.py

@ -7,6 +7,8 @@ @@ -7,6 +7,8 @@
from .script import hash256, hash160, sha256, CScript, OP_0
from .util import bytes_to_hex_str, hex_str_to_bytes
from . import segwit_addr
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def byte_to_base58(b, version):
@ -44,6 +46,32 @@ def script_to_p2sh(script, main = False): @@ -44,6 +46,32 @@ def script_to_p2sh(script, main = False):
script = check_script(script)
return scripthash_to_p2sh(hash160(script), main)
def key_to_p2sh_p2wpkh(key, main = False):
key = check_key(key)
p2shscript = CScript([OP_0, hash160(key)])
return script_to_p2sh(p2shscript, main)
def program_to_witness(version, program, main = False):
if (type(program) is str):
program = hex_str_to_bytes(program)
assert 0 <= version <= 16
assert 2 <= len(program) <= 40
assert version > 0 or len(program) in [20, 32]
return segwit_addr.encode("bc" if main else "bcrt", version, program)
def script_to_p2wsh(script, main = False):
script = check_script(script)
return program_to_witness(0, sha256(script), main)
def key_to_p2wpkh(key, main = False):
key = check_key(key)
return program_to_witness(0, hash160(key), main)
def script_to_p2sh_p2wsh(script, main = False):
script = check_script(script)
p2shscript = CScript([OP_0, sha256(script)])
return script_to_p2sh(p2shscript, main)
def check_key(key):
if (type(key) is str):
key = hex_str_to_bytes(key) # Assuming this is hex string

107
test/functional/test_framework/segwit_addr.py

@ -0,0 +1,107 @@ @@ -0,0 +1,107 @@
#!/usr/bin/env python3
# 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.
"""Reference implementation for Bech32 and segwit addresses."""
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
def bech32_polymod(values):
"""Internal function that computes the Bech32 checksum."""
generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
chk = 1
for value in values:
top = chk >> 25
chk = (chk & 0x1ffffff) << 5 ^ value
for i in range(5):
chk ^= generator[i] if ((top >> i) & 1) else 0
return chk
def bech32_hrp_expand(hrp):
"""Expand the HRP into values for checksum computation."""
return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
def bech32_verify_checksum(hrp, data):
"""Verify a checksum given HRP and converted data characters."""
return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1
def bech32_create_checksum(hrp, data):
"""Compute the checksum values given HRP and data."""
values = bech32_hrp_expand(hrp) + data
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
def bech32_encode(hrp, data):
"""Compute a Bech32 string given HRP and data values."""
combined = data + bech32_create_checksum(hrp, data)
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
def bech32_decode(bech):
"""Validate a Bech32 string, and determine HRP and data."""
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
(bech.lower() != bech and bech.upper() != bech)):
return (None, None)
bech = bech.lower()
pos = bech.rfind('1')
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
return (None, None)
if not all(x in CHARSET for x in bech[pos+1:]):
return (None, None)
hrp = bech[:pos]
data = [CHARSET.find(x) for x in bech[pos+1:]]
if not bech32_verify_checksum(hrp, data):
return (None, None)
return (hrp, data[:-6])
def convertbits(data, frombits, tobits, pad=True):
"""General power-of-2 base conversion."""
acc = 0
bits = 0
ret = []
maxv = (1 << tobits) - 1
max_acc = (1 << (frombits + tobits - 1)) - 1
for value in data:
if value < 0 or (value >> frombits):
return None
acc = ((acc << frombits) | value) & max_acc
bits += frombits
while bits >= tobits:
bits -= tobits
ret.append((acc >> bits) & maxv)
if pad:
if bits:
ret.append((acc << (tobits - bits)) & maxv)
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
return None
return ret
def decode(hrp, addr):
"""Decode a segwit address."""
hrpgot, data = bech32_decode(addr)
if hrpgot != hrp:
return (None, None)
decoded = convertbits(data[1:], 5, 8, False)
if decoded is None or len(decoded) < 2 or len(decoded) > 40:
return (None, None)
if data[0] > 16:
return (None, None)
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
return (None, None)
return (data[0], decoded)
def encode(hrp, witver, witprog):
"""Encode a segwit address."""
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
if decode(hrp, ret) == (None, None):
return None
return ret

6
test/util/data/txcreatemultisig3.json

@ -14,7 +14,11 @@ @@ -14,7 +14,11 @@
"scriptPubKey": {
"asm": "0 e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05",
"hex": "0020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05",
"type": "witness_v0_scripthash"
"reqSigs": 1,
"type": "witness_v0_scripthash",
"addresses": [
"bc1qu9dgdg330r6r84g5mw7wqshg04exv2uttmw2elfwx74h5tgntuzs44gyfg"
]
}
}
],

6
test/util/data/txcreateoutpubkey2.json

@ -14,7 +14,11 @@ @@ -14,7 +14,11 @@
"scriptPubKey": {
"asm": "0 a2516e770582864a6a56ed21a102044e388c62e3",
"hex": "0014a2516e770582864a6a56ed21a102044e388c62e3",
"type": "witness_v0_keyhash"
"reqSigs": 1,
"type": "witness_v0_keyhash",
"addresses": [
"bc1q5fgkuac9s2ry56jka5s6zqsyfcugcchry5cwu0"
]
}
}
],

6
test/util/data/txcreatescript3.json

@ -14,7 +14,11 @@ @@ -14,7 +14,11 @@
"scriptPubKey": {
"asm": "0 0bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6",
"hex": "00200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6",
"type": "witness_v0_scripthash"
"reqSigs": 1,
"type": "witness_v0_scripthash",
"addresses": [
"bc1qp0lfxhnscvsu0j36l36uurgv5tuck4pzuqytkvwqp3kh78cupttqyf705v"
]
}
}
],

Loading…
Cancel
Save