libzcash::PaymentAddress has been renamed to libzcash::SproutPaymentAddress, and a new typedef boost::variant is now libzcash::PaymentAddress. Similarly for ViewingKey and SpendingKey. A new class InvalidEncoding is introduced as the default boost::variant option for each address and key type; it is used during decoding instead of boost::optional. All address and key storage functions in the wallet have been modified to refer specifically to the Sprout types, as they are used very precisely. In most other cases, the more general type is leveraged as much as possible, and we convert to the Sprout type when necessary. This will be subsequently wrapped in, or replaced with, context-specific functions once Sapling types are implemented.
307 lines
11 KiB
C++
307 lines
11 KiB
C++
// Copyright (c) 2014-2016 The Bitcoin Core developers
|
|
// Copyright (c) 2016-2018 The Zcash developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <key_io.h>
|
|
|
|
#include <base58.h>
|
|
#include <bech32.h>
|
|
#include <script/script.h>
|
|
#include <utilstrencodings.h>
|
|
|
|
#include <boost/variant/apply_visitor.hpp>
|
|
#include <boost/variant/static_visitor.hpp>
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <algorithm>
|
|
|
|
namespace
|
|
{
|
|
class DestinationEncoder : public boost::static_visitor<std::string>
|
|
{
|
|
private:
|
|
const CChainParams& m_params;
|
|
|
|
public:
|
|
DestinationEncoder(const CChainParams& params) : m_params(params) {}
|
|
|
|
std::string operator()(const CKeyID& id) const
|
|
{
|
|
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
|
|
data.insert(data.end(), id.begin(), id.end());
|
|
return EncodeBase58Check(data);
|
|
}
|
|
|
|
std::string operator()(const CScriptID& id) const
|
|
{
|
|
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
|
|
data.insert(data.end(), id.begin(), id.end());
|
|
return EncodeBase58Check(data);
|
|
}
|
|
|
|
std::string operator()(const 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);
|
|
}
|
|
}
|
|
return CNoDestination();
|
|
}
|
|
|
|
class PaymentAddressEncoder : public boost::static_visitor<std::string>
|
|
{
|
|
private:
|
|
const CChainParams& m_params;
|
|
|
|
public:
|
|
PaymentAddressEncoder(const CChainParams& params) : m_params(params) {}
|
|
|
|
std::string operator()(const libzcash::SproutPaymentAddress& zaddr) const
|
|
{
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
ss << zaddr;
|
|
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS);
|
|
data.insert(data.end(), ss.begin(), ss.end());
|
|
return EncodeBase58Check(data);
|
|
}
|
|
|
|
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
|
|
};
|
|
|
|
class ViewingKeyEncoder : public boost::static_visitor<std::string>
|
|
{
|
|
private:
|
|
const CChainParams& m_params;
|
|
|
|
public:
|
|
ViewingKeyEncoder(const CChainParams& params) : m_params(params) {}
|
|
|
|
std::string operator()(const libzcash::SproutViewingKey& vk) const
|
|
{
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
ss << vk;
|
|
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCVIEWING_KEY);
|
|
data.insert(data.end(), ss.begin(), ss.end());
|
|
std::string ret = EncodeBase58Check(data);
|
|
memory_cleanse(data.data(), data.size());
|
|
return ret;
|
|
}
|
|
|
|
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
|
|
};
|
|
|
|
class SpendingKeyEncoder : public boost::static_visitor<std::string>
|
|
{
|
|
private:
|
|
const CChainParams& m_params;
|
|
|
|
public:
|
|
SpendingKeyEncoder(const CChainParams& params) : m_params(params) {}
|
|
|
|
std::string operator()(const libzcash::SproutSpendingKey& zkey) const
|
|
{
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
ss << zkey;
|
|
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCSPENDING_KEY);
|
|
data.insert(data.end(), ss.begin(), ss.end());
|
|
std::string ret = EncodeBase58Check(data);
|
|
memory_cleanse(data.data(), data.size());
|
|
return ret;
|
|
}
|
|
|
|
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
|
|
};
|
|
} // namespace
|
|
|
|
CKey DecodeSecret(const std::string& str)
|
|
{
|
|
CKey key;
|
|
std::vector<unsigned char> data;
|
|
if (DecodeBase58Check(str, data)) {
|
|
const std::vector<unsigned char>& privkey_prefix = Params().Base58Prefix(CChainParams::SECRET_KEY);
|
|
if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) &&
|
|
std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) {
|
|
bool compressed = data.size() == 33 + privkey_prefix.size();
|
|
key.Set(data.begin() + privkey_prefix.size(), data.begin() + privkey_prefix.size() + 32, compressed);
|
|
}
|
|
}
|
|
memory_cleanse(data.data(), data.size());
|
|
return key;
|
|
}
|
|
|
|
std::string EncodeSecret(const CKey& key)
|
|
{
|
|
assert(key.IsValid());
|
|
std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::SECRET_KEY);
|
|
data.insert(data.end(), key.begin(), key.end());
|
|
if (key.IsCompressed()) {
|
|
data.push_back(1);
|
|
}
|
|
std::string ret = EncodeBase58Check(data);
|
|
memory_cleanse(data.data(), data.size());
|
|
return ret;
|
|
}
|
|
|
|
CExtPubKey DecodeExtPubKey(const std::string& str)
|
|
{
|
|
CExtPubKey key;
|
|
std::vector<unsigned char> data;
|
|
if (DecodeBase58Check(str, data)) {
|
|
const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY);
|
|
if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) {
|
|
key.Decode(data.data() + prefix.size());
|
|
}
|
|
}
|
|
return key;
|
|
}
|
|
|
|
std::string EncodeExtPubKey(const CExtPubKey& key)
|
|
{
|
|
std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY);
|
|
size_t size = data.size();
|
|
data.resize(size + BIP32_EXTKEY_SIZE);
|
|
key.Encode(data.data() + size);
|
|
std::string ret = EncodeBase58Check(data);
|
|
return ret;
|
|
}
|
|
|
|
CExtKey DecodeExtKey(const std::string& str)
|
|
{
|
|
CExtKey key;
|
|
std::vector<unsigned char> data;
|
|
if (DecodeBase58Check(str, data)) {
|
|
const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY);
|
|
if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) {
|
|
key.Decode(data.data() + prefix.size());
|
|
}
|
|
}
|
|
return key;
|
|
}
|
|
|
|
std::string EncodeExtKey(const CExtKey& key)
|
|
{
|
|
std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY);
|
|
size_t size = data.size();
|
|
data.resize(size + BIP32_EXTKEY_SIZE);
|
|
key.Encode(data.data() + size);
|
|
std::string ret = EncodeBase58Check(data);
|
|
memory_cleanse(data.data(), data.size());
|
|
return ret;
|
|
}
|
|
|
|
std::string EncodeDestination(const CTxDestination& dest)
|
|
{
|
|
return boost::apply_visitor(DestinationEncoder(Params()), dest);
|
|
}
|
|
|
|
CTxDestination DecodeDestination(const std::string& str)
|
|
{
|
|
return DecodeDestination(str, Params());
|
|
}
|
|
|
|
bool IsValidDestinationString(const std::string& str, const CChainParams& params)
|
|
{
|
|
return IsValidDestination(DecodeDestination(str, params));
|
|
}
|
|
|
|
bool IsValidDestinationString(const std::string& str)
|
|
{
|
|
return IsValidDestinationString(str, Params());
|
|
}
|
|
|
|
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr)
|
|
{
|
|
return boost::apply_visitor(PaymentAddressEncoder(Params()), zaddr);
|
|
}
|
|
|
|
libzcash::PaymentAddress DecodePaymentAddress(const std::string& str)
|
|
{
|
|
std::vector<unsigned char> data;
|
|
if (DecodeBase58Check(str, data)) {
|
|
const std::vector<unsigned char>& zaddr_prefix = Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS);
|
|
if ((data.size() == libzcash::SerializedPaymentAddressSize + zaddr_prefix.size()) &&
|
|
std::equal(zaddr_prefix.begin(), zaddr_prefix.end(), data.begin())) {
|
|
CSerializeData serialized(data.begin() + zaddr_prefix.size(), data.end());
|
|
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
|
|
libzcash::SproutPaymentAddress ret;
|
|
ss >> ret;
|
|
return ret;
|
|
}
|
|
}
|
|
return libzcash::InvalidEncoding();
|
|
}
|
|
|
|
bool IsValidPaymentAddressString(const std::string& str) {
|
|
return IsValidPaymentAddress(DecodePaymentAddress(str));
|
|
}
|
|
|
|
std::string EncodeViewingKey(const libzcash::ViewingKey& vk)
|
|
{
|
|
return boost::apply_visitor(ViewingKeyEncoder(Params()), vk);
|
|
}
|
|
|
|
libzcash::ViewingKey DecodeViewingKey(const std::string& str)
|
|
{
|
|
std::vector<unsigned char> data;
|
|
if (DecodeBase58Check(str, data)) {
|
|
const std::vector<unsigned char>& vk_prefix = Params().Base58Prefix(CChainParams::ZCVIEWING_KEY);
|
|
if ((data.size() == libzcash::SerializedViewingKeySize + vk_prefix.size()) &&
|
|
std::equal(vk_prefix.begin(), vk_prefix.end(), data.begin())) {
|
|
CSerializeData serialized(data.begin() + vk_prefix.size(), data.end());
|
|
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
|
|
libzcash::SproutViewingKey ret;
|
|
ss >> ret;
|
|
memory_cleanse(serialized.data(), serialized.size());
|
|
memory_cleanse(data.data(), data.size());
|
|
return ret;
|
|
}
|
|
}
|
|
memory_cleanse(data.data(), data.size());
|
|
return libzcash::InvalidEncoding();
|
|
}
|
|
|
|
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey)
|
|
{
|
|
return boost::apply_visitor(SpendingKeyEncoder(Params()), zkey);
|
|
}
|
|
|
|
libzcash::SpendingKey DecodeSpendingKey(const std::string& str)
|
|
{
|
|
std::vector<unsigned char> data;
|
|
if (DecodeBase58Check(str, data)) {
|
|
const std::vector<unsigned char>& zkey_prefix = Params().Base58Prefix(CChainParams::ZCSPENDING_KEY);
|
|
if ((data.size() == libzcash::SerializedSpendingKeySize + zkey_prefix.size()) &&
|
|
std::equal(zkey_prefix.begin(), zkey_prefix.end(), data.begin())) {
|
|
CSerializeData serialized(data.begin() + zkey_prefix.size(), data.end());
|
|
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
|
|
libzcash::SproutSpendingKey ret;
|
|
ss >> ret;
|
|
memory_cleanse(serialized.data(), serialized.size());
|
|
memory_cleanse(data.data(), data.size());
|
|
return ret;
|
|
}
|
|
}
|
|
memory_cleanse(data.data(), data.size());
|
|
return libzcash::InvalidEncoding();
|
|
}
|