Add encoding and decoding for Sapling extended full viewing keys

This commit is contained in:
Jack Grigg 2020-02-18 21:27:26 +00:00
parent 55354537b7
commit 3c7385bc8e
5 changed files with 56 additions and 22 deletions

View File

@ -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" };

View File

@ -61,6 +61,7 @@ public:
SAPLING_FULL_VIEWING_KEY,
SAPLING_INCOMING_VIEWING_KEY,
SAPLING_EXTENDED_SPEND_KEY,
SAPLING_EXTENDED_FVK,
MAX_BECH32_TYPES
};

View File

@ -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();

View File

@ -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)
);
}

View File

@ -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>> {