2 *****************************************************************************
4 Implementation of interfaces for a compliance predicate for RAM.
6 See ram_compliance_predicate.hpp .
8 *****************************************************************************
9 * @author This file is part of libsnark, developed by SCIPR Lab
10 * and contributors (see AUTHORS).
11 * @copyright MIT license (see LICENSE file)
12 *****************************************************************************/
14 #ifndef RAM_COMPLIANCE_PREDICATE_TCC_
15 #define RAM_COMPLIANCE_PREDICATE_TCC_
17 #include <libsnark/gadgetlib1/constraint_profiling.hpp>
22 template<typename ramT>
23 ram_pcd_message<ramT>::ram_pcd_message(
25 const ram_architecture_params<ramT> &ap,
26 const size_t timestamp,
27 const libff::bit_vector root_initial,
28 const libff::bit_vector root,
30 const libff::bit_vector cpu_state,
31 const size_t pc_addr_initial,
32 const libff::bit_vector cpu_state_initial,
33 const bool has_accepted)
34 : r1cs_pcd_message<FieldT>(type)
36 , timestamp(timestamp)
37 , root_initial(root_initial)
40 , cpu_state(cpu_state)
41 , pc_addr_initial(pc_addr_initial)
42 , cpu_state_initial(cpu_state_initial)
43 , has_accepted(has_accepted)
45 const size_t digest_size =
46 CRH_with_bit_out_gadget<FieldT>::get_digest_len();
47 assert(libff::log2(timestamp) < ramT::timestamp_length);
48 assert(root_initial.size() == digest_size);
49 assert(root.size() == digest_size);
50 assert(libff::log2(pc_addr) < ap.address_size());
51 assert(cpu_state.size() == ap.cpu_state_size());
52 assert(libff::log2(pc_addr_initial) < ap.address_size());
53 assert(cpu_state_initial.size() == ap.cpu_state_size());
56 template<typename ramT>
57 libff::bit_vector ram_pcd_message<ramT>::unpacked_payload_as_bits() const
59 libff::bit_vector result;
61 const libff::bit_vector timestamp_bits =
62 libff::convert_field_element_to_bit_vector<FieldT>(
63 FieldT(timestamp), ramT::timestamp_length);
64 const libff::bit_vector pc_addr_bits =
65 libff::convert_field_element_to_bit_vector<FieldT>(
66 FieldT(pc_addr), ap.address_size());
67 const libff::bit_vector pc_addr_initial_bits =
68 libff::convert_field_element_to_bit_vector<FieldT>(
69 FieldT(pc_addr_initial), ap.address_size());
71 result.insert(result.end(), timestamp_bits.begin(), timestamp_bits.end());
72 result.insert(result.end(), root_initial.begin(), root_initial.end());
73 result.insert(result.end(), root.begin(), root.end());
74 result.insert(result.end(), pc_addr_bits.begin(), pc_addr_bits.end());
75 result.insert(result.end(), cpu_state.begin(), cpu_state.end());
77 result.end(), pc_addr_initial_bits.begin(), pc_addr_initial_bits.end());
79 result.end(), cpu_state_initial.begin(), cpu_state_initial.end());
80 result.insert(result.end(), has_accepted);
82 assert(result.size() == unpacked_payload_size_in_bits(ap));
86 template<typename ramT>
87 r1cs_variable_assignment<ram_base_field<ramT>> ram_pcd_message<
88 ramT>::payload_as_r1cs_variable_assignment() const
90 const libff::bit_vector payload_bits = unpacked_payload_as_bits();
91 const r1cs_variable_assignment<FieldT> result =
92 libff::pack_bit_vector_into_field_element_vector<FieldT>(payload_bits);
96 template<typename ramT>
97 void ram_pcd_message<ramT>::print_bits(const libff::bit_vector &bv) const
100 printf("%d", b ? 1 : 0);
105 template<typename ramT> void ram_pcd_message<ramT>::print() const
107 printf("ram_pcd_message:\n");
108 printf(" type: %zu\n", this->type);
109 printf(" timestamp: %zu\n", timestamp);
110 printf(" root_initial: ");
111 print_bits(root_initial);
114 printf(" pc_addr: %zu\n", pc_addr);
115 printf(" cpu_state: ");
116 print_bits(cpu_state);
117 printf(" pc_addr_initial: %zu\n", pc_addr_initial);
118 printf(" cpu_state_initial: ");
119 print_bits(cpu_state_initial);
120 printf(" has_accepted: %s\n", has_accepted ? "YES" : "no");
123 template<typename ramT>
124 size_t ram_pcd_message<ramT>::unpacked_payload_size_in_bits(
125 const ram_architecture_params<ramT> &ap)
127 const size_t digest_size =
128 CRH_with_bit_out_gadget<FieldT>::get_digest_len();
131 ramT::timestamp_length + // timestamp
132 2 * digest_size + // root, root_initial
133 2 * ap.address_size() + // pc_addr, pc_addr_initial
134 2 * ap.cpu_state_size() + // cpu_state, cpu_state_initial
138 template<typename ramT>
139 ram_pcd_message_variable<ramT>::ram_pcd_message_variable(
140 protoboard<FieldT> &pb,
141 const ram_architecture_params<ramT> &ap,
142 const std::string &annotation_prefix)
143 : r1cs_pcd_message_variable<ram_base_field<ramT>>(pb, annotation_prefix)
146 const size_t unpacked_payload_size_in_bits =
147 ram_pcd_message<ramT>::unpacked_payload_size_in_bits(ap);
148 const size_t packed_payload_size =
149 libff::div_ceil(unpacked_payload_size_in_bits, FieldT::capacity());
150 packed_payload.allocate(
151 pb, packed_payload_size, FMT(annotation_prefix, " packed_payload"));
153 this->update_all_vars();
156 template<typename ramT>
157 void ram_pcd_message_variable<ramT>::allocate_unpacked_part()
159 const size_t digest_size =
160 CRH_with_bit_out_gadget<FieldT>::get_digest_len();
164 ramT::timestamp_length,
165 FMT(this->annotation_prefix, " timestamp"));
166 root_initial.allocate(
167 this->pb, digest_size, FMT(this->annotation_prefix, " root_initial"));
168 root.allocate(this->pb, digest_size, FMT(this->annotation_prefix, " root"));
170 this->pb, ap.address_size(), FMT(this->annotation_prefix, " pc_addr"));
174 FMT(this->annotation_prefix, " cpu_state"));
175 pc_addr_initial.allocate(
178 FMT(this->annotation_prefix, " pc_addr_initial"));
179 cpu_state_initial.allocate(
182 FMT(this->annotation_prefix, " cpu_state_initial"));
183 has_accepted.allocate(
184 this->pb, FMT(this->annotation_prefix, " has_accepted"));
186 all_unpacked_vars.insert(
187 all_unpacked_vars.end(), timestamp.begin(), timestamp.end());
188 all_unpacked_vars.insert(
189 all_unpacked_vars.end(), root_initial.begin(), root_initial.end());
190 all_unpacked_vars.insert(all_unpacked_vars.end(), root.begin(), root.end());
191 all_unpacked_vars.insert(
192 all_unpacked_vars.end(), pc_addr.begin(), pc_addr.end());
193 all_unpacked_vars.insert(
194 all_unpacked_vars.end(), cpu_state.begin(), cpu_state.end());
195 all_unpacked_vars.insert(
196 all_unpacked_vars.end(),
197 pc_addr_initial.begin(),
198 pc_addr_initial.end());
199 all_unpacked_vars.insert(
200 all_unpacked_vars.end(),
201 cpu_state_initial.begin(),
202 cpu_state_initial.end());
203 all_unpacked_vars.insert(all_unpacked_vars.end(), has_accepted);
205 unpack_payload.reset(new multipacking_gadget<FieldT>(
210 FMT(this->annotation_prefix, " unpack_payload")));
213 template<typename ramT>
214 void ram_pcd_message_variable<ramT>::generate_r1cs_witness_from_bits()
216 unpack_payload->generate_r1cs_witness_from_bits();
219 template<typename ramT>
220 void ram_pcd_message_variable<ramT>::generate_r1cs_witness_from_packed()
222 unpack_payload->generate_r1cs_witness_from_packed();
225 template<typename ramT>
226 void ram_pcd_message_variable<ramT>::generate_r1cs_constraints()
228 unpack_payload->generate_r1cs_constraints(true);
231 template<typename ramT>
232 std::shared_ptr<r1cs_pcd_message<ram_base_field<ramT>>> ram_pcd_message_variable<
233 ramT>::get_message() const
235 const size_t type_val = this->pb.val(this->type).as_ulong();
236 const size_t timestamp_val =
237 timestamp.get_field_element_from_bits(this->pb).as_ulong();
238 const libff::bit_vector root_initial_val = root_initial.get_bits(this->pb);
239 const libff::bit_vector root_val = root.get_bits(this->pb);
240 const size_t pc_addr_val =
241 pc_addr.get_field_element_from_bits(this->pb).as_ulong();
242 const libff::bit_vector cpu_state_val = cpu_state.get_bits(this->pb);
243 const size_t pc_addr_initial_val =
244 pc_addr_initial.get_field_element_from_bits(this->pb).as_ulong();
245 const libff::bit_vector cpu_state_initial_val =
246 cpu_state_initial.get_bits(this->pb);
247 const bool has_accepted_val = (this->pb.val(has_accepted) == FieldT::one());
249 std::shared_ptr<r1cs_pcd_message<FieldT>> result;
250 result.reset(new ram_pcd_message<ramT>(
259 cpu_state_initial_val,
264 template<typename ramT>
265 ram_pcd_local_data<ramT>::ram_pcd_local_data(
266 const bool is_halt_case,
267 delegated_ra_memory<CRH_with_bit_out_gadget<FieldT>> &mem,
268 typename ram_input_tape<ramT>::const_iterator &aux_it,
269 const typename ram_input_tape<ramT>::const_iterator &aux_end)
270 : is_halt_case(is_halt_case), mem(mem), aux_it(aux_it), aux_end(aux_end)
274 template<typename ramT>
275 r1cs_variable_assignment<ram_base_field<ramT>> ram_pcd_local_data<
276 ramT>::as_r1cs_variable_assignment() const
278 r1cs_variable_assignment<FieldT> result;
279 result.emplace_back(is_halt_case ? FieldT::one() : FieldT::zero());
283 template<typename ramT>
284 ram_pcd_local_data_variable<ramT>::ram_pcd_local_data_variable(
285 protoboard<FieldT> &pb, const std::string &annotation_prefix)
286 : r1cs_pcd_local_data_variable<ram_base_field<ramT>>(pb, annotation_prefix)
288 is_halt_case.allocate(pb, FMT(annotation_prefix, " is_halt_case"));
290 this->update_all_vars();
294 We need to perform the following checks:
297 next.root_initial = cur.root_initial
298 next.pc_addr_init = cur.pc_addr_initial
299 next.cpu_state_initial = cur.cpu_state_initial
301 If is_is_base_case = 1: (base case)
302 that cur.timestamp = 0, cur.cpu_state = cpu_state_init, cur.pc_addr =
303 pc_addr_initial, cur.has_accepted = 0 that cur.root = cur.root_initial
305 If do_halt = 0: (regular case)
306 that instruction fetch was correctly executed
307 next.timestamp = cur.timestamp + 1
308 that CPU accepted on (cur, temp)
309 that load-then-store was correctly handled
310 that next.root = temp.root, next.cpu_state = temp.cpu_state, next.pc_addr =
313 If do_halt = 1: (final case)
314 that cur.has_accepted = 1
315 that next.root = 0, next.cpu_state = 0, next.pc_addr = 0
316 that next.timestamp = cur.timestamp and next.has_accepted = cur.has_accepted
319 template<typename ramT>
320 ram_compliance_predicate_handler<ramT>::ram_compliance_predicate_handler(
321 const ram_architecture_params<ramT> &ap)
322 : compliance_predicate_handler<ram_base_field<ramT>, ram_protoboard<ramT>>(
323 ram_protoboard<ramT>(ap), 100, 1, 1, true, std::set<size_t>{1})
325 , addr_size(ap.address_size())
326 , value_size(ap.value_size())
327 , digest_size(CRH_with_bit_out_gadget<FieldT>::get_digest_len())
329 // TODO: assert that message has fields of lengths consistent with
330 // num_addresses/value_size (as a method for ram_message) choose a constant
331 // for timestamp_len check that value_size <= digest_size; digest_size is
332 // not assumed to fit in chunk size (more precisely, it is handled correctly
333 // in the other gadgets). check if others fit (timestamp_length, value_size,
336 // the variables allocated are: next, cur, local data (nil for us),
337 // is_base_case, witness
339 this->outgoing_message.reset(
340 new ram_pcd_message_variable<ramT>(this->pb, ap, "outgoing_message"));
341 this->arity.allocate(this->pb, "arity");
342 this->incoming_messages[0].reset(
343 new ram_pcd_message_variable<ramT>(this->pb, ap, "incoming_message"));
344 this->local_data.reset(
345 new ram_pcd_local_data_variable<ramT>(this->pb, "local_data"));
347 is_base_case.allocate(this->pb, "is_base_case");
349 next = std::dynamic_pointer_cast<ram_pcd_message_variable<ramT>>(
350 this->outgoing_message);
351 cur = std::dynamic_pointer_cast<ram_pcd_message_variable<ramT>>(
352 this->incoming_messages[0]);
354 next->allocate_unpacked_part();
355 cur->allocate_unpacked_part();
357 // work-around for bad linear combination handling
359 this->pb, "zero"); // will go away when we properly support linear terms
361 temp_next_pc_addr.allocate(this->pb, addr_size, "temp_next_pc_addr");
362 temp_next_cpu_state.allocate(
363 this->pb, ap.cpu_state_size(), "temp_next_cpu_state");
365 const size_t chunk_size = FieldT::capacity();
369 next.root_initial = cur.root_initial
370 next.pc_addr_init = cur.pc_addr_initial
371 next.cpu_state_initial = cur.cpu_state_initial
373 copy_root_initial.reset(new bit_vector_copy_gadget<FieldT>(
379 "copy_root_initial"));
380 copy_pc_addr_initial.reset(new bit_vector_copy_gadget<FieldT>(
382 cur->pc_addr_initial,
383 next->pc_addr_initial,
386 "copy_pc_addr_initial"));
387 copy_cpu_state_initial.reset(new bit_vector_copy_gadget<FieldT>(
389 cur->cpu_state_initial,
390 next->cpu_state_initial,
393 "copy_cpu_state_initial"));
396 If is_base_case = 1: (base case)
397 that cur.timestamp = 0, cur.cpu_state = 0, cur.pc_addr = 0,
398 cur.has_accepted = 0 that cur.root = cur.root_initial
400 packed_cur_timestamp.allocate(this->pb, "packed_cur_timestamp");
401 pack_cur_timestamp.reset(new packing_gadget<FieldT>(
402 this->pb, cur->timestamp, packed_cur_timestamp, "pack_cur_timestamp"));
404 zero_cpu_state = pb_variable_array<FieldT>(cur->cpu_state.size(), zero);
405 zero_pc_addr = pb_variable_array<FieldT>(cur->pc_addr.size(), zero);
407 initialize_cur_cpu_state.reset(new bit_vector_copy_gadget<FieldT>(
409 cur->cpu_state_initial,
413 "initialize_cur_cpu_state"));
414 initialize_prev_pc_addr.reset(new bit_vector_copy_gadget<FieldT>(
416 cur->pc_addr_initial,
420 "initialize_prev_pc_addr"));
422 initialize_root.reset(new bit_vector_copy_gadget<FieldT>(
430 If do_halt = 0: (regular case)
431 that instruction fetch was correctly executed
432 next.timestamp = cur.timestamp + 1
433 that CPU accepted on (cur, next)
434 that load-then-store was correctly handled
436 is_not_halt_case.allocate(this->pb, "is_not_halt_case");
437 // for performing instruction fetch
438 prev_pc_val.allocate(this->pb, value_size, "prev_pc_val");
439 prev_pc_val_digest.reset(new digest_variable<FieldT>(
440 this->pb, digest_size, prev_pc_val, zero, "prev_pc_val_digest"));
441 cur_root_digest.reset(new digest_variable<FieldT>(
442 this->pb, digest_size, cur->root, zero, "cur_root_digest"));
443 instruction_fetch_merkle_proof.reset(
444 new merkle_authentication_path_variable<FieldT, HashT>(
445 this->pb, addr_size, "instruction_fetch_merkle_proof"));
446 instruction_fetch.reset(new memory_load_gadget<FieldT, HashT>(
452 *instruction_fetch_merkle_proof,
454 "instruction_fetch"));
456 // for next.timestamp = cur.timestamp + 1
457 packed_next_timestamp.allocate(this->pb, "packed_next_timestamp");
458 pack_next_timestamp.reset(new packing_gadget<FieldT>(
461 packed_next_timestamp,
462 "pack_next_timestamp"));
464 // that CPU accepted on (cur, temp)
465 ls_addr.allocate(this->pb, addr_size, "ls_addr");
466 ls_prev_val.allocate(this->pb, value_size, "ls_prev_val");
467 ls_next_val.allocate(this->pb, value_size, "ls_next_val");
468 cpu_checker.reset(new ram_cpu_checker<ramT>(
481 // that load-then-store was correctly handled
482 ls_prev_val_digest.reset(new digest_variable<FieldT>(
483 this->pb, digest_size, ls_prev_val, zero, "ls_prev_val_digest"));
484 ls_next_val_digest.reset(new digest_variable<FieldT>(
485 this->pb, digest_size, ls_next_val, zero, "ls_next_val_digest"));
486 next_root_digest.reset(new digest_variable<FieldT>(
487 this->pb, digest_size, next->root, zero, "next_root_digest"));
488 load_merkle_proof.reset(
489 new merkle_authentication_path_variable<FieldT, HashT>(
490 this->pb, addr_size, "load_merkle_proof"));
491 store_merkle_proof.reset(
492 new merkle_authentication_path_variable<FieldT, HashT>(
493 this->pb, addr_size, "store_merkle_proof"));
494 load_store_checker.reset(new memory_load_store_gadget<FieldT, HashT>(
505 "load_store_checker"));
507 If do_halt = 1: (final case)
508 that cur.has_accepted = 1
509 that next.root = 0, next.cpu_state = 0, next.pc_addr = 0
510 that next.timestamp = cur.timestamp and next.has_accepted =
513 do_halt.allocate(this->pb, "do_halt");
514 zero_root = pb_variable_array<FieldT>(next->root.size(), zero);
515 clear_next_root.reset(new bit_vector_copy_gadget<FieldT>(
522 clear_next_pc_addr.reset(new bit_vector_copy_gadget<FieldT>(
528 "clear_next_pc_addr"));
529 clear_next_cpu_state.reset(new bit_vector_copy_gadget<FieldT>(
537 copy_temp_next_pc_addr.reset(new bit_vector_copy_gadget<FieldT>(
543 "copy_temp_next_pc_addr"));
544 copy_temp_next_cpu_state.reset(new bit_vector_copy_gadget<FieldT>(
550 "copy_temp_next_cpu_state"));
553 template<typename ramT>
554 void ram_compliance_predicate_handler<ramT>::generate_r1cs_constraints()
556 libff::print_indent();
557 printf("* Message size: %zu\n", next->all_vars.size());
558 libff::print_indent();
559 printf("* Address size: %zu\n", addr_size);
560 libff::print_indent();
561 printf("* CPU state size: %zu\n", ap.cpu_state_size());
562 libff::print_indent();
563 printf("* Digest size: %zu\n", digest_size);
565 PROFILE_CONSTRAINTS(this->pb, "handle next_type, arity and cur_type")
567 generate_r1cs_equals_const_constraint<FieldT>(
568 this->pb, next->type, FieldT::one(), "next_type");
569 generate_r1cs_equals_const_constraint<FieldT>(
570 this->pb, this->arity, FieldT::one(), "arity");
571 this->pb.add_r1cs_constraint(
572 r1cs_constraint<FieldT>(is_base_case, cur->type, 0),
573 "nonzero_cur_type_implies_base_case_0");
574 generate_boolean_r1cs_constraint<FieldT>(
575 this->pb, cur->type, "cur_type_boolean");
576 generate_boolean_r1cs_constraint<FieldT>(
577 this->pb, is_base_case, "is_base_case_boolean");
580 PROFILE_CONSTRAINTS(this->pb, "unpack messages")
582 next->generate_r1cs_constraints();
583 cur->generate_r1cs_constraints();
586 // work-around for bad linear combination handling
587 generate_r1cs_equals_const_constraint<FieldT>(
588 this->pb, zero, FieldT::zero(), " zero");
590 /* recall that Booleanity of PCD messages has already been enforced by the
591 * PCD machine, which is explains the absence of Booleanity checks */
593 We need to perform the following checks:
596 next.root_initial = cur.root_initial
597 next.pc_addr_init = cur.pc_addr_initial
598 next.cpu_state_initial = cur.cpu_state_initial
600 PROFILE_CONSTRAINTS(this->pb, "copy root_initial")
602 copy_root_initial->generate_r1cs_constraints(false, false);
605 PROFILE_CONSTRAINTS(this->pb, "copy pc_addr_initial and cpu_state_initial")
607 copy_pc_addr_initial->generate_r1cs_constraints(false, false);
608 copy_cpu_state_initial->generate_r1cs_constraints(false, false);
612 If is_base_case = 1: (base case)
613 that cur.timestamp = 0, cur.cpu_state = 0, cur.pc_addr = 0,
614 cur.has_accepted = 0 that cur.root = cur.root_initial
616 pack_cur_timestamp->generate_r1cs_constraints(false);
617 this->pb.add_r1cs_constraint(
618 r1cs_constraint<FieldT>(is_base_case, packed_cur_timestamp, 0),
619 "clear_ts_on_is_base_case");
620 PROFILE_CONSTRAINTS(this->pb, "copy cur_cpu_state and prev_pc_addr")
622 initialize_cur_cpu_state->generate_r1cs_constraints(false, false);
623 initialize_prev_pc_addr->generate_r1cs_constraints(false, false);
625 this->pb.add_r1cs_constraint(
626 r1cs_constraint<FieldT>(is_base_case, cur->has_accepted, 0),
627 "is_base_case_is_not_accepting");
628 PROFILE_CONSTRAINTS(this->pb, "initialize root")
630 initialize_root->generate_r1cs_constraints(false, false);
634 If do_halt = 0: (regular case)
635 that instruction fetch was correctly executed
636 next.timestamp = cur.timestamp + 1
637 that CPU accepted on (cur, next)
638 that load-then-store was correctly handled
639 that next.root = temp.root, next.cpu_state = temp.cpu_state, next.pc_addr
642 this->pb.add_r1cs_constraint(
643 r1cs_constraint<FieldT>(1, 1 - do_halt, is_not_halt_case),
645 PROFILE_CONSTRAINTS(this->pb, "instruction fetch")
647 instruction_fetch_merkle_proof->generate_r1cs_constraints();
648 instruction_fetch->generate_r1cs_constraints();
650 pack_next_timestamp->generate_r1cs_constraints(false);
651 this->pb.add_r1cs_constraint(
652 r1cs_constraint<FieldT>(
654 (packed_cur_timestamp + 1) - packed_next_timestamp,
656 "increment_timestamp");
657 PROFILE_CONSTRAINTS(this->pb, "CPU checker")
659 cpu_checker->generate_r1cs_constraints();
661 PROFILE_CONSTRAINTS(this->pb, "load/store checker")
664 // merkle_tree_check_update_gadget::generate_r1cs_witness() for why we
665 // don't need to call store_merkle_proof->generate_r1cs_constraints()
666 load_merkle_proof->generate_r1cs_constraints();
667 load_store_checker->generate_r1cs_constraints();
671 this->pb, "copy temp_next_pc_addr and temp_next_cpu_state")
673 copy_temp_next_pc_addr->generate_r1cs_constraints(true, false);
674 copy_temp_next_cpu_state->generate_r1cs_constraints(true, false);
678 If do_halt = 1: (final case)
679 that cur.has_accepted = 1
680 that next.root = 0, next.cpu_state = 0, next.pc_addr = 0
681 that next.timestamp = cur.timestamp and next.has_accepted =
684 this->pb.add_r1cs_constraint(
685 r1cs_constraint<FieldT>(do_halt, 1 - cur->has_accepted, 0),
686 "final_case_must_be_accepting");
688 PROFILE_CONSTRAINTS(this->pb, "clear next root")
690 clear_next_root->generate_r1cs_constraints(false, false);
693 PROFILE_CONSTRAINTS(this->pb, "clear next_pc_addr and next_cpu_state")
695 clear_next_pc_addr->generate_r1cs_constraints(false, false);
696 clear_next_cpu_state->generate_r1cs_constraints(false, false);
699 this->pb.add_r1cs_constraint(
700 r1cs_constraint<FieldT>(
701 do_halt, packed_cur_timestamp - packed_next_timestamp, 0),
704 const size_t accounted = PRINT_CONSTRAINT_PROFILING();
705 const size_t total = this->pb.num_constraints();
706 libff::print_indent();
707 printf("* Unaccounted constraints: %zu\n", total - accounted);
708 libff::print_indent();
709 printf("* Number of constraints in ram_compliance_predicate: %zu\n", total);
712 template<typename ramT>
713 void ram_compliance_predicate_handler<ramT>::generate_r1cs_witness(
714 const std::vector<std::shared_ptr<r1cs_pcd_message<FieldT>>>
715 &incoming_message_values,
716 const std::shared_ptr<r1cs_pcd_local_data<FieldT>> &local_data_value)
718 const std::shared_ptr<ram_pcd_local_data<ramT>> ram_local_data_value =
719 std::dynamic_pointer_cast<ram_pcd_local_data<ramT>>(local_data_value);
721 ram_local_data_value->mem.num_addresses ==
722 1ul << addr_size); // check value_size and num_addresses too
724 base_handler::generate_r1cs_witness(
725 incoming_message_values, local_data_value);
726 cur->generate_r1cs_witness_from_packed();
728 this->pb.val(next->type) = FieldT::one();
729 this->pb.val(this->arity) = FieldT::one();
730 this->pb.val(is_base_case) =
731 (this->pb.val(cur->type) == FieldT::zero() ? FieldT::one()
734 this->pb.val(zero) = FieldT::zero();
737 next.root_initial = cur.root_initial
738 next.pc_addr_init = cur.pc_addr_initial
739 next.cpu_state_initial = cur.cpu_state_initial
741 copy_root_initial->generate_r1cs_witness();
742 for (size_t i = 0; i < next->root_initial.size(); ++i) {
743 this->pb.val(cur->root_initial[i]).print();
744 this->pb.val(next->root_initial[i]).print();
746 this->pb.val(cur->root_initial[i]) ==
747 this->pb.val(next->root_initial[i]));
750 copy_pc_addr_initial->generate_r1cs_witness();
751 copy_cpu_state_initial->generate_r1cs_witness();
754 If is_base_case = 1: (base case)
755 that cur.timestamp = 0, cur.cpu_state = 0, cur.pc_addr = 0,
756 cur.has_accepted = 0 that cur.root = cur.root_initial
758 const bool base_case = (incoming_message_values[0]->type == 0);
759 this->pb.val(is_base_case) = base_case ? FieldT::one() : FieldT::zero();
761 initialize_cur_cpu_state->generate_r1cs_witness();
762 initialize_prev_pc_addr->generate_r1cs_witness();
765 this->pb.val(packed_cur_timestamp) = FieldT::zero();
766 this->pb.val(cur->has_accepted) = FieldT::zero();
767 pack_cur_timestamp->generate_r1cs_witness_from_packed();
769 pack_cur_timestamp->generate_r1cs_witness_from_bits();
772 initialize_root->generate_r1cs_witness();
775 If do_halt = 0: (regular case)
776 that instruction fetch was correctly executed
777 next.timestamp = cur.timestamp + 1
778 that CPU accepted on (cur, temp)
779 that load-then-store was correctly handled
781 this->pb.val(do_halt) =
782 ram_local_data_value->is_halt_case ? FieldT::one() : FieldT::zero();
783 this->pb.val(is_not_halt_case) = FieldT::one() - this->pb.val(do_halt);
785 // that instruction fetch was correctly executed
786 const size_t int_pc_addr =
787 libff::convert_bit_vector_to_field_element<FieldT>(
788 cur->pc_addr.get_bits(this->pb))
790 const size_t int_pc_val = ram_local_data_value->mem.get_value(int_pc_addr);
793 "pc_addr (in units) = %zu, pc_val = %zu (0x%08zx)\n",
798 libff::bit_vector pc_val_bv =
799 libff::int_list_to_bits({int_pc_val}, value_size);
800 std::reverse(pc_val_bv.begin(), pc_val_bv.end());
802 prev_pc_val.fill_with_bits(this->pb, pc_val_bv);
803 const merkle_authentication_path pc_path =
804 ram_local_data_value->mem.get_path(int_pc_addr);
805 instruction_fetch_merkle_proof->generate_r1cs_witness(int_pc_addr, pc_path);
806 instruction_fetch->generate_r1cs_witness();
808 // next.timestamp = cur.timestamp + 1 (or cur.timestamp if do_halt)
809 this->pb.val(packed_next_timestamp) =
810 this->pb.val(packed_cur_timestamp) + this->pb.val(is_not_halt_case);
811 pack_next_timestamp->generate_r1cs_witness_from_packed();
813 // that CPU accepted on (cur, temp)
814 // Step 1: Get address and old witnesses for delegated memory.
815 cpu_checker->generate_r1cs_witness_address();
816 const size_t int_ls_addr =
817 ls_addr.get_field_element_from_bits(this->pb).as_ulong();
818 const size_t int_ls_prev_val =
819 ram_local_data_value->mem.get_value(int_ls_addr);
820 const merkle_authentication_path prev_path =
821 ram_local_data_value->mem.get_path(int_ls_addr);
822 ls_prev_val.fill_with_bits_of_ulong(this->pb, int_ls_prev_val);
824 ls_prev_val.get_field_element_from_bits(this->pb) ==
825 FieldT(int_ls_prev_val, true));
826 // Step 2: Execute CPU checker and delegated memory
827 cpu_checker->generate_r1cs_witness_other(
828 ram_local_data_value->aux_it, ram_local_data_value->aux_end);
830 printf("Debugging information from transition function:\n");
833 const size_t int_ls_next_val =
834 ls_next_val.get_field_element_from_bits(this->pb).as_ulong();
835 ram_local_data_value->mem.set_value(int_ls_addr, int_ls_next_val);
838 "Memory location %zu changed from %zu (0x%08zx) to %zu (0x%08zx)\n",
845 // Step 4: Use both to satisfy load_store_checker
846 load_merkle_proof->generate_r1cs_witness(int_ls_addr, prev_path);
847 load_store_checker->generate_r1cs_witness();
850 If do_halt = 1: (final case)
851 that cur.has_accepted = 1
852 that next.root = 0, next.cpu_state = 0, next.pc_addr = 0
853 that next.timestamp = cur.timestamp and next.has_accepted =
857 // Order matters here: both witness maps touch next_root, but the
858 // one that does not set values must be executed the last, so its
859 // auxiliary variables are filled in correctly according to values
860 // actually set by the other witness map.
861 if (this->pb.val(do_halt).is_zero()) {
862 copy_temp_next_pc_addr->generate_r1cs_witness();
863 copy_temp_next_cpu_state->generate_r1cs_witness();
865 clear_next_root->generate_r1cs_witness();
866 clear_next_pc_addr->generate_r1cs_witness();
867 clear_next_cpu_state->generate_r1cs_witness();
869 clear_next_root->generate_r1cs_witness();
870 clear_next_pc_addr->generate_r1cs_witness();
871 clear_next_cpu_state->generate_r1cs_witness();
873 copy_temp_next_pc_addr->generate_r1cs_witness();
874 copy_temp_next_cpu_state->generate_r1cs_witness();
878 printf("next.has_accepted: ");
879 this->pb.val(next->has_accepted).print();
882 next->generate_r1cs_witness_from_bits();
885 template<typename ramT>
886 std::shared_ptr<r1cs_pcd_message<ram_base_field<ramT>>>
887 ram_compliance_predicate_handler<ramT>::get_base_case_message(
888 const ram_architecture_params<ramT> &ap,
889 const ram_boot_trace<ramT> &primary_input)
892 "Call to ram_compliance_predicate_handler::get_base_case_message");
893 const size_t num_addresses = 1ul << ap.address_size();
894 const size_t value_size = ap.value_size();
895 delegated_ra_memory<CRH_with_bit_out_gadget<FieldT>> mem(
896 num_addresses, value_size, primary_input.as_memory_contents());
898 const size_t type = 0;
900 const size_t timestamp = 0;
902 const libff::bit_vector root_initial = mem.get_root();
903 const size_t pc_addr_initial = ap.initial_pc_addr();
904 const libff::bit_vector cpu_state_initial(ap.cpu_state_size(), false);
906 const libff::bit_vector root = root_initial;
907 const size_t pc_addr = pc_addr_initial;
908 const libff::bit_vector cpu_state = cpu_state_initial;
910 const bool has_accepted = false;
912 std::shared_ptr<r1cs_pcd_message<FieldT>> result;
913 result.reset(new ram_pcd_message<ramT>(
925 "Call to ram_compliance_predicate_handler::get_base_case_message");
929 template<typename ramT>
930 std::shared_ptr<r1cs_pcd_message<ram_base_field<ramT>>>
931 ram_compliance_predicate_handler<ramT>::get_final_case_msg(
932 const ram_architecture_params<ramT> &ap,
933 const ram_boot_trace<ramT> &primary_input,
934 const size_t time_bound)
937 "Call to ram_compliance_predicate_handler::get_final_case_msg");
938 const size_t num_addresses = 1ul << ap.address_size();
939 const size_t value_size = ap.value_size();
940 delegated_ra_memory<CRH_with_bit_out_gadget<FieldT>> mem(
941 num_addresses, value_size, primary_input.as_memory_contents());
943 const size_t type = 1;
945 const size_t timestamp = time_bound;
947 const libff::bit_vector root_initial = mem.get_root();
948 const size_t pc_addr_initial = ap.initial_pc_addr();
949 const libff::bit_vector cpu_state_initial(ap.cpu_state_size(), false);
951 const libff::bit_vector root(root_initial.size(), false);
952 const size_t pc_addr = 0;
953 const libff::bit_vector cpu_state = cpu_state_initial;
955 const bool has_accepted = true;
957 std::shared_ptr<r1cs_pcd_message<FieldT>> result;
958 result.reset(new ram_pcd_message<ramT>(
970 "Call to ram_compliance_predicate_handler::get_final_case_msg");
975 } // namespace libsnark
977 #endif // RAM_COMPLIANCE_PREDICATE_TCC_