Skip to content

Instantly share code, notes, and snippets.

@shamatar
Created October 7, 2018 12:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save shamatar/d262e5e5b62e4002b8ae59dfcb56d65e to your computer and use it in GitHub Desktop.
Save shamatar/d262e5e5b62e4002b8ae59dfcb56d65e to your computer and use it in GitHub Desktop.
roll_up_for_fungible_tokens
namespace libsnark {
template<typename FieldT, typename HashT>
tx<FieldT,HashT>::tx(protoboard<FieldT> &pb,
pb_variable_array<FieldT> &pub_key_x_bin, // sender's pubkey X
pb_variable_array<FieldT> &pub_key_y_bin, // sender's pubkey Y
pb_variable_array<FieldT> &balance_bin, // sender's leaf's balance
pb_variable_array<FieldT> &balance_recipient_bin, // recipients's leaf's balance
pb_variable_array<FieldT> &balance_new_bin, // sender's leaf's balance
pb_variable_array<FieldT> &balance_recipient_new_bin, // recipients's leaf's balance
pb_variable_array<FieldT> &tx_amount_bin, // tx amount
pb_variable_array<FieldT> &nonce_bin, // sender's nonce
pb_variable_array<FieldT> &new_nonce_bin, // sender's nonce
pb_variable_array<FieldT> &pub_key_x_rec_bin, // recipients's pubkey X
pb_variable_array<FieldT> &pub_key_y_rec_bin, // recipients's pubkey Y
pb_variable_array<FieldT> &nonce_rec_bin, // recipients's nonce
int tree_depth, // tree depth
pb_variable_array<FieldT> address_bits_va, // sender's address
pb_variable_array<FieldT> address_bits_va_destination, // recipient's address
std::shared_ptr<digest_variable<FieldT>> root_digest_old, // old merkle root
std::shared_ptr<digest_variable<FieldT>> root_digest_new, // new merkle root
std::vector<merkle_authentication_node> path_old, // old merkle proof for sender
std::vector<merkle_authentication_node> path_new, // new merkle proof for sender
std::vector<merkle_authentication_node> path_old_recipient, // old merkle proof for recipient
std::vector<merkle_authentication_node> path_new_recipient, // new merkle proof for recipient
pb_variable_array<FieldT> S, // signature S
pb_variable_array<FieldT> r_x_bin, // signature R_x
pb_variable_array<FieldT> r_y_bin, // signature R_y
// pb_variable_array<FieldT> rhs_leaf, // these two are from roll-up, discard them
// std::shared_ptr<digest_variable<FieldT>> new_leaf,
const std::string &annotation_prefix): gadget<FieldT>(pb, annotation_prefix) ,
// f..k cpp, add these variables please
pub_key_x_bin(pub_key_x_bin),
pub_key_y_bin(pub_key_y_bin), tree_depth(tree_depth), path_old(path_old),
path_new(path_new), address_bits_va(address_bits_va), rhs_leaf(rhs_leaf),
root_digest_old(root_digest_old), root_digest_new(root_digest_new), new_leaf(new_leaf) {
// base-point for EDDSA
pb_variable<FieldT> base_x;
pb_variable<FieldT> base_y;
// baby jubjub's curve params
pb_variable<FieldT> a;
pb_variable<FieldT> d;
//public key assembled from bits
pb_variable<FieldT> pub_x;
pb_variable<FieldT> pub_y;
// balances and nonce from bits
pb_variable<FieldT> balance;
pb_variable<FieldT> balance_recipient;
pb_variable<FieldT> balance_new;
pb_variable<FieldT> balance_recipient_new;
pb_variable<FieldT> tx_amount;
pb_variable<FieldT> nonce;
pb_variable<FieldT> new_nonce;
base_x.allocate(pb, "base x");
base_y.allocate(pb, "base y");
pub_x.allocate(pb, "pub_x");
pub_y.allocate(pb, "pub_y");
a.allocate(pb, "a");
d.allocate(pb, "d");
pb.val(base_x) = FieldT("17777552123799933955779906779655732241715742912184938656739573121738514868268");
pb.val(base_y) = FieldT("2626589144620713026669568689430873010625803728049924121243784502389097019475");
pb.val(a) = FieldT("168700");
pb.val(d) = FieldT("168696");
pub_key_x.allocate(pb,2, "ZERO");
pub_key_y.allocate(pb,2, "ZERO");
ZERO.allocate(pb, "ZERO");
pb.val(ZERO) = 0;
F_ONE.allocate(pb, "ONE");
pb.val(F_ONE) = 1;
// allocate vars for digests
sender_h1.reset(new digest_variable<FieldT>(pb, 256, "sender_h1"));
sender_h2.reset(new digest_variable<FieldT>(pb, 256, "sender_h2"));
sender_leaf.reset(new digest_variable<FieldT>(pb, 256, "sender leaf"));
rec_h1.reset(new digest_variable<FieldT>(pb, 256, "rec_h1"));
rec_h1.reset(new digest_variable<FieldT>(pb, 256, "rec_h1"));
rec_leaf.reset(new digest_variable<FieldT>(pb, 256, "rec leaf"));
message_interm.reset(new digest_variable<FieldT>(pb, 256, "message intermediate digest"));
message.reset(new digest_variable<FieldT>(pb, 256, "message digest"));
// assemble current leaf's digest as
// t1 = concat[pub_key_x_bin, pub_key_y_bin]
// t2 = concat[balance, nonce]
// t3 = concat[sha256(t1), sha256(t2)]
input_variable.reset(new block_variable<FieldT>(pb, {pub_key_x_bin, pub_key_y_bin}, "input_variable"));
input_variable2.reset(new block_variable<FieldT>(pb, {balance_bin, nonce_bin}, "input_variable2"));
input_variable3.reset(new block_variable<FieldT>(pb, {sender_h1->bits, sender_h2->bits}, "input_variable3"));
input_variable_rec.reset(new block_variable<FieldT>(pb, {pub_key_x_rec_bin, pub_key_y_rec_bin}, "input_variable_rec"));
input_variable2_rec.reset(new block_variable<FieldT>(pb, {balance_rec_bin, nonce_rec_bin}, "input_variable2_rec"));
input_variable3.reset(new block_variable<FieldT>(pb, {rec_h1->bits, rec_h2->bits}, "input_variable3_rec"));
t1_hash.reset(new sha256_ethereum(pb, 256, *input_variable, *sender_h1, "t1 hash"));
t2_hash.reset(new sha256_ethereum(pb, 256, *input_variable2, *sender_h2, "t2 hash"));
t3_hash.reset(new sha256_ethereum(pb, 256, *input_variable3, *sender_leaf, "sender leaf hash"));
t1_rec_hash.reset(new sha256_ethereum(pb, 256, *input_variable_rec, *rec_h1, "t1 rec hash"));
t2_rec_hash.reset(new sha256_ethereum(pb, 256, *input_variable2_rec, *rec_h2, "t2 rec hash"));
t3_rec_hash.reset(new sha256_ethereum(pb, 256, *input_variable3_rec, *rec_leaf, "rec leaf hash"));
// tx_hash = sha256(sha256(amount, nonce), recipient)
message_interm_var.reset(new block_variable<FieldT>(pb, {tx_amount_bin, nonce_bin}, "msg interm"));
message_interm_var2.reset(new block_variable<FieldT>(pb, {message_interm->bits, &address_bits_va_destination}, "msg"));
message_hash_interm.reset(new sha256_ethereum(pb, 256, *message_interm_var, *message_interm, "msg interm hash"));
message_hash.reset(new sha256_ethereum(pb, 256, *message_interm_var2, *message, "msg hash"));
// pack pubkey X
unpacker_pub_key_x.reset(new multipacking_gadget<FieldT>(
pb,
pub_key_x_bin, //pb_linear_combination_array<FieldT>(cm->bits.begin(), cm->bits.begin() , cm->bits.size()),
pub_key_x,
FieldT::capacity() + 1,
"pack pub key x into var"
));
// pack pubkey Y
unpacker_pub_key_y.reset(new multipacking_gadget<FieldT>(
pb,
pub_key_y_bin, //pb_linear_combination_array<FieldT>(cm->bits.begin(), cm->bits.begin() , cm->bits.size()),
pub_key_y,
FieldT::capacity() + 1,
"pack pub key y into var"
));
// pack sender's balance
unpacker_balance.reset(new multipacking_gadget<FieldT>(
pb,
balance_bin, //pb_linear_combination_array<FieldT>(cm->bits.begin(), cm->bits.begin() , cm->bits.size()),
balance,
FieldT::capacity() + 1,
"pack sender's balance into var"
));
// pack recipient's balance
unpacker_balance_recipient.reset(new multipacking_gadget<FieldT>(
pb,
balance_recipient_bin, //pb_linear_combination_array<FieldT>(cm->bits.begin(), cm->bits.begin() , cm->bits.size()),
balance_recipient,
FieldT::capacity() + 1,
"pack recipient's balance into var"
));
// pack sender's balance
unpacker_balance_new.reset(new multipacking_gadget<FieldT>(
pb,
balance_new_bin, //pb_linear_combination_array<FieldT>(cm->bits.begin(), cm->bits.begin() , cm->bits.size()),
balance_new,
FieldT::capacity() + 1,
"pack sender's new balance into var"
));
// pack recipient's balance
unpacker_balance_recipient_new.reset(new multipacking_gadget<FieldT>(
pb,
balance_recipient_new_bin, //pb_linear_combination_array<FieldT>(cm->bits.begin(), cm->bits.begin() , cm->bits.size()),
balance_recipient_new,
FieldT::capacity() + 1,
"pack recipient's new balance into var"
));
unpacker_tx_amount.reset(new multipacking_gadget<FieldT>(
pb,
tx_amount_bin, //pb_linear_combination_array<FieldT>(cm->bits.begin(), cm->bits.begin() , cm->bits.size()),
tx_amount,
FieldT::capacity() + 1,
"pack tx amount into var"
));
unpacker_nonce.reset(new multipacking_gadget<FieldT>(
pb,
nonce_bin, //pb_linear_combination_array<FieldT>(cm->bits.begin(), cm->bits.begin() , cm->bits.size()),
nonce,
FieldT::capacity() + 1,
"pack old nonce into var"
));
unpacker_new_nonce.reset(new multipacking_gadget<FieldT>(
pb,
new_nonce_bin, //pb_linear_combination_array<FieldT>(cm->bits.begin(), cm->bits.begin() , cm->bits.size()),
new_nonce,
FieldT::capacity() + 1,
"pack new nonce into var"
));
path_var_old.reset(new merkle_authentication_path_variable<FieldT, HashT> (pb, tree_depth, "path_var" ));
path_var_new.reset(new merkle_authentication_path_variable<FieldT, HashT> (pb, tree_depth, "path_var" ));
path_var_old_rec.reset(new merkle_authentication_path_variable<FieldT, HashT> (pb, tree_depth, "path_var_rec" ));
path_var_new_rec.reset(new merkle_authentication_path_variable<FieldT, HashT> (pb, tree_depth, "path_var_rec" ));
ml.reset(new merkle_tree_check_update_gadget<FieldT, HashT>(pb, tree_depth, address_bits_va, *leaf, *root_digest_old, *path_var_old, *new_leaf, *root_digest_new, *path_var_new, ONE, "ml"));
ml_rec.reset(new merkle_tree_check_update_gadget<FieldT, HashT>(pb, tree_depth, address_bits_va_destination, *leaf_rec, *root_digest_old, *path_var_old_rec, *new_leaf_rec, *root_digest_new, *path_var_new_rec, ONE, "ml_rec"));
jubjub_eddsa.reset(new eddsa<FieldT, HashT> (pb,a,d, pub_key_x_bin, pub_key_y_bin, base_x,base_y,r_x_bin, r_y_bin, message->bits, S));
// constraint nonce
pb.add_r1cs_constraint(r1cs_constraint<FieldT>(zero, zero, new_nonce - nonce - F_ONE), "constraint nonces"));
// constraint balances
pb.add_r1cs_constraint(r1cs_constraint<FieldT>(zero, zero, balance - balance_new - tx_amount), "constraint sender's balance"));
pb.add_r1cs_constraint(r1cs_constraint<FieldT>(zero, zero, balance_recipient_new - balance_recipient - tx_amount), "constraint recipient's balance"));
// ideally we should constraint common prefix for addresses and merkle proofs, but too complicated for a hackathon
}
// add here constraints generation and witness generation for a new set of vars in constructor
template<typename FieldT, typename HashT>
void tx<FieldT, HashT>::generate_r1cs_constraints() {
jubjub_eddsa->generate_r1cs_constraints();
public_key_hash->generate_r1cs_constraints(true);
leaf_hash->generate_r1cs_constraints(true);
message_hash->generate_r1cs_constraints(true);
unpacker_pub_key_x->generate_r1cs_constraints(true);
unpacker_pub_key_y->generate_r1cs_constraints(true);
path_var_old->generate_r1cs_constraints();
path_var_new->generate_r1cs_constraints();
root_digest_old->generate_r1cs_constraints();
root_digest_new->generate_r1cs_constraints();
ml->generate_r1cs_constraints();
ml_rec->generate_r1cs_constraints();
//make sure the traget root matched the calculated root
//for(int i = 0 ; i < 255; i++) {
// this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1, root_digest_old->bits[i], root_digest_calculated->bits[i]),
// FMT(annotation_prefix, " root digests equal"));
//}
}
template<typename FieldT, typename HashT>
void tx<FieldT, HashT>::generate_r1cs_witness() {
//debug
public_key_hash->generate_r1cs_witness();
leaf_hash->generate_r1cs_witness();
message_hash->generate_r1cs_witness();
unpacker_pub_key_x->generate_r1cs_witness_from_bits();
unpacker_pub_key_y->generate_r1cs_witness_from_bits();
auto address = address_bits_va.get_field_element_from_bits(this->pb);
path_var_old->generate_r1cs_witness(address.as_ulong(), path_old);
path_var_new->generate_r1cs_witness(address.as_ulong(), path_new);
ml->generate_r1cs_witness();
ml_rec->generate_r1cs_witness();
jubjub_eddsa->generate_r1cs_witness();
//debug
/*
std::cout << " leaf " ;
for(uint i =0;i<256;i++) {
std::cout << " , " << this->pb.lc_val(leaf->bits[i]);
}
std::cout << "new leaf " ;
for(uint i =0;i<256;i++) {
std::cout << " , " << this->pb.lc_val(new_leaf->bits[i]);
}
std::cout << "message " ;
for(uint i =0;i<256;i++) {
std::cout << " , " << this->pb.lc_val(message->bits[i]);
}
std::cout << " pub_key_x " << this->pb.lc_val(pub_key_x[0]) << " " << this->pb.lc_val(pub_key_x[1]) << std::endl;
std::cout << " pub_key_y " << this->pb.lc_val(pub_key_y[0]) << " " << this->pb.lc_val(pub_key_y[1]) << std::endl;
*/
std::cout << "pub_key_x " ;
for(uint i =0;i<256;i++) {
std::cout << " , " << this->pb.lc_val(pub_key_x_bin[i]);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment