Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ecdsa_constraints.test.cpp
Go to the documentation of this file.
2#include "acir_format.hpp"
9
10#include <algorithm>
11#include <gtest/gtest.h>
12#include <vector>
13
14using namespace bb;
15using namespace bb::crypto;
16using namespace acir_format;
17
18template <class Curve> class EcdsaConstraintsTest : public ::testing::Test {
19 public:
20 using Builder = Curve::Builder;
21 using FrNative = Curve::fr;
22 using FqNative = Curve::fq;
23 using G1Native = Curve::g1;
25
26 // Reproducible test
27 static constexpr FrNative private_key =
28 FrNative("0xd67abee717b3fc725adf59e2cc8cd916435c348b277dd814a34e3ceb279436c2");
29
30 static size_t generate_ecdsa_constraint(EcdsaConstraint& ecdsa_constraint,
31 WitnessVector& witness_values,
32 bool tweak_pub_key_x = false,
33 bool tweak_pub_key_y = false)
34 {
35 std::string message_string = "Instructions unclear, ask again later.";
36
37 // Hash the message
38 std::vector<uint8_t> message_buffer(message_string.begin(), message_string.end());
39 std::array<uint8_t, 32> hashed_message = Sha256Hasher::hash(message_buffer);
40
41 // Generate ECDSA key pair
43 account.private_key = private_key;
44 account.public_key = G1Native::one * account.private_key;
45
46 // Generate signature
47 ecdsa_signature signature =
48 ecdsa_construct_signature<Sha256Hasher, FqNative, FrNative, G1Native>(message_string, account);
49
50 // Serialize public key coordinates into bytes
51 std::array<uint8_t, 32> buffer_x;
52 std::array<uint8_t, 32> buffer_y;
53 FqNative::serialize_to_buffer(account.public_key.x, &buffer_x[0]);
54 FqNative::serialize_to_buffer(account.public_key.y, &buffer_y[0]);
55 if (tweak_pub_key_x || tweak_pub_key_y) {
56 std::vector<uint8_t> modulus_plus_one = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
57 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
58 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30 };
59 for (auto [byte, tweaked_byte] : zip_view(tweak_pub_key_x ? buffer_x : buffer_y, modulus_plus_one)) {
60 byte = tweaked_byte;
61 }
62 }
63
64 // Create witness indices and witnesses
65 size_t num_variables = 0;
66
67 std::array<uint32_t, 32> hashed_message_indices =
68 add_to_witness_and_track_indices<uint8_t, 32>(witness_values, std::span(hashed_message));
69 num_variables += hashed_message_indices.size();
70
71 std::array<uint32_t, 32> pub_x_indices =
72 add_to_witness_and_track_indices<uint8_t, 32>(witness_values, std::span(buffer_x));
73 num_variables += pub_x_indices.size();
74
75 std::array<uint32_t, 32> pub_y_indices =
76 add_to_witness_and_track_indices<uint8_t, 32>(witness_values, std::span(buffer_y));
77 num_variables += pub_y_indices.size();
78
79 std::array<uint32_t, 32> r_indices =
80 add_to_witness_and_track_indices<uint8_t, 32>(witness_values, std::span(signature.r));
81 num_variables += r_indices.size();
82
83 std::array<uint32_t, 32> s_indices =
84 add_to_witness_and_track_indices<uint8_t, 32>(witness_values, std::span(signature.s));
85 num_variables += s_indices.size();
86
87 uint32_t result_index = static_cast<uint32_t>(num_variables);
88 bb::fr result = bb::fr::one();
89 witness_values.emplace_back(result);
90 num_variables += 1;
91
92 // Restructure vectors into array
93 std::array<uint32_t, 64> signature_indices;
94 std::ranges::copy(r_indices, signature_indices.begin());
95 std::ranges::copy(s_indices, signature_indices.begin() + 32);
96
97 ecdsa_constraint = EcdsaConstraint{ .hashed_message = hashed_message_indices,
98 .signature = signature_indices,
99 .pub_x_indices = pub_x_indices,
100 .pub_y_indices = pub_y_indices,
102 .result = result_index };
103
104 return num_variables;
105 }
106
108 bool tweak_pub_key_y = false)
109 {
110 EcdsaConstraint ecdsa_constraint;
111 WitnessVector witness_values;
112 size_t num_variables =
113 generate_ecdsa_constraint(ecdsa_constraint, witness_values, tweak_pub_key_x, tweak_pub_key_y);
114 AcirFormat constraint_system = {
115 .varnum = static_cast<uint32_t>(num_variables),
116 .num_acir_opcodes = 1,
117 .public_inputs = {},
118 .original_opcode_indices = create_empty_original_opcode_indices(),
119 };
120
121 if constexpr (Curve::type == bb::CurveType::SECP256K1) {
122 constraint_system.ecdsa_k1_constraints = { ecdsa_constraint };
123 } else {
124 constraint_system.ecdsa_r1_constraints = { ecdsa_constraint };
125 }
126
127 mock_opcode_indices(constraint_system);
128
129 return { constraint_system, witness_values };
130 }
131
132 protected:
134};
135
136using CurveTypes = testing::Types<stdlib::secp256k1<MegaCircuitBuilder>,
140
142
143TYPED_TEST(EcdsaConstraintsTest, GenerateVKFromConstraints)
144{
145 using Flavor = TestFixture::Flavor;
146 using Builder = TestFixture::Builder;
147 using ProvingKey = ProverInstance_<Flavor>;
149
150 auto [constraint_system, witness_values] = TestFixture::generate_constraint_system();
151
152 std::shared_ptr<VerificationKey> vk_from_witness;
153 {
154 AcirProgram program{ constraint_system, witness_values };
155 auto builder = create_circuit<Builder>(program);
156 info("Num gates: ", builder.get_estimated_num_finalized_gates());
157
158 auto prover_instance = std::make_shared<ProvingKey>(builder);
159 vk_from_witness = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
160
161 // Validate the builder
162 EXPECT_TRUE(CircuitChecker::check(builder));
163 }
164
165 std::shared_ptr<VerificationKey> vk_from_constraint;
166 {
167 AcirProgram program{ constraint_system, /*witness=*/{} };
168 auto builder = create_circuit<Builder>(program);
169 auto prover_instance = std::make_shared<ProvingKey>(builder);
170 vk_from_constraint = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
171 }
172
173 EXPECT_EQ(*vk_from_witness, *vk_from_constraint);
174}
175
176// Validate the predicate for EcdsaConstraint
178{
179 using Builder = TestFixture::Builder;
180 auto [constraint_system, witness_values] = TestFixture::generate_constraint_system();
181
182 // Create a predicate witness or constant which takes the index of the last witness in the array
183 auto predicate = WitnessOrConstant<fr>::from_index(static_cast<uint32_t>(witness_values.size()));
184
185 witness_values.push_back(fr(1));
186 if (constraint_system.ecdsa_k1_constraints.size() == 1) {
187 constraint_system.ecdsa_k1_constraints[0].predicate = predicate;
188 } else if (constraint_system.ecdsa_r1_constraints.size() == 1) {
189 constraint_system.ecdsa_r1_constraints[0].predicate = predicate;
190 }
191
192 // Correct input AND true predicate => Valid Circuit
193 {
194 AcirProgram program{ constraint_system, witness_values };
195 auto builder = create_circuit<Builder>(program);
196
197 info("Num gates: ", builder.get_estimated_num_finalized_gates());
198
199 // Validate the builder
200 EXPECT_TRUE(CircuitChecker::check(builder));
201 }
202 // Correct input AND false predicate => Valid Circuit
203 witness_values.back() = fr(0);
204 {
205 AcirProgram program{ constraint_system, witness_values };
206 auto builder = create_circuit<Builder>(program);
207
208 info("Num gates: ", builder.get_estimated_num_finalized_gates());
209
210 // Validate the builder
211 EXPECT_TRUE(CircuitChecker::check(builder));
212 }
213 // Incorrect input AND false predicate => Valid Circuit
214 witness_values[40] = fr(0); // change a byte in the signature
215 {
216 AcirProgram program{ constraint_system, witness_values };
217 auto builder = create_circuit<Builder>(program);
218
219 info("Num gates: ", builder.get_estimated_num_finalized_gates());
220
221 // Validate the builder
222 EXPECT_TRUE(CircuitChecker::check(builder));
223 }
224 // Incorrect input AND true predicate => Invalid Circuit
225 witness_values.back() = fr(1);
226 {
227 AcirProgram program{ constraint_system, witness_values };
228 auto builder = create_circuit<Builder>(program);
229
230 info("Num gates: ", builder.get_estimated_num_finalized_gates());
231
232 EXPECT_TRUE(builder.failed());
233 }
234}
235
237{
238 // Disable asserts otherwise the test fails because the public keys are not on the curve
240
241 for (size_t idx = 0; idx < 2; idx++) {
242 bool tweak_x = idx == 0;
243 bool tweak_y = idx == 1;
244 std::string failure_msg =
245 idx == 0
246 ? "ECDSA input validation: the x coordinate of the public key is larger than Fq::modulus: hi limb."
247 : "ECDSA input validation: the y coordinate of the public key is larger than Fq::modulus: hi limb.";
248
249 using Builder = TestFixture::Builder;
250
251 auto [constraint_system, witness_values] =
252 TestFixture::generate_constraint_system(/*tweak_pub_key_x=*/tweak_x, /*tweak_pub_key_y=*/tweak_y);
253
254 AcirProgram program{ constraint_system, witness_values };
255 auto builder = create_circuit<Builder>(program);
256
257 // Validate the builder
258 EXPECT_FALSE(CircuitChecker::check(builder));
259
260 // Check error message
261 EXPECT_EQ(builder.err(), failure_msg);
262 }
263}
acir_format::AcirFormatOriginalOpcodeIndices create_empty_original_opcode_indices()
void mock_opcode_indices(acir_format::AcirFormat &constraint_system)
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:32
static size_t generate_ecdsa_constraint(EcdsaConstraint &ecdsa_constraint, WitnessVector &witness_values, bool tweak_pub_key_x=false, bool tweak_pub_key_y=false)
std::conditional_t< std::is_same_v< Builder, UltraCircuitBuilder >, UltraFlavor, MegaFlavor > Flavor
static std::pair< AcirFormat, WitnessVector > generate_constraint_system(bool tweak_pub_key_x=false, bool tweak_pub_key_y=false)
static constexpr FrNative private_key
The verification key is responsible for storing the commitments to the precomputed (non-witnessk) pol...
A ProverInstance is normally constructed from a finalized circuit and it contains all the information...
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
void info(Args... args)
Definition log.hpp:74
AluTraceBuilder builder
Definition alu.test.cpp:123
UltraKeccakFlavor::VerificationKey VerificationKey
bb::SlabVector< bb::fr > WitnessVector
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
TYPED_TEST_SUITE(BoomerangRecursiveVerifierTest, Flavors)
Entry point for Barretenberg command-line interface.
field< Bn254FrParams > fr
Definition fr.hpp:174
::testing::Types< curve::BN254, curve::Grumpkin > CurveTypes
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
@ SECP256K1
Definition types.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::vector< EcdsaConstraint > ecdsa_r1_constraints
std::vector< EcdsaConstraint > ecdsa_k1_constraints
std::array< uint32_t, 32 > hashed_message
static WitnessOrConstant from_index(uint32_t index)
static WitnessOrConstant from_constant(FF value)
static auto hash(const B &message)
Definition hashers.hpp:36
G1::affine_element public_key
Definition ecdsa.hpp:20
std::array< uint8_t, 32 > r
Definition ecdsa.hpp:26
std::array< uint8_t, 32 > s
Definition ecdsa.hpp:27
static constexpr field one()