Implement encoding and decoding of Sapling keys and addresses
This commit is contained in:
parent
c8511dfc07
commit
bec3e62bc1
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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. */
|
||||
|
|
Loading…
Reference in New Issue