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. 285
      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 = \
addrdb.h \ addrdb.h \
addrman.h \ addrman.h \
base58.h \ base58.h \
bech32.h \
bloom.h \ bloom.h \
blockencodings.h \ blockencodings.h \
chain.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_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_common_a_SOURCES = \ libbitcoin_common_a_SOURCES = \
base58.cpp \ base58.cpp \
bech32.cpp \
chainparams.cpp \ chainparams.cpp \
coins.cpp \ coins.cpp \
compressor.cpp \ compressor.cpp \

1
src/Makefile.test.include

@ -31,6 +31,7 @@ BITCOIN_TESTS =\
test/base32_tests.cpp \ test/base32_tests.cpp \
test/base58_tests.cpp \ test/base58_tests.cpp \
test/base64_tests.cpp \ test/base64_tests.cpp \
test/bech32_tests.cpp \
test/bip32_tests.cpp \ test/bip32_tests.cpp \
test/blockencodings_tests.cpp \ test/blockencodings_tests.cpp \
test/bloom_tests.cpp \ test/bloom_tests.cpp \

184
src/base58.cpp

@ -4,17 +4,20 @@
#include "base58.h" #include "base58.h"
#include "bech32.h"
#include "hash.h" #include "hash.h"
#include "script/script.h"
#include "uint256.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/apply_visitor.hpp>
#include <boost/variant/static_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" */ /** All alphanumeric characters except for "0", "I", "O", and "l" */
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
@ -212,86 +215,113 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const
namespace namespace
{ {
/** base58-encoded Bitcoin addresses. class DestinationEncoder : public boost::static_visitor<std::string>
* 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>
{ {
private: private:
CBitcoinAddress* addr; const CChainParams& m_params;
public: public:
explicit CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {} DestinationEncoder(const CChainParams& params) : m_params(params) {}
bool operator()(const CKeyID& id) const { return addr->Set(id); } std::string operator()(const CKeyID& id) const
bool operator()(const CScriptID& id) const { return addr->Set(id); } {
bool operator()(const CNoDestination& no) const { return false; } std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
}; data.insert(data.end(), id.begin(), id.end());
return EncodeBase58Check(data);
} // namespace }
bool CBitcoinAddress::Set(const CKeyID& id) std::string operator()(const CScriptID& id) const
{ {
SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20); std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
return true; data.insert(data.end(), id.begin(), id.end());
} return EncodeBase58Check(data);
}
bool CBitcoinAddress::Set(const CScriptID& id) std::string operator()(const WitnessV0KeyHash& id) const
{ {
SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20); std::vector<unsigned char> data = {0};
return true; ConvertBits<8, 5, true>(data, id.begin(), id.end());
} return bech32::Encode(m_params.Bech32HRP(), data);
}
bool CBitcoinAddress::Set(const CTxDestination& dest) std::string operator()(const WitnessV0ScriptHash& id) const
{ {
return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); 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 std::string operator()(const WitnessUnknown& id) const
{ {
return IsValid(Params()); 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 std::string operator()(const CNoDestination& no) const { return {}; }
{ };
bool fCorrectSize = vchData.size() == 20;
bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) ||
vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
return fCorrectSize && fKnownVersion;
}
CTxDestination CBitcoinAddress::Get() const CTxDestination DecodeDestination(const std::string& str, const CChainParams& params)
{ {
if (!IsValid()) std::vector<unsigned char> data;
return CNoDestination(); uint160 hash;
uint160 id; if (DecodeBase58Check(str, data)) {
memcpy(&id, vchData.data(), 20); // base58-encoded Bitcoin addresses.
if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) // Public-key-hash-addresses have version 0 (or 111 testnet).
return CKeyID(id); // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
return CScriptID(id); if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
else std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin());
return CNoDestination(); 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) void CBitcoinSecret::SetKey(const CKey& vchSecret)
{ {
@ -328,22 +358,20 @@ bool CBitcoinSecret::SetString(const std::string& strSecret)
std::string EncodeDestination(const CTxDestination& dest) std::string EncodeDestination(const CTxDestination& dest)
{ {
CBitcoinAddress addr(dest); return boost::apply_visitor(DestinationEncoder(Params()), dest);
if (!addr.IsValid()) return "";
return addr.ToString();
} }
CTxDestination DecodeDestination(const std::string& str) CTxDestination DecodeDestination(const std::string& str)
{ {
return CBitcoinAddress(str).Get(); return DecodeDestination(str, Params());
} }
bool IsValidDestinationString(const std::string& str, const CChainParams& 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) bool IsValidDestinationString(const std::string& str)
{ {
return CBitcoinAddress(str).IsValid(); return IsValidDestinationString(str, Params());
} }

1
src/base58.h

@ -17,7 +17,6 @@
#include "chainparams.h" #include "chainparams.h"
#include "key.h" #include "key.h"
#include "pubkey.h" #include "pubkey.h"
#include "script/script.h"
#include "script/standard.h" #include "script/standard.h"
#include "support/allocators/zeroafterfree.h" #include "support/allocators/zeroafterfree.h"

191
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

25
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

6
src/chainparams.cpp

@ -137,6 +137,8 @@ public:
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E}; base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4};
bech32_hrp = "bc";
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
fDefaultConsistencyChecks = false; fDefaultConsistencyChecks = false;
@ -236,6 +238,8 @@ public:
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
bech32_hrp = "tb";
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
fDefaultConsistencyChecks = false; fDefaultConsistencyChecks = false;
@ -330,6 +334,8 @@ public:
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
bech32_hrp = "bcrt";
} }
}; };

2
src/chainparams.h

@ -73,6 +73,7 @@ public:
std::string NetworkIDString() const { return strNetworkID; } std::string NetworkIDString() const { return strNetworkID; }
const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; } const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } 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 std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; } const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; } const ChainTxData& TxData() const { return chainTxData; }
@ -86,6 +87,7 @@ protected:
uint64_t nPruneAfterHeight; uint64_t nPruneAfterHeight;
std::vector<CDNSSeedData> vSeeds; std::vector<CDNSSeedData> vSeeds;
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES]; std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
std::string bech32_hrp;
std::string strNetworkID; std::string strNetworkID;
CBlock genesis; CBlock genesis;
std::vector<SeedSpec6> vFixedSeeds; std::vector<SeedSpec6> vFixedSeeds;

2
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)) else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH))
return false; 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) 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
if (((ch >= '0' && ch<='9') || if (((ch >= '0' && ch<='9') ||
(ch >= 'a' && ch<='z') || (ch >= 'a' && ch<='z') ||
(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 // Alphanumeric and not a 'forbidden' character
} }

1
src/rpc/client.cpp

@ -129,6 +129,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "logging", 0, "include" }, { "logging", 0, "include" },
{ "logging", 1, "exclude" }, { "logging", 1, "exclude" },
{ "disconnectnode", 1, "nodeid" }, { "disconnectnode", 1, "nodeid" },
{ "addwitnessaddress", 1, "p2sh" },
// Echo with conversion (For testing only) // Echo with conversion (For testing only)
{ "echojson", 0, "arg0" }, { "echojson", 0, "arg0" },
{ "echojson", 1, "arg1" }, { "echojson", 1, "arg1" },

44
src/rpc/misc.cpp

@ -7,6 +7,7 @@
#include "chain.h" #include "chain.h"
#include "clientversion.h" #include "clientversion.h"
#include "core_io.h" #include "core_io.h"
#include "crypto/ripemd160.h"
#include "init.h" #include "init.h"
#include "validation.h" #include "validation.h"
#include "httpserver.h" #include "httpserver.h"
@ -45,6 +46,7 @@ public:
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
CPubKey vchPubKey; CPubKey vchPubKey;
obj.push_back(Pair("isscript", false)); obj.push_back(Pair("isscript", false));
obj.push_back(Pair("iswitness", false));
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
obj.push_back(Pair("pubkey", HexStr(vchPubKey))); obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
@ -56,6 +58,7 @@ public:
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
CScript subscript; CScript subscript;
obj.push_back(Pair("isscript", true)); obj.push_back(Pair("isscript", true));
obj.push_back(Pair("iswitness", false));
if (pwallet && pwallet->GetCScript(scriptID, subscript)) { if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
std::vector<CTxDestination> addresses; std::vector<CTxDestination> addresses;
txnouttype whichType; txnouttype whichType;
@ -73,6 +76,47 @@ public:
} }
return obj; 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 #endif

1
src/script/ismine.cpp

@ -61,6 +61,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
{ {
case TX_NONSTANDARD: case TX_NONSTANDARD:
case TX_NULL_DATA: case TX_NULL_DATA:
case TX_WITNESS_UNKNOWN:
break; break;
case TX_PUBKEY: case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID(); keyID = CPubKey(vSolutions[0]).GetID();

2
src/script/sign.cpp

@ -79,6 +79,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
{ {
case TX_NONSTANDARD: case TX_NONSTANDARD:
case TX_NULL_DATA: case TX_NULL_DATA:
case TX_WITNESS_UNKNOWN:
return false; return false;
case TX_PUBKEY: case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID(); keyID = CPubKey(vSolutions[0]).GetID();
@ -309,6 +310,7 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature
{ {
case TX_NONSTANDARD: case TX_NONSTANDARD:
case TX_NULL_DATA: case TX_NULL_DATA:
case TX_WITNESS_UNKNOWN:
// Don't know anything about this, assume bigger one is correct: // Don't know anything about this, assume bigger one is correct:
if (sigs1.script.size() >= sigs2.script.size()) if (sigs1.script.size() >= sigs2.script.size())
return sigs1; return sigs1;

45
src/script/standard.cpp

@ -30,6 +30,7 @@ const char* GetTxnOutputType(txnouttype t)
case TX_NULL_DATA: return "nulldata"; case TX_NULL_DATA: return "nulldata";
case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
case TX_WITNESS_UNKNOWN: return "witness_unknown";
} }
return nullptr; return nullptr;
} }
@ -75,6 +76,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
vSolutionsRet.push_back(witnessprogram); vSolutionsRet.push_back(witnessprogram);
return true; 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; return false;
} }
@ -198,6 +205,23 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
{ {
addressRet = CScriptID(uint160(vSolutions[0])); addressRet = CScriptID(uint160(vSolutions[0]));
return true; 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... // Multisig txns have more than one address...
return false; return false;
@ -268,6 +292,27 @@ public:
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; *script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
return true; 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 } // namespace

37
src/script/standard.h

@ -64,6 +64,7 @@ enum txnouttype
TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data
TX_WITNESS_V0_SCRIPTHASH, TX_WITNESS_V0_SCRIPTHASH,
TX_WITNESS_V0_KEYHASH, TX_WITNESS_V0_KEYHASH,
TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
}; };
class CNoDestination { class CNoDestination {
@ -72,14 +73,42 @@ public:
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } 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: * A txout script template with a specific destination. It is either:
* * CNoDestination: no destination set * * CNoDestination: no destination set
* * CKeyID: TX_PUBKEYHASH destination * * CKeyID: TX_PUBKEYHASH destination (P2PKH)
* * CScriptID: TX_SCRIPTHASH destination * * 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 * 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. */ /** Check whether a CTxDestination is a CNoDestination. */
bool IsValidDestination(const CTxDestination& dest); 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 * Parse a standard scriptPubKey for the destination address. Assigns result to
* the addressRet parameter and returns true if successful. For multisig * the addressRet parameter and returns true if successful. For multisig
* scripts, instead use ExtractDestinations. Currently only works for P2PK, * 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); bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);

