Implement encoding and decoding of Sapling keys and addresses

This commit is contained in:
Jack Grigg 2018-06-07 16:40:59 +12:00
parent c8511dfc07
commit bec3e62bc1
No known key found for this signature in database
GPG Key ID: 1B8D649257DB0829
3 changed files with 92 additions and 4 deletions

View File

@ -85,6 +85,18 @@ public:
return EncodeBase58Check(data);
}
std::string operator()(const libzcash::SaplingPaymentAddress& zaddr) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << zaddr;
// ConvertBits requires unsigned char, but CDataStream uses char
std::vector<unsigned char> seraddr(ss.begin(), ss.end());
std::vector<unsigned char> data;
data.reserve((seraddr.size() * 8 + 4) / 5);
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, seraddr.begin(), seraddr.end());
return bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS), data);
}
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
};
@ -129,8 +141,26 @@ public:
return ret;
}
std::string operator()(const libzcash::SaplingSpendingKey& zkey) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << zkey;
// ConvertBits requires unsigned char, but CDataStream uses char
std::vector<unsigned char> serkey(ss.begin(), ss.end());
std::vector<unsigned char> data;
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_SPENDING_KEY), data);
memory_cleanse(serkey.data(), serkey.size());
memory_cleanse(data.data(), data.size());
return ret;
}
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
};
const size_t ConvertedSaplingPaymentAddressSize = ((32 + 11) * 8 + 4) / 5;
const size_t ConvertedSaplingSpendingKeySize = (32 * 8 + 4) / 5;
} // namespace
CKey DecodeSecret(const std::string& str)
@ -248,6 +278,19 @@ libzcash::PaymentAddress DecodePaymentAddress(const std::string& str)
return ret;
}
}
data.clear();
auto bech = bech32::Decode(str);
if (bech.first == Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS) &&
bech.second.size() == ConvertedSaplingPaymentAddressSize) {
// 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);
libzcash::SaplingPaymentAddress ret;
ss >> ret;
return ret;
}
}
return libzcash::InvalidEncoding();
}
@ -301,6 +344,20 @@ libzcash::SpendingKey DecodeSpendingKey(const std::string& str)
return ret;
}
}
data.clear();
auto bech = bech32::Decode(str);
if (bech.first == Params().Bech32HRP(CChainParams::SAPLING_SPENDING_KEY) &&
bech.second.size() == ConvertedSaplingSpendingKeySize) {
// 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);
libzcash::SaplingSpendingKey ret;
ss >> ret;
memory_cleanse(data.data(), data.size());
return ret;
}
}
memory_cleanse(data.data(), data.size());
return libzcash::InvalidEncoding();
}

View File

@ -220,4 +220,35 @@ BOOST_AUTO_TEST_CASE(zc_address_test)
}
}
BOOST_AUTO_TEST_CASE(zs_address_test)
{
for (size_t i = 0; i < 1000; i++) {
auto sk = SaplingSpendingKey::random();
{
std::string sk_string = EncodeSpendingKey(sk);
BOOST_CHECK(sk_string.compare(0, 24, "secret-spending-key-main") == 0);
auto spendingkey2 = DecodeSpendingKey(sk_string);
BOOST_CHECK(IsValidSpendingKey(spendingkey2));
BOOST_ASSERT(boost::get<SaplingSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = boost::get<SaplingSpendingKey>(spendingkey2);
BOOST_CHECK(sk == sk2);
}
{
auto addr = sk.default_address();
std::string addr_string = EncodePaymentAddress(*addr);
BOOST_CHECK(addr_string.compare(0, 2, "zs") == 0);
auto paymentaddr2 = DecodePaymentAddress(addr_string);
BOOST_CHECK(IsValidPaymentAddress(paymentaddr2));
BOOST_ASSERT(boost::get<SaplingPaymentAddress>(&paymentaddr2) != nullptr);
auto addr2 = boost::get<SaplingPaymentAddress>(paymentaddr2);
BOOST_CHECK(addr == addr2);
}
}
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -95,10 +95,6 @@ public:
SproutPaymentAddress address() const;
};
typedef boost::variant<InvalidEncoding, SproutPaymentAddress> PaymentAddress;
typedef boost::variant<InvalidEncoding, SproutViewingKey> ViewingKey;
typedef boost::variant<InvalidEncoding, SproutSpendingKey> SpendingKey;
//! Sapling functions.
class SaplingPaymentAddress {
public:
@ -209,6 +205,10 @@ public:
boost::optional<SaplingPaymentAddress> default_address() const;
};
typedef boost::variant<InvalidEncoding, SproutPaymentAddress, SaplingPaymentAddress> PaymentAddress;
typedef boost::variant<InvalidEncoding, SproutViewingKey> ViewingKey;
typedef boost::variant<InvalidEncoding, SproutSpendingKey, SaplingSpendingKey> SpendingKey;
}
/** Check whether a PaymentAddress is not an InvalidEncoding. */