Changeset View
Changeset View
Standalone View
Standalone View
src/key_io.cpp
- This file was copied from src/base58.cpp.
// Copyright (c) 2014-2016 The Bitcoin Core developers | // Copyright (c) 2014-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <base58.h> | #include <key_io.h> | ||||
#include <hash.h> | #include <base58.h> | ||||
#include <cashaddrenc.h> | |||||
#include <chainparams.h> | |||||
#include <config.h> | |||||
#include <script/script.h> | #include <script/script.h> | ||||
#include <uint256.h> | |||||
#include <utilstrencodings.h> | #include <utilstrencodings.h> | ||||
#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 <cassert> | #include <cassert> | ||||
#include <cstdint> | |||||
#include <cstring> | #include <cstring> | ||||
#include <string> | |||||
#include <vector> | |||||
/** All alphanumeric characters except for "0", "I", "O", and "l" */ | |||||
static const char *pszBase58 = | |||||
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; | |||||
bool DecodeBase58(const char *psz, std::vector<uint8_t> &vch) { | |||||
// Skip leading spaces. | |||||
while (*psz && IsSpace(*psz)) { | |||||
psz++; | |||||
} | |||||
// Skip and count leading '1's. | |||||
int zeroes = 0; | |||||
int length = 0; | |||||
while (*psz == '1') { | |||||
zeroes++; | |||||
psz++; | |||||
} | |||||
// Allocate enough space in big-endian base256 representation. | |||||
// log(58) / log(256), rounded up. | |||||
int size = strlen(psz) * 733 / 1000 + 1; | |||||
std::vector<uint8_t> b256(size); | |||||
// Process the characters. | |||||
while (*psz && !IsSpace(*psz)) { | |||||
// Decode base58 character | |||||
const char *ch = strchr(pszBase58, *psz); | |||||
if (ch == nullptr) { | |||||
return false; | |||||
} | |||||
// Apply "b256 = b256 * 58 + ch". | |||||
int carry = ch - pszBase58; | |||||
int i = 0; | |||||
for (std::vector<uint8_t>::reverse_iterator it = b256.rbegin(); | |||||
(carry != 0 || i < length) && (it != b256.rend()); ++it, ++i) { | |||||
carry += 58 * (*it); | |||||
*it = carry % 256; | |||||
carry /= 256; | |||||
} | |||||
assert(carry == 0); | |||||
length = i; | |||||
psz++; | |||||
} | |||||
// Skip trailing spaces. | |||||
while (IsSpace(*psz)) { | |||||
psz++; | |||||
} | |||||
if (*psz != 0) { | |||||
return false; | |||||
} | |||||
// Skip leading zeroes in b256. | |||||
std::vector<uint8_t>::iterator it = b256.begin() + (size - length); | |||||
while (it != b256.end() && *it == 0) | |||||
it++; | |||||
// Copy result into output vector. | |||||
vch.reserve(zeroes + (b256.end() - it)); | |||||
vch.assign(zeroes, 0x00); | |||||
while (it != b256.end()) { | |||||
vch.push_back(*(it++)); | |||||
} | |||||
return true; | |||||
} | |||||
std::string EncodeBase58(const uint8_t *pbegin, const uint8_t *pend) { | |||||
// Skip & count leading zeroes. | |||||
int zeroes = 0; | |||||
int length = 0; | |||||
while (pbegin != pend && *pbegin == 0) { | |||||
pbegin++; | |||||
zeroes++; | |||||
} | |||||
// Allocate enough space in big-endian base58 representation. | |||||
// log(256) / log(58), rounded up. | |||||
int size = (pend - pbegin) * 138 / 100 + 1; | |||||
std::vector<uint8_t> b58(size); | |||||
// Process the bytes. | |||||
while (pbegin != pend) { | |||||
int carry = *pbegin; | |||||
int i = 0; | |||||
// Apply "b58 = b58 * 256 + ch". | |||||
for (std::vector<uint8_t>::reverse_iterator it = b58.rbegin(); | |||||
(carry != 0 || i < length) && (it != b58.rend()); it++, i++) { | |||||
carry += 256 * (*it); | |||||
*it = carry % 58; | |||||
carry /= 58; | |||||
} | |||||
assert(carry == 0); | |||||
length = i; | |||||
pbegin++; | |||||
} | |||||
// Skip leading zeroes in base58 result. | |||||
std::vector<uint8_t>::iterator it = b58.begin() + (size - length); | |||||
while (it != b58.end() && *it == 0) { | |||||
it++; | |||||
} | |||||
// Translate the result into a string. | |||||
std::string str; | |||||
str.reserve(zeroes + (b58.end() - it)); | |||||
str.assign(zeroes, '1'); | |||||
while (it != b58.end()) { | |||||
str += pszBase58[*(it++)]; | |||||
} | |||||
return str; | |||||
} | |||||
std::string EncodeBase58(const std::vector<uint8_t> &vch) { | |||||
return EncodeBase58(vch.data(), vch.data() + vch.size()); | |||||
} | |||||
bool DecodeBase58(const std::string &str, std::vector<uint8_t> &vchRet) { | |||||
return DecodeBase58(str.c_str(), vchRet); | |||||
} | |||||
std::string EncodeBase58Check(const std::vector<uint8_t> &vchIn) { | |||||
// add 4-byte hash check to the end | |||||
std::vector<uint8_t> vch(vchIn); | |||||
uint256 hash = Hash(vch.begin(), vch.end()); | |||||
vch.insert(vch.end(), (uint8_t *)&hash, (uint8_t *)&hash + 4); | |||||
return EncodeBase58(vch); | |||||
} | |||||
bool DecodeBase58Check(const char *psz, std::vector<uint8_t> &vchRet) { | |||||
if (!DecodeBase58(psz, vchRet) || (vchRet.size() < 4)) { | |||||
vchRet.clear(); | |||||
return false; | |||||
} | |||||
// re-calculate the checksum, ensure it matches the included 4-byte checksum | |||||
uint256 hash = Hash(vchRet.begin(), vchRet.end() - 4); | |||||
if (memcmp(&hash, &vchRet[vchRet.size() - 4], 4) != 0) { | |||||
vchRet.clear(); | |||||
return false; | |||||
} | |||||
vchRet.resize(vchRet.size() - 4); | |||||
return true; | |||||
} | |||||
bool DecodeBase58Check(const std::string &str, std::vector<uint8_t> &vchRet) { | |||||
return DecodeBase58Check(str.c_str(), vchRet); | |||||
} | |||||
namespace { | namespace { | ||||
class DestinationEncoder : public boost::static_visitor<std::string> { | class DestinationEncoder : public boost::static_visitor<std::string> { | ||||
private: | private: | ||||
const CChainParams &m_params; | const CChainParams &m_params; | ||||
public: | public: | ||||
explicit DestinationEncoder(const CChainParams ¶ms) | DestinationEncoder(const CChainParams ¶ms) : m_params(params) {} | ||||
: m_params(params) {} | |||||
std::string operator()(const CKeyID &id) const { | std::string operator()(const CKeyID &id) const { | ||||
std::vector<uint8_t> data = | std::vector<uint8_t> data = | ||||
m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); | m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); | ||||
data.insert(data.end(), id.begin(), id.end()); | data.insert(data.end(), id.begin(), id.end()); | ||||
return EncodeBase58Check(data); | return EncodeBase58Check(data); | ||||
} | } | ||||
std::string operator()(const CScriptID &id) const { | std::string operator()(const CScriptID &id) const { | ||||
std::vector<uint8_t> data = | std::vector<uint8_t> data = | ||||
m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); | m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); | ||||
data.insert(data.end(), id.begin(), id.end()); | data.insert(data.end(), id.begin(), id.end()); | ||||
return EncodeBase58Check(data); | return EncodeBase58Check(data); | ||||
} | } | ||||
std::string operator()(const CNoDestination &no) const { return ""; } | std::string operator()(const CNoDestination &no) const { return {}; } | ||||
}; | }; | ||||
CTxDestination DecodeLegacyDestination(const std::string &str, | CTxDestination DecodeLegacyDestination(const std::string &str, | ||||
const CChainParams ¶ms) { | const CChainParams ¶ms) { | ||||
std::vector<uint8_t> data; | std::vector<uint8_t> data; | ||||
uint160 hash; | uint160 hash; | ||||
if (!DecodeBase58Check(str, data)) { | if (!DecodeBase58Check(str, data)) { | ||||
return CNoDestination(); | return CNoDestination(); | ||||
} | } | ||||
// Base58Check decoding | // 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<uint8_t> &pubkey_prefix = | const std::vector<uint8_t> &pubkey_prefix = | ||||
params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); | params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); | ||||
if (data.size() == 20 + pubkey_prefix.size() && | if (data.size() == hash.size() + pubkey_prefix.size() && | ||||
std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { | std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { | ||||
memcpy(hash.begin(), &data[pubkey_prefix.size()], 20); | std::copy(data.begin() + pubkey_prefix.size(), data.end(), | ||||
hash.begin()); | |||||
return CKeyID(hash); | 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<uint8_t> &script_prefix = | const std::vector<uint8_t> &script_prefix = | ||||
params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); | params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); | ||||
if (data.size() == 20 + script_prefix.size() && | if (data.size() == hash.size() + script_prefix.size() && | ||||
std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { | std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { | ||||
memcpy(hash.begin(), &data[script_prefix.size()], 20); | std::copy(data.begin() + script_prefix.size(), data.end(), | ||||
hash.begin()); | |||||
return CScriptID(hash); | return CScriptID(hash); | ||||
} | } | ||||
return CNoDestination(); | return CNoDestination(); | ||||
} | } | ||||
} // namespace | } // namespace | ||||
CKey DecodeSecret(const std::string &str) { | CKey DecodeSecret(const std::string &str) { | ||||
CKey key; | CKey key; | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | std::string EncodeExtKey(const CExtKey &key) { | ||||
size_t size = data.size(); | size_t size = data.size(); | ||||
data.resize(size + BIP32_EXTKEY_SIZE); | data.resize(size + BIP32_EXTKEY_SIZE); | ||||
key.Encode(data.data() + size); | key.Encode(data.data() + size); | ||||
std::string ret = EncodeBase58Check(data); | std::string ret = EncodeBase58Check(data); | ||||
memory_cleanse(data.data(), data.size()); | memory_cleanse(data.data(), data.size()); | ||||
return ret; | return ret; | ||||
} | } | ||||
std::string EncodeDestination(const CTxDestination &dest, | |||||
const Config &config) { | |||||
const CChainParams ¶ms = config.GetChainParams(); | |||||
return config.UseCashAddrEncoding() ? EncodeCashAddr(dest, params) | |||||
: EncodeLegacyAddr(dest, params); | |||||
} | |||||
CTxDestination DecodeDestination(const std::string &addr, | |||||
const CChainParams ¶ms) { | |||||
CTxDestination dst = DecodeCashAddr(addr, params); | |||||
if (IsValidDestination(dst)) { | |||||
return dst; | |||||
} | |||||
return DecodeLegacyAddr(addr, params); | |||||
} | |||||
bool IsValidDestinationString(const std::string &str, | |||||
const CChainParams ¶ms) { | |||||
return IsValidDestination(DecodeDestination(str, params)); | |||||
} | |||||
std::string EncodeLegacyAddr(const CTxDestination &dest, | std::string EncodeLegacyAddr(const CTxDestination &dest, | ||||
const CChainParams ¶ms) { | const CChainParams ¶ms) { | ||||
return boost::apply_visitor(DestinationEncoder(params), dest); | return boost::apply_visitor(DestinationEncoder(params), dest); | ||||
} | } | ||||
CTxDestination DecodeLegacyAddr(const std::string &str, | CTxDestination DecodeLegacyAddr(const std::string &str, | ||||
const CChainParams ¶ms) { | const CChainParams ¶ms) { | ||||
return DecodeLegacyDestination(str, params); | return DecodeLegacyDestination(str, params); | ||||
} | } |