135
src/test/base58_tests.cpp

@ -10,14 +10,15 @@
#include "key.h" #include "key.h"
#include "script/script.h" #include "script/script.h"
#include "test/test_bitcoin.h"
#include "uint256.h" #include "uint256.h"
#include "util.h" #include "util.h"
#include "utilstrencodings.h" #include "utilstrencodings.h"
#include "test/test_bitcoin.h"
#include <univalue.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <univalue.h>
extern UniValue read_json(const std::string& jsondata); 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()); 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 // Goal: check that parsed keys match test payload
BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) 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++) { for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx]; UniValue test = tests[idx];
std::string strTest = test.write(); 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); BOOST_ERROR("Bad test: " << strTest);
continue; continue;
} }
@ -136,13 +92,9 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
const UniValue &metadata = test[2].get_obj(); const UniValue &metadata = test[2].get_obj();
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
bool isTestnet = find_value(metadata, "isTestnet").get_bool(); SelectParams(find_value(metadata, "chain").get_str());
if (isTestnet) bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool();
SelectParams(CBaseChainParams::TESTNET); if (isPrivkey) {
else
SelectParams(CBaseChainParams::MAIN);
if(isPrivkey)
{
bool isCompressed = find_value(metadata, "isCompressed").get_bool(); bool isCompressed = find_value(metadata, "isCompressed").get_bool();
// Must be valid private key // Must be valid private key
BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest); 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 // Private key must be invalid public key
destination = DecodeDestination(exp_base58string); destination = DecodeDestination(exp_base58string);
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest); BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest);
} } else {
else
{
std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey"
// Must be valid public key // Must be valid public key
destination = DecodeDestination(exp_base58string); destination = DecodeDestination(exp_base58string);
CScript script = GetScriptForDestination(destination);
BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest); BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
BOOST_CHECK_MESSAGE((boost::get<CScriptID>(&destination) != nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest); BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), destination), "addrType mismatch" + strTest);
// 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 // Public key must be invalid private key
secret.SetString(exp_base58string); 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()); std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
const UniValue &metadata = test[2].get_obj(); const UniValue &metadata = test[2].get_obj();
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
bool isTestnet = find_value(metadata, "isTestnet").get_bool(); SelectParams(find_value(metadata, "chain").get_str());
if (isTestnet) if (isPrivkey) {
SelectParams(CBaseChainParams::TESTNET);
else
SelectParams(CBaseChainParams::MAIN);
if(isPrivkey)
{
bool isCompressed = find_value(metadata, "isCompressed").get_bool(); bool isCompressed = find_value(metadata, "isCompressed").get_bool();
CKey key; CKey key;
key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
@ -202,36 +161,20 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
CBitcoinSecret secret; CBitcoinSecret secret;
secret.SetKey(key); secret.SetKey(key);
BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest); BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest);
} } else {
else
{
std::string exp_addrType = find_value(metadata, "addrType").get_str();
CTxDestination dest; CTxDestination dest;
if(exp_addrType == "pubkey") CScript exp_script(exp_payload.begin(), exp_payload.end());
{ ExtractDestination(exp_script, dest);
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;
}
std::string address = EncodeDestination(dest); std::string address = EncodeDestination(dest);
BOOST_CHECK_MESSAGE(address == exp_base58string, "mismatch: " + strTest);
BOOST_CHECK_EQUAL(address, exp_base58string);
} }
} }
SelectParams(CBaseChainParams::MAIN); SelectParams(CBaseChainParams::MAIN);
} }
// Goal: check that base58 parsing code is robust against a variety of corrupted data // Goal: check that base58 parsing code is robust against a variety of corrupted data
BOOST_AUTO_TEST_CASE(base58_keys_invalid) 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(); std::string exp_base58string = test[0].get_str();
// must be invalid as public and as private key // must be invalid as public and as private key
destination = DecodeDestination(exp_base58string); for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) {
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest); SelectParams(chain);
secret.SetString(exp_base58string); destination = DecodeDestination(exp_base58string);
BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest); 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() BOOST_AUTO_TEST_SUITE_END()

