Introduce new `libzcash` Zcash protocol API and crypto constructions surrounding the zkSNARK circuit.
This commit is contained in:
parent
5961dcb6da
commit
369df06583
|
@ -16,6 +16,9 @@ src/zerocash/tests/utilTest
|
||||||
src/zerocash/tests/zerocashTest
|
src/zerocash/tests/zerocashTest
|
||||||
src/zerocash/tests/test_zerocash_pour_ppzksnark
|
src/zerocash/tests/test_zerocash_pour_ppzksnark
|
||||||
|
|
||||||
|
*zcashTest.pk
|
||||||
|
*zcashTest.vk
|
||||||
|
|
||||||
# autoreconf
|
# autoreconf
|
||||||
Makefile.in
|
Makefile.in
|
||||||
aclocal.m4
|
aclocal.m4
|
||||||
|
|
|
@ -86,6 +86,9 @@ LIBZEROCASH_H = \
|
||||||
zerocash/zerocash_pour_params.hpp \
|
zerocash/zerocash_pour_params.hpp \
|
||||||
zerocash/utils/util.h \
|
zerocash/utils/util.h \
|
||||||
zcash/NoteEncryption.hpp \
|
zcash/NoteEncryption.hpp \
|
||||||
|
zcash/Address.hpp \
|
||||||
|
zcash/JoinSplit.hpp \
|
||||||
|
zcash/Note.hpp \
|
||||||
zcash/prf.h
|
zcash/prf.h
|
||||||
|
|
||||||
.PHONY: FORCE
|
.PHONY: FORCE
|
||||||
|
@ -427,6 +430,9 @@ libzerocash_a_SOURCES = \
|
||||||
zerocash/ZerocashParams.cpp \
|
zerocash/ZerocashParams.cpp \
|
||||||
zerocash/utils/util.cpp \
|
zerocash/utils/util.cpp \
|
||||||
zcash/NoteEncryption.cpp \
|
zcash/NoteEncryption.cpp \
|
||||||
|
zcash/Address.cpp \
|
||||||
|
zcash/JoinSplit.cpp \
|
||||||
|
zcash/Note.cpp \
|
||||||
zcash/prf.cpp
|
zcash/prf.cpp
|
||||||
|
|
||||||
libzerocash_a_CPPFLAGS = -fPIC -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -std=c++11 -pipe -O2 -O0 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES)
|
libzerocash_a_CPPFLAGS = -fPIC -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -std=c++11 -pipe -O2 -O0 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES)
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include "Address.hpp"
|
||||||
|
#include "NoteEncryption.hpp"
|
||||||
|
#include "prf.h"
|
||||||
|
|
||||||
|
namespace libzcash {
|
||||||
|
|
||||||
|
uint256 ViewingKey::pk_enc() {
|
||||||
|
return ZCNoteEncryption::generate_pubkey(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewingKey SpendingKey::viewing_key() {
|
||||||
|
return ViewingKey(ZCNoteEncryption::generate_privkey(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
SpendingKey SpendingKey::random() {
|
||||||
|
return SpendingKey(random_uint256());
|
||||||
|
}
|
||||||
|
|
||||||
|
PaymentAddress SpendingKey::address() {
|
||||||
|
return PaymentAddress(PRF_addr_a_pk(*this), viewing_key().pk_enc());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef _ZCADDRESS_H_
|
||||||
|
#define _ZCADDRESS_H_
|
||||||
|
|
||||||
|
#include "uint256.h"
|
||||||
|
#include "serialize.h"
|
||||||
|
|
||||||
|
namespace libzcash {
|
||||||
|
|
||||||
|
class PaymentAddress {
|
||||||
|
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) { }
|
||||||
|
|
||||||
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
template <typename Stream, typename Operation>
|
||||||
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||||
|
unsigned char leadingByte = 0x92;
|
||||||
|
READWRITE(leadingByte);
|
||||||
|
|
||||||
|
if (leadingByte != 0x92) {
|
||||||
|
throw std::ios_base::failure("unrecognized payment address lead byte");
|
||||||
|
}
|
||||||
|
|
||||||
|
READWRITE(a_pk);
|
||||||
|
READWRITE(pk_enc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ViewingKey : public uint256 {
|
||||||
|
public:
|
||||||
|
ViewingKey(uint256 sk_enc) : uint256(sk_enc) { }
|
||||||
|
|
||||||
|
uint256 pk_enc();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SpendingKey : public uint256 {
|
||||||
|
public:
|
||||||
|
SpendingKey() : uint256() { }
|
||||||
|
SpendingKey(uint256 a_sk) : uint256(a_sk) { }
|
||||||
|
|
||||||
|
static SpendingKey random();
|
||||||
|
|
||||||
|
ViewingKey viewing_key();
|
||||||
|
PaymentAddress address();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _ZCADDRESS_H_
|
|
@ -0,0 +1,339 @@
|
||||||
|
#include "JoinSplit.hpp"
|
||||||
|
#include "prf.h"
|
||||||
|
#include "sodium.h"
|
||||||
|
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
|
||||||
|
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
|
||||||
|
#include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp"
|
||||||
|
#include "libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp"
|
||||||
|
|
||||||
|
#include "sync.h"
|
||||||
|
|
||||||
|
using namespace libsnark;
|
||||||
|
|
||||||
|
namespace libzcash {
|
||||||
|
|
||||||
|
#include "zcash/circuit/gadget.tcc"
|
||||||
|
|
||||||
|
CCriticalSection cs_ParamsIO;
|
||||||
|
CCriticalSection cs_InitializeParams;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void saveToFile(std::string path, T& obj) {
|
||||||
|
LOCK(cs_ParamsIO);
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << obj;
|
||||||
|
std::ofstream fh;
|
||||||
|
fh.open(path, std::ios::binary);
|
||||||
|
ss.rdbuf()->pubseekpos(0, std::ios_base::out);
|
||||||
|
fh << ss.rdbuf();
|
||||||
|
fh.flush();
|
||||||
|
fh.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void loadFromFile(std::string path, boost::optional<T>& objIn) {
|
||||||
|
LOCK(cs_ParamsIO);
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
std::ifstream fh(path, std::ios::binary);
|
||||||
|
|
||||||
|
if(!fh.is_open()) {
|
||||||
|
throw std::runtime_error((boost::format("could not load param file at %s") % path).str());
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << fh.rdbuf();
|
||||||
|
fh.close();
|
||||||
|
|
||||||
|
ss.rdbuf()->pubseekpos(0, std::ios_base::in);
|
||||||
|
|
||||||
|
T obj;
|
||||||
|
ss >> obj;
|
||||||
|
|
||||||
|
objIn = std::move(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t NumInputs, size_t NumOutputs>
|
||||||
|
class JoinSplitCircuit : public JoinSplit<NumInputs, NumOutputs> {
|
||||||
|
public:
|
||||||
|
typedef default_r1cs_ppzksnark_pp ppzksnark_ppT;
|
||||||
|
typedef Fr<ppzksnark_ppT> FieldT;
|
||||||
|
|
||||||
|
boost::optional<r1cs_ppzksnark_proving_key<ppzksnark_ppT>> pk;
|
||||||
|
boost::optional<r1cs_ppzksnark_verification_key<ppzksnark_ppT>> vk;
|
||||||
|
boost::optional<std::string> pkPath;
|
||||||
|
|
||||||
|
static void initialize() {
|
||||||
|
LOCK(cs_InitializeParams);
|
||||||
|
|
||||||
|
ppzksnark_ppT::init_public_params();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setProvingKeyPath(std::string path) {
|
||||||
|
pkPath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadProvingKey() {
|
||||||
|
if (!pk) {
|
||||||
|
if (!pkPath) {
|
||||||
|
throw std::runtime_error("proving key path unknown");
|
||||||
|
}
|
||||||
|
loadFromFile(*pkPath, pk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveProvingKey(std::string path) {
|
||||||
|
if (pk) {
|
||||||
|
saveToFile(path, *pk);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("cannot save proving key; key doesn't exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void loadVerifyingKey(std::string path) {
|
||||||
|
loadFromFile(path, vk);
|
||||||
|
}
|
||||||
|
void saveVerifyingKey(std::string path) {
|
||||||
|
if (vk) {
|
||||||
|
saveToFile(path, *vk);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("cannot save verifying key; key doesn't exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate() {
|
||||||
|
protoboard<FieldT> pb;
|
||||||
|
|
||||||
|
joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
|
||||||
|
g.generate_r1cs_constraints();
|
||||||
|
|
||||||
|
const r1cs_constraint_system<FieldT> constraint_system = pb.get_constraint_system();
|
||||||
|
r1cs_ppzksnark_keypair<ppzksnark_ppT> keypair = r1cs_ppzksnark_generator<ppzksnark_ppT>(constraint_system);
|
||||||
|
|
||||||
|
pk = keypair.pk;
|
||||||
|
vk = keypair.vk;
|
||||||
|
}
|
||||||
|
|
||||||
|
JoinSplitCircuit() {}
|
||||||
|
|
||||||
|
bool verify(
|
||||||
|
const std::string& proof,
|
||||||
|
const uint256& pubKeyHash,
|
||||||
|
const uint256& randomSeed,
|
||||||
|
const boost::array<uint256, NumInputs>& hmacs,
|
||||||
|
const boost::array<uint256, NumInputs>& nullifiers,
|
||||||
|
const boost::array<uint256, NumOutputs>& commitments,
|
||||||
|
uint64_t vpub_old,
|
||||||
|
uint64_t vpub_new,
|
||||||
|
const uint256& rt
|
||||||
|
) {
|
||||||
|
if (!vk) {
|
||||||
|
throw std::runtime_error("JoinSplit verifying key not loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
r1cs_ppzksnark_proof<ppzksnark_ppT> r1cs_proof;
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.str(proof);
|
||||||
|
ss >> r1cs_proof;
|
||||||
|
|
||||||
|
uint256 h_sig = this->h_sig(randomSeed, nullifiers, pubKeyHash);
|
||||||
|
|
||||||
|
auto witness = joinsplit_gadget<FieldT, NumInputs, NumOutputs>::witness_map(
|
||||||
|
rt,
|
||||||
|
h_sig,
|
||||||
|
hmacs,
|
||||||
|
nullifiers,
|
||||||
|
commitments,
|
||||||
|
vpub_old,
|
||||||
|
vpub_new
|
||||||
|
);
|
||||||
|
|
||||||
|
return r1cs_ppzksnark_verifier_strong_IC<ppzksnark_ppT>(*vk, witness, r1cs_proof);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string 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,
|
||||||
|
uint256& out_ephemeralKey,
|
||||||
|
const uint256& pubKeyHash,
|
||||||
|
uint256& out_randomSeed,
|
||||||
|
boost::array<uint256, NumInputs>& out_macs,
|
||||||
|
boost::array<uint256, NumInputs>& out_nullifiers,
|
||||||
|
boost::array<uint256, NumOutputs>& out_commitments,
|
||||||
|
uint64_t vpub_old,
|
||||||
|
uint64_t vpub_new,
|
||||||
|
const uint256& rt
|
||||||
|
) {
|
||||||
|
if (!pk) {
|
||||||
|
throw std::runtime_error("JoinSplit proving key not loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute nullifiers of inputs
|
||||||
|
for (size_t i = 0; i < NumInputs; i++) {
|
||||||
|
out_nullifiers[i] = inputs[i].nullifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample randomSeed
|
||||||
|
out_randomSeed = random_uint256();
|
||||||
|
|
||||||
|
// Compute h_sig
|
||||||
|
uint256 h_sig = this->h_sig(out_randomSeed, out_nullifiers, pubKeyHash);
|
||||||
|
|
||||||
|
// Sample phi
|
||||||
|
uint256 phi = random_uint256();
|
||||||
|
|
||||||
|
// Compute notes for outputs
|
||||||
|
for (size_t i = 0; i < NumOutputs; i++) {
|
||||||
|
// Sample r
|
||||||
|
uint256 r = random_uint256();
|
||||||
|
|
||||||
|
out_notes[i] = outputs[i].note(phi, r, i, h_sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the output commitments
|
||||||
|
for (size_t i = 0; i < NumOutputs; i++) {
|
||||||
|
out_commitments[i] = out_notes[i].cm();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt the ciphertexts containing the note
|
||||||
|
// plaintexts to the recipients of the value.
|
||||||
|
{
|
||||||
|
ZCNoteEncryption encryptor(h_sig);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NumOutputs; i++) {
|
||||||
|
// TODO: expose memo in the public interface
|
||||||
|
// 0xF6 is invalid UTF8 as per spec
|
||||||
|
boost::array<unsigned char, ZC_MEMO_SIZE> memo = {{0xF6}};
|
||||||
|
|
||||||
|
NotePlaintext pt(out_notes[i], memo);
|
||||||
|
|
||||||
|
out_ciphertexts[i] = pt.encrypt(encryptor, outputs[i].addr.pk_enc);
|
||||||
|
}
|
||||||
|
|
||||||
|
out_ephemeralKey = encryptor.get_epk();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate h_sig with each of the input
|
||||||
|
// spending keys, producing macs which protect
|
||||||
|
// against malleability.
|
||||||
|
for (size_t i = 0; i < NumInputs; i++) {
|
||||||
|
out_macs[i] = PRF_pk(inputs[i].key, i, h_sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<FieldT> primary_input;
|
||||||
|
std::vector<FieldT> aux_input;
|
||||||
|
|
||||||
|
{
|
||||||
|
protoboard<FieldT> pb;
|
||||||
|
{
|
||||||
|
joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
|
||||||
|
g.generate_r1cs_constraints();
|
||||||
|
g.generate_r1cs_witness(
|
||||||
|
phi,
|
||||||
|
rt,
|
||||||
|
h_sig,
|
||||||
|
inputs,
|
||||||
|
out_notes,
|
||||||
|
vpub_old,
|
||||||
|
vpub_new
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pb.is_satisfied()) {
|
||||||
|
throw std::invalid_argument("Constraint system not satisfied by inputs");
|
||||||
|
}
|
||||||
|
|
||||||
|
primary_input = pb.primary_input();
|
||||||
|
aux_input = pb.auxiliary_input();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto proof = r1cs_ppzksnark_prover<ppzksnark_ppT>(
|
||||||
|
*pk,
|
||||||
|
primary_input,
|
||||||
|
aux_input
|
||||||
|
);
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << proof;
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<size_t NumInputs, size_t NumOutputs>
|
||||||
|
JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Generate()
|
||||||
|
{
|
||||||
|
JoinSplitCircuit<NumInputs, NumOutputs>::initialize();
|
||||||
|
auto js = new JoinSplitCircuit<NumInputs, NumOutputs>();
|
||||||
|
js->generate();
|
||||||
|
|
||||||
|
return js;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t NumInputs, size_t NumOutputs>
|
||||||
|
JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Unopened()
|
||||||
|
{
|
||||||
|
JoinSplitCircuit<NumInputs, NumOutputs>::initialize();
|
||||||
|
return new JoinSplitCircuit<NumInputs, NumOutputs>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
) {
|
||||||
|
unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES]
|
||||||
|
= {'Z','c','a','s','h','C','o','m','p','u','t','e','h','S','i','g'};
|
||||||
|
|
||||||
|
std::vector<unsigned char> block(randomSeed.begin(), randomSeed.end());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NumInputs; i++) {
|
||||||
|
block.insert(block.end(), nullifiers[i].begin(), nullifiers[i].end());
|
||||||
|
}
|
||||||
|
|
||||||
|
block.insert(block.end(), pubKeyHash.begin(), pubKeyHash.end());
|
||||||
|
|
||||||
|
uint256 output;
|
||||||
|
|
||||||
|
if (crypto_generichash_blake2b_salt_personal(output.begin(), 32,
|
||||||
|
&block[0], block.size(),
|
||||||
|
NULL, 0, // No key.
|
||||||
|
NULL, // No salt.
|
||||||
|
personalization
|
||||||
|
) != 0)
|
||||||
|
{
|
||||||
|
throw std::logic_error("hash function failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
Note JSOutput::note(const uint256& 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSOutput::JSOutput() : addr(uint256(), uint256()), value(0) {
|
||||||
|
SpendingKey a_sk(random_uint256());
|
||||||
|
addr = a_sk.address();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSInput::JSInput() : witness(ZCIncrementalMerkleTree().witness()),
|
||||||
|
key(random_uint256()) {
|
||||||
|
note = Note(key.address().a_pk, 0, random_uint256(), random_uint256());
|
||||||
|
ZCIncrementalMerkleTree dummy_tree;
|
||||||
|
dummy_tree.append(note.cm());
|
||||||
|
witness = dummy_tree.witness();
|
||||||
|
}
|
||||||
|
|
||||||
|
template class JoinSplit<ZC_NUM_JS_INPUTS,
|
||||||
|
ZC_NUM_JS_OUTPUTS>;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
#ifndef _ZCJOINSPLIT_H_
|
||||||
|
#define _ZCJOINSPLIT_H_
|
||||||
|
|
||||||
|
#include "Zcash.h"
|
||||||
|
#include "Address.hpp"
|
||||||
|
#include "Note.hpp"
|
||||||
|
#include "IncrementalMerkleTree.hpp"
|
||||||
|
#include "NoteEncryption.hpp"
|
||||||
|
|
||||||
|
#include "uint256.h"
|
||||||
|
|
||||||
|
#include <boost/array.hpp>
|
||||||
|
|
||||||
|
namespace libzcash {
|
||||||
|
|
||||||
|
class JSInput {
|
||||||
|
public:
|
||||||
|
ZCIncrementalWitness witness;
|
||||||
|
Note note;
|
||||||
|
SpendingKey key;
|
||||||
|
|
||||||
|
JSInput();
|
||||||
|
JSInput(ZCIncrementalWitness witness,
|
||||||
|
Note note,
|
||||||
|
SpendingKey key) : witness(witness), note(note), key(key) { }
|
||||||
|
|
||||||
|
uint256 nullifier() const {
|
||||||
|
return note.nullifier(key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JSOutput {
|
||||||
|
public:
|
||||||
|
PaymentAddress addr;
|
||||||
|
uint64_t value;
|
||||||
|
|
||||||
|
JSOutput();
|
||||||
|
JSOutput(PaymentAddress addr, uint64_t value) : addr(addr), value(value) { }
|
||||||
|
|
||||||
|
Note note(const uint256& phi, const uint256& r, size_t i, const uint256& h_sig) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<size_t NumInputs, size_t NumOutputs>
|
||||||
|
class JoinSplit {
|
||||||
|
public:
|
||||||
|
static JoinSplit<NumInputs, NumOutputs>* Generate();
|
||||||
|
static JoinSplit<NumInputs, NumOutputs>* Unopened();
|
||||||
|
static uint256 h_sig(const uint256& randomSeed,
|
||||||
|
const boost::array<uint256, NumInputs>& nullifiers,
|
||||||
|
const uint256& pubKeyHash
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: #789
|
||||||
|
virtual void setProvingKeyPath(std::string) = 0;
|
||||||
|
virtual void loadProvingKey() = 0;
|
||||||
|
|
||||||
|
virtual void saveProvingKey(std::string path) = 0;
|
||||||
|
virtual void loadVerifyingKey(std::string path) = 0;
|
||||||
|
virtual void saveVerifyingKey(std::string path) = 0;
|
||||||
|
|
||||||
|
virtual std::string 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,
|
||||||
|
uint256& out_ephemeralKey,
|
||||||
|
const uint256& pubKeyHash,
|
||||||
|
uint256& out_randomSeed,
|
||||||
|
boost::array<uint256, NumInputs>& out_hmacs,
|
||||||
|
boost::array<uint256, NumInputs>& out_nullifiers,
|
||||||
|
boost::array<uint256, NumOutputs>& out_commitments,
|
||||||
|
uint64_t vpub_old,
|
||||||
|
uint64_t vpub_new,
|
||||||
|
const uint256& rt
|
||||||
|
) = 0;
|
||||||
|
|
||||||
|
virtual bool verify(
|
||||||
|
const std::string& proof,
|
||||||
|
const uint256& pubKeyHash,
|
||||||
|
const uint256& randomSeed,
|
||||||
|
const boost::array<uint256, NumInputs>& hmacs,
|
||||||
|
const boost::array<uint256, NumInputs>& nullifiers,
|
||||||
|
const boost::array<uint256, NumOutputs>& commitments,
|
||||||
|
uint64_t vpub_old,
|
||||||
|
uint64_t vpub_new,
|
||||||
|
const uint256& rt
|
||||||
|
) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
JoinSplit() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef libzcash::JoinSplit<ZC_NUM_JS_INPUTS,
|
||||||
|
ZC_NUM_JS_OUTPUTS> ZCJoinSplit;
|
||||||
|
|
||||||
|
#endif // _ZCJOINSPLIT_H_
|
|
@ -0,0 +1,92 @@
|
||||||
|
#include "Note.hpp"
|
||||||
|
#include "prf.h"
|
||||||
|
#include "crypto/sha256.h"
|
||||||
|
#include "zerocash/utils/util.h"
|
||||||
|
|
||||||
|
#include "version.h"
|
||||||
|
#include "streams.h"
|
||||||
|
|
||||||
|
namespace libzcash {
|
||||||
|
|
||||||
|
Note::Note() {
|
||||||
|
a_pk = random_uint256();
|
||||||
|
rho = random_uint256();
|
||||||
|
r = random_uint256();
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 Note::cm() const {
|
||||||
|
unsigned char discriminant = 0xb0;
|
||||||
|
|
||||||
|
CSHA256 hasher;
|
||||||
|
hasher.Write(&discriminant, 1);
|
||||||
|
hasher.Write(a_pk.begin(), 32);
|
||||||
|
|
||||||
|
std::vector<unsigned char> value_vec(sizeof(value), 0);
|
||||||
|
libzerocash::convertIntToBytesVector(value, value_vec);
|
||||||
|
|
||||||
|
hasher.Write(&value_vec[0], value_vec.size());
|
||||||
|
hasher.Write(rho.begin(), 32);
|
||||||
|
hasher.Write(r.begin(), 32);
|
||||||
|
|
||||||
|
uint256 result;
|
||||||
|
hasher.Finalize(result.begin());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 Note::nullifier(const SpendingKey& a_sk) const {
|
||||||
|
return PRF_nf(a_sk, rho);
|
||||||
|
}
|
||||||
|
|
||||||
|
NotePlaintext::NotePlaintext(
|
||||||
|
const Note& note,
|
||||||
|
boost::array<unsigned char, ZCASH_MEMO_SIZE> memo) : memo(memo)
|
||||||
|
{
|
||||||
|
value = note.value;
|
||||||
|
rho = note.rho;
|
||||||
|
r = note.r;
|
||||||
|
}
|
||||||
|
|
||||||
|
Note NotePlaintext::note(const PaymentAddress& addr) const
|
||||||
|
{
|
||||||
|
return Note(addr.a_pk, value, rho, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
NotePlaintext NotePlaintext::decrypt(const ZCNoteDecryption& decryptor,
|
||||||
|
const ZCNoteDecryption::Ciphertext& ciphertext,
|
||||||
|
const uint256& ephemeralKey,
|
||||||
|
const uint256& h_sig,
|
||||||
|
unsigned char nonce
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto plaintext = decryptor.decrypt(ciphertext, ephemeralKey, h_sig, nonce);
|
||||||
|
|
||||||
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
ss << plaintext;
|
||||||
|
|
||||||
|
NotePlaintext ret;
|
||||||
|
ss >> ret;
|
||||||
|
|
||||||
|
assert(ss.size() == 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZCNoteEncryption::Ciphertext NotePlaintext::encrypt(ZCNoteEncryption& encryptor,
|
||||||
|
const uint256& pk_enc
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
ss << (*this);
|
||||||
|
|
||||||
|
ZCNoteEncryption::Plaintext pt;
|
||||||
|
|
||||||
|
assert(pt.size() == ss.size());
|
||||||
|
|
||||||
|
memcpy(&pt[0], &ss[0], pt.size());
|
||||||
|
|
||||||
|
return encryptor.encrypt(pk_enc, pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
#ifndef _ZCNOTE_H_
|
||||||
|
#define _ZCNOTE_H_
|
||||||
|
|
||||||
|
#include "uint256.h"
|
||||||
|
#include "Zcash.h"
|
||||||
|
#include "Address.hpp"
|
||||||
|
#include "NoteEncryption.hpp"
|
||||||
|
|
||||||
|
namespace libzcash {
|
||||||
|
|
||||||
|
class Note {
|
||||||
|
public:
|
||||||
|
uint256 a_pk;
|
||||||
|
uint64_t value;
|
||||||
|
uint256 rho;
|
||||||
|
uint256 r;
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NotePlaintext {
|
||||||
|
public:
|
||||||
|
uint64_t value;
|
||||||
|
uint256 rho;
|
||||||
|
uint256 r;
|
||||||
|
boost::array<unsigned char, ZC_MEMO_SIZE> memo;
|
||||||
|
|
||||||
|
NotePlaintext() {}
|
||||||
|
|
||||||
|
NotePlaintext(const Note& note, boost::array<unsigned char, ZC_MEMO_SIZE> memo);
|
||||||
|
|
||||||
|
Note note(const PaymentAddress& addr) const;
|
||||||
|
|
||||||
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
template <typename Stream, typename Operation>
|
||||||
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||||
|
unsigned char leadingByte = 0x00;
|
||||||
|
READWRITE(leadingByte);
|
||||||
|
|
||||||
|
if (leadingByte != 0x00) {
|
||||||
|
throw std::ios_base::failure("lead byte of NotePlaintext is not recognized");
|
||||||
|
}
|
||||||
|
|
||||||
|
READWRITE(value);
|
||||||
|
READWRITE(rho);
|
||||||
|
READWRITE(r);
|
||||||
|
READWRITE(memo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NotePlaintext decrypt(const ZCNoteDecryption& decryptor,
|
||||||
|
const ZCNoteDecryption::Ciphertext& ciphertext,
|
||||||
|
const uint256& ephemeralKey,
|
||||||
|
const uint256& h_sig,
|
||||||
|
unsigned char nonce
|
||||||
|
);
|
||||||
|
|
||||||
|
ZCNoteEncryption::Ciphertext encrypt(ZCNoteEncryption& encryptor,
|
||||||
|
const uint256& pk_enc
|
||||||
|
) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _ZCNOTE_H_
|
|
@ -0,0 +1,44 @@
|
||||||
|
template<typename FieldT, size_t NumInputs, size_t NumOutputs>
|
||||||
|
class joinsplit_gadget : gadget<FieldT> {
|
||||||
|
public:
|
||||||
|
joinsplit_gadget(protoboard<FieldT> &pb) : gadget<FieldT>(pb) {
|
||||||
|
pb_variable_array<FieldT> test;
|
||||||
|
test.allocate(pb, 1);
|
||||||
|
pb.set_input_sizes(1);
|
||||||
|
|
||||||
|
// TODO!
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_r1cs_constraints() {
|
||||||
|
// TODO!
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_r1cs_witness(
|
||||||
|
const uint256& phi,
|
||||||
|
const uint256& rt,
|
||||||
|
const uint256& h_sig,
|
||||||
|
const boost::array<JSInput, NumInputs>& inputs,
|
||||||
|
const boost::array<Note, NumOutputs>& outputs,
|
||||||
|
uint64_t vpub_old,
|
||||||
|
uint64_t vpub_new
|
||||||
|
) {
|
||||||
|
// TODO!
|
||||||
|
}
|
||||||
|
|
||||||
|
static r1cs_primary_input<FieldT> witness_map(
|
||||||
|
const uint256& rt,
|
||||||
|
const uint256& h_sig,
|
||||||
|
const boost::array<uint256, NumInputs>& hmacs,
|
||||||
|
const boost::array<uint256, NumInputs>& nullifiers,
|
||||||
|
const boost::array<uint256, NumOutputs>& commitments,
|
||||||
|
uint64_t vpub_old,
|
||||||
|
uint64_t vpub_new
|
||||||
|
) {
|
||||||
|
// todo
|
||||||
|
|
||||||
|
std::vector<FieldT> input_as_field_elements;
|
||||||
|
input_as_field_elements.push_back(FieldT::zero());
|
||||||
|
|
||||||
|
return input_as_field_elements;
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue