]>
Commit | Line | Data |
---|---|---|
81469bbb SB |
1 | #include <gtest/gtest.h> |
2 | #include "uint256.h" | |
3 | ||
81469bbb SB |
4 | #include "zcash/util.h" |
5 | ||
6 | #include <boost/foreach.hpp> | |
7 | #include <boost/format.hpp> | |
8 | #include <boost/optional.hpp> | |
9 | ||
fee88353 JG |
10 | #include <libsnark/common/default_types/r1cs_ppzksnark_pp.hpp> |
11 | #include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp> | |
12 | #include <libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp> | |
13 | #include <libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp> | |
14 | ||
4d66f8f6 | 15 | #include "zcash/IncrementalMerkleTree.hpp" |
81469bbb SB |
16 | |
17 | using namespace libsnark; | |
4d66f8f6 | 18 | using namespace libzcash; |
81469bbb SB |
19 | |
20 | #include "zcash/circuit/utils.tcc" | |
4d66f8f6 | 21 | #include "zcash/circuit/merkle.tcc" |
81469bbb SB |
22 | |
23 | template<typename FieldT> | |
24 | void test_value_equals(uint64_t i) { | |
25 | protoboard<FieldT> pb; | |
26 | pb_variable_array<FieldT> num; | |
27 | num.allocate(pb, 64, ""); | |
28 | num.fill_with_bits(pb, uint64_to_bool_vector(i)); | |
29 | pb.add_r1cs_constraint(r1cs_constraint<FieldT>( | |
30 | packed_addition(num), | |
31 | FieldT::one(), | |
32 | FieldT::one() * i | |
33 | ), ""); | |
34 | ASSERT_TRUE(pb.is_satisfied()); | |
35 | } | |
36 | ||
37 | TEST(circuit, values) | |
38 | { | |
81469bbb SB |
39 | typedef Fr<default_r1cs_ppzksnark_pp> FieldT; |
40 | test_value_equals<FieldT>(0); | |
41 | test_value_equals<FieldT>(1); | |
42 | test_value_equals<FieldT>(3); | |
43 | test_value_equals<FieldT>(5391); | |
44 | test_value_equals<FieldT>(883128374); | |
45 | test_value_equals<FieldT>(173419028459); | |
46 | test_value_equals<FieldT>(2205843009213693953); | |
47 | } | |
48 | ||
49 | TEST(circuit, endianness) | |
50 | { | |
51 | std::vector<unsigned char> before = { | |
52 | 0, 1, 2, 3, 4, 5, 6, 7, | |
53 | 8, 9, 10, 11, 12, 13, 14, 15, | |
54 | 16, 17, 18, 19, 20, 21, 22, 23, | |
55 | 24, 25, 26, 27, 28, 29, 30, 31, | |
56 | 32, 33, 34, 35, 36, 37, 38, 39, | |
57 | 40, 41, 42, 43, 44, 45, 46, 47, | |
58 | 48, 49, 50, 51, 52, 53, 54, 55, | |
59 | 56, 57, 58, 59, 60, 61, 62, 63 | |
60 | }; | |
61 | auto result = swap_endianness_u64(before); | |
62 | ||
63 | std::vector<unsigned char> after = { | |
64 | 56, 57, 58, 59, 60, 61, 62, 63, | |
65 | 48, 49, 50, 51, 52, 53, 54, 55, | |
66 | 40, 41, 42, 43, 44, 45, 46, 47, | |
67 | 32, 33, 34, 35, 36, 37, 38, 39, | |
68 | 24, 25, 26, 27, 28, 29, 30, 31, | |
69 | 16, 17, 18, 19, 20, 21, 22, 23, | |
70 | 8, 9, 10, 11, 12, 13, 14, 15, | |
71 | 0, 1, 2, 3, 4, 5, 6, 7 | |
72 | }; | |
73 | ||
74 | EXPECT_EQ(after, result); | |
75 | ||
76 | std::vector<unsigned char> bad = {0, 1, 2, 3}; | |
77 | ||
78 | ASSERT_THROW(swap_endianness_u64(bad), std::length_error); | |
79 | } | |
4d66f8f6 SB |
80 | |
81 | template<typename FieldT> | |
82 | bool test_merkle_gadget( | |
83 | bool enforce_a, | |
84 | bool enforce_b, | |
85 | bool write_root_first | |
86 | ) | |
87 | { | |
88 | protoboard<FieldT> pb; | |
89 | digest_variable<FieldT> root(pb, 256, "root"); | |
90 | pb.set_input_sizes(256); | |
91 | ||
92 | digest_variable<FieldT> commitment1(pb, 256, "commitment1"); | |
93 | digest_variable<FieldT> commitment2(pb, 256, "commitment2"); | |
94 | ||
95 | pb_variable<FieldT> commitment1_read; | |
96 | commitment1_read.allocate(pb); | |
97 | pb_variable<FieldT> commitment2_read; | |
98 | commitment2_read.allocate(pb); | |
99 | ||
100 | merkle_tree_gadget<FieldT> mgadget1(pb, commitment1, root, commitment1_read); | |
101 | merkle_tree_gadget<FieldT> mgadget2(pb, commitment2, root, commitment2_read); | |
102 | ||
103 | commitment1.generate_r1cs_constraints(); | |
104 | commitment2.generate_r1cs_constraints(); | |
105 | root.generate_r1cs_constraints(); | |
106 | mgadget1.generate_r1cs_constraints(); | |
107 | mgadget2.generate_r1cs_constraints(); | |
108 | ||
4fc309f0 | 109 | SproutMerkleTree tree; |
4d66f8f6 SB |
110 | uint256 commitment1_data = uint256S("54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c"); |
111 | uint256 commitment2_data = uint256S("59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7"); | |
112 | tree.append(commitment1_data); | |
113 | auto wit1 = tree.witness(); | |
114 | tree.append(commitment2_data); | |
115 | wit1.append(commitment2_data); | |
116 | auto wit2 = tree.witness(); | |
117 | auto expected_root = tree.root(); | |
118 | tree.append(uint256S("3e243c8798678570bb8d42616c23a536af44be15c4eef073490c2a44ae5f32c3")); | |
119 | auto unexpected_root = tree.root(); | |
120 | tree.append(uint256S("26d9b20c7f1c3d2528bbcd43cd63344b0afd3b6a0a8ebd37ec51cba34907bec7")); | |
121 | auto badwit1 = tree.witness(); | |
122 | tree.append(uint256S("02c2467c9cd15e0d150f74cd636505ed675b0b71b66a719f6f52fdb49a5937bb")); | |
123 | auto badwit2 = tree.witness(); | |
124 | ||
125 | // Perform the test | |
126 | ||
127 | pb.val(commitment1_read) = enforce_a ? FieldT::one() : FieldT::zero(); | |
128 | pb.val(commitment2_read) = enforce_b ? FieldT::one() : FieldT::zero(); | |
129 | ||
130 | commitment1.bits.fill_with_bits(pb, uint256_to_bool_vector(commitment1_data)); | |
131 | commitment2.bits.fill_with_bits(pb, uint256_to_bool_vector(commitment2_data)); | |
132 | ||
133 | if (write_root_first) { | |
134 | root.bits.fill_with_bits(pb, uint256_to_bool_vector(expected_root)); | |
135 | } | |
136 | ||
137 | mgadget1.generate_r1cs_witness(wit1.path()); | |
138 | mgadget2.generate_r1cs_witness(wit2.path()); | |
139 | ||
140 | // Overwrite with our expected root | |
141 | root.bits.fill_with_bits(pb, uint256_to_bool_vector(expected_root)); | |
142 | ||
143 | return pb.is_satisfied(); | |
144 | } | |
145 | ||
146 | TEST(circuit, merkle_tree_gadget_weirdness) | |
147 | { | |
148 | /* | |
149 | The merkle tree gadget takes a leaf in the merkle tree (the Note commitment), | |
150 | a merkle tree authentication path, and a root (anchor). It also takes a parameter | |
151 | called read_success, which is used to determine if the commitment actually needs to | |
152 | appear in the tree. | |
153 | ||
154 | If two input notes use the same root (which our protocol does) then if `read_success` | |
155 | is disabled on the first note but enabled on the second note (i.e., the first note | |
156 | has value of zero and second note has nonzero value) then there is an edge case in | |
157 | the witnessing behavior. The first witness will accidentally constrain the root to | |
158 | equal null (the default value of the anchor) and the second witness will actually | |
159 | copy the bits, violating the constraint system. | |
160 | ||
161 | Notice that this edge case is not in the constraint system but in the witnessing | |
162 | behavior. | |
163 | */ | |
164 | ||
4d66f8f6 SB |
165 | typedef Fr<default_r1cs_ppzksnark_pp> FieldT; |
166 | ||
167 | // Test the normal case | |
168 | ASSERT_TRUE(test_merkle_gadget<FieldT>(true, true, false)); | |
169 | ASSERT_TRUE(test_merkle_gadget<FieldT>(true, true, true)); | |
170 | ||
171 | // Test the case where the first commitment is enforced but the second isn't | |
172 | // Works because the first read is performed before the second one | |
173 | ASSERT_TRUE(test_merkle_gadget<FieldT>(true, false, false)); | |
174 | ASSERT_TRUE(test_merkle_gadget<FieldT>(true, false, true)); | |
175 | ||
176 | // Test the case where the first commitment isn't enforced but the second is | |
177 | // Doesn't work because the first multipacker witnesses the existing root (which | |
178 | // is null) | |
179 | ASSERT_TRUE(!test_merkle_gadget<FieldT>(false, true, false)); | |
180 | ||
181 | // Test the last again, except this time write the root first. | |
182 | ASSERT_TRUE(test_merkle_gadget<FieldT>(false, true, true)); | |
183 | } |