template class note_gadget : public gadget { public: pb_variable_array value; std::shared_ptr> r; note_gadget(protoboard &pb) : gadget(pb) { value.allocate(pb, 64); r.reset(new digest_variable(pb, 256, "")); } void generate_r1cs_constraints() { for (size_t i = 0; i < 64; i++) { generate_boolean_r1cs_constraint( this->pb, value[i], "boolean_value" ); } r->generate_r1cs_constraints(); } 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())); } }; template class input_note_gadget : public note_gadget { private: std::shared_ptr> a_pk; std::shared_ptr> rho; std::shared_ptr> commitment; std::shared_ptr> commit_to_inputs; pb_variable value_enforce; std::shared_ptr> witness_input; std::shared_ptr> spend_authority; std::shared_ptr> expose_nullifiers; public: std::shared_ptr> a_sk; input_note_gadget( protoboard& pb, pb_variable& ZERO, std::shared_ptr> nullifier, digest_variable rt ) : note_gadget(pb) { a_sk.reset(new digest_variable(pb, 252, "")); a_pk.reset(new digest_variable(pb, 256, "")); rho.reset(new digest_variable(pb, 256, "")); commitment.reset(new digest_variable(pb, 256, "")); spend_authority.reset(new PRF_addr_a_pk_gadget( pb, ZERO, a_sk->bits, a_pk )); expose_nullifiers.reset(new PRF_nf_gadget( pb, ZERO, a_sk->bits, rho->bits, nullifier )); commit_to_inputs.reset(new note_commitment_gadget( pb, ZERO, a_pk->bits, this->value, rho->bits, this->r->bits, commitment )); value_enforce.allocate(pb); witness_input.reset(new merkle_tree_gadget( pb, *commitment, rt, value_enforce )); } void generate_r1cs_constraints() { note_gadget::generate_r1cs_constraints(); a_sk->generate_r1cs_constraints(); rho->generate_r1cs_constraints(); spend_authority->generate_r1cs_constraints(); expose_nullifiers->generate_r1cs_constraints(); commit_to_inputs->generate_r1cs_constraints(); // value * (1 - enforce) = 0 // Given `enforce` is boolean constrained: // If `value` is zero, `enforce` _can_ be zero. // If `value` is nonzero, `enforce` _must_ be one. generate_boolean_r1cs_constraint(this->pb, value_enforce,""); this->pb.add_r1cs_constraint(r1cs_constraint( packed_addition(this->value), (1 - value_enforce), 0 ), ""); witness_input->generate_r1cs_constraints(); } void generate_r1cs_witness( const MerklePath& path, const SproutSpendingKey& key, const SproutNote& note ) { note_gadget::generate_r1cs_witness(note); // Witness a_sk for the input a_sk->bits.fill_with_bits( this->pb, uint252_to_bool_vector(key) ); // Witness a_pk for a_sk with PRF_addr spend_authority->generate_r1cs_witness(); // [SANITY CHECK] Witness a_pk with note information a_pk->bits.fill_with_bits( this->pb, uint256_to_bool_vector(note.a_pk) ); // Witness rho for the input note rho->bits.fill_with_bits( this->pb, uint256_to_bool_vector(note.rho) ); // Witness the nullifier for the input note expose_nullifiers->generate_r1cs_witness(); // Witness the commitment of the input note commit_to_inputs->generate_r1cs_witness(); // [SANITY CHECK] Ensure the commitment is // valid. commitment->bits.fill_with_bits( this->pb, uint256_to_bool_vector(note.cm()) ); // Set enforce flag for nonzero input value this->pb.val(value_enforce) = (note.value() != 0) ? FieldT::one() : FieldT::zero(); // Witness merkle tree authentication path witness_input->generate_r1cs_witness(path); } }; template class output_note_gadget : public note_gadget { private: std::shared_ptr> rho; std::shared_ptr> a_pk; std::shared_ptr> prevent_faerie_gold; std::shared_ptr> commit_to_outputs; public: output_note_gadget( protoboard& pb, pb_variable& ZERO, pb_variable_array& phi, pb_variable_array& h_sig, bool nonce, std::shared_ptr> commitment ) : note_gadget(pb) { rho.reset(new digest_variable(pb, 256, "")); a_pk.reset(new digest_variable(pb, 256, "")); // Do not allow the caller to choose the same "rho" // for any two valid notes in a given view of the // blockchain. See protocol specification for more // details. prevent_faerie_gold.reset(new PRF_rho_gadget( pb, ZERO, phi, h_sig, nonce, rho )); // Commit to the output notes publicly without // disclosing them. commit_to_outputs.reset(new note_commitment_gadget( pb, ZERO, a_pk->bits, this->value, rho->bits, this->r->bits, commitment )); } void generate_r1cs_constraints() { note_gadget::generate_r1cs_constraints(); a_pk->generate_r1cs_constraints(); prevent_faerie_gold->generate_r1cs_constraints(); commit_to_outputs->generate_r1cs_constraints(); } void generate_r1cs_witness(const SproutNote& note) { note_gadget::generate_r1cs_witness(note); prevent_faerie_gold->generate_r1cs_witness(); // [SANITY CHECK] Witness rho ourselves with the // note information. rho->bits.fill_with_bits( this->pb, 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(); } };