noir-lang / noir

Noir is a domain specific language for zero knowledge proofs
https://noir-lang.org
Apache License 2.0
893 stars 199 forks source link

Usage of `.as_array()` method on slice results in panic during inlining #5245

Closed TomAFrench closed 4 months ago

TomAFrench commented 5 months ago

Aim

I would like to write a very simple program which just returns the modulus of the field being used.

use dep::std::field::modulus_be_bytes;

fn main() -> pub [u8; 32] {
    let bytes = dep::std::field::modulus_be_bytes();
    bytes.as_array()
}

Expected Behavior

I would expect that this program would successfully compile and I would get ACIR which directly commits to the values of each public input.

Bug

Compilation fails with a panic inside the inlining pass.

After Resolving IsUnconstrained:
acir(inline) fn main f0 {
  b0():
    v31 = call f1([u8 2⁴×3, u8 100, u8 78, u8 114, u8 225, u8 49, u8 2⁴×10, u8 41, u8 184, u8 2⁴×5, u8 69, u8 182, u8 129, u8 129, u8 88, u8 93, u8 40, u8 51, u8 232, u8 72, u8 121, u8 185, u8 2⁴×7, u8 145, u8 67, u8 225, u8 245, u8 147, u8 2⁴×15, u8 0, u8 0, u8 1])
    return v31
}
acir(inline) fn as_array f1 {
  b0(v0: u32, v1: [u8]):
    v4 = eq v0, u32 2⁵
    constrain v0 == u32 2⁵
    inc_rc [u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0]
    v8 = allocate
    store [u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0, u8 0] at v8
    jmp b1(u32 0)
  b1(v9: u32):
    v11 = lt v9, u32 2⁵
    jmpif v11 then: b2, else: b3
  b2():
    v12 = load v8
    v14 = lt v9, v0
    constrain v14 == u1 1 '"Index out of bounds"'
    v15 = array_get v1, index v9
    v16 = array_set v12, index v9, value v15
    v17 = add v9, u32 1
    store v16 at v8
    v18 = add v9, u32 1
    jmp b1(v18)
  b3():
    v19 = load v8
    return v19
}

The application panicked (crashed).
Message:  assertion `left == right` failed
  left: 2
 right: 1
Location: compiler/noirc_evaluator/src/ssa/opt/inlining.rs:299

This is a bug. We may have already fixed this in newer versions of Nargo so try searching for similar issues at https://github.com/noir-lang/noir/issues/.
If there isn't an open issue for this bug, consider opening one at https://github.com/noir-lang/noir/issues/new?labels=bug&template=bug_report.yml

To Reproduce

1. 2. 3. 4.

Project Impact

None

Impact Context

No response

Workaround

None

Workaround Description

I can work around this by directly inlining the contents of as_array into my function as so.

use dep::std::field::modulus_be_bytes;

fn main() -> pub [u8; 32] {
    let bytes = dep::std::field::modulus_be_bytes();

    assert(bytes.len() == 32);

    let mut array = [0; 32];
    for i in 0..32 {
        array[i] = bytes[i];
    }
    array
}

Additional Context

No response

Installation Method

Compiled from source

Nargo Version

No response

NoirJS Version

No response

Would you like to submit a PR for this Issue?

None

Support Needs

No response

TomAFrench commented 5 months ago
use dep::std::field::modulus_be_bytes;

global BN254_MODULUS_BE_BYTES: [u8] = &[
            48, 100, 78, 114, 225, 49, 160, 41, 184, 80, 69, 182, 129, 129, 88, 93, 40, 51, 232, 72, 121, 185, 112, 145, 67, 225, 245, 147, 240, 0, 0, 1
        ];

fn main() -> pub bool {
    dep::std::field::modulus_be_bytes() == BN254_MODULUS_BE_BYTES
}

This can also be run into when just checking equality between two slices

jfecher commented 5 months ago

We used to encounter this a lot when array literals & types were polymorphic over arrays and structs. Not sure why it is happening now.

TomAFrench commented 4 months ago

Fixed by https://github.com/noir-lang/noir/pull/5278