zkSNARK: Enforce disclosure of commitments to output notes.
This commit is contained in:
parent
6b010d9bfd
commit
5e61a78fec
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
template<typename FieldT>
|
||||||
|
class note_commitment_gadget : gadget<FieldT> {
|
||||||
|
private:
|
||||||
|
std::shared_ptr<block_variable<FieldT>> block1;
|
||||||
|
std::shared_ptr<block_variable<FieldT>> block2;
|
||||||
|
std::shared_ptr<sha256_compression_function_gadget<FieldT>> hasher1;
|
||||||
|
std::shared_ptr<digest_variable<FieldT>> intermediate_hash;
|
||||||
|
std::shared_ptr<sha256_compression_function_gadget<FieldT>> hasher2;
|
||||||
|
|
||||||
|
public:
|
||||||
|
note_commitment_gadget(
|
||||||
|
protoboard<FieldT> &pb,
|
||||||
|
pb_variable<FieldT>& ZERO,
|
||||||
|
pb_variable_array<FieldT>& a_pk,
|
||||||
|
pb_variable_array<FieldT>& v,
|
||||||
|
pb_variable_array<FieldT>& rho,
|
||||||
|
pb_variable_array<FieldT>& r,
|
||||||
|
std::shared_ptr<digest_variable<FieldT>> result
|
||||||
|
) : gadget<FieldT>(pb) {
|
||||||
|
pb_variable_array<FieldT> leading_byte =
|
||||||
|
from_bits({1, 0, 1, 1, 0, 0, 0, 0}, ZERO);
|
||||||
|
|
||||||
|
pb_variable_array<FieldT> first_of_rho(rho.begin(), rho.begin()+184);
|
||||||
|
pb_variable_array<FieldT> last_of_rho(rho.begin()+184, rho.end());
|
||||||
|
|
||||||
|
intermediate_hash.reset(new digest_variable<FieldT>(pb, 256, ""));
|
||||||
|
|
||||||
|
// final padding
|
||||||
|
pb_variable_array<FieldT> length_padding =
|
||||||
|
from_bits({
|
||||||
|
// padding
|
||||||
|
1,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
|
||||||
|
// length of message (840 bits)
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,1,1,
|
||||||
|
0,1,0,0,1,0,0,0
|
||||||
|
}, ZERO);
|
||||||
|
|
||||||
|
block1.reset(new block_variable<FieldT>(pb, {
|
||||||
|
leading_byte,
|
||||||
|
a_pk,
|
||||||
|
v,
|
||||||
|
first_of_rho
|
||||||
|
}, ""));
|
||||||
|
|
||||||
|
block2.reset(new block_variable<FieldT>(pb, {
|
||||||
|
last_of_rho,
|
||||||
|
r,
|
||||||
|
length_padding
|
||||||
|
}, ""));
|
||||||
|
|
||||||
|
pb_linear_combination_array<FieldT> IV = SHA256_default_IV(pb);
|
||||||
|
|
||||||
|
hasher1.reset(new sha256_compression_function_gadget<FieldT>(
|
||||||
|
pb,
|
||||||
|
IV,
|
||||||
|
block1->bits,
|
||||||
|
*intermediate_hash,
|
||||||
|
""));
|
||||||
|
|
||||||
|
pb_linear_combination_array<FieldT> IV2(intermediate_hash->bits);
|
||||||
|
|
||||||
|
hasher2.reset(new sha256_compression_function_gadget<FieldT>(
|
||||||
|
pb,
|
||||||
|
IV2,
|
||||||
|
block2->bits,
|
||||||
|
*result,
|
||||||
|
""));
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_r1cs_constraints() {
|
||||||
|
// TODO: This may not be necessary if SHA256 constrains
|
||||||
|
// its output digests to be boolean anyway.
|
||||||
|
intermediate_hash->generate_r1cs_constraints();
|
||||||
|
|
||||||
|
hasher1->generate_r1cs_constraints();
|
||||||
|
hasher2->generate_r1cs_constraints();
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_r1cs_witness() {
|
||||||
|
hasher1->generate_r1cs_witness();
|
||||||
|
hasher2->generate_r1cs_witness();
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,5 +1,6 @@
|
||||||
#include "zcash/circuit/utils.tcc"
|
#include "zcash/circuit/utils.tcc"
|
||||||
#include "zcash/circuit/prfs.tcc"
|
#include "zcash/circuit/prfs.tcc"
|
||||||
|
#include "zcash/circuit/commitment.tcc"
|
||||||
#include "zcash/circuit/note.tcc"
|
#include "zcash/circuit/note.tcc"
|
||||||
|
|
||||||
template<typename FieldT, size_t NumInputs, size_t NumOutputs>
|
template<typename FieldT, size_t NumInputs, size_t NumOutputs>
|
||||||
|
@ -114,7 +115,8 @@ public:
|
||||||
ZERO,
|
ZERO,
|
||||||
zk_phi->bits,
|
zk_phi->bits,
|
||||||
zk_h_sig->bits,
|
zk_h_sig->bits,
|
||||||
i ? true : false
|
i ? true : false,
|
||||||
|
zk_output_commitments[i]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,7 +208,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < NumOutputs; i++) {
|
for (size_t i = 0; i < NumOutputs; i++) {
|
||||||
insert_uint256(verify_inputs, uint256()); // TODO: commitment
|
insert_uint256(verify_inputs, commitments[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
insert_uint64(verify_inputs, 0); // TODO: vpub_old
|
insert_uint64(verify_inputs, 0); // TODO: vpub_old
|
||||||
|
|
|
@ -110,8 +110,10 @@ template<typename FieldT>
|
||||||
class output_note_gadget : public note_gadget<FieldT> {
|
class output_note_gadget : public note_gadget<FieldT> {
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<digest_variable<FieldT>> rho;
|
std::shared_ptr<digest_variable<FieldT>> rho;
|
||||||
|
std::shared_ptr<digest_variable<FieldT>> a_pk;
|
||||||
|
|
||||||
std::shared_ptr<PRF_rho_gadget<FieldT>> prevent_faerie_gold;
|
std::shared_ptr<PRF_rho_gadget<FieldT>> prevent_faerie_gold;
|
||||||
|
std::shared_ptr<note_commitment_gadget<FieldT>> commit_to_outputs;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
output_note_gadget(
|
output_note_gadget(
|
||||||
|
@ -119,9 +121,11 @@ public:
|
||||||
pb_variable<FieldT>& ZERO,
|
pb_variable<FieldT>& ZERO,
|
||||||
pb_variable_array<FieldT>& phi,
|
pb_variable_array<FieldT>& phi,
|
||||||
pb_variable_array<FieldT>& h_sig,
|
pb_variable_array<FieldT>& h_sig,
|
||||||
bool nonce
|
bool nonce,
|
||||||
|
std::shared_ptr<digest_variable<FieldT>> commitment
|
||||||
) : note_gadget<FieldT>(pb) {
|
) : note_gadget<FieldT>(pb) {
|
||||||
rho.reset(new digest_variable<FieldT>(pb, 256, ""));
|
rho.reset(new digest_variable<FieldT>(pb, 256, ""));
|
||||||
|
a_pk.reset(new digest_variable<FieldT>(pb, 256, ""));
|
||||||
|
|
||||||
// Do not allow the caller to choose the same "rho"
|
// Do not allow the caller to choose the same "rho"
|
||||||
// for any two valid notes in a given view of the
|
// for any two valid notes in a given view of the
|
||||||
|
@ -135,16 +139,32 @@ public:
|
||||||
nonce,
|
nonce,
|
||||||
rho
|
rho
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// Commit to the output notes publicly without
|
||||||
|
// disclosing them.
|
||||||
|
commit_to_outputs.reset(new note_commitment_gadget<FieldT>(
|
||||||
|
pb,
|
||||||
|
ZERO,
|
||||||
|
a_pk->bits,
|
||||||
|
this->value,
|
||||||
|
rho->bits,
|
||||||
|
this->r->bits,
|
||||||
|
commitment
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate_r1cs_constraints() {
|
void generate_r1cs_constraints() {
|
||||||
note_gadget<FieldT>::generate_r1cs_constraints();
|
note_gadget<FieldT>::generate_r1cs_constraints();
|
||||||
|
|
||||||
|
a_pk->generate_r1cs_constraints();
|
||||||
|
|
||||||
// TODO: This constraint may not be necessary if SHA256
|
// TODO: This constraint may not be necessary if SHA256
|
||||||
// already boolean constrains its outputs.
|
// already boolean constrains its outputs.
|
||||||
rho->generate_r1cs_constraints();
|
rho->generate_r1cs_constraints();
|
||||||
|
|
||||||
prevent_faerie_gold->generate_r1cs_constraints();
|
prevent_faerie_gold->generate_r1cs_constraints();
|
||||||
|
|
||||||
|
commit_to_outputs->generate_r1cs_constraints();
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate_r1cs_witness(const Note& note) {
|
void generate_r1cs_witness(const Note& note) {
|
||||||
|
@ -158,5 +178,12 @@ public:
|
||||||
this->pb,
|
this->pb,
|
||||||
uint256_to_bool_vector(note.rho)
|
uint256_to_bool_vector(note.rho)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
a_pk->bits.fill_with_bits(
|
||||||
|
this->pb,
|
||||||
|
uint256_to_bool_vector(note.a_pk)
|
||||||
|
);
|
||||||
|
|
||||||
|
commit_to_outputs->generate_r1cs_witness();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
template<typename FieldT>
|
||||||
|
pb_variable_array<FieldT> from_bits(std::vector<bool> bits, pb_variable<FieldT>& ZERO) {
|
||||||
|
pb_variable_array<FieldT> acc;
|
||||||
|
|
||||||
|
BOOST_FOREACH(bool bit, bits) {
|
||||||
|
acc.emplace_back(bit ? ONE : ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<bool> trailing252(std::vector<bool> input) {
|
std::vector<bool> trailing252(std::vector<bool> input) {
|
||||||
if (input.size() != 256) {
|
if (input.size() != 256) {
|
||||||
throw std::length_error("trailing252 input invalid length");
|
throw std::length_error("trailing252 input invalid length");
|
||||||
|
|
Loading…
Reference in New Issue