67
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()

30
src/test/data/base58_keys_invalid.json

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

285
src/test/data/base58_keys_valid.json

@ -1,38 +1,42 @@
[ [
[ [
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
"65a16059864a2fdbc7c99a4723a8395bc6f188eb", "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou",
"74f209f6ea907e2ea48f74fae05782ae8a665257", "a91474f209f6ea907e2ea48f74fae05782ae8a66525787",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",
"53c0307d6851aa0ce7825ba883c6bd9ad242b486", "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": true "chain": "test"
}
],
[
"mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",
"76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",
{
"isPrivkey": false,
"chain": "regtest"
} }
], ],
[ [
"2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br",
"6349a418fc4578d10a372b54b45c280cc8c4382f", "a9146349a418fc4578d10a372b54b45c280cc8c4382f87",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": true "chain": "test"
} }
], ],
[ [
@ -41,7 +45,7 @@
{ {
"isCompressed": false, "isCompressed": false,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": false "chain": "main"
} }
], ],
[ [
@ -50,7 +54,16 @@
{ {
"isCompressed": true, "isCompressed": true,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": false "chain": "main"
}
],
[
"9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",
"36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",
{
"isCompressed": false,
"isPrivkey": true,
"chain": "test"
} }
], ],
[ [
@ -59,7 +72,7 @@
{ {
"isCompressed": false, "isCompressed": false,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": true "chain": "regtest"
} }
], ],
[ [
@ -68,43 +81,48 @@
{ {
"isCompressed": true, "isCompressed": true,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": true "chain": "test"
}
],
[
"cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",
"b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",
{
"isCompressed": true,
"isPrivkey": true,
"chain": "regtest"
} }
], ],
[ [
"1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ",
"6d23156cbbdcc82a5a47eee4c2c7c583c18b6bf4", "76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy",
"fcc5460dd6e2487c7d75b1963625da0e8f4c5975", "a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ",
"f1d470f9b02370fdec2e6b708b08ac431bf7a5f7", "76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": true "chain": "test"
} }
], ],
[ [
"2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n",
"c579342c2c4c9220205e2cdc285617040c924a0a", "a914c579342c2c4c9220205e2cdc285617040c924a0a87",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": true "chain": "test"
} }
], ],
[ [
@ -113,7 +131,7 @@
{ {
"isCompressed": false, "isCompressed": false,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": false "chain": "main"
} }
], ],
[ [
@ -122,7 +140,7 @@
{ {
"isCompressed": true, "isCompressed": true,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": false "chain": "main"
} }
], ],
[ [
@ -131,7 +149,7 @@
{ {
"isCompressed": false, "isCompressed": false,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": true "chain": "test"
} }
], ],
[ [
@ -140,43 +158,39 @@
{ {
"isCompressed": true, "isCompressed": true,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": true "chain": "test"
} }
], ],
[ [
"1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv",
"7987ccaa53d02c8873487ef919677cd3db7a6912", "76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks",
"63bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb", "a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk",
"ef66444b5b17f14e8fae6e7e19b045a78c54fd79", "76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": true "chain": "test"
} }
], ],
[ [
"2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o",
"c3e55fceceaa4391ed2a9677f4a4d34eacd021a0", "a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": true "chain": "test"
} }
], ],
[ [
@ -185,7 +199,7 @@
{ {
"isCompressed": false, "isCompressed": false,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": false "chain": "main"
} }
], ],
[ [
@ -194,7 +208,7 @@
{ {
"isCompressed": true, "isCompressed": true,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": false "chain": "main"
} }
], ],
[ [
@ -203,7 +217,7 @@
{ {
"isCompressed": false, "isCompressed": false,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": true "chain": "test"
} }
], ],
[ [
@ -212,43 +226,39 @@
{ {
"isCompressed": true, "isCompressed": true,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": true "chain": "test"
} }
], ],
[ [
"1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu",
"adc1cc2081a27206fae25792f28bbc55b831549d", "76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk",
"188f91a931947eddd7432d6e614387e32b244709", "a914188f91a931947eddd7432d6e614387e32b24470987",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H",
"1694f5bc1a7295b600f40018a618a6ea48eeb498", "76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": true "chain": "test"
} }
], ],
[ [
"2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN",
"3b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f3", "a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": true "chain": "test"
} }
], ],
[ [
@ -257,7 +267,7 @@
{ {
"isCompressed": false, "isCompressed": false,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": false "chain": "main"
} }
], ],
[ [
@ -266,7 +276,7 @@
{ {
"isCompressed": true, "isCompressed": true,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": false "chain": "main"
} }
], ],
[ [
@ -275,7 +285,16 @@
{ {
"isCompressed": false, "isCompressed": false,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": true "chain": "test"
}
],
[
"92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",
"b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",
{
"isCompressed": false,
"isPrivkey": true,
"chain": "regtest"
} }
], ],
[ [
@ -284,43 +303,39 @@
{ {
"isCompressed": true, "isCompressed": true,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": true "chain": "test"
} }
], ],
[ [
"1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4",
"c4c1b72491ede1eedaca00618407ee0b772cad0d", "76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y",
"f6fe69bcb548a829cce4c57bf6fff8af3a5981f9", "a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6",
"261f83568a098a8638844bd7aeca039d5f2352c0", "76a914261f83568a098a8638844bd7aeca039d5f2352c088ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": true "chain": "test"
} }
], ],
[ [
"2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda",
"e930e1834a4d234702773951d627cce82fbb5d2e", "a914e930e1834a4d234702773951d627cce82fbb5d2e87",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": true "chain": "test"
} }
], ],
[ [
@ -329,7 +344,7 @@
{ {
"isCompressed": false, "isCompressed": false,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": false "chain": "main"
} }
], ],
[ [
@ -338,7 +353,7 @@
{ {
"isCompressed": true, "isCompressed": true,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": false "chain": "main"
} }
], ],
[ [
@ -347,7 +362,7 @@
{ {
"isCompressed": false, "isCompressed": false,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": true "chain": "test"
} }
], ],
[ [
@ -356,43 +371,39 @@
{ {
"isCompressed": true, "isCompressed": true,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": true "chain": "test"
} }
], ],
[ [
"19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r",
"5eadaf9bb7121f0f192561a5a62f5e5f54210292", "76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3",
"3f210e7277c899c3a155cc1c90f4106cbddeec6e", "a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"myoqcgYiehufrsnnkqdqbp69dddVDMopJu", "myoqcgYiehufrsnnkqdqbp69dddVDMopJu",
"c8a3c2a09a298592c3e180f02487cd91ba3400b5", "76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": true "chain": "test"
} }
], ],
[ [
"2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C",
"99b31df7c9068d1481b596578ddbb4d3bd90baeb", "a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": true "chain": "test"
} }
], ],
[ [
@ -401,7 +412,7 @@
{ {
"isCompressed": false, "isCompressed": false,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": false "chain": "main"
} }
], ],
[ [
@ -410,7 +421,7 @@
{ {
"isCompressed": true, "isCompressed": true,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": false "chain": "main"
} }
], ],
[ [
@ -419,7 +430,7 @@
{ {
"isCompressed": false, "isCompressed": false,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": true "chain": "test"
} }
], ],
[ [
@ -428,25 +439,95 @@
{ {
"isCompressed": true, "isCompressed": true,
"isPrivkey": true, "isPrivkey": true,
"isTestnet": true "chain": "test"
} }
], ],
[ [
"13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE",
"1ed467017f043e91ed4c44b4e8dd674db211c4e6", "76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac",
{ {
"addrType": "pubkey",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "main"
} }
], ],
[ [
"3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G",
"5ece0cadddc415b1980f001785947120acdb36fc", "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",
{ {
"addrType": "script",
"isPrivkey": false, "isPrivkey": false,
"isTestnet": false "chain": "regtest",
"tryCaseFlip": true
} }
] ]
] ]

