Add encoding and decoding for Sapling extended full viewing keys
This commit is contained in:
parent
55354537b7
commit
3c7385bc8e
|
@ -171,6 +171,7 @@ public:
|
|||
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviews";
|
||||
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivks";
|
||||
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-main";
|
||||
bech32HRPs[SAPLING_EXTENDED_FVK] = "zxviews";
|
||||
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
|
||||
|
||||
|
@ -384,6 +385,7 @@ public:
|
|||
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewtestsapling";
|
||||
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivktestsapling";
|
||||
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-test";
|
||||
bech32HRPs[SAPLING_EXTENDED_FVK] = "zxviewtestsapling";
|
||||
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
|
||||
|
||||
|
@ -529,6 +531,7 @@ public:
|
|||
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewregtestsapling";
|
||||
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivkregtestsapling";
|
||||
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-regtest";
|
||||
bech32HRPs[SAPLING_EXTENDED_FVK] = "zxviewregtestsapling";
|
||||
|
||||
// Founders reward script expects a vector of 2-of-3 multisig addresses
|
||||
vFoundersRewardAddress = { "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg" };
|
||||
|
|
|
@ -61,6 +61,7 @@ public:
|
|||
SAPLING_FULL_VIEWING_KEY,
|
||||
SAPLING_INCOMING_VIEWING_KEY,
|
||||
SAPLING_EXTENDED_SPEND_KEY,
|
||||
SAPLING_EXTENDED_FVK,
|
||||
|
||||
MAX_BECH32_TYPES
|
||||
};
|
||||
|
|
|
@ -27,6 +27,20 @@ TEST(Keys, EncodeAndDecodeSapling)
|
|||
auto sk2 = boost::get<libzcash::SaplingExtendedSpendingKey>(spendingkey2);
|
||||
EXPECT_EQ(sk, sk2);
|
||||
}
|
||||
{
|
||||
auto extfvk = sk.ToXFVK();
|
||||
std::string vk_string = EncodeViewingKey(extfvk);
|
||||
EXPECT_EQ(
|
||||
vk_string.substr(0, 7),
|
||||
Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_FVK));
|
||||
|
||||
auto viewingkey2 = DecodeViewingKey(vk_string);
|
||||
EXPECT_TRUE(IsValidViewingKey(viewingkey2));
|
||||
|
||||
ASSERT_TRUE(boost::get<libzcash::SaplingExtendedFullViewingKey>(&viewingkey2) != nullptr);
|
||||
auto extfvk2 = boost::get<libzcash::SaplingExtendedFullViewingKey>(viewingkey2);
|
||||
EXPECT_EQ(extfvk, extfvk2);
|
||||
}
|
||||
{
|
||||
auto addr = sk.DefaultAddress();
|
||||
|
||||
|
|
|
@ -120,6 +120,22 @@ public:
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::string operator()(const libzcash::SaplingExtendedFullViewingKey& extfvk) const
|
||||
{
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << extfvk;
|
||||
// ConvertBits requires unsigned char, but CDataStream uses char
|
||||
std::vector<unsigned char> serkey(ss.begin(), ss.end());
|
||||
std::vector<unsigned char> data;
|
||||
// See calculation comment below
|
||||
data.reserve((serkey.size() * 8 + 4) / 5);
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, serkey.begin(), serkey.end());
|
||||
std::string ret = bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_EXTENDED_FVK), data);
|
||||
memory_cleanse(serkey.data(), serkey.size());
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
|
||||
};
|
||||
|
||||
|
@ -161,11 +177,12 @@ public:
|
|||
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
|
||||
};
|
||||
|
||||
// Sizes of SaplingPaymentAddress and SaplingSpendingKey after
|
||||
// ConvertBits<8, 5, true>(). The calculations below take the
|
||||
// regular serialized size in bytes, convert to bits, and then
|
||||
// Sizes of SaplingPaymentAddress, SaplingExtendedFullViewingKey, and
|
||||
// SaplingExtendedSpendingKey after ConvertBits<8, 5, true>(). The calculations
|
||||
// below take the regular serialized size in bytes, convert to bits, and then
|
||||
// perform ceiling division to get the number of 5-bit clusters.
|
||||
const size_t ConvertedSaplingPaymentAddressSize = ((32 + 11) * 8 + 4) / 5;
|
||||
const size_t ConvertedSaplingExtendedFullViewingKeySize = (ZIP32_XFVK_SIZE * 8 + 4) / 5;
|
||||
const size_t ConvertedSaplingExtendedSpendingKeySize = (ZIP32_XSK_SIZE * 8 + 4) / 5;
|
||||
} // namespace
|
||||
|
||||
|
@ -270,11 +287,11 @@ std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr)
|
|||
return boost::apply_visitor(PaymentAddressEncoder(Params()), zaddr);
|
||||
}
|
||||
|
||||
template<typename T1, typename T2, typename T3 = T2>
|
||||
template<typename T1, typename T2, typename T3>
|
||||
T1 DecodeAny(
|
||||
const std::string& str,
|
||||
std::pair<CChainParams::Base58Type, size_t> sprout,
|
||||
boost::optional<std::pair<CChainParams::Bech32Type, size_t>> sapling)
|
||||
std::pair<CChainParams::Bech32Type, size_t> sapling)
|
||||
{
|
||||
std::vector<unsigned char> data;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
|
@ -291,20 +308,18 @@ T1 DecodeAny(
|
|||
}
|
||||
}
|
||||
|
||||
if (sapling) {
|
||||
data.clear();
|
||||
auto bech = bech32::Decode(str);
|
||||
if (bech.first == Params().Bech32HRP(sapling.get().first) &&
|
||||
bech.second.size() == sapling.get().second) {
|
||||
// Bech32 decoding
|
||||
data.reserve((bech.second.size() * 5) / 8);
|
||||
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) {
|
||||
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
|
||||
T3 ret;
|
||||
ss >> ret;
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
data.clear();
|
||||
auto bech = bech32::Decode(str);
|
||||
if (bech.first == Params().Bech32HRP(sapling.first) &&
|
||||
bech.second.size() == sapling.second) {
|
||||
// Bech32 decoding
|
||||
data.reserve((bech.second.size() * 5) / 8);
|
||||
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) {
|
||||
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
|
||||
T3 ret;
|
||||
ss >> ret;
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -335,10 +350,11 @@ std::string EncodeViewingKey(const libzcash::ViewingKey& vk)
|
|||
libzcash::ViewingKey DecodeViewingKey(const std::string& str)
|
||||
{
|
||||
return DecodeAny<libzcash::ViewingKey,
|
||||
libzcash::SproutViewingKey>(
|
||||
libzcash::SproutViewingKey,
|
||||
libzcash::SaplingExtendedFullViewingKey>(
|
||||
str,
|
||||
std::make_pair(CChainParams::ZCVIEWING_KEY, libzcash::SerializedSproutViewingKeySize),
|
||||
boost::none
|
||||
std::make_pair(CChainParams::SAPLING_EXTENDED_FVK, ConvertedSaplingExtendedFullViewingKeySize)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ public:
|
|||
};
|
||||
|
||||
typedef boost::variant<InvalidEncoding, SproutPaymentAddress, SaplingPaymentAddress> PaymentAddress;
|
||||
typedef boost::variant<InvalidEncoding, SproutViewingKey> ViewingKey;
|
||||
typedef boost::variant<InvalidEncoding, SproutViewingKey, SaplingExtendedFullViewingKey> ViewingKey;
|
||||
typedef boost::variant<InvalidEncoding, SproutSpendingKey, SaplingExtendedSpendingKey> SpendingKey;
|
||||
|
||||
class AddressInfoFromSpendingKey : public boost::static_visitor<std::pair<std::string, PaymentAddress>> {
|
||||
|
|
Loading…
Reference in New Issue