Skip to content

Commit

Permalink
Assertions for groth16 public inputs parser (#19014)
Browse files Browse the repository at this point in the history
## Description 

Adds assertions to the public inputs from bytes function. The inputs are
expected to be concatenated which has caused some confusion. These new
assertions should give a more meaningful error msg to devs.

This also adds a `flatten` function for vectors.

## Test plan 

Unit tests.

---

## Release notes

Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.

For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.

- [x] Protocol: Fail fast on invalid public inputs to Groth16 zk-proof
verification. Add a `flatten` Move function which flattens a vector of
vectors into a single vector. This introduces a new protocol version 59.
- [ ] Nodes (Validators and Full nodes): 
- [ ] Indexer: 
- [ ] JSON-RPC: 
- [ ] GraphQL: 
- [ ] CLI: 
- [ ] Rust SDK:
- [ ] REST API:

---------

Co-authored-by: Todd Nowacki <[email protected]>
  • Loading branch information
jonas-lj and tnowacki authored Sep 10, 2024
1 parent 055c20c commit 6b37277
Show file tree
Hide file tree
Showing 17 changed files with 1,216 additions and 169 deletions.
28 changes: 28 additions & 0 deletions crates/sui-framework/docs/move-stdlib/vector.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ vectors are growable. This module has many native functions.
- [Function `remove`](#0x1_vector_remove)
- [Function `insert`](#0x1_vector_insert)
- [Function `swap_remove`](#0x1_vector_swap_remove)
- [Function `flatten`](#0x1_vector_flatten)


<pre><code></code></pre>
Expand Down Expand Up @@ -509,4 +510,31 @@ Aborts if <code>i</code> is out of bounds.



</details>

<a name="0x1_vector_flatten"></a>

## Function `flatten`

Concatenate the vectors of <code>v</code> into a single vector, keeping the order of the elements.


<pre><code><b>public</b> <b>fun</b> <a href="../move-stdlib/vector.md#0x1_vector_flatten">flatten</a>&lt;T&gt;(v: <a href="../move-stdlib/vector.md#0x1_vector">vector</a>&lt;<a href="../move-stdlib/vector.md#0x1_vector">vector</a>&lt;T&gt;&gt;): <a href="../move-stdlib/vector.md#0x1_vector">vector</a>&lt;T&gt;
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="../move-stdlib/vector.md#0x1_vector_flatten">flatten</a>&lt;T&gt;(v: <a href="../move-stdlib/vector.md#0x1_vector">vector</a>&lt;<a href="../move-stdlib/vector.md#0x1_vector">vector</a>&lt;T&gt;&gt;): <a href="../move-stdlib/vector.md#0x1_vector">vector</a>&lt;T&gt; {
<b>let</b> <b>mut</b> r = <a href="../move-stdlib/vector.md#0x1_vector">vector</a>[];
v.do!(|u| r.<a href="../move-stdlib/vector.md#0x1_vector_append">append</a>(u));
r
}
</code></pre>



</details>
23 changes: 22 additions & 1 deletion crates/sui-framework/docs/sui-framework/groth16.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,15 @@ A <code><a href="groth16.md#0x2_groth16_ProofPoints">ProofPoints</a></code> wrap



<a name="0x2_groth16_EInvalidScalar"></a>



<pre><code><b>const</b> <a href="groth16.md#0x2_groth16_EInvalidScalar">EInvalidScalar</a>: <a href="../move-stdlib/u64.md#0x1_u64">u64</a> = 3;
</code></pre>



<a name="0x2_groth16_EInvalidVerifyingKey"></a>


Expand All @@ -188,6 +197,15 @@ A <code><a href="groth16.md#0x2_groth16_ProofPoints">ProofPoints</a></code> wrap



<a name="0x2_groth16_MaxPublicInputs"></a>



<pre><code><b>const</b> <a href="groth16.md#0x2_groth16_MaxPublicInputs">MaxPublicInputs</a>: <a href="../move-stdlib/u64.md#0x1_u64">u64</a> = 8;
</code></pre>



<a name="0x2_groth16_bls12381"></a>

## Function `bls12381`
Expand Down Expand Up @@ -298,7 +316,8 @@ Returns bytes of the four components of the <code><a href="groth16.md#0x2_groth1

## Function `public_proof_inputs_from_bytes`

Creates a <code><a href="groth16.md#0x2_groth16_PublicProofInputs">PublicProofInputs</a></code> wrapper from bytes.
Creates a <code><a href="groth16.md#0x2_groth16_PublicProofInputs">PublicProofInputs</a></code> wrapper from bytes. The <code>bytes</code> parameter should be a concatenation of a number of
32 bytes scalar field elements to be used as public inputs in little-endian format to a circuit.


<pre><code><b>public</b> <b>fun</b> <a href="groth16.md#0x2_groth16_public_proof_inputs_from_bytes">public_proof_inputs_from_bytes</a>(bytes: <a href="../move-stdlib/vector.md#0x1_vector">vector</a>&lt;u8&gt;): <a href="groth16.md#0x2_groth16_PublicProofInputs">groth16::PublicProofInputs</a>
Expand All @@ -311,6 +330,8 @@ Creates a <code><a href="groth16.md#0x2_groth16_PublicProofInputs">PublicProofIn


<pre><code><b>public</b> <b>fun</b> <a href="groth16.md#0x2_groth16_public_proof_inputs_from_bytes">public_proof_inputs_from_bytes</a>(bytes: <a href="../move-stdlib/vector.md#0x1_vector">vector</a>&lt;u8&gt;): <a href="groth16.md#0x2_groth16_PublicProofInputs">PublicProofInputs</a> {
<b>assert</b>!(bytes.length() % 32 == 0, <a href="groth16.md#0x2_groth16_EInvalidScalar">EInvalidScalar</a>);
<b>assert</b>!(bytes.length() / 32 &lt;= <a href="groth16.md#0x2_groth16_MaxPublicInputs">MaxPublicInputs</a>, <a href="groth16.md#0x2_groth16_ETooManyPublicInputs">ETooManyPublicInputs</a>);
<a href="groth16.md#0x2_groth16_PublicProofInputs">PublicProofInputs</a> { bytes }
}
</code></pre>
Expand Down
7 changes: 7 additions & 0 deletions crates/sui-framework/packages/move-stdlib/sources/vector.move
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,13 @@ module std::vector {
acc
}

/// Concatenate the vectors of `v` into a single vector, keeping the order of the elements.
public fun flatten<T>(v: vector<vector<T>>): vector<T> {
let mut r = vector[];
v.do!(|u| r.append(u));
r
}

/// Whether any element in the vector `v` satisfies the predicate `f`.
/// If the vector is empty, returns `false`.
public macro fun any<$T>($v: &vector<$T>, $f: |&$T| -> bool): bool {
Expand Down
10 changes: 10 additions & 0 deletions crates/sui-framework/packages/move-stdlib/tests/vector_tests.move
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,16 @@ module std::vector_tests {
assert!(r.fold!(10, |acc, e| acc + e) == 16);
}

#[test]
fun test_flatten() {
assert!(vector<vector<u8>>[].flatten().is_empty());
assert!(vector<vector<u8>>[vector[], vector[]].flatten().is_empty());
assert!(vector[vector[1]].flatten() == vector[1]);
assert!(vector[vector[1], vector[]].flatten() == vector[1]);
assert!(vector[vector[1], vector[2]].flatten() == vector[1, 2]);
assert!(vector[vector[1], vector[2, 3]].flatten() == vector[1, 2, 3]);
}

#[test]
fun any_all_macro() {
assert!(vector<u8>[].any!(|e| *e == 2) == false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ module sui::groth16 {
// Error if the given curve is not supported
const EInvalidCurve: u64 = 1;

#[allow(unused_const)]
// Error if the number of public inputs given exceeds the max.
const ETooManyPublicInputs: u64 = 2;

// Error a public input does not have the correct length.
const EInvalidScalar: u64 = 3;

// We need to set an upper bound on the number of public inputs to avoid a DoS attack.
const MaxPublicInputs: u64 = 8; // This must match the corresponding constant in the native verify function.

/// Represents an elliptic curve construction to be used in the verifier. Currently we support BLS12-381 and BN254.
/// This should be given as the first parameter to `prepare_verifying_key` or `verify_groth16_proof`.
public struct Curve has store, copy, drop {
Expand Down Expand Up @@ -60,8 +65,11 @@ module sui::groth16 {
bytes: vector<u8>,
}

/// Creates a `PublicProofInputs` wrapper from bytes.
/// Creates a `PublicProofInputs` wrapper from bytes. The `bytes` parameter should be a concatenation of a number of
/// 32 bytes scalar field elements to be used as public inputs in little-endian format to a circuit.
public fun public_proof_inputs_from_bytes(bytes: vector<u8>): PublicProofInputs {
assert!(bytes.length() % 32 == 0, EInvalidScalar);
assert!(bytes.length() / 32 <= MaxPublicInputs, ETooManyPublicInputs);
PublicProofInputs { bytes }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ module sui::groth16_tests {
groth16::prepare_verifying_key(&bls12381(), &invalid_vk);
}

#[test]
#[expected_failure(abort_code = groth16::EInvalidScalar)]
fun test_invalid_public_inputs() {
let public_inputs_too_short = vector[x"440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849", x"1234"].flatten();
groth16::public_proof_inputs_from_bytes(public_inputs_too_short);
}

#[test]
fun test_verify_groth_16_proof_bls12381() {
let curve = bls12381();
Expand All @@ -60,33 +67,27 @@ module sui::groth16_tests {
let invalid_pvk = groth16::pvk_from_bytes(vk_bytes, alpha_bytes, gamma_bytes, delta_bytes);
assert!(groth16::verify_groth16_proof(&curve, &invalid_pvk, &inputs, &proof) == false);

// Invalid public inputs bytes.
let invalid_inputs = groth16::public_proof_inputs_from_bytes(x"cf");
assert!(groth16::verify_groth16_proof(&curve, &pvk, &invalid_inputs, &proof) == false);

// Invalid proof bytes.
let invalid_proof = groth16::proof_points_from_bytes(x"4a");
assert!(groth16::verify_groth16_proof(&curve, &pvk, &inputs, &invalid_proof) == false);
}

#[test]
#[expected_failure(abort_code = groth16::ETooManyPublicInputs)]
fun test_too_many_public_inputs_bls12381() {
let curve = bls12381();

let vk_bytes = x"ada3c24e8c2e63579cc03fd1f112a093a17fc8ab0ff6eee7e04cab7bf8e03e7645381f309ec113309e05ac404c77ac7c8585d5e4328594f5a70a81f6bd4f29073883ee18fd90e2aa45d0fc7376e81e2fdf5351200386f5732e58eb6ff4d318dc";
let alpha_bytes = x"8b0f85a9e7d929244b0af9a35af10717bd667b6227aae37a6d336e815fb0d850873e0d87968345a493b2d31aa8aa400d9820af1d35fa862d1b339ea1f98ac70db7faa304bff120a151a1741d782d08b8f1c1080d4d2f3ebee63ac6cadc666605be306de0973be38fbbf0f54b476bbb002a74ff9506a2b9b9a34b99bfa7481a84a2c9face7065c19d7069cc5738c5350b886a5eeebe656499d2ffb360afc7aff20fa9ee689fb8b46863e90c85224e8f597bf323ad4efb02ee96eb40221fc89918a2c740eabd2886476c7f247a3eb34f0106b3b51cf040e2cdcafea68b0d8eecabf58b5aa2ece3d86259cf2dfa3efab1170c6eb11948826def533849b68335d76d60f3e16bb5c629b1c24df2bdd1a7f13c754d7fe38617ecd7783504e4615e5c13168185cc08de8d63a0f7032ab7e82ff78cf0bc46a84c98f2d95bb5af355cbbe525c44d5c1549c169dfe119a219dbf9038ec73729d187bd0e3ed369e4a2ec2be837f3dcfd958aea7110627d2c0192d262f17e722509c17196005b646a556cf010ef9bd2a2a9b937516a5ecdee516e77d14278e96bc891b630fc833dda714343554ae127c49460416430b7d4f048d08618058335dec0728ad37d10dd9d859c385a38673e71cc98e8439da0accc29de5c92d3c3dc98e199361e9f7558e8b0a2a315ccc5a72f54551f07fad6f6f4615af498aba98aea01a13a4eb84667fd87ee9782b1d812a03f8814f042823a7701238d0fec1e7dec2a26ffea00330b5c7930e95138381435d2a59f51313a48624e30b0a685e357874d41a0a19d83f7420c1d9c04";
let gamma_bytes = x"b675d1ff988116d1f2965d3c0c373569b74d0a1762ea7c4f4635faa5b5a8fa198a2a2ce6153f390a658dc9ad01a415491747e9de7d5f493f59cf05a52eb46eaac397ffc47aef1396cf0d8b75d0664077ea328ad6b63284b42972a8f11c523a60";
let delta_bytes = x"8229cb9443ef1fb72887f917f500e2aef998717d91857bcb92061ecd74d1d24c2b2b282736e8074e4316939b4c9853c117aa08ed49206860d648818b2cccb526585f5790161b1730d39c73603b482424a27bba891aaa6d99f3025d3df2a6bd42";
let pvk = groth16::pvk_from_bytes(vk_bytes, alpha_bytes, gamma_bytes, delta_bytes);

let inputs_bytes = x"440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849";
let inputs = groth16::public_proof_inputs_from_bytes(inputs_bytes);

let proof_bytes = x"a29981304df8e0f50750b558d4de59dbc8329634b81c986e28e9fff2b0faa52333b14a1f7b275b029e13499d1f5dd8ab955cf5fa3000a097920180381a238ce12df52207597eade4a365a6872c0a19a39c08a9bfb98b69a15615f90cc32660180ca32e565c01a49b505dd277713b1eae834df49643291a3601b11f56957bde02d5446406d0e4745d1bd32c8ccb8d8e80b877712f5f373016d2ecdeebb58caebc7a425b8137ebb1bd0c5b81c1d48151b25f0f24fe9602ba4e403811fb17db6f14";
let proof = groth16::proof_points_from_bytes(proof_bytes);

groth16::verify_groth16_proof(&curve, &pvk, &inputs, &proof);
fun test_too_many_public_inputs() {
// We give 9 inputs which exceeds the limit of 8
let inputs_bytes = vector[
x"440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849",
x"440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849",
x"440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849",
x"440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849",
x"440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849",
x"440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849",
x"440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849",
x"440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849",
x"440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849"
].flatten();
groth16::public_proof_inputs_from_bytes(inputs_bytes);
}

#[test]
Expand Down Expand Up @@ -119,6 +120,13 @@ module sui::groth16_tests {
groth16::prepare_verifying_key(&bn254(), &invalid_vk);
}

#[test]
#[expected_failure(abort_code = groth16::EInvalidScalar)]
fun test_invalid_public_inputs_bn254() {
let public_inputs_too_short = vector[x"3fd7c445c6845a9399d1a7b8394c16373399a037786c169f16219359d3be840a", x"1234"].flatten();
groth16::public_proof_inputs_from_bytes(public_inputs_too_short);
}

#[test]
fun test_verify_groth_16_proof_bn254() {
let curve = bn254();
Expand All @@ -143,33 +151,8 @@ module sui::groth16_tests {
let invalid_pvk = groth16::pvk_from_bytes(vk_bytes, alpha_bytes, gamma_bytes, delta_bytes);
assert!(groth16::verify_groth16_proof(&curve, &invalid_pvk, &inputs, &proof) == false);

// Invalid public inputs bytes.
let invalid_inputs = groth16::public_proof_inputs_from_bytes(x"cf");
assert!(groth16::verify_groth16_proof(&curve, &pvk, &invalid_inputs, &proof) == false);

// Invalid proof bytes.
let invalid_proof = groth16::proof_points_from_bytes(x"4a");
assert!(groth16::verify_groth16_proof(&curve, &pvk, &inputs, &invalid_proof) == false);
}

#[test]
#[expected_failure(abort_code = groth16::ETooManyPublicInputs)]
fun test_too_many_public_inputs_bn254() {
let curve = bn254();

let vk_bytes = x"e8324a3242be5193eb38cca8761691ce061e89ce86f1fce8fd7ef40808f12da3c67d9ed5667c841f956e11adbbe240ddf37a1e3a4a890600dc88f608b897898e";
let alpha_bytes = x"51e6d72cd3b0914dd232653f84e7971d3e5bbcde6b47ff8d6c05277e579f1c1eb2fe30aa252c63950de6ea00dd21a1027f6d130357e47c31fafeca0d31e19406231df42bc11ce376f8cf75135d9074f081c242c31f198d151ec69ec37d67cc2b12542cb306a7823c8b194f13672176c6ee8266b2a0c9f57a5dbdb2278046b511d44e715a3ebe02ec2e1cf493c1b1ada84676e234134a6da5a552f61d4e905e15c0dc58a3414d74304775de5ba8571128f3548d269b51fdc08d5b646fd9157e0a2bc0c4bec5a9a6048d17d1d6cd941b4d459f1de0c7c1d417f33995d2a8dd670b91f0baaccaaf2802100901711885026a5ec97fbbb801000d0d01185651947c1900e336921d07eb16d0e25a2192829540ad5eeb1c498ba9c6316e16807a55dc2b9a7f3dea2e4a2f485ed1295a96d6ca86851842b3a22f83507f93ac66a1dc341d5d22f592527d8ea5c12db16bbabe24b76b3e1baf825c8dcf147be369fd8c5300fd77d0aa8dce730e4e7442c93c4890023f3a266c9fbc90ebbf72825e798c4c00";
let gamma_bytes = x"240a80664919b9f7490209cff12bfd81c32c272607dc004661c792082cbe282ef826f56a3822ebd72345f86c7ee9872e23f10d1f2dbf43f8aca5dc2ceb5388a5";
let delta_bytes = x"f755df8c90edab48ac5adafef6a5a461902217f392e3aa4c34c0462b700c18164f79018778755980d491647de11ecc51fda2cc17171c4b44485ec37ccd23a69b";
let pvk = groth16::pvk_from_bytes(vk_bytes, alpha_bytes, gamma_bytes, delta_bytes);

// We give 9 equal inputs which exceeds the limit of 8
let inputs_bytes = x"3fd7c445c6845a9399d1a7b8394c16373399a037786c169f16219359d3be840a3fd7c445c6845a9399d1a7b8394c16373399a037786c169f16219359d3be840a3fd7c445c6845a9399d1a7b8394c16373399a037786c169f16219359d3be840a3fd7c445c6845a9399d1a7b8394c16373399a037786c169f16219359d3be840a3fd7c445c6845a9399d1a7b8394c16373399a037786c169f16219359d3be840a3fd7c445c6845a9399d1a7b8394c16373399a037786c169f16219359d3be840a3fd7c445c6845a9399d1a7b8394c16373399a037786c169f16219359d3be840a3fd7c445c6845a9399d1a7b8394c16373399a037786c169f16219359d3be840a3fd7c445c6845a9399d1a7b8394c16373399a037786c169f16219359d3be840a";
let inputs = groth16::public_proof_inputs_from_bytes(inputs_bytes);

let proof_bytes = x"dd2ef02e57d6a282df6b7f36c134ab7e55c2e04c5b8cbd7831be18e0e7224623ae8bd6c41637c10cbd02f5e68de6394461f417895ddd264d6f0ddacf68c6cd02feb8881f0efa599139a6faf4223dd8743777c4346cba52322eb466af96f2be9f813af1450f84d6f8029804f60cac1add70ad1a3d4226404f84f4022dc18caa0f";
let proof = groth16::proof_points_from_bytes(proof_bytes);

groth16::verify_groth16_proof(&curve, &pvk, &inputs, &proof);
}
}
Binary file modified crates/sui-framework/packages_compiled/move-stdlib
Binary file not shown.
Binary file modified crates/sui-framework/packages_compiled/sui-framework
Binary file not shown.
87 changes: 45 additions & 42 deletions crates/sui-framework/published_api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -985,48 +985,6 @@ ecvrf_verify
ed25519_verify
public fun
0x2::ed25519
Curve
public struct
0x2::groth16
PreparedVerifyingKey
public struct
0x2::groth16
PublicProofInputs
public struct
0x2::groth16
ProofPoints
public struct
0x2::groth16
bls12381
public fun
0x2::groth16
bn254
public fun
0x2::groth16
pvk_from_bytes
public fun
0x2::groth16
pvk_to_bytes
public fun
0x2::groth16
public_proof_inputs_from_bytes
public fun
0x2::groth16
proof_points_from_bytes
public fun
0x2::groth16
prepare_verifying_key
public fun
0x2::groth16
prepare_verifying_key_internal
fun
0x2::groth16
verify_groth16_proof
public fun
0x2::groth16
verify_groth16_proof_internal
fun
0x2::groth16
blake2b256
public fun
0x2::hash
Expand Down Expand Up @@ -2332,6 +2290,48 @@ create_internal
add_internal
fun
0x2::display
Curve
public struct
0x2::groth16
PreparedVerifyingKey
public struct
0x2::groth16
PublicProofInputs
public struct
0x2::groth16
ProofPoints
public struct
0x2::groth16
bls12381
public fun
0x2::groth16
bn254
public fun
0x2::groth16
pvk_from_bytes
public fun
0x2::groth16
pvk_to_bytes
public fun
0x2::groth16
public_proof_inputs_from_bytes
public fun
0x2::groth16
proof_points_from_bytes
public fun
0x2::groth16
prepare_verifying_key
public fun
0x2::groth16
prepare_verifying_key_internal
fun
0x2::groth16
verify_groth16_proof
public fun
0x2::groth16
verify_groth16_proof_internal
fun
0x2::groth16
SUI
public struct
0x2::sui
Expand Down Expand Up @@ -4264,6 +4264,9 @@ insert
swap_remove
public fun
0x1::vector
flatten
public fun
0x1::vector
Option
public struct
0x1::option
Expand Down
2 changes: 1 addition & 1 deletion crates/sui-open-rpc/spec/openrpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -1293,7 +1293,7 @@
"name": "Result",
"value": {
"minSupportedProtocolVersion": "1",
"maxSupportedProtocolVersion": "58",
"maxSupportedProtocolVersion": "59",
"protocolVersion": "6",
"featureFlags": {
"accept_zklogin_in_multisig": false,
Expand Down
Loading

0 comments on commit 6b37277

Please sign in to comment.