sync jl777:FSM
This commit is contained in:
@@ -4,9 +4,16 @@
|
||||
#include "prf.h"
|
||||
#include "streams.h"
|
||||
|
||||
#include <librustzcash.h>
|
||||
|
||||
const unsigned char ZCASH_SAPLING_FVFP_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] =
|
||||
{'Z', 'c', 'a', 's', 'h', 'S', 'a', 'p', 'l', 'i', 'n', 'g', 'F', 'V', 'F', 'P'};
|
||||
|
||||
const uint32_t SAPLING_BRANCH_ID = 0x76b809bb;
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
uint256 PaymentAddress::GetHash() const {
|
||||
uint256 SproutPaymentAddress::GetHash() const {
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << *this;
|
||||
return Hash(ss.begin(), ss.end());
|
||||
@@ -16,24 +23,122 @@ uint256 ReceivingKey::pk_enc() const {
|
||||
return ZCNoteEncryption::generate_pubkey(*this);
|
||||
}
|
||||
|
||||
PaymentAddress ViewingKey::address() const {
|
||||
return PaymentAddress(a_pk, sk_enc.pk_enc());
|
||||
SproutPaymentAddress SproutViewingKey::address() const {
|
||||
return SproutPaymentAddress(a_pk, sk_enc.pk_enc());
|
||||
}
|
||||
|
||||
ReceivingKey SpendingKey::receiving_key() const {
|
||||
ReceivingKey SproutSpendingKey::receiving_key() const {
|
||||
return ReceivingKey(ZCNoteEncryption::generate_privkey(*this));
|
||||
}
|
||||
|
||||
ViewingKey SpendingKey::viewing_key() const {
|
||||
return ViewingKey(PRF_addr_a_pk(*this), receiving_key());
|
||||
SproutViewingKey SproutSpendingKey::viewing_key() const {
|
||||
return SproutViewingKey(PRF_addr_a_pk(*this), receiving_key());
|
||||
}
|
||||
|
||||
SpendingKey SpendingKey::random() {
|
||||
return SpendingKey(random_uint252());
|
||||
SproutSpendingKey SproutSpendingKey::random() {
|
||||
return SproutSpendingKey(random_uint252());
|
||||
}
|
||||
|
||||
PaymentAddress SpendingKey::address() const {
|
||||
SproutPaymentAddress SproutSpendingKey::address() const {
|
||||
return viewing_key().address();
|
||||
}
|
||||
|
||||
//! Sapling
|
||||
uint256 SaplingPaymentAddress::GetHash() const {
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << *this;
|
||||
return Hash(ss.begin(), ss.end());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool SaplingFullViewingKey::is_valid() const {
|
||||
uint256 ivk;
|
||||
librustzcash_crh_ivk(ak.begin(), nk.begin(), ivk.begin());
|
||||
return !ivk.IsNull();
|
||||
}
|
||||
|
||||
uint256 SaplingFullViewingKey::GetFingerprint() const {
|
||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_SAPLING_FVFP_PERSONALIZATION);
|
||||
ss << *this;
|
||||
return ss.GetHash();
|
||||
}
|
||||
|
||||
|
||||
SaplingSpendingKey SaplingSpendingKey::random() {
|
||||
while (true) {
|
||||
auto sk = SaplingSpendingKey(random_uint256());
|
||||
if (sk.full_viewing_key().is_valid()) {
|
||||
return sk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
SaplingPaymentAddress SaplingSpendingKey::default_address() const {
|
||||
// Iterates within default_diversifier to ensure a valid address is returned
|
||||
auto addrOpt = full_viewing_key().in_viewing_key().address(default_diversifier(*this));
|
||||
assert(addrOpt != boost::none);
|
||||
return addrOpt.value();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class IsValidAddressForNetwork : public boost::static_visitor<bool> {
|
||||
private:
|
||||
uint32_t branchId;
|
||||
public:
|
||||
IsValidAddressForNetwork(uint32_t consensusBranchId) : branchId(consensusBranchId) {}
|
||||
|
||||
bool operator()(const libzcash::SproutPaymentAddress &addr) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const libzcash::InvalidEncoding &addr) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator()(const libzcash::SaplingPaymentAddress &addr) const {
|
||||
if (SAPLING_BRANCH_ID == branchId)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr, uint32_t consensusBranchId) {
|
||||
return boost::apply_visitor(IsValidAddressForNetwork(consensusBranchId), zaddr);
|
||||
}
|
||||
|
||||
bool IsValidViewingKey(const libzcash::ViewingKey& vk) {
|
||||
return vk.which() != 0;
|
||||
}
|
||||
|
||||
@@ -4,25 +4,40 @@
|
||||
#include "uint256.h"
|
||||
#include "uint252.h"
|
||||
#include "serialize.h"
|
||||
#include "Zcash.h"
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace libzcash {
|
||||
class InvalidEncoding {
|
||||
public:
|
||||
friend bool operator==(const InvalidEncoding &a, const InvalidEncoding &b) { return true; }
|
||||
friend bool operator<(const InvalidEncoding &a, const InvalidEncoding &b) { return true; }
|
||||
};
|
||||
|
||||
const size_t SerializedPaymentAddressSize = 64;
|
||||
const size_t SerializedViewingKeySize = 64;
|
||||
const size_t SerializedSpendingKeySize = 32;
|
||||
const size_t SerializedSproutPaymentAddressSize = 64;
|
||||
const size_t SerializedSproutViewingKeySize = 64;
|
||||
const size_t SerializedSproutSpendingKeySize = 32;
|
||||
|
||||
class PaymentAddress {
|
||||
const size_t SerializedSaplingPaymentAddressSize = 43;
|
||||
const size_t SerializedSaplingFullViewingKeySize = 96;
|
||||
const size_t SerializedSaplingExpandedSpendingKeySize = 96;
|
||||
const size_t SerializedSaplingSpendingKeySize = 32;
|
||||
|
||||
typedef std::array<unsigned char, ZC_DIVERSIFIER_SIZE> diversifier_t;
|
||||
|
||||
class SproutPaymentAddress {
|
||||
public:
|
||||
uint256 a_pk;
|
||||
uint256 pk_enc;
|
||||
|
||||
PaymentAddress() : a_pk(), pk_enc() { }
|
||||
PaymentAddress(uint256 a_pk, uint256 pk_enc) : a_pk(a_pk), pk_enc(pk_enc) { }
|
||||
SproutPaymentAddress() : a_pk(), pk_enc() { }
|
||||
SproutPaymentAddress(uint256 a_pk, uint256 pk_enc) : a_pk(a_pk), pk_enc(pk_enc) { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(a_pk);
|
||||
READWRITE(pk_enc);
|
||||
}
|
||||
@@ -30,10 +45,10 @@ public:
|
||||
//! Get the 256-bit SHA256d hash of this payment address.
|
||||
uint256 GetHash() const;
|
||||
|
||||
friend inline bool operator==(const PaymentAddress& a, const PaymentAddress& b) {
|
||||
friend inline bool operator==(const SproutPaymentAddress& a, const SproutPaymentAddress& b) {
|
||||
return a.a_pk == b.a_pk && a.pk_enc == b.pk_enc;
|
||||
}
|
||||
friend inline bool operator<(const PaymentAddress& a, const PaymentAddress& b) {
|
||||
friend inline bool operator<(const SproutPaymentAddress& a, const SproutPaymentAddress& b) {
|
||||
return (a.a_pk < b.a_pk ||
|
||||
(a.a_pk == b.a_pk && a.pk_enc < b.pk_enc));
|
||||
}
|
||||
@@ -47,45 +62,172 @@ public:
|
||||
uint256 pk_enc() const;
|
||||
};
|
||||
|
||||
class ViewingKey {
|
||||
class SproutViewingKey {
|
||||
public:
|
||||
uint256 a_pk;
|
||||
ReceivingKey sk_enc;
|
||||
|
||||
ViewingKey() : a_pk(), sk_enc() { }
|
||||
ViewingKey(uint256 a_pk, ReceivingKey sk_enc) : a_pk(a_pk), sk_enc(sk_enc) { }
|
||||
SproutViewingKey() : a_pk(), sk_enc() { }
|
||||
SproutViewingKey(uint256 a_pk, ReceivingKey sk_enc) : a_pk(a_pk), sk_enc(sk_enc) { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(a_pk);
|
||||
READWRITE(sk_enc);
|
||||
}
|
||||
|
||||
PaymentAddress address() const;
|
||||
SproutPaymentAddress address() const;
|
||||
|
||||
friend inline bool operator==(const ViewingKey& a, const ViewingKey& b) {
|
||||
friend inline bool operator==(const SproutViewingKey& a, const SproutViewingKey& b) {
|
||||
return a.a_pk == b.a_pk && a.sk_enc == b.sk_enc;
|
||||
}
|
||||
friend inline bool operator<(const ViewingKey& a, const ViewingKey& b) {
|
||||
friend inline bool operator<(const SproutViewingKey& a, const SproutViewingKey& b) {
|
||||
return (a.a_pk < b.a_pk ||
|
||||
(a.a_pk == b.a_pk && a.sk_enc < b.sk_enc));
|
||||
}
|
||||
};
|
||||
|
||||
class SpendingKey : public uint252 {
|
||||
class SproutSpendingKey : public uint252 {
|
||||
public:
|
||||
SpendingKey() : uint252() { }
|
||||
SpendingKey(uint252 a_sk) : uint252(a_sk) { }
|
||||
SproutSpendingKey() : uint252() { }
|
||||
SproutSpendingKey(uint252 a_sk) : uint252(a_sk) { }
|
||||
|
||||
static SpendingKey random();
|
||||
static SproutSpendingKey random();
|
||||
|
||||
ReceivingKey receiving_key() const;
|
||||
ViewingKey viewing_key() const;
|
||||
PaymentAddress address() const;
|
||||
SproutViewingKey viewing_key() const;
|
||||
SproutPaymentAddress address() const;
|
||||
};
|
||||
|
||||
//! 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);
|
||||
}
|
||||
|
||||
//! Get the 256-bit SHA256d hash of this payment address.
|
||||
uint256 GetHash() const;
|
||||
|
||||
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) {
|
||||
READWRITE(ak);
|
||||
READWRITE(nk);
|
||||
READWRITE(ovk);
|
||||
}
|
||||
|
||||
//! Get the fingerprint of this full viewing key (as defined in ZIP 32).
|
||||
uint256 GetFingerprint() const;
|
||||
|
||||
SaplingIncomingViewingKey in_viewing_key() const;
|
||||
bool is_valid() 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) {
|
||||
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
|
||||
SaplingPaymentAddress default_address() const;
|
||||
};
|
||||
|
||||
typedef boost::variant<InvalidEncoding, SproutPaymentAddress, SaplingPaymentAddress> PaymentAddress;
|
||||
typedef boost::variant<InvalidEncoding, SproutViewingKey> ViewingKey;
|
||||
|
||||
}
|
||||
|
||||
/** Check whether a PaymentAddress is not an InvalidEncoding. */
|
||||
extern const uint32_t SAPLING_BRANCH_ID;
|
||||
bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr, uint32_t consensusBranchId = SAPLING_BRANCH_ID);
|
||||
|
||||
/** Check whether a ViewingKey is not an InvalidEncoding. */
|
||||
bool IsValidViewingKey(const libzcash::ViewingKey& vk);
|
||||
|
||||
#endif // ZC_ADDRESS_H_
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "libsnark/common/profiling.hpp"
|
||||
#include "komodo_defs.h"
|
||||
char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
|
||||
int64_t MAX_MONEY = 200000000 * 100000000LL;
|
||||
uint16_t BITCOIND_RPCPORT = 7771;
|
||||
uint32_t ASSETCHAINS_CC = 0;
|
||||
|
||||
|
||||
@@ -5,10 +5,41 @@
|
||||
#include "zcash/IncrementalMerkleTree.hpp"
|
||||
#include "crypto/sha256.h"
|
||||
#include "zcash/util.h"
|
||||
#include "librustzcash.h"
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
SHA256Compress SHA256Compress::combine(const SHA256Compress& a, const SHA256Compress& b)
|
||||
PedersenHash PedersenHash::combine(
|
||||
const PedersenHash& a,
|
||||
const PedersenHash& b,
|
||||
size_t depth
|
||||
)
|
||||
{
|
||||
PedersenHash res = PedersenHash();
|
||||
|
||||
librustzcash_merkle_hash(
|
||||
depth,
|
||||
a.begin(),
|
||||
b.begin(),
|
||||
res.begin()
|
||||
);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
PedersenHash PedersenHash::uncommitted() {
|
||||
PedersenHash res = PedersenHash();
|
||||
|
||||
librustzcash_tree_uncommitted(res.begin());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
SHA256Compress SHA256Compress::combine(
|
||||
const SHA256Compress& a,
|
||||
const SHA256Compress& b,
|
||||
size_t depth
|
||||
)
|
||||
{
|
||||
SHA256Compress res = SHA256Compress();
|
||||
|
||||
@@ -45,9 +76,6 @@ public:
|
||||
template<size_t Depth, typename Hash>
|
||||
EmptyMerkleRoots<Depth, Hash> PathFiller<Depth, Hash>::emptyroots;
|
||||
|
||||
template<size_t Depth, typename Hash>
|
||||
EmptyMerkleRoots<Depth, Hash> IncrementalMerkleTree<Depth, Hash>::emptyroots;
|
||||
|
||||
template<size_t Depth, typename Hash>
|
||||
void IncrementalMerkleTree<Depth, Hash>::wfcheck() const {
|
||||
if (parents.size() >= Depth) {
|
||||
@@ -114,7 +142,7 @@ void IncrementalMerkleTree<Depth, Hash>::append(Hash obj) {
|
||||
right = obj;
|
||||
} else {
|
||||
// Combine the leaves and propagate it up the tree
|
||||
boost::optional<Hash> combined = Hash::combine(*left, *right);
|
||||
boost::optional<Hash> combined = Hash::combine(*left, *right, 0);
|
||||
|
||||
// Set the "left" leaf to the object and make the "right" leaf none
|
||||
left = obj;
|
||||
@@ -123,7 +151,7 @@ void IncrementalMerkleTree<Depth, Hash>::append(Hash obj) {
|
||||
for (size_t i = 0; i < Depth; i++) {
|
||||
if (i < parents.size()) {
|
||||
if (parents[i]) {
|
||||
combined = Hash::combine(*parents[i], *combined);
|
||||
combined = Hash::combine(*parents[i], *combined, i+1);
|
||||
parents[i] = boost::none;
|
||||
} else {
|
||||
parents[i] = *combined;
|
||||
@@ -205,15 +233,15 @@ Hash IncrementalMerkleTree<Depth, Hash>::root(size_t depth,
|
||||
Hash combine_left = left ? *left : filler.next(0);
|
||||
Hash combine_right = right ? *right : filler.next(0);
|
||||
|
||||
Hash root = Hash::combine(combine_left, combine_right);
|
||||
Hash root = Hash::combine(combine_left, combine_right, 0);
|
||||
|
||||
size_t d = 1;
|
||||
|
||||
BOOST_FOREACH(const boost::optional<Hash>& parent, parents) {
|
||||
if (parent) {
|
||||
root = Hash::combine(*parent, root);
|
||||
root = Hash::combine(*parent, root, d);
|
||||
} else {
|
||||
root = Hash::combine(root, filler.next(d));
|
||||
root = Hash::combine(root, filler.next(d), d);
|
||||
}
|
||||
|
||||
d++;
|
||||
@@ -222,7 +250,7 @@ Hash IncrementalMerkleTree<Depth, Hash>::root(size_t depth,
|
||||
// We may not have parents for ancestor trees, so we fill
|
||||
// the rest in here.
|
||||
while (d < depth) {
|
||||
root = Hash::combine(root, filler.next(d));
|
||||
root = Hash::combine(root, filler.next(d), d);
|
||||
d++;
|
||||
}
|
||||
|
||||
@@ -326,4 +354,10 @@ template class IncrementalMerkleTree<INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, SHA2
|
||||
template class IncrementalWitness<INCREMENTAL_MERKLE_TREE_DEPTH, SHA256Compress>;
|
||||
template class IncrementalWitness<INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, SHA256Compress>;
|
||||
|
||||
template class IncrementalMerkleTree<SAPLING_INCREMENTAL_MERKLE_TREE_DEPTH, PedersenHash>;
|
||||
template class IncrementalMerkleTree<INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, PedersenHash>;
|
||||
|
||||
template class IncrementalWitness<SAPLING_INCREMENTAL_MERKLE_TREE_DEPTH, PedersenHash>;
|
||||
template class IncrementalWitness<INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, PedersenHash>;
|
||||
|
||||
} // end namespace `libzcash`
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef ZC_INCREMENTALMERKLETREE_H_
|
||||
#define ZC_INCREMENTALMERKLETREE_H_
|
||||
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
@@ -9,6 +10,7 @@
|
||||
#include "serialize.h"
|
||||
|
||||
#include "Zcash.h"
|
||||
#include "zcash/util.h"
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
@@ -20,9 +22,30 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(authentication_path);
|
||||
READWRITE(index);
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
std::vector<std::vector<unsigned char>> pathBytes;
|
||||
uint64_t indexInt;
|
||||
if (ser_action.ForRead()) {
|
||||
READWRITE(pathBytes);
|
||||
READWRITE(indexInt);
|
||||
MerklePath &us = *(const_cast<MerklePath*>(this));
|
||||
for (size_t i = 0; i < pathBytes.size(); i++) {
|
||||
us.authentication_path.push_back(convertBytesVectorToVector(pathBytes[i]));
|
||||
us.index.push_back((indexInt >> ((pathBytes.size() - 1) - i)) & 1);
|
||||
}
|
||||
} else {
|
||||
assert(authentication_path.size() == index.size());
|
||||
pathBytes.resize(authentication_path.size());
|
||||
for (size_t i = 0; i < authentication_path.size(); i++) {
|
||||
pathBytes[i].resize((authentication_path[i].size()+7)/8);
|
||||
for (unsigned int p = 0; p < authentication_path[i].size(); p++) {
|
||||
pathBytes[i][p / 8] |= authentication_path[i][p] << (7-(p % 8));
|
||||
}
|
||||
}
|
||||
indexInt = convertVectorToInt(index);
|
||||
READWRITE(pathBytes);
|
||||
READWRITE(indexInt);
|
||||
}
|
||||
}
|
||||
|
||||
MerklePath() { }
|
||||
@@ -35,9 +58,9 @@ template<size_t Depth, typename Hash>
|
||||
class EmptyMerkleRoots {
|
||||
public:
|
||||
EmptyMerkleRoots() {
|
||||
empty_roots.at(0) = Hash();
|
||||
empty_roots.at(0) = Hash::uncommitted();
|
||||
for (size_t d = 1; d <= Depth; d++) {
|
||||
empty_roots.at(d) = Hash::combine(empty_roots.at(d-1), empty_roots.at(d-1));
|
||||
empty_roots.at(d) = Hash::combine(empty_roots.at(d-1), empty_roots.at(d-1), d-1);
|
||||
}
|
||||
}
|
||||
Hash empty_root(size_t depth) {
|
||||
@@ -47,7 +70,7 @@ public:
|
||||
friend bool operator==(const EmptyMerkleRoots<D, H>& a,
|
||||
const EmptyMerkleRoots<D, H>& b);
|
||||
private:
|
||||
boost::array<Hash, Depth+1> empty_roots;
|
||||
std::array<Hash, Depth+1> empty_roots;
|
||||
};
|
||||
|
||||
template<size_t Depth, typename Hash>
|
||||
@@ -90,7 +113,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(left);
|
||||
READWRITE(right);
|
||||
READWRITE(parents);
|
||||
@@ -147,6 +170,10 @@ public:
|
||||
return tree.last();
|
||||
}
|
||||
|
||||
uint64_t position() const {
|
||||
return tree.size() - 1;
|
||||
}
|
||||
|
||||
Hash root() const {
|
||||
return tree.root(Depth, partial_path());
|
||||
}
|
||||
@@ -156,7 +183,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(tree);
|
||||
READWRITE(filled);
|
||||
READWRITE(cursor);
|
||||
@@ -191,15 +218,46 @@ public:
|
||||
SHA256Compress() : uint256() {}
|
||||
SHA256Compress(uint256 contents) : uint256(contents) { }
|
||||
|
||||
static SHA256Compress combine(const SHA256Compress& a, const SHA256Compress& b);
|
||||
static SHA256Compress combine(
|
||||
const SHA256Compress& a,
|
||||
const SHA256Compress& b,
|
||||
size_t depth
|
||||
);
|
||||
|
||||
static SHA256Compress uncommitted() {
|
||||
return SHA256Compress();
|
||||
}
|
||||
};
|
||||
|
||||
class PedersenHash : public uint256 {
|
||||
public:
|
||||
PedersenHash() : uint256() {}
|
||||
PedersenHash(uint256 contents) : uint256(contents) { }
|
||||
|
||||
static PedersenHash combine(
|
||||
const PedersenHash& a,
|
||||
const PedersenHash& b,
|
||||
size_t depth
|
||||
);
|
||||
|
||||
static PedersenHash uncommitted();
|
||||
};
|
||||
|
||||
template<size_t Depth, typename Hash>
|
||||
EmptyMerkleRoots<Depth, Hash> IncrementalMerkleTree<Depth, Hash>::emptyroots;
|
||||
|
||||
} // end namespace `libzcash`
|
||||
|
||||
typedef libzcash::IncrementalMerkleTree<INCREMENTAL_MERKLE_TREE_DEPTH, libzcash::SHA256Compress> ZCIncrementalMerkleTree;
|
||||
typedef libzcash::IncrementalMerkleTree<INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, libzcash::SHA256Compress> ZCTestingIncrementalMerkleTree;
|
||||
typedef libzcash::IncrementalMerkleTree<INCREMENTAL_MERKLE_TREE_DEPTH, libzcash::SHA256Compress> SproutMerkleTree;
|
||||
typedef libzcash::IncrementalMerkleTree<INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, libzcash::SHA256Compress> SproutTestingMerkleTree;
|
||||
|
||||
typedef libzcash::IncrementalWitness<INCREMENTAL_MERKLE_TREE_DEPTH, libzcash::SHA256Compress> ZCIncrementalWitness;
|
||||
typedef libzcash::IncrementalWitness<INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, libzcash::SHA256Compress> ZCTestingIncrementalWitness;
|
||||
typedef libzcash::IncrementalWitness<INCREMENTAL_MERKLE_TREE_DEPTH, libzcash::SHA256Compress> SproutWitness;
|
||||
typedef libzcash::IncrementalWitness<INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, libzcash::SHA256Compress> SproutTestingWitness;
|
||||
|
||||
typedef libzcash::IncrementalMerkleTree<SAPLING_INCREMENTAL_MERKLE_TREE_DEPTH, libzcash::PedersenHash> SaplingMerkleTree;
|
||||
typedef libzcash::IncrementalMerkleTree<INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, libzcash::PedersenHash> SaplingTestingMerkleTree;
|
||||
|
||||
typedef libzcash::IncrementalWitness<SAPLING_INCREMENTAL_MERKLE_TREE_DEPTH, libzcash::PedersenHash> SaplingWitness;
|
||||
typedef libzcash::IncrementalWitness<INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, libzcash::PedersenHash> SaplingTestingWitness;
|
||||
|
||||
#endif /* ZC_INCREMENTALMERKLETREE_H_ */
|
||||
|
||||
@@ -19,14 +19,17 @@
|
||||
#include "amount.h"
|
||||
extern int64_t MAX_MONEY;
|
||||
|
||||
#include "librustzcash.h"
|
||||
#include "streams.h"
|
||||
#include "version.h"
|
||||
|
||||
using namespace libsnark;
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
#include "zcash/circuit/gadget.tcc"
|
||||
|
||||
CCriticalSection cs_ParamsIO;
|
||||
CCriticalSection cs_LoadKeys;
|
||||
static CCriticalSection cs_ParamsIO;
|
||||
|
||||
template<typename T>
|
||||
void saveToFile(const std::string path, T& obj) {
|
||||
@@ -100,13 +103,13 @@ public:
|
||||
}
|
||||
|
||||
bool verify(
|
||||
const ZCProof& proof,
|
||||
const PHGRProof& proof,
|
||||
ProofVerifier& verifier,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& joinSplitPubKey,
|
||||
const uint256& randomSeed,
|
||||
const boost::array<uint256, NumInputs>& macs,
|
||||
const boost::array<uint256, NumInputs>& nullifiers,
|
||||
const boost::array<uint256, NumOutputs>& commitments,
|
||||
const std::array<uint256, NumInputs>& macs,
|
||||
const std::array<uint256, NumInputs>& nullifiers,
|
||||
const std::array<uint256, NumOutputs>& commitments,
|
||||
uint64_t vpub_old,
|
||||
uint64_t vpub_new,
|
||||
const uint256& rt
|
||||
@@ -114,7 +117,7 @@ public:
|
||||
try {
|
||||
auto r1cs_proof = proof.to_libsnark_proof<r1cs_ppzksnark_proof<ppzksnark_ppT>>();
|
||||
|
||||
uint256 h_sig = this->h_sig(randomSeed, nullifiers, pubKeyHash);
|
||||
uint256 h_sig = this->h_sig(randomSeed, nullifiers, joinSplitPubKey);
|
||||
|
||||
auto witness = joinsplit_gadget<FieldT, NumInputs, NumOutputs>::witness_map(
|
||||
rt,
|
||||
@@ -137,17 +140,18 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
ZCProof prove(
|
||||
const boost::array<JSInput, NumInputs>& inputs,
|
||||
const boost::array<JSOutput, NumOutputs>& outputs,
|
||||
boost::array<Note, NumOutputs>& out_notes,
|
||||
boost::array<ZCNoteEncryption::Ciphertext, NumOutputs>& out_ciphertexts,
|
||||
SproutProof prove(
|
||||
bool makeGrothProof,
|
||||
const std::array<JSInput, NumInputs>& inputs,
|
||||
const std::array<JSOutput, NumOutputs>& outputs,
|
||||
std::array<SproutNote, NumOutputs>& out_notes,
|
||||
std::array<ZCNoteEncryption::Ciphertext, NumOutputs>& out_ciphertexts,
|
||||
uint256& out_ephemeralKey,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& joinSplitPubKey,
|
||||
uint256& out_randomSeed,
|
||||
boost::array<uint256, NumInputs>& out_macs,
|
||||
boost::array<uint256, NumInputs>& out_nullifiers,
|
||||
boost::array<uint256, NumOutputs>& out_commitments,
|
||||
std::array<uint256, NumInputs>& out_macs,
|
||||
std::array<uint256, NumInputs>& out_nullifiers,
|
||||
std::array<uint256, NumOutputs>& out_commitments,
|
||||
uint64_t vpub_old,
|
||||
uint64_t vpub_new,
|
||||
const uint256& rt,
|
||||
@@ -169,7 +173,7 @@ public:
|
||||
// Sanity checks of input
|
||||
{
|
||||
// If note has nonzero value
|
||||
if (inputs[i].note.value != 0) {
|
||||
if (inputs[i].note.value() != 0) {
|
||||
// The witness root must equal the input root.
|
||||
if (inputs[i].witness.root() != rt) {
|
||||
throw std::invalid_argument("joinsplit not anchored to the correct root");
|
||||
@@ -187,11 +191,11 @@ public:
|
||||
}
|
||||
|
||||
// Balance must be sensical
|
||||
if (inputs[i].note.value > MAX_MONEY) {
|
||||
if (inputs[i].note.value() > MAX_MONEY) {
|
||||
throw std::invalid_argument("nonsensical input note value");
|
||||
}
|
||||
|
||||
lhs_value += inputs[i].note.value;
|
||||
lhs_value += inputs[i].note.value();
|
||||
|
||||
if (lhs_value > MAX_MONEY) {
|
||||
throw std::invalid_argument("nonsensical left hand size of joinsplit balance");
|
||||
@@ -206,7 +210,7 @@ public:
|
||||
out_randomSeed = random_uint256();
|
||||
|
||||
// Compute h_sig
|
||||
uint256 h_sig = this->h_sig(out_randomSeed, out_nullifiers, pubKeyHash);
|
||||
uint256 h_sig = this->h_sig(out_randomSeed, out_nullifiers, joinSplitPubKey);
|
||||
|
||||
// Sample phi
|
||||
uint252 phi = random_uint252();
|
||||
@@ -247,7 +251,7 @@ public:
|
||||
ZCNoteEncryption encryptor(h_sig);
|
||||
|
||||
for (size_t i = 0; i < NumOutputs; i++) {
|
||||
NotePlaintext pt(out_notes[i], outputs[i].memo);
|
||||
SproutNotePlaintext pt(out_notes[i], outputs[i].memo);
|
||||
|
||||
out_ciphertexts[i] = pt.encrypt(encryptor, outputs[i].addr.pk_enc);
|
||||
}
|
||||
@@ -268,8 +272,57 @@ public:
|
||||
out_macs[i] = PRF_pk(inputs[i].key, i, h_sig);
|
||||
}
|
||||
|
||||
if (makeGrothProof) {
|
||||
if (!computeProof) {
|
||||
return GrothProof();
|
||||
}
|
||||
|
||||
GrothProof proof;
|
||||
|
||||
CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss1 << inputs[0].witness.path();
|
||||
std::vector<unsigned char> auth1(ss1.begin(), ss1.end());
|
||||
|
||||
CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss2 << inputs[1].witness.path();
|
||||
std::vector<unsigned char> auth2(ss2.begin(), ss2.end());
|
||||
|
||||
librustzcash_sprout_prove(
|
||||
proof.begin(),
|
||||
|
||||
phi.begin(),
|
||||
rt.begin(),
|
||||
h_sig.begin(),
|
||||
|
||||
inputs[0].key.begin(),
|
||||
inputs[0].note.value(),
|
||||
inputs[0].note.rho.begin(),
|
||||
inputs[0].note.r.begin(),
|
||||
auth1.data(),
|
||||
|
||||
inputs[1].key.begin(),
|
||||
inputs[1].note.value(),
|
||||
inputs[1].note.rho.begin(),
|
||||
inputs[1].note.r.begin(),
|
||||
auth2.data(),
|
||||
|
||||
out_notes[0].a_pk.begin(),
|
||||
out_notes[0].value(),
|
||||
out_notes[0].r.begin(),
|
||||
|
||||
out_notes[1].a_pk.begin(),
|
||||
out_notes[1].value(),
|
||||
out_notes[1].r.begin(),
|
||||
|
||||
vpub_old,
|
||||
vpub_new
|
||||
);
|
||||
|
||||
return proof;
|
||||
}
|
||||
|
||||
if (!computeProof) {
|
||||
return ZCProof();
|
||||
return PHGRProof();
|
||||
}
|
||||
|
||||
protoboard<FieldT> pb;
|
||||
@@ -307,7 +360,7 @@ public:
|
||||
throw std::runtime_error(strprintf("could not load param file at %s", pkPath));
|
||||
}
|
||||
|
||||
return ZCProof(r1cs_ppzksnark_prover_streaming<ppzksnark_ppT>(
|
||||
return PHGRProof(r1cs_ppzksnark_prover_streaming<ppzksnark_ppT>(
|
||||
fh,
|
||||
primary_input,
|
||||
aux_input,
|
||||
@@ -336,8 +389,8 @@ JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Prepared(con
|
||||
template<size_t NumInputs, size_t NumOutputs>
|
||||
uint256 JoinSplit<NumInputs, NumOutputs>::h_sig(
|
||||
const uint256& randomSeed,
|
||||
const boost::array<uint256, NumInputs>& nullifiers,
|
||||
const uint256& pubKeyHash
|
||||
const std::array<uint256, NumInputs>& nullifiers,
|
||||
const uint256& joinSplitPubKey
|
||||
) {
|
||||
const unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES]
|
||||
= {'Z','c','a','s','h','C','o','m','p','u','t','e','h','S','i','g'};
|
||||
@@ -348,7 +401,7 @@ uint256 JoinSplit<NumInputs, NumOutputs>::h_sig(
|
||||
block.insert(block.end(), nullifiers[i].begin(), nullifiers[i].end());
|
||||
}
|
||||
|
||||
block.insert(block.end(), pubKeyHash.begin(), pubKeyHash.end());
|
||||
block.insert(block.end(), joinSplitPubKey.begin(), joinSplitPubKey.end());
|
||||
|
||||
uint256 output;
|
||||
|
||||
@@ -365,21 +418,21 @@ uint256 JoinSplit<NumInputs, NumOutputs>::h_sig(
|
||||
return output;
|
||||
}
|
||||
|
||||
Note JSOutput::note(const uint252& phi, const uint256& r, size_t i, const uint256& h_sig) const {
|
||||
SproutNote JSOutput::note(const uint252& phi, const uint256& r, size_t i, const uint256& h_sig) const {
|
||||
uint256 rho = PRF_rho(phi, i, h_sig);
|
||||
|
||||
return Note(addr.a_pk, value, rho, r);
|
||||
return SproutNote(addr.a_pk, value, rho, r);
|
||||
}
|
||||
|
||||
JSOutput::JSOutput() : addr(uint256(), uint256()), value(0) {
|
||||
SpendingKey a_sk = SpendingKey::random();
|
||||
SproutSpendingKey a_sk = SproutSpendingKey::random();
|
||||
addr = a_sk.address();
|
||||
}
|
||||
|
||||
JSInput::JSInput() : witness(ZCIncrementalMerkleTree().witness()),
|
||||
key(SpendingKey::random()) {
|
||||
note = Note(key.address().a_pk, 0, random_uint256(), random_uint256());
|
||||
ZCIncrementalMerkleTree dummy_tree;
|
||||
JSInput::JSInput() : witness(SproutMerkleTree().witness()),
|
||||
key(SproutSpendingKey::random()) {
|
||||
note = SproutNote(key.address().a_pk, 0, random_uint256(), random_uint256());
|
||||
SproutMerkleTree dummy_tree;
|
||||
dummy_tree.append(note.cm());
|
||||
witness = dummy_tree.witness();
|
||||
}
|
||||
|
||||
@@ -11,20 +11,28 @@
|
||||
#include "uint256.h"
|
||||
#include "uint252.h"
|
||||
|
||||
#include <boost/array.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
static constexpr size_t GROTH_PROOF_SIZE = (
|
||||
48 + // π_A
|
||||
96 + // π_B
|
||||
48); // π_C
|
||||
|
||||
typedef std::array<unsigned char, GROTH_PROOF_SIZE> GrothProof;
|
||||
typedef boost::variant<PHGRProof, GrothProof> SproutProof;
|
||||
|
||||
class JSInput {
|
||||
public:
|
||||
ZCIncrementalWitness witness;
|
||||
Note note;
|
||||
SpendingKey key;
|
||||
SproutWitness witness;
|
||||
SproutNote note;
|
||||
SproutSpendingKey key;
|
||||
|
||||
JSInput();
|
||||
JSInput(ZCIncrementalWitness witness,
|
||||
Note note,
|
||||
SpendingKey key) : witness(witness), note(note), key(key) { }
|
||||
JSInput(SproutWitness witness,
|
||||
SproutNote note,
|
||||
SproutSpendingKey key) : witness(witness), note(note), key(key) { }
|
||||
|
||||
uint256 nullifier() const {
|
||||
return note.nullifier(key);
|
||||
@@ -33,14 +41,14 @@ public:
|
||||
|
||||
class JSOutput {
|
||||
public:
|
||||
PaymentAddress addr;
|
||||
SproutPaymentAddress addr;
|
||||
uint64_t value;
|
||||
boost::array<unsigned char, ZC_MEMO_SIZE> memo = {{0xF6}}; // 0xF6 is invalid UTF8 as per spec, rest of array is 0x00
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> memo = {{0xF6}}; // 0xF6 is invalid UTF8 as per spec, rest of array is 0x00
|
||||
|
||||
JSOutput();
|
||||
JSOutput(PaymentAddress addr, uint64_t value) : addr(addr), value(value) { }
|
||||
JSOutput(SproutPaymentAddress addr, uint64_t value) : addr(addr), value(value) { }
|
||||
|
||||
Note note(const uint252& phi, const uint256& r, size_t i, const uint256& h_sig) const;
|
||||
SproutNote note(const uint252& phi, const uint256& r, size_t i, const uint256& h_sig) const;
|
||||
};
|
||||
|
||||
template<size_t NumInputs, size_t NumOutputs>
|
||||
@@ -55,21 +63,23 @@ public:
|
||||
const std::string pkPath);
|
||||
|
||||
static uint256 h_sig(const uint256& randomSeed,
|
||||
const boost::array<uint256, NumInputs>& nullifiers,
|
||||
const uint256& pubKeyHash
|
||||
const std::array<uint256, NumInputs>& nullifiers,
|
||||
const uint256& joinSplitPubKey
|
||||
);
|
||||
|
||||
virtual ZCProof prove(
|
||||
const boost::array<JSInput, NumInputs>& inputs,
|
||||
const boost::array<JSOutput, NumOutputs>& outputs,
|
||||
boost::array<Note, NumOutputs>& out_notes,
|
||||
boost::array<ZCNoteEncryption::Ciphertext, NumOutputs>& out_ciphertexts,
|
||||
// Compute nullifiers, macs, note commitments & encryptions, and SNARK proof
|
||||
virtual SproutProof prove(
|
||||
bool makeGrothProof,
|
||||
const std::array<JSInput, NumInputs>& inputs,
|
||||
const std::array<JSOutput, NumOutputs>& outputs,
|
||||
std::array<SproutNote, NumOutputs>& out_notes,
|
||||
std::array<ZCNoteEncryption::Ciphertext, NumOutputs>& out_ciphertexts,
|
||||
uint256& out_ephemeralKey,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& joinSplitPubKey,
|
||||
uint256& out_randomSeed,
|
||||
boost::array<uint256, NumInputs>& out_hmacs,
|
||||
boost::array<uint256, NumInputs>& out_nullifiers,
|
||||
boost::array<uint256, NumOutputs>& out_commitments,
|
||||
std::array<uint256, NumInputs>& out_hmacs,
|
||||
std::array<uint256, NumInputs>& out_nullifiers,
|
||||
std::array<uint256, NumOutputs>& out_commitments,
|
||||
uint64_t vpub_old,
|
||||
uint64_t vpub_new,
|
||||
const uint256& rt,
|
||||
@@ -81,13 +91,13 @@ public:
|
||||
) = 0;
|
||||
|
||||
virtual bool verify(
|
||||
const ZCProof& proof,
|
||||
const PHGRProof& proof,
|
||||
ProofVerifier& verifier,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& joinSplitPubKey,
|
||||
const uint256& randomSeed,
|
||||
const boost::array<uint256, NumInputs>& hmacs,
|
||||
const boost::array<uint256, NumInputs>& nullifiers,
|
||||
const boost::array<uint256, NumOutputs>& commitments,
|
||||
const std::array<uint256, NumInputs>& hmacs,
|
||||
const std::array<uint256, NumInputs>& nullifiers,
|
||||
const std::array<uint256, NumOutputs>& commitments,
|
||||
uint64_t vpub_old,
|
||||
uint64_t vpub_new,
|
||||
const uint256& rt
|
||||
|
||||
@@ -2,28 +2,29 @@
|
||||
#include "prf.h"
|
||||
#include "crypto/sha256.h"
|
||||
|
||||
#include "random.h"
|
||||
#include "version.h"
|
||||
#include "streams.h"
|
||||
|
||||
#include "zcash/util.h"
|
||||
#include "librustzcash.h"
|
||||
|
||||
namespace libzcash {
|
||||
using namespace libzcash;
|
||||
|
||||
Note::Note() {
|
||||
SproutNote::SproutNote() {
|
||||
a_pk = random_uint256();
|
||||
rho = random_uint256();
|
||||
r = random_uint256();
|
||||
value = 0;
|
||||
}
|
||||
|
||||
uint256 Note::cm() const {
|
||||
uint256 SproutNote::cm() const {
|
||||
unsigned char discriminant = 0xb0;
|
||||
|
||||
CSHA256 hasher;
|
||||
hasher.Write(&discriminant, 1);
|
||||
hasher.Write(a_pk.begin(), 32);
|
||||
|
||||
auto value_vec = convertIntToVectorLE(value);
|
||||
auto value_vec = convertIntToVectorLE(value_);
|
||||
|
||||
hasher.Write(&value_vec[0], value_vec.size());
|
||||
hasher.Write(rho.begin(), 32);
|
||||
@@ -35,25 +36,72 @@ uint256 Note::cm() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint256 Note::nullifier(const SpendingKey& a_sk) const {
|
||||
uint256 SproutNote::nullifier(const SproutSpendingKey& a_sk) const {
|
||||
return PRF_nf(a_sk, rho);
|
||||
}
|
||||
|
||||
NotePlaintext::NotePlaintext(
|
||||
const Note& note,
|
||||
boost::array<unsigned char, ZC_MEMO_SIZE> memo) : memo(memo)
|
||||
// Construct and populate Sapling note for a given payment address and value.
|
||||
SaplingNote::SaplingNote(const SaplingPaymentAddress& address, const uint64_t value) : BaseNote(value) {
|
||||
d = address.d;
|
||||
pk_d = address.pk_d;
|
||||
librustzcash_sapling_generate_r(r.begin());
|
||||
}
|
||||
|
||||
// Call librustzcash to compute the commitment
|
||||
boost::optional<uint256> SaplingNote::cm() const {
|
||||
uint256 result;
|
||||
if (!librustzcash_sapling_compute_cm(
|
||||
d.data(),
|
||||
pk_d.begin(),
|
||||
value(),
|
||||
r.begin(),
|
||||
result.begin()
|
||||
))
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Call librustzcash to compute the nullifier
|
||||
boost::optional<uint256> SaplingNote::nullifier(const SaplingFullViewingKey& vk, const uint64_t position) const
|
||||
{
|
||||
auto ak = vk.ak;
|
||||
auto nk = vk.nk;
|
||||
|
||||
uint256 result;
|
||||
if (!librustzcash_sapling_compute_nf(
|
||||
d.data(),
|
||||
pk_d.begin(),
|
||||
value(),
|
||||
r.begin(),
|
||||
ak.begin(),
|
||||
nk.begin(),
|
||||
position,
|
||||
result.begin()
|
||||
))
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SproutNotePlaintext::SproutNotePlaintext(
|
||||
const SproutNote& note,
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> memo) : BaseNotePlaintext(note, memo)
|
||||
{
|
||||
value = note.value;
|
||||
rho = note.rho;
|
||||
r = note.r;
|
||||
}
|
||||
|
||||
Note NotePlaintext::note(const PaymentAddress& addr) const
|
||||
SproutNote SproutNotePlaintext::note(const SproutPaymentAddress& addr) const
|
||||
{
|
||||
return Note(addr.a_pk, value, rho, r);
|
||||
return SproutNote(addr.a_pk, value_, rho, r);
|
||||
}
|
||||
|
||||
NotePlaintext NotePlaintext::decrypt(const ZCNoteDecryption& decryptor,
|
||||
SproutNotePlaintext SproutNotePlaintext::decrypt(const ZCNoteDecryption& decryptor,
|
||||
const ZCNoteDecryption::Ciphertext& ciphertext,
|
||||
const uint256& ephemeralKey,
|
||||
const uint256& h_sig,
|
||||
@@ -65,7 +113,7 @@ NotePlaintext NotePlaintext::decrypt(const ZCNoteDecryption& decryptor,
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << plaintext;
|
||||
|
||||
NotePlaintext ret;
|
||||
SproutNotePlaintext ret;
|
||||
ss >> ret;
|
||||
|
||||
assert(ss.size() == 0);
|
||||
@@ -73,7 +121,7 @@ NotePlaintext NotePlaintext::decrypt(const ZCNoteDecryption& decryptor,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ZCNoteEncryption::Ciphertext NotePlaintext::encrypt(ZCNoteEncryption& encryptor,
|
||||
ZCNoteEncryption::Ciphertext SproutNotePlaintext::encrypt(ZCNoteEncryption& encryptor,
|
||||
const uint256& pk_enc
|
||||
) const
|
||||
{
|
||||
@@ -89,4 +137,177 @@ ZCNoteEncryption::Ciphertext NotePlaintext::encrypt(ZCNoteEncryption& encryptor,
|
||||
return encryptor.encrypt(pk_enc, pt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Construct and populate SaplingNotePlaintext for a given note and memo.
|
||||
SaplingNotePlaintext::SaplingNotePlaintext(
|
||||
const SaplingNote& note,
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> memo) : BaseNotePlaintext(note, memo)
|
||||
{
|
||||
d = note.d;
|
||||
rcm = note.r;
|
||||
}
|
||||
|
||||
|
||||
boost::optional<SaplingNote> SaplingNotePlaintext::note(const SaplingIncomingViewingKey& ivk) const
|
||||
{
|
||||
auto addr = ivk.address(d);
|
||||
if (addr) {
|
||||
return SaplingNote(d, addr.get().pk_d, value_, rcm);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<SaplingOutgoingPlaintext> SaplingOutgoingPlaintext::decrypt(
|
||||
const SaplingOutCiphertext &ciphertext,
|
||||
const uint256& ovk,
|
||||
const uint256& cv,
|
||||
const uint256& cm,
|
||||
const uint256& epk
|
||||
)
|
||||
{
|
||||
auto pt = AttemptSaplingOutDecryption(ciphertext, ovk, cv, cm, epk);
|
||||
if (!pt) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Deserialize from the plaintext
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << pt.get();
|
||||
|
||||
SaplingOutgoingPlaintext ret;
|
||||
ss >> ret;
|
||||
|
||||
assert(ss.size() == 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
|
||||
const SaplingEncCiphertext &ciphertext,
|
||||
const uint256 &ivk,
|
||||
const uint256 &epk,
|
||||
const uint256 &cmu
|
||||
)
|
||||
{
|
||||
auto pt = AttemptSaplingEncDecryption(ciphertext, ivk, epk);
|
||||
if (!pt) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Deserialize from the plaintext
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << pt.get();
|
||||
|
||||
SaplingNotePlaintext ret;
|
||||
ss >> ret;
|
||||
|
||||
assert(ss.size() == 0);
|
||||
|
||||
uint256 pk_d;
|
||||
if (!librustzcash_ivk_to_pkd(ivk.begin(), ret.d.data(), pk_d.begin())) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
uint256 cmu_expected;
|
||||
if (!librustzcash_sapling_compute_cm(
|
||||
ret.d.data(),
|
||||
pk_d.begin(),
|
||||
ret.value(),
|
||||
ret.rcm.begin(),
|
||||
cmu_expected.begin()
|
||||
))
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
if (cmu_expected != cmu) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
|
||||
const SaplingEncCiphertext &ciphertext,
|
||||
const uint256 &epk,
|
||||
const uint256 &esk,
|
||||
const uint256 &pk_d,
|
||||
const uint256 &cmu
|
||||
)
|
||||
{
|
||||
auto pt = AttemptSaplingEncDecryption(ciphertext, epk, esk, pk_d);
|
||||
if (!pt) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Deserialize from the plaintext
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << pt.get();
|
||||
|
||||
SaplingNotePlaintext ret;
|
||||
ss >> ret;
|
||||
|
||||
uint256 cmu_expected;
|
||||
if (!librustzcash_sapling_compute_cm(
|
||||
ret.d.data(),
|
||||
pk_d.begin(),
|
||||
ret.value(),
|
||||
ret.rcm.begin(),
|
||||
cmu_expected.begin()
|
||||
))
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
if (cmu_expected != cmu) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
assert(ss.size() == 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
boost::optional<SaplingNotePlaintextEncryptionResult> SaplingNotePlaintext::encrypt(const uint256& pk_d) const
|
||||
{
|
||||
// Get the encryptor
|
||||
auto sne = SaplingNoteEncryption::FromDiversifier(d);
|
||||
if (!sne) {
|
||||
return boost::none;
|
||||
}
|
||||
auto enc = sne.get();
|
||||
|
||||
// Create the plaintext
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << (*this);
|
||||
SaplingEncPlaintext pt;
|
||||
assert(pt.size() == ss.size());
|
||||
memcpy(&pt[0], &ss[0], pt.size());
|
||||
|
||||
// Encrypt the plaintext
|
||||
auto encciphertext = enc.encrypt_to_recipient(pk_d, pt);
|
||||
if (!encciphertext) {
|
||||
return boost::none;
|
||||
}
|
||||
return SaplingNotePlaintextEncryptionResult(encciphertext.get(), enc);
|
||||
}
|
||||
|
||||
|
||||
SaplingOutCiphertext SaplingOutgoingPlaintext::encrypt(
|
||||
const uint256& ovk,
|
||||
const uint256& cv,
|
||||
const uint256& cm,
|
||||
SaplingNoteEncryption& enc
|
||||
) const
|
||||
{
|
||||
// Create the plaintext
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << (*this);
|
||||
SaplingOutPlaintext pt;
|
||||
assert(pt.size() == ss.size());
|
||||
memcpy(&pt[0], &ss[0], pt.size());
|
||||
|
||||
return enc.encrypt_to_ourselves(ovk, cv, cm, pt);
|
||||
}
|
||||
|
||||
@@ -6,55 +6,105 @@
|
||||
#include "Address.hpp"
|
||||
#include "NoteEncryption.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
class Note {
|
||||
class BaseNote {
|
||||
protected:
|
||||
uint64_t value_ = 0;
|
||||
public:
|
||||
uint256 a_pk;
|
||||
uint64_t value;
|
||||
uint256 rho;
|
||||
uint256 r;
|
||||
BaseNote() {}
|
||||
BaseNote(uint64_t value) : value_(value) {};
|
||||
virtual ~BaseNote() {};
|
||||
|
||||
Note(uint256 a_pk, uint64_t value, uint256 rho, uint256 r)
|
||||
: a_pk(a_pk), value(value), rho(rho), r(r) {}
|
||||
|
||||
Note();
|
||||
|
||||
uint256 cm() const;
|
||||
uint256 nullifier(const SpendingKey& a_sk) const;
|
||||
inline uint64_t value() const { return value_; };
|
||||
};
|
||||
|
||||
class NotePlaintext {
|
||||
class SproutNote : public BaseNote {
|
||||
public:
|
||||
uint64_t value = 0;
|
||||
uint256 a_pk;
|
||||
uint256 rho;
|
||||
uint256 r;
|
||||
boost::array<unsigned char, ZC_MEMO_SIZE> memo;
|
||||
|
||||
NotePlaintext() {}
|
||||
SproutNote(uint256 a_pk, uint64_t value, uint256 rho, uint256 r)
|
||||
: BaseNote(value), a_pk(a_pk), rho(rho), r(r) {}
|
||||
|
||||
NotePlaintext(const Note& note, boost::array<unsigned char, ZC_MEMO_SIZE> memo);
|
||||
SproutNote();
|
||||
|
||||
Note note(const PaymentAddress& addr) const;
|
||||
virtual ~SproutNote() {};
|
||||
|
||||
uint256 cm() const;
|
||||
|
||||
uint256 nullifier(const SproutSpendingKey& a_sk) const;
|
||||
};
|
||||
|
||||
|
||||
class SaplingNote : public BaseNote {
|
||||
public:
|
||||
diversifier_t d;
|
||||
uint256 pk_d;
|
||||
uint256 r;
|
||||
|
||||
SaplingNote(diversifier_t d, uint256 pk_d, uint64_t value, uint256 r)
|
||||
: BaseNote(value), d(d), pk_d(pk_d), r(r) {}
|
||||
|
||||
SaplingNote() {};
|
||||
|
||||
SaplingNote(const SaplingPaymentAddress &address, uint64_t value);
|
||||
|
||||
virtual ~SaplingNote() {};
|
||||
|
||||
boost::optional<uint256> cm() const;
|
||||
boost::optional<uint256> nullifier(const SaplingFullViewingKey &vk, const uint64_t position) const;
|
||||
};
|
||||
|
||||
class BaseNotePlaintext {
|
||||
protected:
|
||||
uint64_t value_ = 0;
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> memo_;
|
||||
public:
|
||||
BaseNotePlaintext() {}
|
||||
BaseNotePlaintext(const BaseNote& note, std::array<unsigned char, ZC_MEMO_SIZE> memo)
|
||||
: value_(note.value()), memo_(memo) {}
|
||||
virtual ~BaseNotePlaintext() {}
|
||||
|
||||
inline uint64_t value() const { return value_; }
|
||||
inline const std::array<unsigned char, ZC_MEMO_SIZE> & memo() const { return memo_; }
|
||||
};
|
||||
|
||||
class SproutNotePlaintext : public BaseNotePlaintext {
|
||||
public:
|
||||
uint256 rho;
|
||||
uint256 r;
|
||||
|
||||
SproutNotePlaintext() {}
|
||||
|
||||
SproutNotePlaintext(const SproutNote& note, std::array<unsigned char, ZC_MEMO_SIZE> memo);
|
||||
|
||||
SproutNote note(const SproutPaymentAddress& addr) const;
|
||||
|
||||
virtual ~SproutNotePlaintext() {}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
unsigned char leadingByte = 0x00;
|
||||
READWRITE(leadingByte);
|
||||
|
||||
if (leadingByte != 0x00) {
|
||||
throw std::ios_base::failure("lead byte of NotePlaintext is not recognized");
|
||||
throw std::ios_base::failure("lead byte of SproutNotePlaintext is not recognized");
|
||||
}
|
||||
|
||||
READWRITE(value);
|
||||
READWRITE(value_);
|
||||
READWRITE(rho);
|
||||
READWRITE(r);
|
||||
READWRITE(memo);
|
||||
READWRITE(memo_);
|
||||
}
|
||||
|
||||
static NotePlaintext decrypt(const ZCNoteDecryption& decryptor,
|
||||
static SproutNotePlaintext decrypt(const ZCNoteDecryption& decryptor,
|
||||
const ZCNoteDecryption::Ciphertext& ciphertext,
|
||||
const uint256& ephemeralKey,
|
||||
const uint256& h_sig,
|
||||
@@ -66,6 +116,91 @@ public:
|
||||
) const;
|
||||
};
|
||||
|
||||
typedef std::pair<SaplingEncCiphertext, SaplingNoteEncryption> SaplingNotePlaintextEncryptionResult;
|
||||
|
||||
class SaplingNotePlaintext : public BaseNotePlaintext {
|
||||
public:
|
||||
diversifier_t d;
|
||||
uint256 rcm;
|
||||
|
||||
SaplingNotePlaintext() {}
|
||||
|
||||
SaplingNotePlaintext(const SaplingNote& note, std::array<unsigned char, ZC_MEMO_SIZE> memo);
|
||||
|
||||
static boost::optional<SaplingNotePlaintext> decrypt(
|
||||
const SaplingEncCiphertext &ciphertext,
|
||||
const uint256 &ivk,
|
||||
const uint256 &epk,
|
||||
const uint256 &cmu
|
||||
);
|
||||
|
||||
static boost::optional<SaplingNotePlaintext> decrypt(
|
||||
const SaplingEncCiphertext &ciphertext,
|
||||
const uint256 &epk,
|
||||
const uint256 &esk,
|
||||
const uint256 &pk_d,
|
||||
const uint256 &cmu
|
||||
);
|
||||
|
||||
boost::optional<SaplingNote> note(const SaplingIncomingViewingKey& ivk) const;
|
||||
|
||||
virtual ~SaplingNotePlaintext() {}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
unsigned char leadingByte = 0x01;
|
||||
READWRITE(leadingByte);
|
||||
|
||||
if (leadingByte != 0x01) {
|
||||
throw std::ios_base::failure("lead byte of SaplingNotePlaintext is not recognized");
|
||||
}
|
||||
|
||||
READWRITE(d); // 11 bytes
|
||||
READWRITE(value_); // 8 bytes
|
||||
READWRITE(rcm); // 32 bytes
|
||||
READWRITE(memo_); // 512 bytes
|
||||
}
|
||||
|
||||
boost::optional<SaplingNotePlaintextEncryptionResult> encrypt(const uint256& pk_d) const;
|
||||
};
|
||||
|
||||
class SaplingOutgoingPlaintext
|
||||
{
|
||||
public:
|
||||
uint256 pk_d;
|
||||
uint256 esk;
|
||||
|
||||
SaplingOutgoingPlaintext() {};
|
||||
|
||||
SaplingOutgoingPlaintext(uint256 pk_d, uint256 esk) : pk_d(pk_d), esk(esk) {}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(pk_d); // 8 bytes
|
||||
READWRITE(esk); // 8 bytes
|
||||
}
|
||||
|
||||
static boost::optional<SaplingOutgoingPlaintext> decrypt(
|
||||
const SaplingOutCiphertext &ciphertext,
|
||||
const uint256& ovk,
|
||||
const uint256& cv,
|
||||
const uint256& cm,
|
||||
const uint256& epk
|
||||
);
|
||||
|
||||
SaplingOutCiphertext encrypt(
|
||||
const uint256& ovk,
|
||||
const uint256& cv,
|
||||
const uint256& cm,
|
||||
SaplingNoteEncryption& enc
|
||||
) const;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // ZC_NOTE_H_
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "sodium.h"
|
||||
#include <boost/static_assert.hpp>
|
||||
#include "prf.h"
|
||||
#include "librustzcash.h"
|
||||
|
||||
#define NOTEENCRYPTION_CIPHER_KEYSIZE 32
|
||||
|
||||
@@ -13,6 +14,58 @@ void clamp_curve25519(unsigned char key[crypto_scalarmult_SCALARBYTES])
|
||||
key[31] |= 64;
|
||||
}
|
||||
|
||||
void PRF_ock(
|
||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
|
||||
const uint256 &ovk,
|
||||
const uint256 &cv,
|
||||
const uint256 &cm,
|
||||
const uint256 &epk
|
||||
)
|
||||
{
|
||||
unsigned char block[128] = {};
|
||||
memcpy(block+0, ovk.begin(), 32);
|
||||
memcpy(block+32, cv.begin(), 32);
|
||||
memcpy(block+64, cm.begin(), 32);
|
||||
memcpy(block+96, epk.begin(), 32);
|
||||
|
||||
unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
|
||||
memcpy(personalization, "Zcash_Derive_ock", 16);
|
||||
|
||||
if (crypto_generichash_blake2b_salt_personal(K, NOTEENCRYPTION_CIPHER_KEYSIZE,
|
||||
block, 128,
|
||||
NULL, 0, // No key.
|
||||
NULL, // No salt.
|
||||
personalization
|
||||
) != 0)
|
||||
{
|
||||
throw std::logic_error("hash function failure");
|
||||
}
|
||||
}
|
||||
|
||||
void KDF_Sapling(
|
||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
|
||||
const uint256 &dhsecret,
|
||||
const uint256 &epk
|
||||
)
|
||||
{
|
||||
unsigned char block[64] = {};
|
||||
memcpy(block+0, dhsecret.begin(), 32);
|
||||
memcpy(block+32, epk.begin(), 32);
|
||||
|
||||
unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
|
||||
memcpy(personalization, "Zcash_SaplingKDF", 16);
|
||||
|
||||
if (crypto_generichash_blake2b_salt_personal(K, NOTEENCRYPTION_CIPHER_KEYSIZE,
|
||||
block, 64,
|
||||
NULL, 0, // No key.
|
||||
NULL, // No salt.
|
||||
personalization
|
||||
) != 0)
|
||||
{
|
||||
throw std::logic_error("hash function failure");
|
||||
}
|
||||
}
|
||||
|
||||
void KDF(unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
|
||||
const uint256 &dhsecret,
|
||||
const uint256 &epk,
|
||||
@@ -48,6 +101,192 @@ void KDF(unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
boost::optional<SaplingNoteEncryption> SaplingNoteEncryption::FromDiversifier(diversifier_t d) {
|
||||
uint256 epk;
|
||||
uint256 esk;
|
||||
|
||||
// Pick random esk
|
||||
librustzcash_sapling_generate_r(esk.begin());
|
||||
|
||||
// Compute epk given the diversifier
|
||||
if (!librustzcash_sapling_ka_derivepublic(d.begin(), esk.begin(), epk.begin())) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return SaplingNoteEncryption(epk, esk);
|
||||
}
|
||||
|
||||
boost::optional<SaplingEncCiphertext> SaplingNoteEncryption::encrypt_to_recipient(
|
||||
const uint256 &pk_d,
|
||||
const SaplingEncPlaintext &message
|
||||
)
|
||||
{
|
||||
if (already_encrypted_enc) {
|
||||
throw std::logic_error("already encrypted to the recipient using this key");
|
||||
}
|
||||
|
||||
uint256 dhsecret;
|
||||
|
||||
if (!librustzcash_sapling_ka_agree(pk_d.begin(), esk.begin(), dhsecret.begin())) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Construct the symmetric key
|
||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
||||
KDF_Sapling(K, dhsecret, epk);
|
||||
|
||||
// The nonce is zero because we never reuse keys
|
||||
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
||||
|
||||
SaplingEncCiphertext ciphertext;
|
||||
|
||||
crypto_aead_chacha20poly1305_ietf_encrypt(
|
||||
ciphertext.begin(), NULL,
|
||||
message.begin(), ZC_SAPLING_ENCPLAINTEXT_SIZE,
|
||||
NULL, 0, // no "additional data"
|
||||
NULL, cipher_nonce, K
|
||||
);
|
||||
|
||||
already_encrypted_enc = true;
|
||||
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
boost::optional<SaplingEncPlaintext> AttemptSaplingEncDecryption(
|
||||
const SaplingEncCiphertext &ciphertext,
|
||||
const uint256 &ivk,
|
||||
const uint256 &epk
|
||||
)
|
||||
{
|
||||
uint256 dhsecret;
|
||||
|
||||
if (!librustzcash_sapling_ka_agree(epk.begin(), ivk.begin(), dhsecret.begin())) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Construct the symmetric key
|
||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
||||
KDF_Sapling(K, dhsecret, epk);
|
||||
|
||||
// The nonce is zero because we never reuse keys
|
||||
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
||||
|
||||
SaplingEncPlaintext plaintext;
|
||||
|
||||
if (crypto_aead_chacha20poly1305_ietf_decrypt(
|
||||
plaintext.begin(), NULL,
|
||||
NULL,
|
||||
ciphertext.begin(), ZC_SAPLING_ENCCIPHERTEXT_SIZE,
|
||||
NULL,
|
||||
0,
|
||||
cipher_nonce, K) != 0)
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
boost::optional<SaplingEncPlaintext> AttemptSaplingEncDecryption (
|
||||
const SaplingEncCiphertext &ciphertext,
|
||||
const uint256 &epk,
|
||||
const uint256 &esk,
|
||||
const uint256 &pk_d
|
||||
)
|
||||
{
|
||||
uint256 dhsecret;
|
||||
|
||||
if (!librustzcash_sapling_ka_agree(pk_d.begin(), esk.begin(), dhsecret.begin())) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Construct the symmetric key
|
||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
||||
KDF_Sapling(K, dhsecret, epk);
|
||||
|
||||
// The nonce is zero because we never reuse keys
|
||||
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
||||
|
||||
SaplingEncPlaintext plaintext;
|
||||
|
||||
if (crypto_aead_chacha20poly1305_ietf_decrypt(
|
||||
plaintext.begin(), NULL,
|
||||
NULL,
|
||||
ciphertext.begin(), ZC_SAPLING_ENCCIPHERTEXT_SIZE,
|
||||
NULL,
|
||||
0,
|
||||
cipher_nonce, K) != 0)
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
|
||||
SaplingOutCiphertext SaplingNoteEncryption::encrypt_to_ourselves(
|
||||
const uint256 &ovk,
|
||||
const uint256 &cv,
|
||||
const uint256 &cm,
|
||||
const SaplingOutPlaintext &message
|
||||
)
|
||||
{
|
||||
if (already_encrypted_out) {
|
||||
throw std::logic_error("already encrypted to the recipient using this key");
|
||||
}
|
||||
|
||||
// Construct the symmetric key
|
||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
||||
PRF_ock(K, ovk, cv, cm, epk);
|
||||
|
||||
// The nonce is zero because we never reuse keys
|
||||
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
||||
|
||||
SaplingOutCiphertext ciphertext;
|
||||
|
||||
crypto_aead_chacha20poly1305_ietf_encrypt(
|
||||
ciphertext.begin(), NULL,
|
||||
message.begin(), ZC_SAPLING_OUTPLAINTEXT_SIZE,
|
||||
NULL, 0, // no "additional data"
|
||||
NULL, cipher_nonce, K
|
||||
);
|
||||
|
||||
already_encrypted_out = true;
|
||||
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
boost::optional<SaplingOutPlaintext> AttemptSaplingOutDecryption(
|
||||
const SaplingOutCiphertext &ciphertext,
|
||||
const uint256 &ovk,
|
||||
const uint256 &cv,
|
||||
const uint256 &cm,
|
||||
const uint256 &epk
|
||||
)
|
||||
{
|
||||
// Construct the symmetric key
|
||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
||||
PRF_ock(K, ovk, cv, cm, epk);
|
||||
|
||||
// The nonce is zero because we never reuse keys
|
||||
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
||||
|
||||
SaplingOutPlaintext plaintext;
|
||||
|
||||
if (crypto_aead_chacha20poly1305_ietf_decrypt(
|
||||
plaintext.begin(), NULL,
|
||||
NULL,
|
||||
ciphertext.begin(), ZC_SAPLING_OUTCIPHERTEXT_SIZE,
|
||||
NULL,
|
||||
0,
|
||||
cipher_nonce, K) != 0)
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
template<size_t MLEN>
|
||||
NoteEncryption<MLEN>::NoteEncryption(uint256 hSig) : nonce(0), hSig(hSig) {
|
||||
// All of this code assumes crypto_scalarmult_BYTES is 32
|
||||
|
||||
@@ -6,15 +6,91 @@ https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
|
||||
#ifndef ZC_NOTE_ENCRYPTION_H_
|
||||
#define ZC_NOTE_ENCRYPTION_H_
|
||||
|
||||
#include <boost/array.hpp>
|
||||
#include "uint256.h"
|
||||
#include "uint252.h"
|
||||
|
||||
#include "zcash/Zcash.h"
|
||||
#include "zcash/Address.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
#define NOTEENCRYPTION_AUTH_BYTES 16
|
||||
// Ciphertext for the recipient to decrypt
|
||||
typedef std::array<unsigned char, ZC_SAPLING_ENCCIPHERTEXT_SIZE> SaplingEncCiphertext;
|
||||
typedef std::array<unsigned char, ZC_SAPLING_ENCPLAINTEXT_SIZE> SaplingEncPlaintext;
|
||||
|
||||
// Ciphertext for outgoing viewing key to decrypt
|
||||
typedef std::array<unsigned char, ZC_SAPLING_OUTCIPHERTEXT_SIZE> SaplingOutCiphertext;
|
||||
typedef std::array<unsigned char, ZC_SAPLING_OUTPLAINTEXT_SIZE> SaplingOutPlaintext;
|
||||
|
||||
//! This is not a thread-safe API.
|
||||
class SaplingNoteEncryption {
|
||||
protected:
|
||||
// Ephemeral public key
|
||||
uint256 epk;
|
||||
|
||||
// Ephemeral secret key
|
||||
uint256 esk;
|
||||
|
||||
bool already_encrypted_enc;
|
||||
bool already_encrypted_out;
|
||||
|
||||
SaplingNoteEncryption(uint256 epk, uint256 esk) : epk(epk), esk(esk), already_encrypted_enc(false), already_encrypted_out(false) {
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static boost::optional<SaplingNoteEncryption> FromDiversifier(diversifier_t d);
|
||||
|
||||
boost::optional<SaplingEncCiphertext> encrypt_to_recipient(
|
||||
const uint256 &pk_d,
|
||||
const SaplingEncPlaintext &message
|
||||
);
|
||||
|
||||
SaplingOutCiphertext encrypt_to_ourselves(
|
||||
const uint256 &ovk,
|
||||
const uint256 &cv,
|
||||
const uint256 &cm,
|
||||
const SaplingOutPlaintext &message
|
||||
);
|
||||
|
||||
uint256 get_epk() const {
|
||||
return epk;
|
||||
}
|
||||
|
||||
uint256 get_esk() const {
|
||||
return esk;
|
||||
}
|
||||
};
|
||||
|
||||
// Attempts to decrypt a Sapling note. This will not check that the contents
|
||||
// of the ciphertext are correct.
|
||||
boost::optional<SaplingEncPlaintext> AttemptSaplingEncDecryption(
|
||||
const SaplingEncCiphertext &ciphertext,
|
||||
const uint256 &ivk,
|
||||
const uint256 &epk
|
||||
);
|
||||
|
||||
// Attempts to decrypt a Sapling note using outgoing plaintext.
|
||||
// This will not check that the contents of the ciphertext are correct.
|
||||
boost::optional<SaplingEncPlaintext> AttemptSaplingEncDecryption (
|
||||
const SaplingEncCiphertext &ciphertext,
|
||||
const uint256 &epk,
|
||||
const uint256 &esk,
|
||||
const uint256 &pk_d
|
||||
);
|
||||
|
||||
// Attempts to decrypt a Sapling note. This will not check that the contents
|
||||
// of the ciphertext are correct.
|
||||
boost::optional<SaplingOutPlaintext> AttemptSaplingOutDecryption(
|
||||
const SaplingOutCiphertext &ciphertext,
|
||||
const uint256 &ovk,
|
||||
const uint256 &cv,
|
||||
const uint256 &cm,
|
||||
const uint256 &epk
|
||||
);
|
||||
|
||||
template<size_t MLEN>
|
||||
class NoteEncryption {
|
||||
@@ -26,8 +102,8 @@ protected:
|
||||
uint256 hSig;
|
||||
|
||||
public:
|
||||
typedef boost::array<unsigned char, CLEN> Ciphertext;
|
||||
typedef boost::array<unsigned char, MLEN> Plaintext;
|
||||
typedef std::array<unsigned char, CLEN> Ciphertext;
|
||||
typedef std::array<unsigned char, MLEN> Plaintext;
|
||||
|
||||
NoteEncryption(uint256 hSig);
|
||||
|
||||
@@ -63,8 +139,8 @@ protected:
|
||||
uint256 pk_enc;
|
||||
|
||||
public:
|
||||
typedef boost::array<unsigned char, CLEN> Ciphertext;
|
||||
typedef boost::array<unsigned char, MLEN> Plaintext;
|
||||
typedef std::array<unsigned char, CLEN> Ciphertext;
|
||||
typedef std::array<unsigned char, MLEN> Plaintext;
|
||||
|
||||
NoteDecryption() { }
|
||||
NoteDecryption(uint256 sk_enc);
|
||||
@@ -100,8 +176,8 @@ class PaymentDisclosureNoteDecryption : public NoteDecryption<MLEN> {
|
||||
protected:
|
||||
public:
|
||||
enum { CLEN=MLEN+NOTEENCRYPTION_AUTH_BYTES };
|
||||
typedef boost::array<unsigned char, CLEN> Ciphertext;
|
||||
typedef boost::array<unsigned char, MLEN> Plaintext;
|
||||
typedef std::array<unsigned char, CLEN> Ciphertext;
|
||||
typedef std::array<unsigned char, MLEN> Plaintext;
|
||||
|
||||
PaymentDisclosureNoteDecryption() : NoteDecryption<MLEN>() {}
|
||||
PaymentDisclosureNoteDecryption(uint256 sk_enc) : NoteDecryption<MLEN>(sk_enc) {}
|
||||
|
||||
@@ -171,7 +171,7 @@ curve_G2 CompressedG2::to_libsnark_g2() const
|
||||
}
|
||||
|
||||
template<>
|
||||
ZCProof::ZCProof(const r1cs_ppzksnark_proof<curve_pp> &proof)
|
||||
PHGRProof::PHGRProof(const r1cs_ppzksnark_proof<curve_pp> &proof)
|
||||
{
|
||||
g_A = CompressedG1(proof.g_A.g);
|
||||
g_A_prime = CompressedG1(proof.g_A.h);
|
||||
@@ -184,7 +184,7 @@ ZCProof::ZCProof(const r1cs_ppzksnark_proof<curve_pp> &proof)
|
||||
}
|
||||
|
||||
template<>
|
||||
r1cs_ppzksnark_proof<curve_pp> ZCProof::to_libsnark_proof() const
|
||||
r1cs_ppzksnark_proof<curve_pp> PHGRProof::to_libsnark_proof() const
|
||||
{
|
||||
r1cs_ppzksnark_proof<curve_pp> proof;
|
||||
|
||||
@@ -200,9 +200,9 @@ r1cs_ppzksnark_proof<curve_pp> ZCProof::to_libsnark_proof() const
|
||||
return proof;
|
||||
}
|
||||
|
||||
ZCProof ZCProof::random_invalid()
|
||||
PHGRProof PHGRProof::random_invalid()
|
||||
{
|
||||
ZCProof p;
|
||||
PHGRProof p;
|
||||
p.g_A = curve_G1::random_element();
|
||||
p.g_A_prime = curve_G1::random_element();
|
||||
p.g_B = curve_G2::random_element();
|
||||
@@ -216,7 +216,7 @@ ZCProof ZCProof::random_invalid()
|
||||
return p;
|
||||
}
|
||||
|
||||
std::once_flag init_public_params_once_flag;
|
||||
static std::once_flag init_public_params_once_flag;
|
||||
|
||||
void initialize_curve_params()
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(data);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(data);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
unsigned char leadingByte = G1_PREFIX_MASK;
|
||||
|
||||
if (y_lsb) {
|
||||
@@ -143,7 +143,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
unsigned char leadingByte = G2_PREFIX_MASK;
|
||||
|
||||
if (y_gt) {
|
||||
@@ -176,7 +176,7 @@ public:
|
||||
};
|
||||
|
||||
// Compressed zkSNARK proof
|
||||
class ZCProof {
|
||||
class PHGRProof {
|
||||
private:
|
||||
CompressedG1 g_A;
|
||||
CompressedG1 g_A_prime;
|
||||
@@ -188,23 +188,23 @@ private:
|
||||
CompressedG1 g_H;
|
||||
|
||||
public:
|
||||
ZCProof() : g_A(), g_A_prime(), g_B(), g_B_prime(), g_C(), g_C_prime(), g_K(), g_H() { }
|
||||
PHGRProof() : g_A(), g_A_prime(), g_B(), g_B_prime(), g_C(), g_C_prime(), g_K(), g_H() { }
|
||||
|
||||
// Produces a compressed proof using a libsnark zkSNARK proof
|
||||
template<typename libsnark_proof>
|
||||
ZCProof(const libsnark_proof& proof);
|
||||
PHGRProof(const libsnark_proof& proof);
|
||||
|
||||
// Produces a libsnark zkSNARK proof out of this proof,
|
||||
// or throws an exception if it is invalid.
|
||||
template<typename libsnark_proof>
|
||||
libsnark_proof to_libsnark_proof() const;
|
||||
|
||||
static ZCProof random_invalid();
|
||||
static PHGRProof random_invalid();
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(g_A);
|
||||
READWRITE(g_A_prime);
|
||||
READWRITE(g_B);
|
||||
@@ -215,7 +215,7 @@ public:
|
||||
READWRITE(g_H);
|
||||
}
|
||||
|
||||
friend bool operator==(const ZCProof& a, const ZCProof& b)
|
||||
friend bool operator==(const PHGRProof& a, const PHGRProof& b)
|
||||
{
|
||||
return (
|
||||
a.g_A == b.g_A &&
|
||||
@@ -229,7 +229,7 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const ZCProof& a, const ZCProof& b)
|
||||
friend bool operator!=(const PHGRProof& a, const PHGRProof& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
@@ -6,12 +6,25 @@
|
||||
#define INCREMENTAL_MERKLE_TREE_DEPTH 29
|
||||
#define INCREMENTAL_MERKLE_TREE_DEPTH_TESTING 4
|
||||
|
||||
#define SAPLING_INCREMENTAL_MERKLE_TREE_DEPTH 32
|
||||
|
||||
#define NOTEENCRYPTION_AUTH_BYTES 16
|
||||
|
||||
#define ZC_NOTEPLAINTEXT_LEADING 1
|
||||
#define ZC_V_SIZE 8
|
||||
#define ZC_RHO_SIZE 32
|
||||
#define ZC_R_SIZE 32
|
||||
#define ZC_MEMO_SIZE 512
|
||||
#define ZC_DIVERSIFIER_SIZE 11
|
||||
#define ZC_JUBJUB_POINT_SIZE 32
|
||||
#define ZC_JUBJUB_SCALAR_SIZE 32
|
||||
|
||||
#define ZC_NOTEPLAINTEXT_SIZE (ZC_NOTEPLAINTEXT_LEADING + ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE)
|
||||
|
||||
#define ZC_SAPLING_ENCPLAINTEXT_SIZE (ZC_NOTEPLAINTEXT_LEADING + ZC_DIVERSIFIER_SIZE + ZC_V_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE)
|
||||
#define ZC_SAPLING_OUTPLAINTEXT_SIZE (ZC_JUBJUB_POINT_SIZE + ZC_JUBJUB_SCALAR_SIZE)
|
||||
|
||||
#define ZC_SAPLING_ENCCIPHERTEXT_SIZE (ZC_SAPLING_ENCPLAINTEXT_SIZE + NOTEENCRYPTION_AUTH_BYTES)
|
||||
#define ZC_SAPLING_OUTCIPHERTEXT_SIZE (ZC_SAPLING_OUTPLAINTEXT_SIZE + NOTEENCRYPTION_AUTH_BYTES)
|
||||
|
||||
#endif // ZC_ZCASH_H_
|
||||
|
||||
@@ -14,9 +14,9 @@ private:
|
||||
|
||||
std::shared_ptr<digest_variable<FieldT>> zk_merkle_root;
|
||||
std::shared_ptr<digest_variable<FieldT>> zk_h_sig;
|
||||
boost::array<std::shared_ptr<digest_variable<FieldT>>, NumInputs> zk_input_nullifiers;
|
||||
boost::array<std::shared_ptr<digest_variable<FieldT>>, NumInputs> zk_input_macs;
|
||||
boost::array<std::shared_ptr<digest_variable<FieldT>>, NumOutputs> zk_output_commitments;
|
||||
std::array<std::shared_ptr<digest_variable<FieldT>>, NumInputs> zk_input_nullifiers;
|
||||
std::array<std::shared_ptr<digest_variable<FieldT>>, NumInputs> zk_input_macs;
|
||||
std::array<std::shared_ptr<digest_variable<FieldT>>, NumOutputs> zk_output_commitments;
|
||||
pb_variable_array<FieldT> zk_vpub_old;
|
||||
pb_variable_array<FieldT> zk_vpub_new;
|
||||
|
||||
@@ -26,11 +26,11 @@ private:
|
||||
pb_variable_array<FieldT> zk_total_uint64;
|
||||
|
||||
// Input note gadgets
|
||||
boost::array<std::shared_ptr<input_note_gadget<FieldT>>, NumInputs> zk_input_notes;
|
||||
boost::array<std::shared_ptr<PRF_pk_gadget<FieldT>>, NumInputs> zk_mac_authentication;
|
||||
std::array<std::shared_ptr<input_note_gadget<FieldT>>, NumInputs> zk_input_notes;
|
||||
std::array<std::shared_ptr<PRF_pk_gadget<FieldT>>, NumInputs> zk_mac_authentication;
|
||||
|
||||
// Output note gadgets
|
||||
boost::array<std::shared_ptr<output_note_gadget<FieldT>>, NumOutputs> zk_output_notes;
|
||||
std::array<std::shared_ptr<output_note_gadget<FieldT>>, NumOutputs> zk_output_notes;
|
||||
|
||||
public:
|
||||
// PRF_pk only has a 1-bit domain separation "nonce"
|
||||
@@ -190,8 +190,8 @@ public:
|
||||
const uint252& phi,
|
||||
const uint256& rt,
|
||||
const uint256& h_sig,
|
||||
const boost::array<JSInput, NumInputs>& inputs,
|
||||
const boost::array<Note, NumOutputs>& outputs,
|
||||
const std::array<JSInput, NumInputs>& inputs,
|
||||
const std::array<SproutNote, NumOutputs>& outputs,
|
||||
uint64_t vpub_old,
|
||||
uint64_t vpub_new
|
||||
) {
|
||||
@@ -222,7 +222,7 @@ public:
|
||||
// Witness total_uint64 bits
|
||||
uint64_t left_side_acc = vpub_old;
|
||||
for (size_t i = 0; i < NumInputs; i++) {
|
||||
left_side_acc += inputs[i].note.value;
|
||||
left_side_acc += inputs[i].note.value();
|
||||
}
|
||||
|
||||
zk_total_uint64.fill_with_bits(
|
||||
@@ -280,9 +280,9 @@ public:
|
||||
static r1cs_primary_input<FieldT> witness_map(
|
||||
const uint256& rt,
|
||||
const uint256& h_sig,
|
||||
const boost::array<uint256, NumInputs>& macs,
|
||||
const boost::array<uint256, NumInputs>& nullifiers,
|
||||
const boost::array<uint256, NumOutputs>& commitments,
|
||||
const std::array<uint256, NumInputs>& macs,
|
||||
const std::array<uint256, NumInputs>& nullifiers,
|
||||
const std::array<uint256, NumOutputs>& commitments,
|
||||
uint64_t vpub_old,
|
||||
uint64_t vpub_new
|
||||
) {
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
// number thing in its API.
|
||||
size_t path_index = convertVectorToInt(path.index);
|
||||
|
||||
positions.fill_with_bits_of_ulong(this->pb, path_index);
|
||||
positions.fill_with_bits_of_uint64(this->pb, path_index);
|
||||
|
||||
authvars->generate_r1cs_witness(path_index, path.authentication_path);
|
||||
auth->generate_r1cs_witness();
|
||||
|
||||
@@ -21,9 +21,9 @@ public:
|
||||
r->generate_r1cs_constraints();
|
||||
}
|
||||
|
||||
void generate_r1cs_witness(const Note& note) {
|
||||
void generate_r1cs_witness(const SproutNote& note) {
|
||||
r->bits.fill_with_bits(this->pb, uint256_to_bool_vector(note.r));
|
||||
value.fill_with_bits(this->pb, uint64_to_bool_vector(note.value));
|
||||
value.fill_with_bits(this->pb, uint64_to_bool_vector(note.value()));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -118,8 +118,8 @@ public:
|
||||
|
||||
void generate_r1cs_witness(
|
||||
const MerklePath& path,
|
||||
const SpendingKey& key,
|
||||
const Note& note
|
||||
const SproutSpendingKey& key,
|
||||
const SproutNote& note
|
||||
) {
|
||||
note_gadget<FieldT>::generate_r1cs_witness(note);
|
||||
|
||||
@@ -158,7 +158,7 @@ public:
|
||||
);
|
||||
|
||||
// Set enforce flag for nonzero input value
|
||||
this->pb.val(value_enforce) = (note.value != 0) ? FieldT::one() : FieldT::zero();
|
||||
this->pb.val(value_enforce) = (note.value() != 0) ? FieldT::one() : FieldT::zero();
|
||||
|
||||
// Witness merkle tree authentication path
|
||||
witness_input->generate_r1cs_witness(path);
|
||||
@@ -222,7 +222,7 @@ public:
|
||||
commit_to_outputs->generate_r1cs_constraints();
|
||||
}
|
||||
|
||||
void generate_r1cs_witness(const Note& note) {
|
||||
void generate_r1cs_witness(const SproutNote& note) {
|
||||
note_gadget<FieldT>::generate_r1cs_witness(note);
|
||||
|
||||
prevent_faerie_gold->generate_r1cs_witness();
|
||||
|
||||
@@ -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_
|
||||
|
||||
170
src/zcash/zip32.cpp
Normal file
170
src/zcash/zip32.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
// Copyright (c) 2018 The Zcash developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "zip32.h"
|
||||
|
||||
#include "hash.h"
|
||||
#include "random.h"
|
||||
#include "streams.h"
|
||||
#include "version.h"
|
||||
#include "zcash/prf.h"
|
||||
|
||||
#include <librustzcash.h>
|
||||
#include <sodium.h>
|
||||
|
||||
const unsigned char ZCASH_HD_SEED_FP_PERSONAL[crypto_generichash_blake2b_PERSONALBYTES] =
|
||||
{'Z', 'c', 'a', 's', 'h', '_', 'H', 'D', '_', 'S', 'e', 'e', 'd', '_', 'F', 'P'};
|
||||
|
||||
const unsigned char ZCASH_TADDR_OVK_PERSONAL[crypto_generichash_blake2b_PERSONALBYTES] =
|
||||
{'Z', 'c', 'T', 'a', 'd', 'd', 'r', 'T', 'o', 'S', 'a', 'p', 'l', 'i', 'n', 'g'};
|
||||
|
||||
HDSeed HDSeed::Random(size_t len)
|
||||
{
|
||||
assert(len >= 32);
|
||||
RawHDSeed rawSeed(len, 0);
|
||||
GetRandBytes(rawSeed.data(), len);
|
||||
return HDSeed(rawSeed);
|
||||
}
|
||||
|
||||
uint256 HDSeed::Fingerprint() const
|
||||
{
|
||||
CBLAKE2bWriter h(SER_GETHASH, 0, ZCASH_HD_SEED_FP_PERSONAL);
|
||||
h << seed;
|
||||
return h.GetHash();
|
||||
}
|
||||
|
||||
uint256 ovkForShieldingFromTaddr(HDSeed& seed) {
|
||||
auto rawSeed = seed.RawSeed();
|
||||
|
||||
// I = BLAKE2b-512("ZcTaddrToSapling", seed)
|
||||
crypto_generichash_blake2b_state state;
|
||||
assert(crypto_generichash_blake2b_init_salt_personal(
|
||||
&state,
|
||||
NULL, 0, // No key.
|
||||
64,
|
||||
NULL, // No salt.
|
||||
ZCASH_TADDR_OVK_PERSONAL) == 0);
|
||||
crypto_generichash_blake2b_update(&state, rawSeed.data(), rawSeed.size());
|
||||
auto intermediate = std::array<unsigned char, 64>();
|
||||
crypto_generichash_blake2b_final(&state, intermediate.data(), 64);
|
||||
|
||||
// I_L = I[0..32]
|
||||
uint256 intermediate_L;
|
||||
memcpy(intermediate_L.begin(), intermediate.data(), 32);
|
||||
|
||||
// ovk = truncate_32(PRF^expand(I_L, [0x02]))
|
||||
return PRF_ovk(intermediate_L);
|
||||
}
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
boost::optional<SaplingExtendedFullViewingKey> SaplingExtendedFullViewingKey::Derive(uint32_t i) const
|
||||
{
|
||||
CDataStream ss_p(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss_p << *this;
|
||||
CSerializeData p_bytes(ss_p.begin(), ss_p.end());
|
||||
|
||||
CSerializeData i_bytes(ZIP32_XFVK_SIZE);
|
||||
if (librustzcash_zip32_xfvk_derive(
|
||||
reinterpret_cast<unsigned char*>(p_bytes.data()),
|
||||
i,
|
||||
reinterpret_cast<unsigned char*>(i_bytes.data())
|
||||
)) {
|
||||
CDataStream ss_i(i_bytes, SER_NETWORK, PROTOCOL_VERSION);
|
||||
SaplingExtendedFullViewingKey xfvk_i;
|
||||
ss_i >> xfvk_i;
|
||||
return xfvk_i;
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<std::pair<diversifier_index_t, libzcash::SaplingPaymentAddress>>
|
||||
SaplingExtendedFullViewingKey::Address(diversifier_index_t j) const
|
||||
{
|
||||
CDataStream ss_xfvk(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss_xfvk << *this;
|
||||
CSerializeData xfvk_bytes(ss_xfvk.begin(), ss_xfvk.end());
|
||||
|
||||
diversifier_index_t j_ret;
|
||||
CSerializeData addr_bytes(libzcash::SerializedSaplingPaymentAddressSize);
|
||||
if (librustzcash_zip32_xfvk_address(
|
||||
reinterpret_cast<unsigned char*>(xfvk_bytes.data()),
|
||||
j.begin(), j_ret.begin(),
|
||||
reinterpret_cast<unsigned char*>(addr_bytes.data()))) {
|
||||
CDataStream ss_addr(addr_bytes, SER_NETWORK, PROTOCOL_VERSION);
|
||||
libzcash::SaplingPaymentAddress addr;
|
||||
ss_addr >> addr;
|
||||
return std::make_pair(j_ret, addr);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
|
||||
libzcash::SaplingPaymentAddress SaplingExtendedFullViewingKey::DefaultAddress() const
|
||||
{
|
||||
diversifier_index_t j0;
|
||||
auto addr = Address(j0);
|
||||
// If we can't obtain a default address, we are *very* unlucky...
|
||||
if (!addr) {
|
||||
throw std::runtime_error("SaplingExtendedFullViewingKey::DefaultAddress(): No valid diversifiers out of 2^88!");
|
||||
}
|
||||
return addr.get().second;
|
||||
}
|
||||
|
||||
SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Master(const HDSeed& seed)
|
||||
{
|
||||
auto rawSeed = seed.RawSeed();
|
||||
CSerializeData m_bytes(ZIP32_XSK_SIZE);
|
||||
librustzcash_zip32_xsk_master(
|
||||
rawSeed.data(),
|
||||
rawSeed.size(),
|
||||
reinterpret_cast<unsigned char*>(m_bytes.data()));
|
||||
|
||||
CDataStream ss(m_bytes, SER_NETWORK, PROTOCOL_VERSION);
|
||||
SaplingExtendedSpendingKey xsk_m;
|
||||
ss >> xsk_m;
|
||||
return xsk_m;
|
||||
}
|
||||
|
||||
SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Derive(uint32_t i) const
|
||||
{
|
||||
CDataStream ss_p(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss_p << *this;
|
||||
CSerializeData p_bytes(ss_p.begin(), ss_p.end());
|
||||
|
||||
CSerializeData i_bytes(ZIP32_XSK_SIZE);
|
||||
librustzcash_zip32_xsk_derive(
|
||||
reinterpret_cast<unsigned char*>(p_bytes.data()),
|
||||
i,
|
||||
reinterpret_cast<unsigned char*>(i_bytes.data()));
|
||||
|
||||
CDataStream ss_i(i_bytes, SER_NETWORK, PROTOCOL_VERSION);
|
||||
SaplingExtendedSpendingKey xsk_i;
|
||||
ss_i >> xsk_i;
|
||||
return xsk_i;
|
||||
}
|
||||
|
||||
SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const
|
||||
{
|
||||
SaplingExtendedFullViewingKey ret;
|
||||
ret.depth = depth;
|
||||
ret.parentFVKTag = parentFVKTag;
|
||||
ret.childIndex = childIndex;
|
||||
ret.chaincode = chaincode;
|
||||
ret.fvk = expsk.full_viewing_key();
|
||||
ret.dk = dk;
|
||||
return ret;
|
||||
}
|
||||
|
||||
libzcash::SaplingPaymentAddress SaplingExtendedSpendingKey::DefaultAddress() const
|
||||
{
|
||||
return ToXFVK().DefaultAddress();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool IsValidSpendingKey(const libzcash::SpendingKey& zkey) {
|
||||
return zkey.which() != 0;
|
||||
}
|
||||
144
src/zcash/zip32.h
Normal file
144
src/zcash/zip32.h
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) 2018 The Zcash developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef ZCASH_ZIP32_H
|
||||
#define ZCASH_ZIP32_H
|
||||
|
||||
#include "serialize.h"
|
||||
#include "support/allocators/secure.h"
|
||||
#include "uint256.h"
|
||||
#include "zcash/Address.hpp"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
const uint32_t ZIP32_HARDENED_KEY_LIMIT = 0x80000000;
|
||||
const size_t ZIP32_XFVK_SIZE = 169;
|
||||
const size_t ZIP32_XSK_SIZE = 169;
|
||||
|
||||
typedef std::vector<unsigned char, secure_allocator<unsigned char>> RawHDSeed;
|
||||
|
||||
class HDSeed {
|
||||
private:
|
||||
RawHDSeed seed;
|
||||
|
||||
public:
|
||||
HDSeed() {}
|
||||
HDSeed(RawHDSeed& seedIn) : seed(seedIn) {}
|
||||
|
||||
static HDSeed Random(size_t len = 32);
|
||||
bool IsNull() const { return seed.empty(); };
|
||||
uint256 Fingerprint() const;
|
||||
RawHDSeed RawSeed() const { return seed; }
|
||||
|
||||
friend bool operator==(const HDSeed& a, const HDSeed& b)
|
||||
{
|
||||
return a.seed == b.seed;
|
||||
}
|
||||
|
||||
friend bool operator!=(const HDSeed& a, const HDSeed& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
// This is not part of ZIP 32, but is here because it's linked to the HD seed.
|
||||
uint256 ovkForShieldingFromTaddr(HDSeed& seed);
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
typedef blob88 diversifier_index_t;
|
||||
|
||||
struct SaplingExtendedFullViewingKey {
|
||||
uint8_t depth;
|
||||
uint32_t parentFVKTag;
|
||||
uint32_t childIndex;
|
||||
uint256 chaincode;
|
||||
libzcash::SaplingFullViewingKey fvk;
|
||||
uint256 dk;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(depth);
|
||||
READWRITE(parentFVKTag);
|
||||
READWRITE(childIndex);
|
||||
READWRITE(chaincode);
|
||||
READWRITE(fvk);
|
||||
READWRITE(dk);
|
||||
}
|
||||
|
||||
boost::optional<SaplingExtendedFullViewingKey> Derive(uint32_t i) const;
|
||||
|
||||
// Returns the first index starting from j that generates a valid
|
||||
// payment address, along with the corresponding address. Returns
|
||||
// an error if the diversifier space is exhausted.
|
||||
boost::optional<std::pair<diversifier_index_t, libzcash::SaplingPaymentAddress>>
|
||||
Address(diversifier_index_t j) const;
|
||||
|
||||
libzcash::SaplingPaymentAddress DefaultAddress() const;
|
||||
|
||||
friend inline bool operator==(const SaplingExtendedFullViewingKey& a, const SaplingExtendedFullViewingKey& b) {
|
||||
return (
|
||||
a.depth == b.depth &&
|
||||
a.parentFVKTag == b.parentFVKTag &&
|
||||
a.childIndex == b.childIndex &&
|
||||
a.chaincode == b.chaincode &&
|
||||
a.fvk == b.fvk &&
|
||||
a.dk == b.dk);
|
||||
}
|
||||
friend inline bool operator<(const SaplingExtendedFullViewingKey& a, const SaplingExtendedFullViewingKey& b) {
|
||||
return (a.depth < b.depth ||
|
||||
(a.depth == b.depth && a.childIndex < b.childIndex) ||
|
||||
(a.depth == b.depth && a.childIndex == b.childIndex && a.fvk < b.fvk));
|
||||
}
|
||||
};
|
||||
|
||||
struct SaplingExtendedSpendingKey {
|
||||
uint8_t depth;
|
||||
uint32_t parentFVKTag;
|
||||
uint32_t childIndex;
|
||||
uint256 chaincode;
|
||||
libzcash::SaplingExpandedSpendingKey expsk;
|
||||
uint256 dk;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(depth);
|
||||
READWRITE(parentFVKTag);
|
||||
READWRITE(childIndex);
|
||||
READWRITE(chaincode);
|
||||
READWRITE(expsk);
|
||||
READWRITE(dk);
|
||||
}
|
||||
|
||||
static SaplingExtendedSpendingKey Master(const HDSeed& seed);
|
||||
|
||||
SaplingExtendedSpendingKey Derive(uint32_t i) const;
|
||||
|
||||
SaplingExtendedFullViewingKey ToXFVK() const;
|
||||
|
||||
libzcash::SaplingPaymentAddress DefaultAddress() const;
|
||||
|
||||
friend bool operator==(const SaplingExtendedSpendingKey& a, const SaplingExtendedSpendingKey& b)
|
||||
{
|
||||
return a.depth == b.depth &&
|
||||
a.parentFVKTag == b.parentFVKTag &&
|
||||
a.childIndex == b.childIndex &&
|
||||
a.chaincode == b.chaincode &&
|
||||
a.expsk == b.expsk &&
|
||||
a.dk == b.dk;
|
||||
}
|
||||
};
|
||||
|
||||
typedef boost::variant<InvalidEncoding, SproutSpendingKey, SaplingExtendedSpendingKey> SpendingKey;
|
||||
|
||||
}
|
||||
|
||||
/** Check whether a SpendingKey is not an InvalidEncoding. */
|
||||
bool IsValidSpendingKey(const libzcash::SpendingKey& zkey);
|
||||
|
||||
#endif // ZCASH_ZIP32_H
|
||||
Reference in New Issue
Block a user