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:
30 files changed
+1098
-339
No files matched your search
@@ -78,6 +78,7 @@ BITCOIN_CORE_H = \
|
|||||||
addrdb.h \
|
addrdb.h \
|
||||||
addrman.h \
|
addrman.h \
|
||||||
base58.h \
|
base58.h \
|
||||||
|
bech32.h \
|
||||||
bloom.h \
|
bloom.h \
|
||||||
blockencodings.h \
|
blockencodings.h \
|
||||||
chain.h \
|
chain.h \
|
||||||
@@ -316,6 +317,7 @@ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
|||||||
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||||
libbitcoin_common_a_SOURCES = \
|
libbitcoin_common_a_SOURCES = \
|
||||||
base58.cpp \
|
base58.cpp \
|
||||||
|
bech32.cpp \
|
||||||
chainparams.cpp \
|
chainparams.cpp \
|
||||||
coins.cpp \
|
coins.cpp \
|
||||||
compressor.cpp \
|
compressor.cpp \
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ BITCOIN_TESTS =\
|
|||||||
test/base32_tests.cpp \
|
test/base32_tests.cpp \
|
||||||
test/base58_tests.cpp \
|
test/base58_tests.cpp \
|
||||||
test/base64_tests.cpp \
|
test/base64_tests.cpp \
|
||||||
|
test/bech32_tests.cpp \
|
||||||
test/bip32_tests.cpp \
|
test/bip32_tests.cpp \
|
||||||
test/blockencodings_tests.cpp \
|
test/blockencodings_tests.cpp \
|
||||||
test/bloom_tests.cpp \
|
test/bloom_tests.cpp \
|
||||||
|
|||||||
+111
-83
@@ -4,17 +4,20 @@
|
|||||||
|
|
||||||
#include "base58.h"
|
#include "base58.h"
|
||||||
|
|
||||||
|
#include "bech32.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
|
#include "script/script.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
#include "utilstrencodings.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <boost/variant/apply_visitor.hpp>
|
#include <boost/variant/apply_visitor.hpp>
|
||||||
#include <boost/variant/static_visitor.hpp>
|
#include <boost/variant/static_visitor.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
/** All alphanumeric characters except for "0", "I", "O", and "l" */
|
/** All alphanumeric characters except for "0", "I", "O", and "l" */
|
||||||
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||||
|
|
||||||
@@ -212,87 +215,114 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
/** base58-encoded Bitcoin addresses.
|
class DestinationEncoder : public boost::static_visitor<std::string>
|
||||||
* Public-key-hash-addresses have version 0 (or 111 testnet).
|
|
||||||
* The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
|
|
||||||
* Script-hash-addresses have version 5 (or 196 testnet).
|
|
||||||
* The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
|
|
||||||
*/
|
|
||||||
class CBitcoinAddress : public CBase58Data {
|
|
||||||
public:
|
|
||||||
bool Set(const CKeyID &id);
|
|
||||||
bool Set(const CScriptID &id);
|
|
||||||
bool Set(const CTxDestination &dest);
|
|
||||||
bool IsValid() const;
|
|
||||||
bool IsValid(const CChainParams ¶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>
|
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
CBitcoinAddress* addr;
|
const CChainParams& m_params;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {}
|
DestinationEncoder(const CChainParams& params) : m_params(params) {}
|
||||||
|
|
||||||
bool operator()(const CKeyID& id) const { return addr->Set(id); }
|
std::string operator()(const CKeyID& id) const
|
||||||
bool operator()(const CScriptID& id) const { return addr->Set(id); }
|
{
|
||||||
bool operator()(const CNoDestination& no) const { return false; }
|
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
|
||||||
|
data.insert(data.end(), id.begin(), id.end());
|
||||||
|
return EncodeBase58Check(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
} // 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)
|
void CBitcoinSecret::SetKey(const CKey& vchSecret)
|
||||||
{
|
{
|
||||||
assert(vchSecret.IsValid());
|
assert(vchSecret.IsValid());
|
||||||
@@ -328,22 +358,20 @@ bool CBitcoinSecret::SetString(const std::string& strSecret)
|
|||||||
|
|
||||||
std::string EncodeDestination(const CTxDestination& dest)
|
std::string EncodeDestination(const CTxDestination& dest)
|
||||||
{
|
{
|
||||||
CBitcoinAddress addr(dest);
|
return boost::apply_visitor(DestinationEncoder(Params()), dest);
|
||||||
if (!addr.IsValid()) return "";
|
|
||||||
return addr.ToString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CTxDestination DecodeDestination(const std::string& str)
|
CTxDestination DecodeDestination(const std::string& str)
|
||||||
{
|
{
|
||||||
return CBitcoinAddress(str).Get();
|
return DecodeDestination(str, Params());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidDestinationString(const std::string& str, const CChainParams& params)
|
bool IsValidDestinationString(const std::string& str, const CChainParams& params)
|
||||||
{
|
{
|
||||||
return CBitcoinAddress(str).IsValid(params);
|
return IsValidDestination(DecodeDestination(str, params));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidDestinationString(const std::string& str)
|
bool IsValidDestinationString(const std::string& str)
|
||||||
{
|
{
|
||||||
return CBitcoinAddress(str).IsValid();
|
return IsValidDestinationString(str, Params());
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
#include "key.h"
|
#include "key.h"
|
||||||
#include "pubkey.h"
|
#include "pubkey.h"
|
||||||
#include "script/script.h"
|
|
||||||
#include "script/standard.h"
|
#include "script/standard.h"
|
||||||
#include "support/allocators/zeroafterfree.h"
|
#include "support/allocators/zeroafterfree.h"
|
||||||
|
|
||||||
|
|||||||
+191
@@ -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
|
||||||
@@ -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_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E};
|
||||||
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4};
|
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4};
|
||||||
|
|
||||||
|
bech32_hrp = "bc";
|
||||||
|
|
||||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
|
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
|
||||||
|
|
||||||
fDefaultConsistencyChecks = false;
|
fDefaultConsistencyChecks = false;
|
||||||
@@ -236,6 +238,8 @@ public:
|
|||||||
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
|
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
|
||||||
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
||||||
|
|
||||||
|
bech32_hrp = "tb";
|
||||||
|
|
||||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
|
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
|
||||||
|
|
||||||
fDefaultConsistencyChecks = false;
|
fDefaultConsistencyChecks = false;
|
||||||
@@ -330,6 +334,8 @@ public:
|
|||||||
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
|
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
|
||||||
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
|
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
|
||||||
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
||||||
|
|
||||||
|
bech32_hrp = "bcrt";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ public:
|
|||||||
std::string NetworkIDString() const { return strNetworkID; }
|
std::string NetworkIDString() const { return strNetworkID; }
|
||||||
const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; }
|
const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; }
|
||||||
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
|
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
|
||||||
|
const std::string& Bech32HRP() const { return bech32_hrp; }
|
||||||
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
|
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
|
||||||
const CCheckpointData& Checkpoints() const { return checkpointData; }
|
const CCheckpointData& Checkpoints() const { return checkpointData; }
|
||||||
const ChainTxData& TxData() const { return chainTxData; }
|
const ChainTxData& TxData() const { return chainTxData; }
|
||||||
@@ -86,6 +87,7 @@ protected:
|
|||||||
uint64_t nPruneAfterHeight;
|
uint64_t nPruneAfterHeight;
|
||||||
std::vector<CDNSSeedData> vSeeds;
|
std::vector<CDNSSeedData> vSeeds;
|
||||||
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
|
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
|
||||||
|
std::string bech32_hrp;
|
||||||
std::string strNetworkID;
|
std::string strNetworkID;
|
||||||
CBlock genesis;
|
CBlock genesis;
|
||||||
std::vector<SeedSpec6> vFixedSeeds;
|
std::vector<SeedSpec6> vFixedSeeds;
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool w
|
|||||||
else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH))
|
else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return whichType != TX_NONSTANDARD;
|
return whichType != TX_NONSTANDARD && whichType != TX_WITNESS_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled)
|
bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &po
|
|||||||
if (((ch >= '0' && ch<='9') ||
|
if (((ch >= '0' && ch<='9') ||
|
||||||
(ch >= 'a' && ch<='z') ||
|
(ch >= 'a' && ch<='z') ||
|
||||||
(ch >= 'A' && ch<='Z')) &&
|
(ch >= 'A' && ch<='Z')) &&
|
||||||
ch != 'l' && ch != 'I' && ch != '0' && ch != 'O')
|
ch != 'I' && ch != 'O') // Characters invalid in both Base58 and Bech32
|
||||||
{
|
{
|
||||||
// Alphanumeric and not a 'forbidden' character
|
// Alphanumeric and not a 'forbidden' character
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||||||
{ "logging", 0, "include" },
|
{ "logging", 0, "include" },
|
||||||
{ "logging", 1, "exclude" },
|
{ "logging", 1, "exclude" },
|
||||||
{ "disconnectnode", 1, "nodeid" },
|
{ "disconnectnode", 1, "nodeid" },
|
||||||
|
{ "addwitnessaddress", 1, "p2sh" },
|
||||||
// Echo with conversion (For testing only)
|
// Echo with conversion (For testing only)
|
||||||
{ "echojson", 0, "arg0" },
|
{ "echojson", 0, "arg0" },
|
||||||
{ "echojson", 1, "arg1" },
|
{ "echojson", 1, "arg1" },
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "chain.h"
|
#include "chain.h"
|
||||||
#include "clientversion.h"
|
#include "clientversion.h"
|
||||||
#include "core_io.h"
|
#include "core_io.h"
|
||||||
|
#include "crypto/ripemd160.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "validation.h"
|
#include "validation.h"
|
||||||
#include "httpserver.h"
|
#include "httpserver.h"
|
||||||
@@ -45,6 +46,7 @@ public:
|
|||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
CPubKey vchPubKey;
|
CPubKey vchPubKey;
|
||||||
obj.push_back(Pair("isscript", false));
|
obj.push_back(Pair("isscript", false));
|
||||||
|
obj.push_back(Pair("iswitness", false));
|
||||||
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
|
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
|
||||||
obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
|
obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
|
||||||
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
|
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
|
||||||
@@ -56,6 +58,7 @@ public:
|
|||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
CScript subscript;
|
CScript subscript;
|
||||||
obj.push_back(Pair("isscript", true));
|
obj.push_back(Pair("isscript", true));
|
||||||
|
obj.push_back(Pair("iswitness", false));
|
||||||
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
|
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
|
||||||
std::vector<CTxDestination> addresses;
|
std::vector<CTxDestination> addresses;
|
||||||
txnouttype whichType;
|
txnouttype whichType;
|
||||||
@@ -73,6 +76,47 @@ public:
|
|||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniValue operator()(const WitnessV0KeyHash& id) const
|
||||||
|
{
|
||||||
|
UniValue obj(UniValue::VOBJ);
|
||||||
|
CPubKey pubkey;
|
||||||
|
obj.push_back(Pair("isscript", false));
|
||||||
|
obj.push_back(Pair("iswitness", true));
|
||||||
|
obj.push_back(Pair("witness_version", 0));
|
||||||
|
obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end())));
|
||||||
|
if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) {
|
||||||
|
obj.push_back(Pair("pubkey", HexStr(pubkey)));
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue operator()(const WitnessV0ScriptHash& id) const
|
||||||
|
{
|
||||||
|
UniValue obj(UniValue::VOBJ);
|
||||||
|
CScript subscript;
|
||||||
|
obj.push_back(Pair("isscript", true));
|
||||||
|
obj.push_back(Pair("iswitness", true));
|
||||||
|
obj.push_back(Pair("witness_version", 0));
|
||||||
|
obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end())));
|
||||||
|
CRIPEMD160 hasher;
|
||||||
|
uint160 hash;
|
||||||
|
hasher.Write(id.begin(), 32).Finalize(hash.begin());
|
||||||
|
if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) {
|
||||||
|
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue operator()(const WitnessUnknown& id) const
|
||||||
|
{
|
||||||
|
UniValue obj(UniValue::VOBJ);
|
||||||
|
CScript subscript;
|
||||||
|
obj.push_back(Pair("iswitness", true));
|
||||||
|
obj.push_back(Pair("witness_version", (int)id.version));
|
||||||
|
obj.push_back(Pair("witness_program", HexStr(id.program, id.program + id.length)));
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
|
|||||||
{
|
{
|
||||||
case TX_NONSTANDARD:
|
case TX_NONSTANDARD:
|
||||||
case TX_NULL_DATA:
|
case TX_NULL_DATA:
|
||||||
|
case TX_WITNESS_UNKNOWN:
|
||||||
break;
|
break;
|
||||||
case TX_PUBKEY:
|
case TX_PUBKEY:
|
||||||
keyID = CPubKey(vSolutions[0]).GetID();
|
keyID = CPubKey(vSolutions[0]).GetID();
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
|
|||||||
{
|
{
|
||||||
case TX_NONSTANDARD:
|
case TX_NONSTANDARD:
|
||||||
case TX_NULL_DATA:
|
case TX_NULL_DATA:
|
||||||
|
case TX_WITNESS_UNKNOWN:
|
||||||
return false;
|
return false;
|
||||||
case TX_PUBKEY:
|
case TX_PUBKEY:
|
||||||
keyID = CPubKey(vSolutions[0]).GetID();
|
keyID = CPubKey(vSolutions[0]).GetID();
|
||||||
@@ -309,6 +310,7 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature
|
|||||||
{
|
{
|
||||||
case TX_NONSTANDARD:
|
case TX_NONSTANDARD:
|
||||||
case TX_NULL_DATA:
|
case TX_NULL_DATA:
|
||||||
|
case TX_WITNESS_UNKNOWN:
|
||||||
// Don't know anything about this, assume bigger one is correct:
|
// Don't know anything about this, assume bigger one is correct:
|
||||||
if (sigs1.script.size() >= sigs2.script.size())
|
if (sigs1.script.size() >= sigs2.script.size())
|
||||||
return sigs1;
|
return sigs1;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const char* GetTxnOutputType(txnouttype t)
|
|||||||
case TX_NULL_DATA: return "nulldata";
|
case TX_NULL_DATA: return "nulldata";
|
||||||
case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
|
case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
|
||||||
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
|
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
|
||||||
|
case TX_WITNESS_UNKNOWN: return "witness_unknown";
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -75,6 +76,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
|
|||||||
vSolutionsRet.push_back(witnessprogram);
|
vSolutionsRet.push_back(witnessprogram);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (witnessversion != 0) {
|
||||||
|
typeRet = TX_WITNESS_UNKNOWN;
|
||||||
|
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
|
||||||
|
vSolutionsRet.push_back(std::move(witnessprogram));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +205,23 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
|
|||||||
{
|
{
|
||||||
addressRet = CScriptID(uint160(vSolutions[0]));
|
addressRet = CScriptID(uint160(vSolutions[0]));
|
||||||
return true;
|
return true;
|
||||||
|
} else if (whichType == TX_WITNESS_V0_KEYHASH) {
|
||||||
|
WitnessV0KeyHash hash;
|
||||||
|
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
|
||||||
|
addressRet = hash;
|
||||||
|
return true;
|
||||||
|
} else if (whichType == TX_WITNESS_V0_SCRIPTHASH) {
|
||||||
|
WitnessV0ScriptHash hash;
|
||||||
|
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
|
||||||
|
addressRet = hash;
|
||||||
|
return true;
|
||||||
|
} else if (whichType == TX_WITNESS_UNKNOWN) {
|
||||||
|
WitnessUnknown unk;
|
||||||
|
unk.version = vSolutions[0][0];
|
||||||
|
std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
|
||||||
|
unk.length = vSolutions[1].size();
|
||||||
|
addressRet = unk;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
// Multisig txns have more than one address...
|
// Multisig txns have more than one address...
|
||||||
return false;
|
return false;
|
||||||
@@ -268,6 +292,27 @@ public:
|
|||||||
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
|
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator()(const WitnessV0KeyHash& id) const
|
||||||
|
{
|
||||||
|
script->clear();
|
||||||
|
*script << OP_0 << ToByteVector(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const WitnessV0ScriptHash& id) const
|
||||||
|
{
|
||||||
|
script->clear();
|
||||||
|
*script << OP_0 << ToByteVector(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const WitnessUnknown& id) const
|
||||||
|
{
|
||||||
|
script->clear();
|
||||||
|
*script << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
+33
-4
@@ -64,6 +64,7 @@ enum txnouttype
|
|||||||
TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data
|
TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data
|
||||||
TX_WITNESS_V0_SCRIPTHASH,
|
TX_WITNESS_V0_SCRIPTHASH,
|
||||||
TX_WITNESS_V0_KEYHASH,
|
TX_WITNESS_V0_KEYHASH,
|
||||||
|
TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
|
||||||
};
|
};
|
||||||
|
|
||||||
class CNoDestination {
|
class CNoDestination {
|
||||||
@@ -72,14 +73,42 @@ public:
|
|||||||
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
|
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct WitnessV0ScriptHash : public uint256 {};
|
||||||
|
struct WitnessV0KeyHash : public uint160 {};
|
||||||
|
|
||||||
|
//! CTxDestination subtype to encode any future Witness version
|
||||||
|
struct WitnessUnknown
|
||||||
|
{
|
||||||
|
unsigned int version;
|
||||||
|
unsigned int length;
|
||||||
|
unsigned char program[40];
|
||||||
|
|
||||||
|
friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) {
|
||||||
|
if (w1.version != w2.version) return false;
|
||||||
|
if (w1.length != w2.length) return false;
|
||||||
|
return std::equal(w1.program, w1.program + w1.length, w2.program);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator<(const WitnessUnknown& w1, const WitnessUnknown& w2) {
|
||||||
|
if (w1.version < w2.version) return true;
|
||||||
|
if (w1.version > w2.version) return false;
|
||||||
|
if (w1.length < w2.length) return true;
|
||||||
|
if (w1.length > w2.length) return false;
|
||||||
|
return std::lexicographical_compare(w1.program, w1.program + w1.length, w2.program, w2.program + w2.length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A txout script template with a specific destination. It is either:
|
* A txout script template with a specific destination. It is either:
|
||||||
* * CNoDestination: no destination set
|
* * CNoDestination: no destination set
|
||||||
* * CKeyID: TX_PUBKEYHASH destination
|
* * CKeyID: TX_PUBKEYHASH destination (P2PKH)
|
||||||
* * CScriptID: TX_SCRIPTHASH destination
|
* * CScriptID: TX_SCRIPTHASH destination (P2SH)
|
||||||
|
* * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH)
|
||||||
|
* * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH)
|
||||||
|
* * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???)
|
||||||
* A CTxDestination is the internal data type encoded in a bitcoin address
|
* A CTxDestination is the internal data type encoded in a bitcoin address
|
||||||
*/
|
*/
|
||||||
typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
|
typedef boost::variant<CNoDestination, CKeyID, CScriptID, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
|
||||||
|
|
||||||
/** Check whether a CTxDestination is a CNoDestination. */
|
/** Check whether a CTxDestination is a CNoDestination. */
|
||||||
bool IsValidDestination(const CTxDestination& dest);
|
bool IsValidDestination(const CTxDestination& dest);
|
||||||
@@ -104,7 +133,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
|
|||||||
* Parse a standard scriptPubKey for the destination address. Assigns result to
|
* Parse a standard scriptPubKey for the destination address. Assigns result to
|
||||||
* the addressRet parameter and returns true if successful. For multisig
|
* the addressRet parameter and returns true if successful. For multisig
|
||||||
* scripts, instead use ExtractDestinations. Currently only works for P2PK,
|
* scripts, instead use ExtractDestinations. Currently only works for P2PK,
|
||||||
* P2PKH, and P2SH scripts.
|
* P2PKH, P2SH, P2WPKH, and P2WSH scripts.
|
||||||
*/
|
*/
|
||||||
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
|
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
|
||||||
|
|
||||||
|
|||||||
+40
-95
@@ -10,14 +10,15 @@
|
|||||||
|
|
||||||
#include "key.h"
|
#include "key.h"
|
||||||
#include "script/script.h"
|
#include "script/script.h"
|
||||||
|
#include "test/test_bitcoin.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
#include "test/test_bitcoin.h"
|
|
||||||
|
#include <univalue.h>
|
||||||
|
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
#include <univalue.h>
|
|
||||||
|
|
||||||
extern UniValue read_json(const std::string& jsondata);
|
extern UniValue read_json(const std::string& jsondata);
|
||||||
|
|
||||||
@@ -72,50 +73,6 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
|
|||||||
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visitor to check address type
|
|
||||||
class TestAddrTypeVisitor : public boost::static_visitor<bool>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::string exp_addrType;
|
|
||||||
public:
|
|
||||||
explicit TestAddrTypeVisitor(const std::string &_exp_addrType) : exp_addrType(_exp_addrType) { }
|
|
||||||
bool operator()(const CKeyID &id) const
|
|
||||||
{
|
|
||||||
return (exp_addrType == "pubkey");
|
|
||||||
}
|
|
||||||
bool operator()(const CScriptID &id) const
|
|
||||||
{
|
|
||||||
return (exp_addrType == "script");
|
|
||||||
}
|
|
||||||
bool operator()(const CNoDestination &no) const
|
|
||||||
{
|
|
||||||
return (exp_addrType == "none");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Visitor to check address payload
|
|
||||||
class TestPayloadVisitor : public boost::static_visitor<bool>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::vector<unsigned char> exp_payload;
|
|
||||||
public:
|
|
||||||
explicit TestPayloadVisitor(std::vector<unsigned char> &_exp_payload) : exp_payload(_exp_payload) { }
|
|
||||||
bool operator()(const CKeyID &id) const
|
|
||||||
{
|
|
||||||
uint160 exp_key(exp_payload);
|
|
||||||
return exp_key == id;
|
|
||||||
}
|
|
||||||
bool operator()(const CScriptID &id) const
|
|
||||||
{
|
|
||||||
uint160 exp_key(exp_payload);
|
|
||||||
return exp_key == id;
|
|
||||||
}
|
|
||||||
bool operator()(const CNoDestination &no) const
|
|
||||||
{
|
|
||||||
return exp_payload.size() == 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Goal: check that parsed keys match test payload
|
// Goal: check that parsed keys match test payload
|
||||||
BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
|
BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
|
||||||
{
|
{
|
||||||
@@ -127,8 +84,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
|
|||||||
for (unsigned int idx = 0; idx < tests.size(); idx++) {
|
for (unsigned int idx = 0; idx < tests.size(); idx++) {
|
||||||
UniValue test = tests[idx];
|
UniValue test = tests[idx];
|
||||||
std::string strTest = test.write();
|
std::string strTest = test.write();
|
||||||
if (test.size() < 3) // Allow for extra stuff (useful for comments)
|
if (test.size() < 3) { // Allow for extra stuff (useful for comments)
|
||||||
{
|
|
||||||
BOOST_ERROR("Bad test: " << strTest);
|
BOOST_ERROR("Bad test: " << strTest);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -136,13 +92,9 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
|
|||||||
std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
|
std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
|
||||||
const UniValue &metadata = test[2].get_obj();
|
const UniValue &metadata = test[2].get_obj();
|
||||||
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
|
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
|
||||||
bool isTestnet = find_value(metadata, "isTestnet").get_bool();
|
SelectParams(find_value(metadata, "chain").get_str());
|
||||||
if (isTestnet)
|
bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool();
|
||||||
SelectParams(CBaseChainParams::TESTNET);
|
if (isPrivkey) {
|
||||||
else
|
|
||||||
SelectParams(CBaseChainParams::MAIN);
|
|
||||||
if(isPrivkey)
|
|
||||||
{
|
|
||||||
bool isCompressed = find_value(metadata, "isCompressed").get_bool();
|
bool isCompressed = find_value(metadata, "isCompressed").get_bool();
|
||||||
// Must be valid private key
|
// Must be valid private key
|
||||||
BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest);
|
BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest);
|
||||||
@@ -154,15 +106,27 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
|
|||||||
// Private key must be invalid public key
|
// Private key must be invalid public key
|
||||||
destination = DecodeDestination(exp_base58string);
|
destination = DecodeDestination(exp_base58string);
|
||||||
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest);
|
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey"
|
|
||||||
// Must be valid public key
|
// Must be valid public key
|
||||||
destination = DecodeDestination(exp_base58string);
|
destination = DecodeDestination(exp_base58string);
|
||||||
|
CScript script = GetScriptForDestination(destination);
|
||||||
BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
|
BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
|
||||||
BOOST_CHECK_MESSAGE((boost::get<CScriptID>(&destination) != nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest);
|
BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
|
||||||
BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), destination), "addrType mismatch" + strTest);
|
|
||||||
|
// Try flipped case version
|
||||||
|
for (char& c : exp_base58string) {
|
||||||
|
if (c >= 'a' && c <= 'z') {
|
||||||
|
c = (c - 'a') + 'A';
|
||||||
|
} else if (c >= 'A' && c <= 'Z') {
|
||||||
|
c = (c - 'A') + 'a';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
destination = DecodeDestination(exp_base58string);
|
||||||
|
BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest);
|
||||||
|
if (IsValidDestination(destination)) {
|
||||||
|
script = GetScriptForDestination(destination);
|
||||||
|
BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
|
||||||
|
}
|
||||||
|
|
||||||
// Public key must be invalid private key
|
// Public key must be invalid private key
|
||||||
secret.SetString(exp_base58string);
|
secret.SetString(exp_base58string);
|
||||||
@@ -188,13 +152,8 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
|
|||||||
std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
|
std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
|
||||||
const UniValue &metadata = test[2].get_obj();
|
const UniValue &metadata = test[2].get_obj();
|
||||||
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
|
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
|
||||||
bool isTestnet = find_value(metadata, "isTestnet").get_bool();
|
SelectParams(find_value(metadata, "chain").get_str());
|
||||||
if (isTestnet)
|
if (isPrivkey) {
|
||||||
SelectParams(CBaseChainParams::TESTNET);
|
|
||||||
else
|
|
||||||
SelectParams(CBaseChainParams::MAIN);
|
|
||||||
if(isPrivkey)
|
|
||||||
{
|
|
||||||
bool isCompressed = find_value(metadata, "isCompressed").get_bool();
|
bool isCompressed = find_value(metadata, "isCompressed").get_bool();
|
||||||
CKey key;
|
CKey key;
|
||||||
key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
|
key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
|
||||||
@@ -202,36 +161,20 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
|
|||||||
CBitcoinSecret secret;
|
CBitcoinSecret secret;
|
||||||
secret.SetKey(key);
|
secret.SetKey(key);
|
||||||
BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest);
|
BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string exp_addrType = find_value(metadata, "addrType").get_str();
|
|
||||||
CTxDestination dest;
|
CTxDestination dest;
|
||||||
if(exp_addrType == "pubkey")
|
CScript exp_script(exp_payload.begin(), exp_payload.end());
|
||||||
{
|
ExtractDestination(exp_script, dest);
|
||||||
dest = CKeyID(uint160(exp_payload));
|
|
||||||
}
|
|
||||||
else if(exp_addrType == "script")
|
|
||||||
{
|
|
||||||
dest = CScriptID(uint160(exp_payload));
|
|
||||||
}
|
|
||||||
else if(exp_addrType == "none")
|
|
||||||
{
|
|
||||||
dest = CNoDestination();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BOOST_ERROR("Bad addrtype: " << strTest);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
std::string address = EncodeDestination(dest);
|
std::string address = EncodeDestination(dest);
|
||||||
BOOST_CHECK_MESSAGE(address == exp_base58string, "mismatch: " + strTest);
|
|
||||||
|
BOOST_CHECK_EQUAL(address, exp_base58string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectParams(CBaseChainParams::MAIN);
|
SelectParams(CBaseChainParams::MAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Goal: check that base58 parsing code is robust against a variety of corrupted data
|
// Goal: check that base58 parsing code is robust against a variety of corrupted data
|
||||||
BOOST_AUTO_TEST_CASE(base58_keys_invalid)
|
BOOST_AUTO_TEST_CASE(base58_keys_invalid)
|
||||||
{
|
{
|
||||||
@@ -250,13 +193,15 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid)
|
|||||||
std::string exp_base58string = test[0].get_str();
|
std::string exp_base58string = test[0].get_str();
|
||||||
|
|
||||||
// must be invalid as public and as private key
|
// must be invalid as public and as private key
|
||||||
destination = DecodeDestination(exp_base58string);
|
for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) {
|
||||||
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest);
|
SelectParams(chain);
|
||||||
secret.SetString(exp_base58string);
|
destination = DecodeDestination(exp_base58string);
|
||||||
BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest);
|
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest);
|
||||||
|
secret.SetString(exp_base58string);
|
||||||
|
BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey in mainnet:" + strTest);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
@@ -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"
|
"2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"bc1rw5uspcuh"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"bc1gmk9yu"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@@ -1,38 +1,42 @@
|
|||||||
[
|
[
|
||||||
[
|
[
|
||||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
||||||
"65a16059864a2fdbc7c99a4723a8395bc6f188eb",
|
"76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou",
|
"3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou",
|
||||||
"74f209f6ea907e2ea48f74fae05782ae8a665257",
|
"a91474f209f6ea907e2ea48f74fae05782ae8a66525787",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",
|
"mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",
|
||||||
"53c0307d6851aa0ce7825ba883c6bd9ad242b486",
|
"76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",
|
||||||
|
"76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",
|
||||||
|
{
|
||||||
|
"isPrivkey": false,
|
||||||
|
"chain": "regtest"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br",
|
"2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br",
|
||||||
"6349a418fc4578d10a372b54b45c280cc8c4382f",
|
"a9146349a418fc4578d10a372b54b45c280cc8c4382f87",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -41,7 +45,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": false,
|
"isCompressed": false,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -50,7 +54,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": true,
|
"isCompressed": true,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -59,7 +63,16 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": false,
|
"isCompressed": false,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",
|
||||||
|
"36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",
|
||||||
|
{
|
||||||
|
"isCompressed": false,
|
||||||
|
"isPrivkey": true,
|
||||||
|
"chain": "regtest"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -68,43 +81,48 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": true,
|
"isCompressed": true,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",
|
||||||
|
"b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",
|
||||||
|
{
|
||||||
|
"isCompressed": true,
|
||||||
|
"isPrivkey": true,
|
||||||
|
"chain": "regtest"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ",
|
"1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ",
|
||||||
"6d23156cbbdcc82a5a47eee4c2c7c583c18b6bf4",
|
"76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy",
|
"3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy",
|
||||||
"fcc5460dd6e2487c7d75b1963625da0e8f4c5975",
|
"a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ",
|
"n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ",
|
||||||
"f1d470f9b02370fdec2e6b708b08ac431bf7a5f7",
|
"76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n",
|
"2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n",
|
||||||
"c579342c2c4c9220205e2cdc285617040c924a0a",
|
"a914c579342c2c4c9220205e2cdc285617040c924a0a87",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -113,7 +131,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": false,
|
"isCompressed": false,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -122,7 +140,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": true,
|
"isCompressed": true,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -131,7 +149,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": false,
|
"isCompressed": false,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -140,43 +158,39 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": true,
|
"isCompressed": true,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv",
|
"1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv",
|
||||||
"7987ccaa53d02c8873487ef919677cd3db7a6912",
|
"76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks",
|
"3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks",
|
||||||
"63bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb",
|
"a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk",
|
"n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk",
|
||||||
"ef66444b5b17f14e8fae6e7e19b045a78c54fd79",
|
"76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"2NB72XtkjpnATMggui83aEtPawyyKvnbX2o",
|
"2NB72XtkjpnATMggui83aEtPawyyKvnbX2o",
|
||||||
"c3e55fceceaa4391ed2a9677f4a4d34eacd021a0",
|
"a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -185,7 +199,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": false,
|
"isCompressed": false,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -194,7 +208,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": true,
|
"isCompressed": true,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -203,7 +217,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": false,
|
"isCompressed": false,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -212,43 +226,39 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": true,
|
"isCompressed": true,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu",
|
"1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu",
|
||||||
"adc1cc2081a27206fae25792f28bbc55b831549d",
|
"76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk",
|
"33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk",
|
||||||
"188f91a931947eddd7432d6e614387e32b244709",
|
"a914188f91a931947eddd7432d6e614387e32b24470987",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H",
|
"mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H",
|
||||||
"1694f5bc1a7295b600f40018a618a6ea48eeb498",
|
"76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN",
|
"2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN",
|
||||||
"3b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f3",
|
"a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -257,7 +267,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": false,
|
"isCompressed": false,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -266,7 +276,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": true,
|
"isCompressed": true,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -275,7 +285,16 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": false,
|
"isCompressed": false,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",
|
||||||
|
"b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",
|
||||||
|
{
|
||||||
|
"isCompressed": false,
|
||||||
|
"isPrivkey": true,
|
||||||
|
"chain": "regtest"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -284,43 +303,39 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": true,
|
"isCompressed": true,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4",
|
"1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4",
|
||||||
"c4c1b72491ede1eedaca00618407ee0b772cad0d",
|
"76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y",
|
"3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y",
|
||||||
"f6fe69bcb548a829cce4c57bf6fff8af3a5981f9",
|
"a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6",
|
"mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6",
|
||||||
"261f83568a098a8638844bd7aeca039d5f2352c0",
|
"76a914261f83568a098a8638844bd7aeca039d5f2352c088ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda",
|
"2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda",
|
||||||
"e930e1834a4d234702773951d627cce82fbb5d2e",
|
"a914e930e1834a4d234702773951d627cce82fbb5d2e87",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -329,7 +344,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": false,
|
"isCompressed": false,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -338,7 +353,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": true,
|
"isCompressed": true,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -347,7 +362,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": false,
|
"isCompressed": false,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -356,43 +371,39 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": true,
|
"isCompressed": true,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r",
|
"19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r",
|
||||||
"5eadaf9bb7121f0f192561a5a62f5e5f54210292",
|
"76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3",
|
"37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3",
|
||||||
"3f210e7277c899c3a155cc1c90f4106cbddeec6e",
|
"a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"myoqcgYiehufrsnnkqdqbp69dddVDMopJu",
|
"myoqcgYiehufrsnnkqdqbp69dddVDMopJu",
|
||||||
"c8a3c2a09a298592c3e180f02487cd91ba3400b5",
|
"76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C",
|
"2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C",
|
||||||
"99b31df7c9068d1481b596578ddbb4d3bd90baeb",
|
"a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -401,7 +412,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": false,
|
"isCompressed": false,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -410,7 +421,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": true,
|
"isCompressed": true,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -419,7 +430,7 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": false,
|
"isCompressed": false,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -428,25 +439,95 @@
|
|||||||
{
|
{
|
||||||
"isCompressed": true,
|
"isCompressed": true,
|
||||||
"isPrivkey": true,
|
"isPrivkey": true,
|
||||||
"isTestnet": true
|
"chain": "test"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE",
|
"13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE",
|
||||||
"1ed467017f043e91ed4c44b4e8dd674db211c4e6",
|
"76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac",
|
||||||
{
|
{
|
||||||
"addrType": "pubkey",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": false
|
"chain": "main"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G",
|
"3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G",
|
||||||
"5ece0cadddc415b1980f001785947120acdb36fc",
|
"a9145ece0cadddc415b1980f001785947120acdb36fc87",
|
||||||
{
|
{
|
||||||
"addrType": "script",
|
|
||||||
"isPrivkey": false,
|
"isPrivkey": false,
|
||||||
"isTestnet": 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;
|
s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD;
|
||||||
BOOST_CHECK(!Solver(s, whichType, solutions));
|
BOOST_CHECK(!Solver(s, whichType, solutions));
|
||||||
|
|
||||||
// TX_WITNESS with unknown version
|
|
||||||
s.clear();
|
|
||||||
s << OP_1 << ToByteVector(pubkey);
|
|
||||||
BOOST_CHECK(!Solver(s, whichType, solutions));
|
|
||||||
|
|
||||||
// TX_WITNESS with incorrect program size
|
// TX_WITNESS with incorrect program size
|
||||||
s.clear();
|
s.clear();
|
||||||
s << OP_0 << std::vector<unsigned char>(19, 0x01);
|
s << OP_0 << std::vector<unsigned char>(19, 0x01);
|
||||||
@@ -225,13 +220,29 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
|
|||||||
|
|
||||||
// TX_WITNESS_V0_KEYHASH
|
// TX_WITNESS_V0_KEYHASH
|
||||||
s.clear();
|
s.clear();
|
||||||
s << OP_0 << ToByteVector(pubkey);
|
s << OP_0 << ToByteVector(pubkey.GetID());
|
||||||
BOOST_CHECK(!ExtractDestination(s, address));
|
BOOST_CHECK(ExtractDestination(s, address));
|
||||||
|
WitnessV0KeyHash keyhash;
|
||||||
|
CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(keyhash.begin());
|
||||||
|
BOOST_CHECK(boost::get<WitnessV0KeyHash>(&address) && *boost::get<WitnessV0KeyHash>(&address) == keyhash);
|
||||||
|
|
||||||
// TX_WITNESS_V0_SCRIPTHASH
|
// TX_WITNESS_V0_SCRIPTHASH
|
||||||
s.clear();
|
s.clear();
|
||||||
s << OP_0 << ToByteVector(CScriptID(redeemScript));
|
WitnessV0ScriptHash scripthash;
|
||||||
BOOST_CHECK(!ExtractDestination(s, address));
|
CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin());
|
||||||
|
s << OP_0 << ToByteVector(scripthash);
|
||||||
|
BOOST_CHECK(ExtractDestination(s, address));
|
||||||
|
BOOST_CHECK(boost::get<WitnessV0ScriptHash>(&address) && *boost::get<WitnessV0ScriptHash>(&address) == scripthash);
|
||||||
|
|
||||||
|
// TX_WITNESS with unknown version
|
||||||
|
s.clear();
|
||||||
|
s << OP_1 << ToByteVector(pubkey);
|
||||||
|
BOOST_CHECK(ExtractDestination(s, address));
|
||||||
|
WitnessUnknown unk;
|
||||||
|
unk.length = 33;
|
||||||
|
unk.version = 1;
|
||||||
|
std::copy(pubkey.begin(), pubkey.end(), unk.program);
|
||||||
|
BOOST_CHECK(boost::get<WitnessUnknown>(&address) && *boost::get<WitnessUnknown>(&address) == unk);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
|
BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
|
||||||
@@ -298,16 +309,6 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
|
|||||||
s.clear();
|
s.clear();
|
||||||
s << OP_RETURN << std::vector<unsigned char>({75});
|
s << OP_RETURN << std::vector<unsigned char>({75});
|
||||||
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
|
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
|
||||||
|
|
||||||
// TX_WITNESS_V0_KEYHASH
|
|
||||||
s.clear();
|
|
||||||
s << OP_0 << ToByteVector(pubkeys[0].GetID());
|
|
||||||
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
|
|
||||||
|
|
||||||
// TX_WITNESS_V0_SCRIPTHASH
|
|
||||||
s.clear();
|
|
||||||
s << OP_0 << ToByteVector(CScriptID(redeemScript));
|
|
||||||
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
|
BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
|
||||||
|
|||||||
@@ -149,4 +149,28 @@ bool TimingResistantEqual(const T& a, const T& b)
|
|||||||
*/
|
*/
|
||||||
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
|
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
|
||||||
|
|
||||||
|
/** Convert from one power-of-2 number base to another. */
|
||||||
|
template<int frombits, int tobits, bool pad, typename O, typename I>
|
||||||
|
bool ConvertBits(O& out, I it, I end) {
|
||||||
|
size_t acc = 0;
|
||||||
|
size_t bits = 0;
|
||||||
|
constexpr size_t maxv = (1 << tobits) - 1;
|
||||||
|
constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1;
|
||||||
|
while (it != end) {
|
||||||
|
acc = ((acc << frombits) | *it) & max_acc;
|
||||||
|
bits += frombits;
|
||||||
|
while (bits >= tobits) {
|
||||||
|
bits -= tobits;
|
||||||
|
out.push_back((acc >> bits) & maxv);
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
if (pad) {
|
||||||
|
if (bits) out.push_back((acc << (tobits - bits)) & maxv);
|
||||||
|
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // BITCOIN_UTILSTRENCODINGS_H
|
#endif // BITCOIN_UTILSTRENCODINGS_H
|
||||||
+48
-16
@@ -1154,11 +1154,10 @@ class Witnessifier : public boost::static_visitor<bool>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CWallet * const pwallet;
|
CWallet * const pwallet;
|
||||||
CScriptID result;
|
CTxDestination result;
|
||||||
|
bool already_witness;
|
||||||
|
|
||||||
explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {}
|
explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {}
|
||||||
|
|
||||||
bool operator()(const CNoDestination &dest) const { return false; }
|
|
||||||
|
|
||||||
bool operator()(const CKeyID &keyID) {
|
bool operator()(const CKeyID &keyID) {
|
||||||
if (pwallet) {
|
if (pwallet) {
|
||||||
@@ -1172,9 +1171,7 @@ public:
|
|||||||
!VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) {
|
!VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
pwallet->AddCScript(witscript);
|
return ExtractDestination(witscript, result);
|
||||||
result = CScriptID(witscript);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1185,7 +1182,8 @@ public:
|
|||||||
int witnessversion;
|
int witnessversion;
|
||||||
std::vector<unsigned char> witprog;
|
std::vector<unsigned char> witprog;
|
||||||
if (subscript.IsWitnessProgram(witnessversion, witprog)) {
|
if (subscript.IsWitnessProgram(witnessversion, witprog)) {
|
||||||
result = scriptID;
|
ExtractDestination(subscript, result);
|
||||||
|
already_witness = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
CScript witscript = GetScriptForWitness(subscript);
|
CScript witscript = GetScriptForWitness(subscript);
|
||||||
@@ -1197,12 +1195,27 @@ public:
|
|||||||
!VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) {
|
!VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
pwallet->AddCScript(witscript);
|
return ExtractDestination(witscript, result);
|
||||||
result = CScriptID(witscript);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator()(const WitnessV0KeyHash& id)
|
||||||
|
{
|
||||||
|
already_witness = true;
|
||||||
|
result = id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const WitnessV0ScriptHash& id)
|
||||||
|
{
|
||||||
|
already_witness = true;
|
||||||
|
result = id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool operator()(const T& dest) { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
UniValue addwitnessaddress(const JSONRPCRequest& request)
|
UniValue addwitnessaddress(const JSONRPCRequest& request)
|
||||||
@@ -1212,17 +1225,18 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
|
|||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.fHelp || request.params.size() < 1 || request.params.size() > 1)
|
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
|
||||||
{
|
{
|
||||||
std::string msg = "addwitnessaddress \"address\"\n"
|
std::string msg = "addwitnessaddress \"address\" ( p2sh )\n"
|
||||||
"\nAdd a witness address for a script (with pubkey or redeemscript known).\n"
|
"\nAdd a witness address for a script (with pubkey or redeemscript known).\n"
|
||||||
"It returns the witness script.\n"
|
"It returns the witness script.\n"
|
||||||
|
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. \"address\" (string, required) An address known to the wallet\n"
|
"1. \"address\" (string, required) An address known to the wallet\n"
|
||||||
|
"2. p2sh (bool, optional, default=true) Embed inside P2SH\n"
|
||||||
|
|
||||||
"\nResult:\n"
|
"\nResult:\n"
|
||||||
"\"witnessaddress\", (string) The value of the new address (P2SH of witness script).\n"
|
"\"witnessaddress\", (string) The value of the new address (P2SH or BIP173).\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
;
|
;
|
||||||
throw std::runtime_error(msg);
|
throw std::runtime_error(msg);
|
||||||
@@ -1240,13 +1254,31 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
|
|||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool p2sh = true;
|
||||||
|
if (!request.params[1].isNull()) {
|
||||||
|
p2sh = request.params[1].get_bool();
|
||||||
|
}
|
||||||
|
|
||||||
Witnessifier w(pwallet);
|
Witnessifier w(pwallet);
|
||||||
bool ret = boost::apply_visitor(w, dest);
|
bool ret = boost::apply_visitor(w, dest);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed");
|
||||||
}
|
}
|
||||||
|
|
||||||
pwallet->SetAddressBook(w.result, "", "receive");
|
CScript witprogram = GetScriptForDestination(w.result);
|
||||||
|
|
||||||
|
if (p2sh) {
|
||||||
|
w.result = CScriptID(witprogram);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w.already_witness) {
|
||||||
|
if (!(dest == w.result)) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pwallet->AddCScript(witprogram);
|
||||||
|
pwallet->SetAddressBook(w.result, "", "receive");
|
||||||
|
}
|
||||||
|
|
||||||
return EncodeDestination(w.result);
|
return EncodeDestination(w.result);
|
||||||
}
|
}
|
||||||
@@ -3199,7 +3231,7 @@ static const CRPCCommand commands[] =
|
|||||||
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
|
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
|
||||||
{ "wallet", "abortrescan", &abortrescan, {} },
|
{ "wallet", "abortrescan", &abortrescan, {} },
|
||||||
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} },
|
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} },
|
||||||
{ "wallet", "addwitnessaddress", &addwitnessaddress, {"address"} },
|
{ "wallet", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
|
||||||
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
|
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
|
||||||
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
|
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
|
||||||
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
|
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
|
||||||
|
|||||||
+20
-1
@@ -111,7 +111,26 @@ public:
|
|||||||
Process(script);
|
Process(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(const CNoDestination &none) {}
|
void operator()(const WitnessV0ScriptHash& scriptID)
|
||||||
|
{
|
||||||
|
CScriptID id;
|
||||||
|
CRIPEMD160().Write(scriptID.begin(), 32).Finalize(id.begin());
|
||||||
|
CScript script;
|
||||||
|
if (keystore.GetCScript(id, script)) {
|
||||||
|
Process(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const WitnessV0KeyHash& keyid)
|
||||||
|
{
|
||||||
|
CKeyID id(keyid);
|
||||||
|
if (keystore.HaveKey(id)) {
|
||||||
|
vKeys.push_back(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename X>
|
||||||
|
void operator()(const X &none) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
|
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
|
||||||
|
|||||||
+50
-13
@@ -7,7 +7,7 @@
|
|||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import *
|
from test_framework.util import *
|
||||||
from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex
|
from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex
|
||||||
from test_framework.address import script_to_p2sh, key_to_p2pkh
|
from test_framework.address import script_to_p2sh, key_to_p2pkh, key_to_p2sh_p2wpkh, key_to_p2wpkh, script_to_p2sh_p2wsh, script_to_p2wsh, program_to_witness
|
||||||
from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE
|
from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
@@ -33,15 +33,15 @@ def witness_script(use_p2wsh, pubkey):
|
|||||||
|
|
||||||
# Return a transaction (in hex) that spends the given utxo to a segwit output,
|
# Return a transaction (in hex) that spends the given utxo to a segwit output,
|
||||||
# optionally wrapping the segwit output using P2SH.
|
# optionally wrapping the segwit output using P2SH.
|
||||||
def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount):
|
def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
|
||||||
pkscript = hex_str_to_bytes(witness_script(use_p2wsh, pubkey))
|
if use_p2wsh:
|
||||||
if (encode_p2sh):
|
program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
|
||||||
p2sh_hash = hash160(pkscript)
|
addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
|
||||||
pkscript = CScript([OP_HASH160, p2sh_hash, OP_EQUAL])
|
else:
|
||||||
tx = CTransaction()
|
addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey)
|
||||||
tx.vin.append(CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), b""))
|
if not encode_p2sh:
|
||||||
tx.vout.append(CTxOut(int(amount*COIN), pkscript))
|
assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey))
|
||||||
return ToHex(tx)
|
return node.createrawtransaction([utxo], {addr: amount})
|
||||||
|
|
||||||
# Create a transaction spending a given utxo to a segwit output corresponding
|
# Create a transaction spending a given utxo to a segwit output corresponding
|
||||||
# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH;
|
# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH;
|
||||||
@@ -49,7 +49,7 @@ def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount):
|
|||||||
# sign=True will have the given node sign the transaction.
|
# sign=True will have the given node sign the transaction.
|
||||||
# insert_redeem_script will be added to the scriptSig, if given.
|
# insert_redeem_script will be added to the scriptSig, if given.
|
||||||
def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""):
|
def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""):
|
||||||
tx_to_witness = create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount)
|
tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount)
|
||||||
if (sign):
|
if (sign):
|
||||||
signed = node.signrawtransaction(tx_to_witness)
|
signed = node.signrawtransaction(tx_to_witness)
|
||||||
assert("errors" not in signed or len(["errors"]) == 0)
|
assert("errors" not in signed or len(["errors"]) == 0)
|
||||||
@@ -133,8 +133,15 @@ class SegWitTest(BitcoinTestFramework):
|
|||||||
newaddress = self.nodes[i].getnewaddress()
|
newaddress = self.nodes[i].getnewaddress()
|
||||||
self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"])
|
self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"])
|
||||||
multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]])
|
multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]])
|
||||||
self.nodes[i].addwitnessaddress(newaddress)
|
multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG])
|
||||||
self.nodes[i].addwitnessaddress(multiaddress)
|
p2sh_addr = self.nodes[i].addwitnessaddress(newaddress, True)
|
||||||
|
bip173_addr = self.nodes[i].addwitnessaddress(newaddress, False)
|
||||||
|
p2sh_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, True)
|
||||||
|
bip173_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, False)
|
||||||
|
assert_equal(p2sh_addr, key_to_p2sh_p2wpkh(self.pubkey[-1]))
|
||||||
|
assert_equal(bip173_addr, key_to_p2wpkh(self.pubkey[-1]))
|
||||||
|
assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript))
|
||||||
|
assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript))
|
||||||
p2sh_ids.append([])
|
p2sh_ids.append([])
|
||||||
wit_ids.append([])
|
wit_ids.append([])
|
||||||
for v in range(2):
|
for v in range(2):
|
||||||
@@ -558,6 +565,13 @@ class SegWitTest(BitcoinTestFramework):
|
|||||||
solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1))
|
solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1))
|
||||||
self.mine_and_test_listunspent(unseen_anytime, 0)
|
self.mine_and_test_listunspent(unseen_anytime, 0)
|
||||||
|
|
||||||
|
# Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works
|
||||||
|
v1_addr = program_to_witness(1, [3,5])
|
||||||
|
v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])],{v1_addr: 1})
|
||||||
|
v1_decoded = self.nodes[1].decoderawtransaction(v1_tx)
|
||||||
|
assert_equal(v1_decoded['vout'][0]['scriptPubKey']['addresses'][0], v1_addr)
|
||||||
|
assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305")
|
||||||
|
|
||||||
# Check that spendable outputs are really spendable
|
# Check that spendable outputs are really spendable
|
||||||
self.create_and_mine_tx_from_txids(spendable_txid)
|
self.create_and_mine_tx_from_txids(spendable_txid)
|
||||||
|
|
||||||
@@ -570,6 +584,29 @@ class SegWitTest(BitcoinTestFramework):
|
|||||||
self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K")
|
self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K")
|
||||||
self.create_and_mine_tx_from_txids(solvable_txid)
|
self.create_and_mine_tx_from_txids(solvable_txid)
|
||||||
|
|
||||||
|
# Test that importing native P2WPKH/P2WSH scripts works
|
||||||
|
for use_p2wsh in [False, True]:
|
||||||
|
if use_p2wsh:
|
||||||
|
scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a"
|
||||||
|
transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000"
|
||||||
|
else:
|
||||||
|
scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87"
|
||||||
|
transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000"
|
||||||
|
|
||||||
|
self.nodes[1].importaddress(scriptPubKey, "", False)
|
||||||
|
rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex']
|
||||||
|
rawtxfund = self.nodes[1].signrawtransaction(rawtxfund)["hex"]
|
||||||
|
txid = self.nodes[1].sendrawtransaction(rawtxfund)
|
||||||
|
|
||||||
|
assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
|
||||||
|
assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
|
||||||
|
|
||||||
|
# Assert it is properly saved
|
||||||
|
self.stop_node(1)
|
||||||
|
self.start_node(1)
|
||||||
|
assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
|
||||||
|
assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
|
||||||
|
|
||||||
def mine_and_test_listunspent(self, script_list, ismine):
|
def mine_and_test_listunspent(self, script_list, ismine):
|
||||||
utxo = find_unspent(self.nodes[0], 50)
|
utxo = find_unspent(self.nodes[0], 50)
|
||||||
tx = CTransaction()
|
tx = CTransaction()
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
from .script import hash256, hash160, sha256, CScript, OP_0
|
from .script import hash256, hash160, sha256, CScript, OP_0
|
||||||
from .util import bytes_to_hex_str, hex_str_to_bytes
|
from .util import bytes_to_hex_str, hex_str_to_bytes
|
||||||
|
|
||||||
|
from . import segwit_addr
|
||||||
|
|
||||||
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||||
|
|
||||||
def byte_to_base58(b, version):
|
def byte_to_base58(b, version):
|
||||||
@@ -44,6 +46,32 @@ def script_to_p2sh(script, main = False):
|
|||||||
script = check_script(script)
|
script = check_script(script)
|
||||||
return scripthash_to_p2sh(hash160(script), main)
|
return scripthash_to_p2sh(hash160(script), main)
|
||||||
|
|
||||||
|
def key_to_p2sh_p2wpkh(key, main = False):
|
||||||
|
key = check_key(key)
|
||||||
|
p2shscript = CScript([OP_0, hash160(key)])
|
||||||
|
return script_to_p2sh(p2shscript, main)
|
||||||
|
|
||||||
|
def program_to_witness(version, program, main = False):
|
||||||
|
if (type(program) is str):
|
||||||
|
program = hex_str_to_bytes(program)
|
||||||
|
assert 0 <= version <= 16
|
||||||
|
assert 2 <= len(program) <= 40
|
||||||
|
assert version > 0 or len(program) in [20, 32]
|
||||||
|
return segwit_addr.encode("bc" if main else "bcrt", version, program)
|
||||||
|
|
||||||
|
def script_to_p2wsh(script, main = False):
|
||||||
|
script = check_script(script)
|
||||||
|
return program_to_witness(0, sha256(script), main)
|
||||||
|
|
||||||
|
def key_to_p2wpkh(key, main = False):
|
||||||
|
key = check_key(key)
|
||||||
|
return program_to_witness(0, hash160(key), main)
|
||||||
|
|
||||||
|
def script_to_p2sh_p2wsh(script, main = False):
|
||||||
|
script = check_script(script)
|
||||||
|
p2shscript = CScript([OP_0, sha256(script)])
|
||||||
|
return script_to_p2sh(p2shscript, main)
|
||||||
|
|
||||||
def check_key(key):
|
def check_key(key):
|
||||||
if (type(key) is str):
|
if (type(key) is str):
|
||||||
key = hex_str_to_bytes(key) # Assuming this is hex string
|
key = hex_str_to_bytes(key) # Assuming this is hex string
|
||||||
|
|||||||
@@ -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": {
|
"scriptPubKey": {
|
||||||
"asm": "0 e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05",
|
"asm": "0 e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05",
|
||||||
"hex": "0020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05",
|
"hex": "0020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05",
|
||||||
"type": "witness_v0_scripthash"
|
"reqSigs": 1,
|
||||||
|
"type": "witness_v0_scripthash",
|
||||||
|
"addresses": [
|
||||||
|
"bc1qu9dgdg330r6r84g5mw7wqshg04exv2uttmw2elfwx74h5tgntuzs44gyfg"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -14,7 +14,11 @@
|
|||||||
"scriptPubKey": {
|
"scriptPubKey": {
|
||||||
"asm": "0 a2516e770582864a6a56ed21a102044e388c62e3",
|
"asm": "0 a2516e770582864a6a56ed21a102044e388c62e3",
|
||||||
"hex": "0014a2516e770582864a6a56ed21a102044e388c62e3",
|
"hex": "0014a2516e770582864a6a56ed21a102044e388c62e3",
|
||||||
"type": "witness_v0_keyhash"
|
"reqSigs": 1,
|
||||||
|
"type": "witness_v0_keyhash",
|
||||||
|
"addresses": [
|
||||||
|
"bc1q5fgkuac9s2ry56jka5s6zqsyfcugcchry5cwu0"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -14,7 +14,11 @@
|
|||||||
"scriptPubKey": {
|
"scriptPubKey": {
|
||||||
"asm": "0 0bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6",
|
"asm": "0 0bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6",
|
||||||
"hex": "00200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6",
|
"hex": "00200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6",
|
||||||
"type": "witness_v0_scripthash"
|
"reqSigs": 1,
|
||||||
|
"type": "witness_v0_scripthash",
|
||||||
|
"addresses": [
|
||||||
|
"bc1qp0lfxhnscvsu0j36l36uurgv5tuck4pzuqytkvwqp3kh78cupttqyf705v"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in new issue
Block a user