Implement BIP173 addresses and tests

This commit is contained in:
Pieter Wuille 2017-08-25 19:55:52 -07:00
parent bd355b8db9
commit c091b99379
19 changed files with 397 additions and 32 deletions

View file

@ -4,9 +4,11 @@
#include "base58.h"
#include "bech32.h"
#include "hash.h"
#include "script/script.h"
#include "uint256.h"
#include "utilstrencodings.h"
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
@ -235,7 +237,31 @@ public:
return EncodeBase58Check(data);
}
std::string operator()(const CNoDestination& no) const { return ""; }
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)
@ -259,6 +285,40 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
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

View file

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

View file

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

View file

@ -76,7 +76,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool w
else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH))
return false;
return whichType != TX_NONSTANDARD;
return whichType != TX_NONSTANDARD && whichType != TX_WITNESS_UNKNOWN;
}
bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled)

View file

@ -7,6 +7,7 @@
#include "chain.h"
#include "clientversion.h"
#include "core_io.h"
#include "crypto/ripemd160.h"
#include "init.h"
#include "validation.h"
#include "httpserver.h"
@ -45,6 +46,7 @@ public:
UniValue obj(UniValue::VOBJ);
CPubKey vchPubKey;
obj.push_back(Pair("isscript", false));
obj.push_back(Pair("iswitness", false));
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
@ -56,6 +58,7 @@ public:
UniValue obj(UniValue::VOBJ);
CScript subscript;
obj.push_back(Pair("isscript", true));
obj.push_back(Pair("iswitness", false));
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
std::vector<CTxDestination> addresses;
txnouttype whichType;
@ -73,6 +76,47 @@ public:
}
return obj;
}
UniValue operator()(const WitnessV0KeyHash& id) const
{
UniValue obj(UniValue::VOBJ);
CPubKey pubkey;
obj.push_back(Pair("isscript", false));
obj.push_back(Pair("iswitness", true));
obj.push_back(Pair("witness_version", 0));
obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end())));
if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) {
obj.push_back(Pair("pubkey", HexStr(pubkey)));
}
return obj;
}
UniValue operator()(const WitnessV0ScriptHash& id) const
{
UniValue obj(UniValue::VOBJ);
CScript subscript;
obj.push_back(Pair("isscript", true));
obj.push_back(Pair("iswitness", true));
obj.push_back(Pair("witness_version", 0));
obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end())));
CRIPEMD160 hasher;
uint160 hash;
hasher.Write(id.begin(), 32).Finalize(hash.begin());
if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) {
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
}
return obj;
}
UniValue operator()(const WitnessUnknown& id) const
{
UniValue obj(UniValue::VOBJ);
CScript subscript;
obj.push_back(Pair("iswitness", true));
obj.push_back(Pair("witness_version", (int)id.version));
obj.push_back(Pair("witness_program", HexStr(id.program, id.program + id.length)));
return obj;
}
};
#endif

View file

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

View file

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

View file

@ -30,6 +30,7 @@ const char* GetTxnOutputType(txnouttype t)
case TX_NULL_DATA: return "nulldata";
case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
case TX_WITNESS_UNKNOWN: return "witness_unknown";
}
return nullptr;
}
@ -75,6 +76,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
vSolutionsRet.push_back(witnessprogram);
return true;
}
if (witnessversion != 0) {
typeRet = TX_WITNESS_UNKNOWN;
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
vSolutionsRet.push_back(std::move(witnessprogram));
return true;
}
return false;
}
@ -198,6 +205,23 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
{
addressRet = CScriptID(uint160(vSolutions[0]));
return true;
} else if (whichType == TX_WITNESS_V0_KEYHASH) {
WitnessV0KeyHash hash;
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
addressRet = hash;
return true;
} else if (whichType == TX_WITNESS_V0_SCRIPTHASH) {
WitnessV0ScriptHash hash;
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
addressRet = hash;
return true;
} else if (whichType == TX_WITNESS_UNKNOWN) {
WitnessUnknown unk;
unk.version = vSolutions[0][0];
std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
unk.length = vSolutions[1].size();
addressRet = unk;
return true;
}
// Multisig txns have more than one address...
return false;
@ -268,6 +292,27 @@ public:
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
return true;
}
bool operator()(const WitnessV0KeyHash& id) const
{
script->clear();
*script << OP_0 << ToByteVector(id);
return true;
}
bool operator()(const WitnessV0ScriptHash& id) const
{
script->clear();
*script << OP_0 << ToByteVector(id);
return true;
}
bool operator()(const WitnessUnknown& id) const
{
script->clear();
*script << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length);
return true;
}
};
} // namespace

