Auto merge of #3269 - arcalinea:sapling_wallet_classes, r=ebfull
Add Sapling key classes to wallet Leverages new librustzcash APIs added in https://github.com/zcash/librustzcash/pull/9
This commit is contained in:
commit
b4874e9b09
|
@ -3,8 +3,8 @@ $(package)_version=0.1
|
|||
$(package)_download_path=https://github.com/zcash/$(package)/archive/
|
||||
$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz
|
||||
$(package)_download_file=$($(package)_git_commit).tar.gz
|
||||
$(package)_sha256_hash=1fb331a92b63da41e95ef9db671982d243a13bcd6d25570760c9ca83b8996887
|
||||
$(package)_git_commit=36d7acf3f37570f499fc8fe79fda372e5eb873ca
|
||||
$(package)_sha256_hash=b96a0646d4c4856bc6171dc26cce10644a6129ac92b73a91f94246fb6b7f3516
|
||||
$(package)_git_commit=18f4945d942cc53e336c40bf13080934179a9047
|
||||
$(package)_dependencies=rust $(rust_crates)
|
||||
$(package)_patches=cargo.config
|
||||
|
||||
|
|
|
@ -7,6 +7,18 @@
|
|||
#endif
|
||||
#include "zcash/Address.hpp"
|
||||
|
||||
TEST(keystore_tests, sapling_keys) {
|
||||
auto sk = libzcash::SaplingSpendingKey::random();
|
||||
auto addr = sk.default_address();
|
||||
|
||||
// Check that full viewing key derived from sk and expanded sk are the same
|
||||
auto exp_sk = sk.expanded_spending_key();
|
||||
auto full_viewing_key = sk.full_viewing_key();
|
||||
EXPECT_EQ(full_viewing_key, exp_sk.full_viewing_key());
|
||||
|
||||
auto in_viewing_key = full_viewing_key.in_viewing_key();
|
||||
}
|
||||
|
||||
TEST(keystore_tests, store_and_retrieve_spending_key) {
|
||||
CBasicKeyStore keyStore;
|
||||
libzcash::SproutSpendingKey skOut;
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "prf.h"
|
||||
#include "streams.h"
|
||||
|
||||
#include <librustzcash.h>
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
uint256 SproutPaymentAddress::GetHash() const {
|
||||
|
@ -36,6 +38,47 @@ SproutPaymentAddress SproutSpendingKey::address() const {
|
|||
return viewing_key().address();
|
||||
}
|
||||
|
||||
//! Sapling
|
||||
SaplingFullViewingKey SaplingExpandedSpendingKey::full_viewing_key() const {
|
||||
uint256 ak;
|
||||
uint256 nk;
|
||||
librustzcash_ask_to_ak(ask.begin(), ak.begin());
|
||||
librustzcash_nsk_to_nk(nsk.begin(), nk.begin());
|
||||
return SaplingFullViewingKey(ak, nk, ovk);
|
||||
}
|
||||
|
||||
SaplingExpandedSpendingKey SaplingSpendingKey::expanded_spending_key() const {
|
||||
return SaplingExpandedSpendingKey(PRF_ask(*this), PRF_nsk(*this), PRF_ovk(*this));
|
||||
}
|
||||
|
||||
SaplingFullViewingKey SaplingSpendingKey::full_viewing_key() const {
|
||||
return expanded_spending_key().full_viewing_key();
|
||||
}
|
||||
|
||||
SaplingIncomingViewingKey SaplingFullViewingKey::in_viewing_key() const {
|
||||
uint256 ivk;
|
||||
librustzcash_crh_ivk(ak.begin(), nk.begin(), ivk.begin());
|
||||
return SaplingIncomingViewingKey(ivk);
|
||||
}
|
||||
|
||||
SaplingSpendingKey SaplingSpendingKey::random() {
|
||||
return SaplingSpendingKey(random_uint256());
|
||||
}
|
||||
|
||||
boost::optional<SaplingPaymentAddress> SaplingIncomingViewingKey::address(diversifier_t d) const {
|
||||
uint256 pk_d;
|
||||
if (librustzcash_check_diversifier(d.data())) {
|
||||
librustzcash_ivk_to_pkd(this->begin(), d.data(), pk_d.begin());
|
||||
return SaplingPaymentAddress(d, pk_d);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<SaplingPaymentAddress> SaplingSpendingKey::default_address() const {
|
||||
return full_viewing_key().in_viewing_key().address(default_diversifier(*this));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ const size_t SerializedPaymentAddressSize = 64;
|
|||
const size_t SerializedViewingKeySize = 64;
|
||||
const size_t SerializedSpendingKeySize = 32;
|
||||
|
||||
typedef std::array<unsigned char, 11> diversifier_t;
|
||||
|
||||
class SproutPaymentAddress {
|
||||
public:
|
||||
uint256 a_pk;
|
||||
|
@ -97,6 +99,116 @@ typedef boost::variant<InvalidEncoding, SproutPaymentAddress> PaymentAddress;
|
|||
typedef boost::variant<InvalidEncoding, SproutViewingKey> ViewingKey;
|
||||
typedef boost::variant<InvalidEncoding, SproutSpendingKey> SpendingKey;
|
||||
|
||||
//! Sapling functions.
|
||||
class SaplingPaymentAddress {
|
||||
public:
|
||||
diversifier_t d;
|
||||
uint256 pk_d;
|
||||
|
||||
SaplingPaymentAddress() : d(), pk_d() { }
|
||||
SaplingPaymentAddress(diversifier_t d, uint256 pk_d) : d(d), pk_d(pk_d) { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(d);
|
||||
READWRITE(pk_d);
|
||||
}
|
||||
|
||||
friend inline bool operator==(const SaplingPaymentAddress& a, const SaplingPaymentAddress& b) {
|
||||
return a.d == b.d && a.pk_d == b.pk_d;
|
||||
}
|
||||
friend inline bool operator<(const SaplingPaymentAddress& a, const SaplingPaymentAddress& b) {
|
||||
return (a.d < b.d ||
|
||||
(a.d == b.d && a.pk_d < b.pk_d));
|
||||
}
|
||||
};
|
||||
|
||||
class SaplingIncomingViewingKey : public uint256 {
|
||||
public:
|
||||
SaplingIncomingViewingKey() : uint256() { }
|
||||
SaplingIncomingViewingKey(uint256 ivk) : uint256(ivk) { }
|
||||
|
||||
// Can pass in diversifier for Sapling addr
|
||||
boost::optional<SaplingPaymentAddress> address(diversifier_t d) const;
|
||||
};
|
||||
|
||||
class SaplingFullViewingKey {
|
||||
public:
|
||||
uint256 ak;
|
||||
uint256 nk;
|
||||
uint256 ovk;
|
||||
|
||||
SaplingFullViewingKey() : ak(), nk(), ovk() { }
|
||||
SaplingFullViewingKey(uint256 ak, uint256 nk, uint256 ovk) : ak(ak), nk(nk), ovk(ovk) { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(ak);
|
||||
READWRITE(nk);
|
||||
READWRITE(ovk);
|
||||
}
|
||||
|
||||
SaplingIncomingViewingKey in_viewing_key() const;
|
||||
|
||||
friend inline bool operator==(const SaplingFullViewingKey& a, const SaplingFullViewingKey& b) {
|
||||
return a.ak == b.ak && a.nk == b.nk && a.ovk == b.ovk;
|
||||
}
|
||||
friend inline bool operator<(const SaplingFullViewingKey& a, const SaplingFullViewingKey& b) {
|
||||
return (a.ak < b.ak ||
|
||||
(a.ak == b.ak && a.nk < b.nk) ||
|
||||
(a.ak == b.ak && a.nk == b.nk && a.ovk < b.ovk));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class SaplingExpandedSpendingKey {
|
||||
public:
|
||||
uint256 ask;
|
||||
uint256 nsk;
|
||||
uint256 ovk;
|
||||
|
||||
SaplingExpandedSpendingKey() : ask(), nsk(), ovk() { }
|
||||
SaplingExpandedSpendingKey(uint256 ask, uint256 nsk, uint256 ovk) : ask(ask), nsk(nsk), ovk(ovk) { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(ask);
|
||||
READWRITE(nsk);
|
||||
READWRITE(ovk);
|
||||
}
|
||||
|
||||
SaplingFullViewingKey full_viewing_key() const;
|
||||
|
||||
friend inline bool operator==(const SaplingExpandedSpendingKey& a, const SaplingExpandedSpendingKey& b) {
|
||||
return a.ask == b.ask && a.nsk == b.nsk && a.ovk == b.ovk;
|
||||
}
|
||||
friend inline bool operator<(const SaplingExpandedSpendingKey& a, const SaplingExpandedSpendingKey& b) {
|
||||
return (a.ask < b.ask ||
|
||||
(a.ask == b.ask && a.nsk < b.nsk) ||
|
||||
(a.ask == b.ask && a.nsk == b.nsk && a.ovk < b.ovk));
|
||||
}
|
||||
};
|
||||
|
||||
class SaplingSpendingKey : public uint256 {
|
||||
public:
|
||||
SaplingSpendingKey() : uint256() { }
|
||||
SaplingSpendingKey(uint256 sk) : uint256(sk) { }
|
||||
|
||||
static SaplingSpendingKey random();
|
||||
|
||||
SaplingExpandedSpendingKey expanded_spending_key() const;
|
||||
SaplingFullViewingKey full_viewing_key() const;
|
||||
|
||||
// Can derive Sapling addr from default diversifier
|
||||
boost::optional<SaplingPaymentAddress> default_address() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/** Check whether a PaymentAddress is not an InvalidEncoding. */
|
||||
|
|
|
@ -1,6 +1,80 @@
|
|||
#include "prf.h"
|
||||
#include "crypto/sha256.h"
|
||||
#include "hash.h"
|
||||
|
||||
#include <array>
|
||||
#include <librustzcash.h>
|
||||
|
||||
const unsigned char ZCASH_EXPANDSEED_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] = {'Z','c','a','s','h','_','E','x','p','a','n','d','S','e','e','d'};
|
||||
|
||||
// Sapling
|
||||
std::array<unsigned char, 64> PRF_expand(const uint256& sk, unsigned char t)
|
||||
{
|
||||
std::array<unsigned char, 64> res;
|
||||
unsigned char blob[33];
|
||||
|
||||
memcpy(&blob[0], sk.begin(), 32);
|
||||
blob[32] = t;
|
||||
|
||||
crypto_generichash_blake2b_state state;
|
||||
crypto_generichash_blake2b_init_salt_personal(&state, nullptr, 0, 64, nullptr, ZCASH_EXPANDSEED_PERSONALIZATION);
|
||||
crypto_generichash_blake2b_update(&state, blob, 33);
|
||||
crypto_generichash_blake2b_final(&state, res.data(), 64);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
uint256 PRF_ask(const uint256& sk)
|
||||
{
|
||||
uint256 ask;
|
||||
auto tmp = PRF_expand(sk, 0);
|
||||
librustzcash_to_scalar(tmp.data(), ask.begin());
|
||||
return ask;
|
||||
}
|
||||
|
||||
uint256 PRF_nsk(const uint256& sk)
|
||||
{
|
||||
uint256 nsk;
|
||||
auto tmp = PRF_expand(sk, 1);
|
||||
librustzcash_to_scalar(tmp.data(), nsk.begin());
|
||||
return nsk;
|
||||
}
|
||||
|
||||
uint256 PRF_ovk(const uint256& sk)
|
||||
{
|
||||
uint256 ovk;
|
||||
auto tmp = PRF_expand(sk, 2);
|
||||
memcpy(ovk.begin(), tmp.data(), 32);
|
||||
return ovk;
|
||||
}
|
||||
|
||||
std::array<unsigned char, 11> default_diversifier(const uint256& sk)
|
||||
{
|
||||
std::array<unsigned char, 11> res;
|
||||
unsigned char blob[34];
|
||||
|
||||
memcpy(&blob[0], sk.begin(), 32);
|
||||
blob[32] = 3;
|
||||
|
||||
blob[33] = 0;
|
||||
while (true) {
|
||||
crypto_generichash_blake2b_state state;
|
||||
crypto_generichash_blake2b_init_salt_personal(&state, nullptr, 0, 64, nullptr, ZCASH_EXPANDSEED_PERSONALIZATION);
|
||||
crypto_generichash_blake2b_update(&state, blob, 34);
|
||||
crypto_generichash_blake2b_final(&state, res.data(), 11);
|
||||
|
||||
if (librustzcash_check_diversifier(res.data())) {
|
||||
break;
|
||||
} else if (blob[33] == 255) {
|
||||
throw std::runtime_error("librustzcash_check_diversifier did not return valid diversifier");
|
||||
}
|
||||
blob[33] += 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Sprout
|
||||
uint256 PRF(bool a, bool b, bool c, bool d,
|
||||
const uint252& x,
|
||||
const uint256& y)
|
||||
|
|
|
@ -9,10 +9,20 @@ within the zkSNARK circuit.
|
|||
#include "uint256.h"
|
||||
#include "uint252.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
//! Sprout functions
|
||||
uint256 PRF_addr_a_pk(const uint252& a_sk);
|
||||
uint256 PRF_addr_sk_enc(const uint252& a_sk);
|
||||
uint256 PRF_nf(const uint252& a_sk, const uint256& rho);
|
||||
uint256 PRF_pk(const uint252& a_sk, size_t i0, const uint256& h_sig);
|
||||
uint256 PRF_rho(const uint252& phi, size_t i0, const uint256& h_sig);
|
||||
|
||||
//! Sapling functions
|
||||
uint256 PRF_ask(const uint256& sk);
|
||||
uint256 PRF_nsk(const uint256& sk);
|
||||
uint256 PRF_ovk(const uint256& sk);
|
||||
|
||||
std::array<unsigned char, 11> default_diversifier(const uint256& sk);
|
||||
|
||||
#endif // ZC_PRF_H_
|
||||
|
|
Loading…
Reference in New Issue