39
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; s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD;
BOOST_CHECK(!Solver(s, whichType, solutions)); 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 // TX_WITNESS with incorrect program size
s.clear(); s.clear();
s << OP_0 << std::vector<unsigned char>(19, 0x01); s << OP_0 << std::vector<unsigned char>(19, 0x01);
@ -225,13 +220,29 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
// TX_WITNESS_V0_KEYHASH // TX_WITNESS_V0_KEYHASH
s.clear(); s.clear();
s << OP_0 << ToByteVector(pubkey); s << OP_0 << ToByteVector(pubkey.GetID());
BOOST_CHECK(!ExtractDestination(s, address)); 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 // TX_WITNESS_V0_SCRIPTHASH
s.clear(); s.clear();
s << OP_0 << ToByteVector(CScriptID(redeemScript)); WitnessV0ScriptHash scripthash;
BOOST_CHECK(!ExtractDestination(s, address)); 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) BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
@ -298,16 +309,6 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
s.clear(); s.clear();
s << OP_RETURN << std::vector<unsigned char>({75}); s << OP_RETURN << std::vector<unsigned char>({75});
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); 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_) BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)

24
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); 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 #endif // BITCOIN_UTILSTRENCODINGS_H

64
src/wallet/rpcwallet.cpp

@ -1154,11 +1154,10 @@ class Witnessifier : public boost::static_visitor<bool>
{ {
public: public:
CWallet * const pwallet; CWallet * const pwallet;
CScriptID result; CTxDestination result;
bool already_witness;
explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {} explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {}
bool operator()(const CNoDestination &dest) const { return false; }
bool operator()(const CKeyID &keyID) { bool operator()(const CKeyID &keyID) {
if (pwallet) { if (pwallet) {
@ -1172,9 +1171,7 @@ public:
!VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) {
return false; return false;
} }
pwallet->AddCScript(witscript); return ExtractDestination(witscript, result);
result = CScriptID(witscript);
return true;
} }
return false; return false;
} }
@ -1185,7 +1182,8 @@ public:
int witnessversion; int witnessversion;
std::vector<unsigned char> witprog; std::vector<unsigned char> witprog;
if (subscript.IsWitnessProgram(witnessversion, witprog)) { if (subscript.IsWitnessProgram(witnessversion, witprog)) {
result = scriptID; ExtractDestination(subscript, result);
already_witness = true;
return true; return true;
} }
CScript witscript = GetScriptForWitness(subscript); 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())) { !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) {
return false; return false;
} }
pwallet->AddCScript(witscript); return ExtractDestination(witscript, result);
result = CScriptID(witscript);
return true;
} }
return false; 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) UniValue addwitnessaddress(const JSONRPCRequest& request)
@ -1212,17 +1225,18 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
return NullUniValue; 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" "\nAdd a witness address for a script (with pubkey or redeemscript known).\n"
"It returns the witness script.\n" "It returns the witness script.\n"
"\nArguments:\n" "\nArguments:\n"
"1. \"address\" (string, required) An address known to the wallet\n" "1. \"address\" (string, required) An address known to the wallet\n"
"2. p2sh (bool, optional, default=true) Embed inside P2SH\n"
"\nResult:\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" "}\n"
; ;
throw std::runtime_error(msg); throw std::runtime_error(msg);
@ -1240,13 +1254,31 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); 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); Witnessifier w(pwallet);
bool ret = boost::apply_visitor(w, dest); bool ret = boost::apply_visitor(w, dest);
if (!ret) { if (!ret) {
throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed"); 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); return EncodeDestination(w.result);
} }
@ -3199,7 +3231,7 @@ static const CRPCCommand commands[] =
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} }, { "wallet", "abandontransaction", &abandontransaction, {"txid"} },
{ "wallet", "abortrescan", &abortrescan, {} }, { "wallet", "abortrescan", &abortrescan, {} },
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} }, { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} },
{ "wallet", "addwitnessaddress", &addwitnessaddress, {"address"} }, { "wallet", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
{ "wallet", "backupwallet", &backupwallet, {"destination"} }, { "wallet", "backupwallet", &backupwallet, {"destination"} },
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, { "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, { "wallet", "dumpprivkey", &dumpprivkey, {"address"} },

21
src/wallet/wallet.cpp

@ -111,7 +111,26 @@ public:
Process(script); 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 const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const

63
test/functional/segwit.py

@ -7,7 +7,7 @@
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import * from test_framework.util import *
from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex 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 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 from io import BytesIO
@ -33,15 +33,15 @@ def witness_script(use_p2wsh, pubkey):
# Return a transaction (in hex) that spends the given utxo to a segwit output, # Return a transaction (in hex) that spends the given utxo to a segwit output,
# optionally wrapping the segwit output using P2SH. # optionally wrapping the segwit output using P2SH.
def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount): def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
pkscript = hex_str_to_bytes(witness_script(use_p2wsh, pubkey)) if use_p2wsh:
if (encode_p2sh): program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
p2sh_hash = hash160(pkscript) addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
pkscript = CScript([OP_HASH160, p2sh_hash, OP_EQUAL]) else:
tx = CTransaction() addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey)
tx.vin.append(CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), b"")) if not encode_p2sh:
tx.vout.append(CTxOut(int(amount*COIN), pkscript)) assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey))
return ToHex(tx) return node.createrawtransaction([utxo], {addr: amount})
# Create a transaction spending a given utxo to a segwit output corresponding # 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; # 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):
# sign=True will have the given node sign the transaction. # sign=True will have the given node sign the transaction.
# insert_redeem_script will be added to the scriptSig, if given. # 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=""): 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): if (sign):
signed = node.signrawtransaction(tx_to_witness) signed = node.signrawtransaction(tx_to_witness)
assert("errors" not in signed or len(["errors"]) == 0) assert("errors" not in signed or len(["errors"]) == 0)
@ -133,8 +133,15 @@ class SegWitTest(BitcoinTestFramework):
newaddress = self.nodes[i].getnewaddress() newaddress = self.nodes[i].getnewaddress()
self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"]) self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"])
multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]]) multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]])
self.nodes[i].addwitnessaddress(newaddress) multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG])
self.nodes[i].addwitnessaddress(multiaddress) 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([]) p2sh_ids.append([])
wit_ids.append([]) wit_ids.append([])
for v in range(2): for v in range(2):
@ -558,6 +565,13 @@ class SegWitTest(BitcoinTestFramework):
solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1)) solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1))
self.mine_and_test_listunspent(unseen_anytime, 0) 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 # Check that spendable outputs are really spendable
self.create_and_mine_tx_from_txids(spendable_txid) self.create_and_mine_tx_from_txids(spendable_txid)
@ -570,6 +584,29 @@ class SegWitTest(BitcoinTestFramework):
self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K") self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K")
self.create_and_mine_tx_from_txids(solvable_txid) 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): def mine_and_test_listunspent(self, script_list, ismine):
utxo = find_unspent(self.nodes[0], 50) utxo = find_unspent(self.nodes[0], 50)
tx = CTransaction() tx = CTransaction()