View file

@ -64,6 +64,7 @@ enum txnouttype
TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data
TX_WITNESS_V0_SCRIPTHASH,
TX_WITNESS_V0_KEYHASH,
TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
};
class CNoDestination {
@ -72,14 +73,42 @@ public:
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
};
struct WitnessV0ScriptHash : public uint256 {};
struct WitnessV0KeyHash : public uint160 {};
//! CTxDestination subtype to encode any future Witness version
struct WitnessUnknown
{
unsigned int version;
unsigned int length;
unsigned char program[40];
friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) {
if (w1.version != w2.version) return false;
if (w1.length != w2.length) return false;
return std::equal(w1.program, w1.program + w1.length, w2.program);
}
friend bool operator<(const WitnessUnknown& w1, const WitnessUnknown& w2) {
if (w1.version < w2.version) return true;
if (w1.version > w2.version) return false;
if (w1.length < w2.length) return true;
if (w1.length > w2.length) return false;
return std::lexicographical_compare(w1.program, w1.program + w1.length, w2.program, w2.program + w2.length);
}
};
/**
* A txout script template with a specific destination. It is either:
* * CNoDestination: no destination set
* * CKeyID: TX_PUBKEYHASH destination
* * CScriptID: TX_SCRIPTHASH destination
* * CKeyID: TX_PUBKEYHASH destination (P2PKH)
* * CScriptID: TX_SCRIPTHASH destination (P2SH)
* * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH)
* * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH)
* * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???)
* A CTxDestination is the internal data type encoded in a bitcoin address
*/
typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
typedef boost::variant<CNoDestination, CKeyID, CScriptID, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
/** Check whether a CTxDestination is a CNoDestination. */
bool IsValidDestination(const CTxDestination& dest);
@ -104,7 +133,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
* Parse a standard scriptPubKey for the destination address. Assigns result to
* the addressRet parameter and returns true if successful. For multisig
* scripts, instead use ExtractDestinations. Currently only works for P2PK,
* P2PKH, and P2SH scripts.
* P2PKH, P2SH, P2WPKH, and P2WSH scripts.
*/
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);

View file

@ -93,6 +93,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
const UniValue &metadata = test[2].get_obj();
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
SelectParams(find_value(metadata, "chain").get_str());
bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool();
if (isPrivkey) {
bool isCompressed = find_value(metadata, "isCompressed").get_bool();
// Must be valid private key
@ -112,6 +113,21 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
// Try flipped case version
for (char& c : exp_base58string) {
if (c >= 'a' && c <= 'z') {
c = (c - 'a') + 'A';
} else if (c >= 'A' && c <= 'Z') {
c = (c - 'A') + 'a';
}
}
destination = DecodeDestination(exp_base58string);
BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest);
if (IsValidDestination(destination)) {
script = GetScriptForDestination(destination);
BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
}
// Public key must be invalid private key
secret.SetString(exp_base58string);
BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid pubkey as privkey:" + strTest);
@ -150,6 +166,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
CScript exp_script(exp_payload.begin(), exp_payload.end());
ExtractDestination(exp_script, dest);
std::string address = EncodeDestination(dest);
BOOST_CHECK_EQUAL(address, exp_base58string);
}
}
@ -157,6 +174,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
SelectParams(CBaseChainParams::MAIN);
}
// Goal: check that base58 parsing code is robust against a variety of corrupted data
BOOST_AUTO_TEST_CASE(base58_keys_invalid)
{
@ -187,4 +205,3 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid)
BOOST_AUTO_TEST_SUITE_END()

View file

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

View file

@ -457,5 +457,77 @@
"isPrivkey": false,
"chain": "main"
}
],
[
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
"0014751e76e8199196d454941c45d1b3a323f1433bd6",
{
"isPrivkey": false,
"chain": "main",
"tryCaseFlip": true
}
],
[
"bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080",
"0014751e76e8199196d454941c45d1b3a323f1433bd6",
{
"isPrivkey": false,
"chain": "regtest",
"tryCaseFlip": true
}
],
[
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
"00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",
{
"isPrivkey": false,
"chain": "test",
"tryCaseFlip": true
}
],
[
"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx",
"5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6",
{
"isPrivkey": false,
"chain": "main",
"tryCaseFlip": true
}
],
[
"bc1sw50qa3jx3s",
"6002751e",
{
"isPrivkey": false,
"chain": "main",
"tryCaseFlip": true
}
],
[
"bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj",
"5210751e76e8199196d454941c45d1b3a323",
{
"isPrivkey": false,
"chain": "main",
"tryCaseFlip": true
}
],
[
"tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",
"0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433",
{
"isPrivkey": false,
"chain": "test",
"tryCaseFlip": true
}
],
[
"bcrt1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvseswlauz7",
"0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433",
{
"isPrivkey": false,
"chain": "regtest",
"tryCaseFlip": true
}
]
]

