mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-10 06:39:15 +02:00
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)fd0041aUse BIP173 addresses in segwit.py test (Pieter Wuille)e278f12Support BIP173 in addwitnessaddress (Pieter Wuille)c091b99Implement BIP173 addresses and tests (Pieter Wuille)bd355b8Add regtest testing to base58_tests (Pieter Wuille)6565c55Convert base58_tests from type/payload to scriptPubKey comparison (Pieter Wuille)8fd2267Import Bech32 C++ reference code & tests (Pieter Wuille)1e46ebdImplement {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
This commit is contained in:
@@ -78,6 +78,7 @@ BITCOIN_CORE_H = \
|
||||
addrdb.h \
|
||||
addrman.h \
|
||||
base58.h \
|
||||
bech32.h \
|
||||
bloom.h \
|
||||
blockencodings.h \
|
||||
chain.h \
|
||||
@@ -316,6 +317,7 @@ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
||||
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
libbitcoin_common_a_SOURCES = \
|
||||
base58.cpp \
|
||||
bech32.cpp \
|
||||
chainparams.cpp \
|
||||
coins.cpp \
|
||||
compressor.cpp \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
194
src/base58.cpp
194
src/base58.cpp
@@ -4,17 +4,20 @@
|
||||
|
||||
#include "base58.h"
|
||||
|
||||
#include "bech32.h"
|
||||
#include "hash.h"
|
||||
#include "script/script.h"
|
||||
#include "uint256.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <boost/variant/apply_visitor.hpp>
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/** All alphanumeric characters except for "0", "I", "O", and "l" */
|
||||
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
@@ -212,87 +215,114 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const
|
||||
|
||||
namespace
|
||||
{
|
||||
/** base58-encoded Bitcoin addresses.
|
||||
* Public-key-hash-addresses have version 0 (or 111 testnet).
|
||||
* The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
|
||||
* Script-hash-addresses have version 5 (or 196 testnet).
|
||||
* The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
|
||||
*/
|
||||
class CBitcoinAddress : public CBase58Data {
|
||||
public:
|
||||
bool Set(const CKeyID &id);
|
||||
bool Set(const CScriptID &id);
|
||||
bool Set(const CTxDestination &dest);
|
||||
bool IsValid() const;
|
||||
bool IsValid(const CChainParams ¶ms) const;
|
||||
|
||||
CBitcoinAddress() {}
|
||||
CBitcoinAddress(const CTxDestination &dest) { Set(dest); }
|
||||
CBitcoinAddress(const std::string& strAddress) { SetString(strAddress); }
|
||||
CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); }
|
||||
|
||||
CTxDestination Get() const;
|
||||
};
|
||||
|
||||
class CBitcoinAddressVisitor : public boost::static_visitor<bool>
|
||||
class DestinationEncoder : public boost::static_visitor<std::string>
|
||||
{
|
||||
private:
|
||||
CBitcoinAddress* addr;
|
||||
const CChainParams& m_params;
|
||||
|
||||
public:
|
||||
explicit CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {}
|
||||
DestinationEncoder(const CChainParams& params) : m_params(params) {}
|
||||
|
||||
bool operator()(const CKeyID& id) const { return addr->Set(id); }
|
||||
bool operator()(const CScriptID& id) const { return addr->Set(id); }
|
||||
bool operator()(const CNoDestination& no) const { return false; }
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string operator()(const CNoDestination& no) const { return {}; }
|
||||
};
|
||||
|
||||
CTxDestination DecodeDestination(const std::string& str, const CChainParams& params)
|
||||
{
|
||||
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
|
||||
|
||||
bool CBitcoinAddress::Set(const CKeyID& id)
|
||||
{
|
||||
SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBitcoinAddress::Set(const CScriptID& id)
|
||||
{
|
||||
SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBitcoinAddress::Set(const CTxDestination& dest)
|
||||
{
|
||||
return boost::apply_visitor(CBitcoinAddressVisitor(this), dest);
|
||||
}
|
||||
|
||||
bool CBitcoinAddress::IsValid() const
|
||||
{
|
||||
return IsValid(Params());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
CTxDestination CBitcoinAddress::Get() const
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
void CBitcoinSecret::SetKey(const CKey& vchSecret)
|
||||
{
|
||||
assert(vchSecret.IsValid());
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
Normal file
191
src/bech32.cpp
Normal file
@@ -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
Normal file
25
src/bech32.h
Normal file
@@ -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
|
||||
@@ -137,6 +137,8 @@ public:
|
||||
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E};
|
||||
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4};
|
||||
|
||||
bech32_hrp = "bc";
|
||||
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
|
||||
|
||||
fDefaultConsistencyChecks = false;
|
||||
@@ -236,6 +238,8 @@ public:
|
||||
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
|
||||
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
||||
|
||||
bech32_hrp = "tb";
|
||||
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
|
||||
|
||||
fDefaultConsistencyChecks = false;
|
||||
@@ -330,6 +334,8 @@ public:
|
||||
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
|
||||
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
|
||||
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
||||
|
||||
bech32_hrp = "bcrt";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ public:
|
||||
std::string NetworkIDString() const { return strNetworkID; }
|
||||
const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; }
|
||||
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
|
||||
const std::string& Bech32HRP() const { return bech32_hrp; }
|
||||
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
|
||||
const CCheckpointData& Checkpoints() const { return checkpointData; }
|
||||
const ChainTxData& TxData() const { return chainTxData; }
|
||||
@@ -86,6 +87,7 @@ protected:
|
||||
uint64_t nPruneAfterHeight;
|
||||
std::vector<CDNSSeedData> vSeeds;
|
||||
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
|
||||
std::string bech32_hrp;
|
||||
std::string strNetworkID;
|
||||
CBlock genesis;
|
||||
std::vector<SeedSpec6> vFixedSeeds;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "chain.h"
|
||||
#include "clientversion.h"
|
||||
#include "core_io.h"
|
||||
#include "crypto/ripemd160.h"
|
||||
#include "init.h"
|
||||
#include "validation.h"
|
||||
#include "httpserver.h"
|
||||
@@ -45,6 +46,7 @@ public:
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
CPubKey vchPubKey;
|
||||
obj.push_back(Pair("isscript", false));
|
||||
obj.push_back(Pair("iswitness", false));
|
||||
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
|
||||
obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
|
||||
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
|
||||
@@ -56,6 +58,7 @@ public:
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
CScript subscript;
|
||||
obj.push_back(Pair("isscript", true));
|
||||
obj.push_back(Pair("iswitness", false));
|
||||
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
|
||||
std::vector<CTxDestination> addresses;
|
||||
txnouttype whichType;
|
||||
@@ -73,6 +76,47 @@ public:
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue operator()(const WitnessV0KeyHash& id) const
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
CPubKey pubkey;
|
||||
obj.push_back(Pair("isscript", false));
|
||||
obj.push_back(Pair("iswitness", true));
|
||||
obj.push_back(Pair("witness_version", 0));
|
||||
obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end())));
|
||||
if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) {
|
||||
obj.push_back(Pair("pubkey", HexStr(pubkey)));
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue operator()(const WitnessV0ScriptHash& id) const
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
CScript subscript;
|
||||
obj.push_back(Pair("isscript", true));
|
||||
obj.push_back(Pair("iswitness", true));
|
||||
obj.push_back(Pair("witness_version", 0));
|
||||
obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end())));
|
||||
CRIPEMD160 hasher;
|
||||
uint160 hash;
|
||||
hasher.Write(id.begin(), 32).Finalize(hash.begin());
|
||||
if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) {
|
||||
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue operator()(const WitnessUnknown& id) const
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
CScript subscript;
|
||||
obj.push_back(Pair("iswitness", true));
|
||||
obj.push_back(Pair("witness_version", (int)id.version));
|
||||
obj.push_back(Pair("witness_program", HexStr(id.program, id.program + id.length)));
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -79,6 +79,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
|
||||
{
|
||||
case TX_NONSTANDARD:
|
||||
case TX_NULL_DATA:
|
||||
case TX_WITNESS_UNKNOWN:
|
||||
return false;
|
||||
case TX_PUBKEY:
|
||||
keyID = CPubKey(vSolutions[0]).GetID();
|
||||
@@ -309,6 +310,7 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature
|
||||
{
|
||||
case TX_NONSTANDARD:
|
||||
case TX_NULL_DATA:
|
||||
case TX_WITNESS_UNKNOWN:
|
||||
// Don't know anything about this, assume bigger one is correct:
|
||||
if (sigs1.script.size() >= sigs2.script.size())
|
||||
return sigs1;
|
||||
|
||||
@@ -30,6 +30,7 @@ const char* GetTxnOutputType(txnouttype t)
|
||||
case TX_NULL_DATA: return "nulldata";
|
||||
case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
|
||||
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
|
||||
case TX_WITNESS_UNKNOWN: return "witness_unknown";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -75,6 +76,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
|
||||
vSolutionsRet.push_back(witnessprogram);
|
||||
return true;
|
||||
}
|
||||
if (witnessversion != 0) {
|
||||
typeRet = TX_WITNESS_UNKNOWN;
|
||||
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
|
||||
vSolutionsRet.push_back(std::move(witnessprogram));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -198,6 +205,23 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
|
||||
{
|
||||
addressRet = CScriptID(uint160(vSolutions[0]));
|
||||
return true;
|
||||
} else if (whichType == TX_WITNESS_V0_KEYHASH) {
|
||||
WitnessV0KeyHash hash;
|
||||
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
|
||||
addressRet = hash;
|
||||
return true;
|
||||
} else if (whichType == TX_WITNESS_V0_SCRIPTHASH) {
|
||||
WitnessV0ScriptHash hash;
|
||||
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
|
||||
addressRet = hash;
|
||||
return true;
|
||||
} else if (whichType == TX_WITNESS_UNKNOWN) {
|
||||
WitnessUnknown unk;
|
||||
unk.version = vSolutions[0][0];
|
||||
std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
|
||||
unk.length = vSolutions[1].size();
|
||||
addressRet = unk;
|
||||
return true;
|
||||
}
|
||||
// Multisig txns have more than one address...
|
||||
return false;
|
||||
@@ -268,6 +292,27 @@ public:
|
||||
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const WitnessV0KeyHash& id) const
|
||||
{
|
||||
script->clear();
|
||||
*script << OP_0 << ToByteVector(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const WitnessV0ScriptHash& id) const
|
||||
{
|
||||
script->clear();
|
||||
*script << OP_0 << ToByteVector(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const WitnessUnknown& id) const
|
||||
{
|
||||
script->clear();
|
||||
*script << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ enum txnouttype
|
||||
TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data
|
||||
TX_WITNESS_V0_SCRIPTHASH,
|
||||
TX_WITNESS_V0_KEYHASH,
|
||||
TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
|
||||
};
|
||||
|
||||
class CNoDestination {
|
||||
@@ -72,14 +73,42 @@ public:
|
||||
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
|
||||
};
|
||||
|
||||
struct WitnessV0ScriptHash : public uint256 {};
|
||||
struct WitnessV0KeyHash : public uint160 {};
|
||||
|
||||
//! CTxDestination subtype to encode any future Witness version
|
||||
struct WitnessUnknown
|
||||
{
|
||||
unsigned int version;
|
||||
unsigned int length;
|
||||
unsigned char program[40];
|
||||
|
||||
friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) {
|
||||
if (w1.version != w2.version) return false;
|
||||
if (w1.length != w2.length) return false;
|
||||
return std::equal(w1.program, w1.program + w1.length, w2.program);
|
||||
}
|
||||
|
||||
friend bool operator<(const WitnessUnknown& w1, const WitnessUnknown& w2) {
|
||||
if (w1.version < w2.version) return true;
|
||||
if (w1.version > w2.version) return false;
|
||||
if (w1.length < w2.length) return true;
|
||||
if (w1.length > w2.length) return false;
|
||||
return std::lexicographical_compare(w1.program, w1.program + w1.length, w2.program, w2.program + w2.length);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A txout script template with a specific destination. It is either:
|
||||
* * CNoDestination: no destination set
|
||||
* * CKeyID: TX_PUBKEYHASH destination
|
||||
* * CScriptID: TX_SCRIPTHASH destination
|
||||
* * CKeyID: TX_PUBKEYHASH destination (P2PKH)
|
||||
* * CScriptID: TX_SCRIPTHASH destination (P2SH)
|
||||
* * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH)
|
||||
* * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH)
|
||||
* * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???)
|
||||
* A CTxDestination is the internal data type encoded in a bitcoin address
|
||||
*/
|
||||
typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
|
||||
typedef boost::variant<CNoDestination, CKeyID, CScriptID, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
|
||||
|
||||
/** Check whether a CTxDestination is a CNoDestination. */
|
||||
bool IsValidDestination(const CTxDestination& dest);
|
||||
@@ -104,7 +133,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
|
||||
* Parse a standard scriptPubKey for the destination address. Assigns result to
|
||||
* the addressRet parameter and returns true if successful. For multisig
|
||||
* scripts, instead use ExtractDestinations. Currently only works for P2PK,
|
||||
* P2PKH, and P2SH scripts.
|
||||
* P2PKH, P2SH, P2WPKH, and P2WSH scripts.
|
||||
*/
|
||||
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
|
||||
|
||||
|
||||
@@ -10,14 +10,15 @@
|
||||
|
||||
#include "key.h"
|
||||
#include "script/script.h"
|
||||
#include "test/test_bitcoin.h"
|
||||
#include "uint256.h"
|
||||
#include "util.h"
|
||||
#include "utilstrencodings.h"
|
||||
#include "test/test_bitcoin.h"
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
extern UniValue read_json(const std::string& jsondata);
|
||||
|
||||
@@ -72,50 +73,6 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
|
||||
}
|
||||
|
||||
// Visitor to check address type
|
||||
class TestAddrTypeVisitor : public boost::static_visitor<bool>
|
||||
{
|
||||
private:
|
||||
std::string exp_addrType;
|
||||
public:
|
||||
explicit TestAddrTypeVisitor(const std::string &_exp_addrType) : exp_addrType(_exp_addrType) { }
|
||||
bool operator()(const CKeyID &id) const
|
||||
{
|
||||
return (exp_addrType == "pubkey");
|
||||
}
|
||||
bool operator()(const CScriptID &id) const
|
||||
{
|
||||
return (exp_addrType == "script");
|
||||
}
|
||||
bool operator()(const CNoDestination &no) const
|
||||
{
|
||||
return (exp_addrType == "none");
|
||||
}
|
||||
};
|
||||
|
||||
// Visitor to check address payload
|
||||
class TestPayloadVisitor : public boost::static_visitor<bool>
|
||||
{
|
||||
private:
|
||||
std::vector<unsigned char> exp_payload;
|
||||
public:
|
||||
explicit TestPayloadVisitor(std::vector<unsigned char> &_exp_payload) : exp_payload(_exp_payload) { }
|
||||
bool operator()(const CKeyID &id) const
|
||||
{
|
||||
uint160 exp_key(exp_payload);
|
||||
return exp_key == id;
|
||||
}
|
||||
bool operator()(const CScriptID &id) const
|
||||
{
|
||||
uint160 exp_key(exp_payload);
|
||||
return exp_key == id;
|
||||
}
|
||||
bool operator()(const CNoDestination &no) const
|
||||
{
|
||||
return exp_payload.size() == 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Goal: check that parsed keys match test payload
|
||||
BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
|
||||
{
|
||||
@@ -127,8 +84,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
|
||||
for (unsigned int idx = 0; idx < tests.size(); idx++) {
|
||||
UniValue test = tests[idx];
|
||||
std::string strTest = test.write();
|
||||
if (test.size() < 3) // Allow for extra stuff (useful for comments)
|
||||
{
|
||||
if (test.size() < 3) { // Allow for extra stuff (useful for comments)
|
||||
BOOST_ERROR("Bad test: " << strTest);
|
||||
continue;
|
||||
}
|
||||
@@ -136,13 +92,9 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
|
||||
std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
|
||||
const UniValue &metadata = test[2].get_obj();
|
||||
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
|
||||
bool isTestnet = find_value(metadata, "isTestnet").get_bool();
|
||||
if (isTestnet)
|
||||
SelectParams(CBaseChainParams::TESTNET);
|
||||
else
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
if(isPrivkey)
|
||||
{
|
||||
SelectParams(find_value(metadata, "chain").get_str());
|
||||
bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool();
|
||||
if (isPrivkey) {
|
||||
bool isCompressed = find_value(metadata, "isCompressed").get_bool();
|
||||
// Must be valid private key
|
||||
BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest);
|
||||
@@ -154,15 +106,27 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
|
||||
// Private key must be invalid public key
|
||||
destination = DecodeDestination(exp_base58string);
|
||||
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey"
|
||||
} else {
|
||||
// Must be valid public key
|
||||
destination = DecodeDestination(exp_base58string);
|
||||
CScript script = GetScriptForDestination(destination);
|
||||
BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
|
||||
BOOST_CHECK_MESSAGE((boost::get<CScriptID>(&destination) != nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest);
|
||||
BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), destination), "addrType mismatch" + strTest);
|
||||
BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
|
||||
|
||||
// Try flipped case version
|
||||
for (char& c : exp_base58string) {
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
c = (c - 'a') + 'A';
|
||||
} else if (c >= 'A' && c <= 'Z') {
|
||||
c = (c - 'A') + 'a';
|
||||
}
|
||||
}
|
||||
destination = DecodeDestination(exp_base58string);
|
||||
BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest);
|
||||
if (IsValidDestination(destination)) {
|
||||
script = GetScriptForDestination(destination);
|
||||
BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
|
||||
}
|
||||
|
||||
// Public key must be invalid private key
|
||||
secret.SetString(exp_base58string);
|
||||
@@ -188,13 +152,8 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
|
||||
std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
|
||||
const UniValue &metadata = test[2].get_obj();
|
||||
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
|
||||
bool isTestnet = find_value(metadata, "isTestnet").get_bool();
|
||||
if (isTestnet)
|
||||
SelectParams(CBaseChainParams::TESTNET);
|
||||
else
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
if(isPrivkey)
|
||||
{
|
||||
SelectParams(find_value(metadata, "chain").get_str());
|
||||
if (isPrivkey) {
|
||||
bool isCompressed = find_value(metadata, "isCompressed").get_bool();
|
||||
CKey key;
|
||||
key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
|
||||
@@ -202,36 +161,20 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
|
||||
CBitcoinSecret secret;
|
||||
secret.SetKey(key);
|
||||
BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string exp_addrType = find_value(metadata, "addrType").get_str();
|
||||
} else {
|
||||
CTxDestination dest;
|
||||
if(exp_addrType == "pubkey")
|
||||
{
|
||||
dest = CKeyID(uint160(exp_payload));
|
||||
}
|
||||
else if(exp_addrType == "script")
|
||||
{
|
||||
dest = CScriptID(uint160(exp_payload));
|
||||
}
|
||||
else if(exp_addrType == "none")
|
||||
{
|
||||
dest = CNoDestination();
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ERROR("Bad addrtype: " << strTest);
|
||||
continue;
|
||||
}
|
||||
CScript exp_script(exp_payload.begin(), exp_payload.end());
|
||||
ExtractDestination(exp_script, dest);
|
||||
std::string address = EncodeDestination(dest);
|
||||
BOOST_CHECK_MESSAGE(address == exp_base58string, "mismatch: " + strTest);
|
||||
|
||||
BOOST_CHECK_EQUAL(address, exp_base58string);
|
||||
}
|
||||
}
|
||||
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
}
|
||||
|
||||
|
||||
// Goal: check that base58 parsing code is robust against a variety of corrupted data
|
||||
BOOST_AUTO_TEST_CASE(base58_keys_invalid)
|
||||
{
|
||||
@@ -250,13 +193,15 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid)
|
||||
std::string exp_base58string = test[0].get_str();
|
||||
|
||||
// must be invalid as public and as private key
|
||||
destination = DecodeDestination(exp_base58string);
|
||||
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest);
|
||||
secret.SetString(exp_base58string);
|
||||
BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest);
|
||||
for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) {
|
||||
SelectParams(chain);
|
||||
destination = DecodeDestination(exp_base58string);
|
||||
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest);
|
||||
secret.SetString(exp_base58string);
|
||||
BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey in mainnet:" + strTest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
|
||||
67
src/test/bech32_tests.cpp
Normal file
67
src/test/bech32_tests.cpp
Normal file
@@ -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()
|
||||
@@ -148,5 +148,35 @@
|
||||
],
|
||||
[
|
||||
"2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED"
|
||||
],
|
||||
[
|
||||
"tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"
|
||||
],
|
||||
[
|
||||
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"
|
||||
],
|
||||
[
|
||||
"BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"
|
||||
],
|
||||
[
|
||||
"bc1rw5uspcuh"
|
||||
],
|
||||
[
|
||||
"bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"
|
||||
],
|
||||
[
|
||||
"BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"
|
||||
],
|
||||
[
|
||||
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"
|
||||
],
|
||||
[
|
||||
"bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"
|
||||
],
|
||||
[
|
||||
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"
|
||||
],
|
||||
[
|
||||
"bc1gmk9yu"
|
||||
]
|
||||
]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -170,11 +170,6 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
|
||||
s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD;
|
||||
BOOST_CHECK(!Solver(s, whichType, solutions));
|
||||
|
||||
// TX_WITNESS with unknown version
|
||||
s.clear();
|
||||
s << OP_1 << ToByteVector(pubkey);
|
||||
BOOST_CHECK(!Solver(s, whichType, solutions));
|
||||
|
||||
// TX_WITNESS with incorrect program size
|
||||
s.clear();
|
||||
s << OP_0 << std::vector<unsigned char>(19, 0x01);
|
||||
@@ -225,13 +220,29 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
|
||||
|
||||
// TX_WITNESS_V0_KEYHASH
|
||||
s.clear();
|
||||
s << OP_0 << ToByteVector(pubkey);
|
||||
BOOST_CHECK(!ExtractDestination(s, address));
|
||||
s << OP_0 << ToByteVector(pubkey.GetID());
|
||||
BOOST_CHECK(ExtractDestination(s, address));
|
||||
WitnessV0KeyHash keyhash;
|
||||
CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(keyhash.begin());
|
||||
BOOST_CHECK(boost::get<WitnessV0KeyHash>(&address) && *boost::get<WitnessV0KeyHash>(&address) == keyhash);
|
||||
|
||||
// TX_WITNESS_V0_SCRIPTHASH
|
||||
s.clear();
|
||||
s << OP_0 << ToByteVector(CScriptID(redeemScript));
|
||||
BOOST_CHECK(!ExtractDestination(s, address));
|
||||
WitnessV0ScriptHash scripthash;
|
||||
CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin());
|
||||
s << OP_0 << ToByteVector(scripthash);
|
||||
BOOST_CHECK(ExtractDestination(s, address));
|
||||
BOOST_CHECK(boost::get<WitnessV0ScriptHash>(&address) && *boost::get<WitnessV0ScriptHash>(&address) == scripthash);
|
||||
|
||||
// TX_WITNESS with unknown version
|
||||
s.clear();
|
||||
s << OP_1 << ToByteVector(pubkey);
|
||||
BOOST_CHECK(ExtractDestination(s, address));
|
||||
WitnessUnknown unk;
|
||||
unk.length = 33;
|
||||
unk.version = 1;
|
||||
std::copy(pubkey.begin(), pubkey.end(), unk.program);
|
||||
BOOST_CHECK(boost::get<WitnessUnknown>(&address) && *boost::get<WitnessUnknown>(&address) == unk);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
|
||||
@@ -298,16 +309,6 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
|
||||
s.clear();
|
||||
s << OP_RETURN << std::vector<unsigned char>({75});
|
||||
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
|
||||
|
||||
// TX_WITNESS_V0_KEYHASH
|
||||
s.clear();
|
||||
s << OP_0 << ToByteVector(pubkeys[0].GetID());
|
||||
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
|
||||
|
||||
// TX_WITNESS_V0_SCRIPTHASH
|
||||
s.clear();
|
||||
s << OP_0 << ToByteVector(CScriptID(redeemScript));
|
||||
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1154,11 +1154,10 @@ class Witnessifier : public boost::static_visitor<bool>
|
||||
{
|
||||
public:
|
||||
CWallet * const pwallet;
|
||||
CScriptID result;
|
||||
CTxDestination result;
|
||||
bool already_witness;
|
||||
|
||||
explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {}
|
||||
|
||||
bool operator()(const CNoDestination &dest) const { return false; }
|
||||
explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {}
|
||||
|
||||
bool operator()(const CKeyID &keyID) {
|
||||
if (pwallet) {
|
||||
@@ -1172,9 +1171,7 @@ public:
|
||||
!VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) {
|
||||
return false;
|
||||
}
|
||||
pwallet->AddCScript(witscript);
|
||||
result = CScriptID(witscript);
|
||||
return true;
|
||||
return ExtractDestination(witscript, result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1185,7 +1182,8 @@ public:
|
||||
int witnessversion;
|
||||
std::vector<unsigned char> witprog;
|
||||
if (subscript.IsWitnessProgram(witnessversion, witprog)) {
|
||||
result = scriptID;
|
||||
ExtractDestination(subscript, result);
|
||||
already_witness = true;
|
||||
return true;
|
||||
}
|
||||
CScript witscript = GetScriptForWitness(subscript);
|
||||
@@ -1197,12 +1195,27 @@ public:
|
||||
!VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) {
|
||||
return false;
|
||||
}
|
||||
pwallet->AddCScript(witscript);
|
||||
result = CScriptID(witscript);
|
||||
return true;
|
||||
return ExtractDestination(witscript, result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator()(const WitnessV0KeyHash& id)
|
||||
{
|
||||
already_witness = true;
|
||||
result = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const WitnessV0ScriptHash& id)
|
||||
{
|
||||
already_witness = true;
|
||||
result = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator()(const T& dest) { return false; }
|
||||
};
|
||||
|
||||
UniValue addwitnessaddress(const JSONRPCRequest& request)
|
||||
@@ -1212,17 +1225,18 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() < 1 || request.params.size() > 1)
|
||||
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
|
||||
{
|
||||
std::string msg = "addwitnessaddress \"address\"\n"
|
||||
std::string msg = "addwitnessaddress \"address\" ( p2sh )\n"
|
||||
"\nAdd a witness address for a script (with pubkey or redeemscript known).\n"
|
||||
"It returns the witness script.\n"
|
||||
|
||||
"\nArguments:\n"
|
||||
"1. \"address\" (string, required) An address known to the wallet\n"
|
||||
"2. p2sh (bool, optional, default=true) Embed inside P2SH\n"
|
||||
|
||||
"\nResult:\n"
|
||||
"\"witnessaddress\", (string) The value of the new address (P2SH of witness script).\n"
|
||||
"\"witnessaddress\", (string) The value of the new address (P2SH or BIP173).\n"
|
||||
"}\n"
|
||||
;
|
||||
throw std::runtime_error(msg);
|
||||
@@ -1240,13 +1254,31 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
|
||||
}
|
||||
|
||||
bool p2sh = true;
|
||||
if (!request.params[1].isNull()) {
|
||||
p2sh = request.params[1].get_bool();
|
||||
}
|
||||
|
||||
Witnessifier w(pwallet);
|
||||
bool ret = boost::apply_visitor(w, dest);
|
||||
if (!ret) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed");
|
||||
}
|
||||
|
||||
pwallet->SetAddressBook(w.result, "", "receive");
|
||||
CScript witprogram = GetScriptForDestination(w.result);
|
||||
|
||||
if (p2sh) {
|
||||
w.result = CScriptID(witprogram);
|
||||
}
|
||||
|
||||
if (w.already_witness) {
|
||||
if (!(dest == w.result)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types");
|
||||
}
|
||||
} else {
|
||||
pwallet->AddCScript(witprogram);
|
||||
pwallet->SetAddressBook(w.result, "", "receive");
|
||||
}
|
||||
|
||||
return EncodeDestination(w.result);
|
||||
}
|
||||
@@ -3199,7 +3231,7 @@ static const CRPCCommand commands[] =
|
||||
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
|
||||
{ "wallet", "abortrescan", &abortrescan, {} },
|
||||
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} },
|
||||
{ "wallet", "addwitnessaddress", &addwitnessaddress, {"address"} },
|
||||
{ "wallet", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
|
||||
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
|
||||
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
|
||||
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
# 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):
|
||||
# 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):
|
||||
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):
|
||||
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):
|
||||
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()
|
||||
|
||||
@@ -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):
|
||||
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
Normal file
107
test/functional/test_framework/segwit_addr.py
Normal file
@@ -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
|
||||
@@ -14,7 +14,11 @@
|
||||
"scriptPubKey": {
|
||||
"asm": "0 e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05",
|
||||
"hex": "0020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05",
|
||||
"type": "witness_v0_scripthash"
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_scripthash",
|
||||
"addresses": [
|
||||
"bc1qu9dgdg330r6r84g5mw7wqshg04exv2uttmw2elfwx74h5tgntuzs44gyfg"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
"scriptPubKey": {
|
||||
"asm": "0 a2516e770582864a6a56ed21a102044e388c62e3",
|
||||
"hex": "0014a2516e770582864a6a56ed21a102044e388c62e3",
|
||||
"type": "witness_v0_keyhash"
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bc1q5fgkuac9s2ry56jka5s6zqsyfcugcchry5cwu0"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
"scriptPubKey": {
|
||||
"asm": "0 0bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6",
|
||||
"hex": "00200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6",
|
||||
"type": "witness_v0_scripthash"
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_scripthash",
|
||||
"addresses": [
|
||||
"bc1qp0lfxhnscvsu0j36l36uurgv5tuck4pzuqytkvwqp3kh78cupttqyf705v"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user