28
test/functional/test_framework/address.py

@ -7,6 +7,8 @@
from .script import hash256, hash160, sha256, CScript, OP_0 from .script import hash256, hash160, sha256, CScript, OP_0
from .util import bytes_to_hex_str, hex_str_to_bytes from .util import bytes_to_hex_str, hex_str_to_bytes
from . import segwit_addr
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def byte_to_base58(b, version): def byte_to_base58(b, version):
@ -44,6 +46,32 @@ def script_to_p2sh(script, main = False):
script = check_script(script) script = check_script(script)
return scripthash_to_p2sh(hash160(script), main) 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): def check_key(key):
if (type(key) is str): if (type(key) is str):
key = hex_str_to_bytes(key) # Assuming this is hex string key = hex_str_to_bytes(key) # Assuming this is hex string

107
test/functional/test_framework/segwit_addr.py

@ -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 @@
"scriptPubKey": { "scriptPubKey": {
"asm": "0 e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05", "asm": "0 e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05",
"hex": "0020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05", "hex": "0020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05",
"type": "witness_v0_scripthash" "reqSigs": 1,
"type": "witness_v0_scripthash",
"addresses": [
"bc1qu9dgdg330r6r84g5mw7wqshg04exv2uttmw2elfwx74h5tgntuzs44gyfg"
]
} }
} }
], ],

6
test/util/data/txcreateoutpubkey2.json

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

6
test/util/data/txcreatescript3.json

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

Loading…
Cancel
Save