View file

@ -170,11 +170,6 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD;
BOOST_CHECK(!Solver(s, whichType, solutions));
// TX_WITNESS with unknown version
s.clear();
s << OP_1 << ToByteVector(pubkey);
BOOST_CHECK(!Solver(s, whichType, solutions));
// TX_WITNESS with incorrect program size
s.clear();
s << OP_0 << std::vector<unsigned char>(19, 0x01);
@ -225,13 +220,29 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
// TX_WITNESS_V0_KEYHASH
s.clear();
s << OP_0 << ToByteVector(pubkey);
BOOST_CHECK(!ExtractDestination(s, address));
s << OP_0 << ToByteVector(pubkey.GetID());
BOOST_CHECK(ExtractDestination(s, address));
WitnessV0KeyHash keyhash;
CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(keyhash.begin());
BOOST_CHECK(boost::get<WitnessV0KeyHash>(&address) && *boost::get<WitnessV0KeyHash>(&address) == keyhash);
// TX_WITNESS_V0_SCRIPTHASH
s.clear();
s << OP_0 << ToByteVector(CScriptID(redeemScript));
BOOST_CHECK(!ExtractDestination(s, address));
WitnessV0ScriptHash scripthash;
CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin());
s << OP_0 << ToByteVector(scripthash);
BOOST_CHECK(ExtractDestination(s, address));
BOOST_CHECK(boost::get<WitnessV0ScriptHash>(&address) && *boost::get<WitnessV0ScriptHash>(&address) == scripthash);
// TX_WITNESS with unknown version
s.clear();
s << OP_1 << ToByteVector(pubkey);
BOOST_CHECK(ExtractDestination(s, address));
WitnessUnknown unk;
unk.length = 33;
unk.version = 1;
std::copy(pubkey.begin(), pubkey.end(), unk.program);
BOOST_CHECK(boost::get<WitnessUnknown>(&address) && *boost::get<WitnessUnknown>(&address) == unk);
}
BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
@ -298,16 +309,6 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
s.clear();
s << OP_RETURN << std::vector<unsigned char>({75});
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
// TX_WITNESS_V0_KEYHASH
s.clear();
s << OP_0 << ToByteVector(pubkeys[0].GetID());
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
// TX_WITNESS_V0_SCRIPTHASH
s.clear();
s << OP_0 << ToByteVector(CScriptID(redeemScript));
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
}
BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)

View file

@ -149,4 +149,28 @@ bool TimingResistantEqual(const T& a, const T& b)
*/
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
/** Convert from one power-of-2 number base to another. */
template<int frombits, int tobits, bool pad, typename O, typename I>
bool ConvertBits(O& out, I it, I end) {
size_t acc = 0;
size_t bits = 0;
constexpr size_t maxv = (1 << tobits) - 1;
constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1;
while (it != end) {
acc = ((acc << frombits) | *it) & max_acc;
bits += frombits;
while (bits >= tobits) {
bits -= tobits;
out.push_back((acc >> bits) & maxv);
}
++it;
}
if (pad) {
if (bits) out.push_back((acc << (tobits - bits)) & maxv);
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
return false;
}
return true;
}
#endif // BITCOIN_UTILSTRENCODINGS_H

View file

@ -1158,8 +1158,6 @@ public:
explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {}
bool operator()(const CNoDestination &dest) const { return false; }
bool operator()(const CKeyID &keyID) {
if (pwallet) {
CScript basescript = GetScriptForDestination(keyID);
@ -1203,6 +1201,9 @@ public:
}
return false;
}
template<typename T>
bool operator()(const T& dest) { return false; }
};
UniValue addwitnessaddress(const JSONRPCRequest& request)

View file

@ -111,7 +111,26 @@ public:
Process(script);
}
void operator()(const CNoDestination &none) {}
void operator()(const WitnessV0ScriptHash& scriptID)
{
CScriptID id;
CRIPEMD160().Write(scriptID.begin(), 32).Finalize(id.begin());
CScript script;
if (keystore.GetCScript(id, script)) {
Process(script);
}
}
void operator()(const WitnessV0KeyHash& keyid)
{
CKeyID id(keyid);
if (keystore.HaveKey(id)) {
vKeys.push_back(id);
}
}
template<typename X>
void operator()(const X &none) {}
};
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const

View file

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

View file

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

View file

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