Skip to content

Commit

Permalink
feat!: Use fixed size arrays in black box functions where sizes are k…
Browse files Browse the repository at this point in the history
…nown (#5620)

This PR enforces the sizes of inputs/outputs of blackbox functions to
remove some runtime checks and allocations.

---------

Co-authored-by: guipublic <guipublic@gmail.com>
Co-authored-by: vezenovm <mvezenov@gmail.com>
  • Loading branch information
3 people authored Apr 16, 2024
1 parent 2907142 commit f50b180
Show file tree
Hide file tree
Showing 33 changed files with 497 additions and 431 deletions.
10 changes: 10 additions & 0 deletions avm-transpiler/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass)
});
}

std::vector<uint32_t> signature(64);
std::array<uint32_t, 64> signature;
for (uint32_t i = 0, value = 12; i < 64; i++, value++) {
signature[i] = value;
range_constraints.push_back(RangeConstraint{
Expand Down Expand Up @@ -289,7 +289,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange)
});
}

std::vector<uint32_t> signature(64);
std::array<uint32_t, 64> signature;
for (uint32_t i = 0, value = 12; i < 64; i++, value++) {
signature[i] = value;
range_constraints.push_back(RangeConstraint{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ struct Blake2sInput {

struct Blake2sConstraint {
std::vector<Blake2sInput> inputs;
std::vector<uint32_t> result;
std::array<uint32_t, 32> result;

// For serialization, update with any new fields
MSGPACK_FIELDS(inputs, result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ struct Blake3Input {

struct Blake3Constraint {
std::vector<Blake3Input> inputs;
std::vector<uint32_t> result;
std::array<uint32_t, 32> result;

// For serialization, update with any new fields
MSGPACK_FIELDS(inputs, result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,6 @@ namespace acir_format {

using namespace bb::plonk;

template <typename Builder>
crypto::ecdsa_signature ecdsa_convert_signature(Builder& builder, std::vector<uint32_t> signature)
{

crypto::ecdsa_signature signature_cr;

// Get the witness assignment for each witness index
// Write the witness assignment to the byte_array

for (unsigned int i = 0; i < 32; i++) {
auto witness_index = signature[i];

std::vector<uint8_t> fr_bytes(sizeof(fr));

fr value = builder.get_variable(witness_index);

fr::serialize_to_buffer(value, &fr_bytes[0]);

signature_cr.r[i] = fr_bytes.back();
}

for (unsigned int i = 32; i < 64; i++) {
auto witness_index = signature[i];

std::vector<uint8_t> fr_bytes(sizeof(fr));

fr value = builder.get_variable(witness_index);

fr::serialize_to_buffer(value, &fr_bytes[0]);

signature_cr.s[i - 32] = fr_bytes.back();
}

signature_cr.v = 27;

return signature_cr;
}

template <typename Builder>
secp256k1_ct::g1_ct ecdsa_convert_inputs(Builder* ctx, const secp256k1::g1::affine_element& input)
{
Expand All @@ -63,9 +25,9 @@ secp256k1_ct::g1_ct ecdsa_convert_inputs(Builder* ctx, const secp256k1::g1::affi
// vector of bytes here, assumes that the witness indices point to a field element which can be represented
// with just a byte.
// notice that this function truncates each field_element to a byte
template <typename Builder>
bb::stdlib::byte_array<Builder> ecdsa_vector_of_bytes_to_byte_array(Builder& builder,
std::vector<uint32_t> vector_of_bytes)
template <std::size_t SIZE, typename Builder>
bb::stdlib::byte_array<Builder> ecdsa_array_of_bytes_to_byte_array(Builder& builder,
std::array<uint32_t, SIZE> vector_of_bytes)
{
using byte_array_ct = bb::stdlib::byte_array<Builder>;
using field_ct = bb::stdlib::field_t<Builder>;
Expand Down Expand Up @@ -106,9 +68,9 @@ void create_ecdsa_k1_verify_constraints(Builder& builder,

auto new_sig = ecdsa_convert_signature(builder, input.signature);

byte_array_ct message = ecdsa_vector_of_bytes_to_byte_array(builder, input.hashed_message);
auto pub_key_x_byte_arr = ecdsa_vector_of_bytes_to_byte_array(builder, input.pub_x_indices);
auto pub_key_y_byte_arr = ecdsa_vector_of_bytes_to_byte_array(builder, input.pub_y_indices);
byte_array_ct message = ecdsa_array_of_bytes_to_byte_array(builder, input.hashed_message);
auto pub_key_x_byte_arr = ecdsa_array_of_bytes_to_byte_array(builder, input.pub_x_indices);
auto pub_key_y_byte_arr = ecdsa_array_of_bytes_to_byte_array(builder, input.pub_y_indices);

auto pub_key_x_fq = typename secp256k1_ct::fq_ct(pub_key_x_byte_arr);
auto pub_key_y_fq = typename secp256k1_ct::fq_ct(pub_key_y_byte_arr);
Expand Down Expand Up @@ -153,11 +115,10 @@ void create_ecdsa_k1_verify_constraints(Builder& builder,
template <typename Builder> void dummy_ecdsa_constraint(Builder& builder, EcdsaSecp256k1Constraint const& input)
{

std::vector<uint32_t> pub_x_indices_;
std::vector<uint32_t> pub_y_indices_;
std::vector<uint32_t> signature_;
std::vector<uint32_t> message_indices_;
signature_.resize(64);
std::array<uint32_t, 32> pub_x_indices_;
std::array<uint32_t, 32> pub_y_indices_;
std::array<uint32_t, 64> signature_;
std::array<uint32_t, 32> message_indices_;

// Create a valid signature with a valid public key
crypto::ecdsa_key_pair<secp256k1_ct::fr, secp256k1_ct::g1> account;
Expand All @@ -179,9 +140,9 @@ template <typename Builder> void dummy_ecdsa_constraint(Builder& builder, EcdsaS
uint32_t y_wit = builder.add_variable(pub_y_value.slice(248 - i * 8, 256 - i * 8));
uint32_t r_wit = builder.add_variable(signature.r[i]);
uint32_t s_wit = builder.add_variable(signature.s[i]);
message_indices_.emplace_back(m_wit);
pub_x_indices_.emplace_back(x_wit);
pub_y_indices_.emplace_back(y_wit);
message_indices_[i] = m_wit;
pub_x_indices_[i] = x_wit;
pub_y_indices_[i] = y_wit;
signature_[i] = r_wit;
signature_[i + 32] = s_wit;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ namespace acir_format {

struct EcdsaSecp256k1Constraint {
// This is the byte representation of the hashed message.
std::vector<uint32_t> hashed_message;
std::array<uint32_t, 32> hashed_message;

// This is the computed signature
//
std::vector<uint32_t> signature;
std::array<uint32_t, 64> signature;

// This is the supposed public key which signed the
// message, giving rise to the signature.
// Since Fr does not have enough bits to represent
// the prime field in secp256k1, a byte array is used.
// Can also use low and hi where lo=128 bits
std::vector<uint32_t> pub_x_indices;
std::vector<uint32_t> pub_y_indices;
std::array<uint32_t, 32> pub_x_indices;
std::array<uint32_t, 32> pub_y_indices;

// This is the result of verifying the signature
uint32_t result;
Expand All @@ -37,11 +37,51 @@ void create_ecdsa_k1_verify_constraints(Builder& builder,

template <typename Builder> void dummy_ecdsa_constraint(Builder& builder, EcdsaSecp256k1Constraint const& input);

template <typename Builder>
crypto::ecdsa_signature ecdsa_convert_signature(Builder& builder, std::vector<uint32_t> signature);
witness_ct ecdsa_index_to_witness(Builder& builder, uint32_t index);
template <std::size_t SIZE, typename Builder>
bb::stdlib::byte_array<Builder> ecdsa_array_of_bytes_to_byte_array(Builder& builder,
std::array<uint32_t, SIZE> vector_of_bytes);

// We have the implementation of this template in the header as this method is used
// by other ecdsa constraints over different curves (e.g. secp256r1).
// gcc needs to be able to see the implementation order to generate code for
// all Builder specializations (e.g. bb::Goblin::Builder vs. bb::UltraCircuitBuilder)
template <typename Builder>
bb::stdlib::byte_array<Builder> ecdsa_vector_of_bytes_to_byte_array(Builder& builder,
std::vector<uint32_t> vector_of_bytes);
crypto::ecdsa_signature ecdsa_convert_signature(Builder& builder, std::array<uint32_t, 64> signature)
{

crypto::ecdsa_signature signature_cr;

// Get the witness assignment for each witness index
// Write the witness assignment to the byte_array

for (unsigned int i = 0; i < 32; i++) {
auto witness_index = signature[i];

std::vector<uint8_t> fr_bytes(sizeof(fr));

fr value = builder.get_variable(witness_index);

fr::serialize_to_buffer(value, &fr_bytes[0]);

signature_cr.r[i] = fr_bytes.back();
}

for (unsigned int i = 32; i < 64; i++) {
auto witness_index = signature[i];

std::vector<uint8_t> fr_bytes(sizeof(fr));

fr value = builder.get_variable(witness_index);

fr::serialize_to_buffer(value, &fr_bytes[0]);

signature_cr.s[i - 32] = fr_bytes.back();
}

signature_cr.v = 27;

return signature_cr;
}

} // namespace acir_format
Original file line number Diff line number Diff line change
Expand Up @@ -37,35 +37,35 @@ size_t generate_ecdsa_constraint(EcdsaSecp256k1Constraint& ecdsa_constraint, Wit
uint256_t pub_x_value = account.public_key.x;
uint256_t pub_y_value = account.public_key.y;

std::vector<uint32_t> message_in;
std::vector<uint32_t> pub_x_indices_in;
std::vector<uint32_t> pub_y_indices_in;
std::vector<uint32_t> signature_in;
std::array<uint32_t, 32> message_in;
std::array<uint32_t, 32> pub_x_indices_in;
std::array<uint32_t, 32> pub_y_indices_in;
std::array<uint32_t, 64> signature_in;
size_t offset = 0;
for (size_t i = 0; i < hashed_message.size(); ++i) {
message_in.emplace_back(i + offset);
message_in[i] = static_cast<uint32_t>(i + offset);
const auto byte = static_cast<uint8_t>(hashed_message[i]);
witness_values.emplace_back(byte);
}
offset += message_in.size();

for (size_t i = 0; i < 32; ++i) {
pub_x_indices_in.emplace_back(i + offset);
pub_x_indices_in[i] = static_cast<uint32_t>(i + offset);
witness_values.emplace_back(pub_x_value.slice(248 - i * 8, 256 - i * 8));
}
offset += pub_x_indices_in.size();
for (size_t i = 0; i < 32; ++i) {
pub_y_indices_in.emplace_back(i + offset);
pub_y_indices_in[i] = static_cast<uint32_t>(i + offset);
witness_values.emplace_back(pub_y_value.slice(248 - i * 8, 256 - i * 8));
}
offset += pub_y_indices_in.size();
for (size_t i = 0; i < 32; ++i) {
signature_in.emplace_back(i + offset);
signature_in[i] = static_cast<uint32_t>(i + offset);
witness_values.emplace_back(signature.r[i]);
}
offset += signature.r.size();
for (size_t i = 0; i < 32; ++i) {
signature_in.emplace_back(i + offset);
signature_in[i + 32] = static_cast<uint32_t>(i + offset);
witness_values.emplace_back(signature.s[i]);
}
offset += signature.s.size();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,17 @@ void create_ecdsa_r1_verify_constraints(Builder& builder,
using secp256r1_ct = bb::stdlib::secp256r1<Builder>;
using bool_ct = bb::stdlib::bool_t<Builder>;
using field_ct = bb::stdlib::field_t<Builder>;
using byte_array_ct = bb::stdlib::byte_array<Builder>;

if (has_valid_witness_assignments == false) {
dummy_ecdsa_constraint(builder, input);
}

auto new_sig = ecdsa_convert_signature(builder, input.signature);

auto message = ecdsa_vector_of_bytes_to_byte_array(builder, input.hashed_message);
auto pub_key_x_byte_arr = ecdsa_vector_of_bytes_to_byte_array(builder, input.pub_x_indices);
auto pub_key_y_byte_arr = ecdsa_vector_of_bytes_to_byte_array(builder, input.pub_y_indices);
byte_array_ct message = ecdsa_array_of_bytes_to_byte_array(builder, input.hashed_message);
auto pub_key_x_byte_arr = ecdsa_array_of_bytes_to_byte_array(builder, input.pub_x_indices);
auto pub_key_y_byte_arr = ecdsa_array_of_bytes_to_byte_array(builder, input.pub_y_indices);

auto pub_key_x_fq = typename secp256r1_ct::fq_ct(pub_key_x_byte_arr);
auto pub_key_y_fq = typename secp256r1_ct::fq_ct(pub_key_y_byte_arr);
Expand Down Expand Up @@ -87,11 +88,10 @@ void create_ecdsa_r1_verify_constraints(Builder& builder,
template <typename Builder> void dummy_ecdsa_constraint(Builder& builder, EcdsaSecp256r1Constraint const& input)
{

std::vector<uint32_t> pub_x_indices_;
std::vector<uint32_t> pub_y_indices_;
std::vector<uint32_t> signature_;
std::vector<uint32_t> message_indices_;
signature_.resize(64);
std::array<uint32_t, 32> pub_x_indices_;
std::array<uint32_t, 32> pub_y_indices_;
std::array<uint32_t, 64> signature_;
std::array<uint32_t, 32> message_indices_;

// Create a valid signature with a valid public key
std::string message_string = "Instructions unclear, ask again later.";
Expand Down Expand Up @@ -121,9 +121,9 @@ template <typename Builder> void dummy_ecdsa_constraint(Builder& builder, EcdsaS
uint32_t y_wit = builder.add_variable(pub_y_value.slice(248 - i * 8, 256 - i * 8));
uint32_t r_wit = builder.add_variable(signature.r[i]);
uint32_t s_wit = builder.add_variable(signature.s[i]);
message_indices_.emplace_back(m_wit);
pub_x_indices_.emplace_back(x_wit);
pub_y_indices_.emplace_back(y_wit);
message_indices_[i] = m_wit;
pub_x_indices_[i] = x_wit;
pub_y_indices_[i] = y_wit;
signature_[i] = r_wit;
signature_[i + 32] = s_wit;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ namespace acir_format {

struct EcdsaSecp256r1Constraint {
// This is the byte representation of the hashed message.
std::vector<uint32_t> hashed_message;
std::array<uint32_t, 32> hashed_message;

// This is the supposed public key which signed the
// message, giving rise to the signature.
// Since Fr does not have enough bits to represent
// the prime field in secp256r1, a byte array is used.
// Can also use low and hi where lo=128 bits
std::vector<uint32_t> pub_x_indices;
std::vector<uint32_t> pub_y_indices;
std::array<uint32_t, 32> pub_x_indices;
std::array<uint32_t, 32> pub_y_indices;

// This is the result of verifying the signature
uint32_t result;

// This is the computed signature
//
std::vector<uint32_t> signature;
std::array<uint32_t, 64> signature;

friend bool operator==(EcdsaSecp256r1Constraint const& lhs, EcdsaSecp256r1Constraint const& rhs) = default;
};
Expand Down
Loading

0 comments on commit f50b180

Please sign in to comment.