snarkVM icon indicating copy to clipboard operation
snarkVM copied to clipboard

[Bug] CI job failure (test_assert_eq_succeeds)

Open ljedrz opened this issue 3 years ago • 15 comments

Detected in https://github.com/AleoHQ/snarkVM/pull/982:

thread 'program::instruction::operation::assert::tests::test_assert_eq_succeeds' panicked at 'assertion failed: `(left == right)`
  left: `0`,
 right: `1`: Constant constraint failed: (0 * 1) =?= 1', /home/circleci/project/circuit/environment/src/circuit.rs:156:29
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    program::instruction::operation::assert::tests::test_assert_eq_succeeds

ljedrz avatar Aug 18 '22 16:08 ljedrz

@ljedrz I have assigned you to both #985 and #986, as I believe the underlying issue (whatever it may be) is the same under the hood.

I originally wrote that test framework 2 weeks ago and am happy to debug it with you. I believe just updating it to do more iterations will cause it to eventually hit the case. I suggest adding some additional println! statements to catch the exact literal_a, literal_b, mode_a, and mode_b case that causes it to fail.

If you need additional guidance, I'm happy to triage it with you.

howardwu avatar Aug 24 '22 18:08 howardwu

Reproducing rng seed (with https://github.com/AleoHQ/snarkVM/pull/999): 1420944100681073831 or 2576108600482558396. There are likely many others.

ljedrz avatar Aug 25 '22 12:08 ljedrz

The Mode (Public or Private) doesn't seem to make a difference.

ljedrz avatar Aug 25 '22 13:08 ljedrz

minified reproducer:

#[test]
fn test_assert_eq_succeeds_minified() {
    // Initialize the operation.
    let operation = |operands| AssertEq::<CurrentNetwork> { operands };
    // Initialize the opcode.
    let opcode = AssertEq::<CurrentNetwork>::opcode();

    // Prepare the test.
    let literal_a = Literal::String(console::types::StringType::new("T0vAPUMXjeh7Gfkj67r5UJTmG7rz44S98owL2jjvAHFKfu"));
    let literal_b = Literal::String(console::types::StringType::new("6KridYJbJ"));

    check_assert(operation, opcode, &literal_a, &literal_b, &circuit::Mode::Public, &circuit::Mode::Public);
}

ljedrz avatar Aug 25 '22 13:08 ljedrz

I haven't fully confirmed it yet, but it seems that this issue is limited to the String literals; also, it doesn't seem to be related to #985.

ljedrz avatar Aug 25 '22 19:08 ljedrz

The specific point of failure is here (called from here), and the value of input_a.is_equal(&input_b) is false.private (when using the minified reproducer provided earlier).

ljedrz avatar Aug 26 '22 09:08 ljedrz

@ljedrz I've added a little println to those tests in #1007 to help with debugging.

It is very sporadic, but there are some corner cases where I seem to be able to still hit it. Unfortunately, I have not hit it after adding the println however.

howardwu avatar Aug 28 '22 22:08 howardwu

What about https://github.com/AleoHQ/snarkVM/issues/986#issuecomment-1227273374? That one should fail every time.

ljedrz avatar Aug 28 '22 22:08 ljedrz

CI came across an error case (now easier to reproduce with the new logging):

Checking 'assert.eq' for '"VP5MvWmcwQcfgUY".public' and '"IprcMHTOjNE7I5yjktaRZ1CMrxMCH9yTfQbJWx7kTquHgWNT".public'

howardwu avatar Aug 28 '22 23:08 howardwu

It seems that strings with a length of 0 or 32 and above cause issues, regardless of their contents.

ljedrz avatar Aug 30 '22 10:08 ljedrz

Confirmed; I can't trigger a failure with the following test case:

#[test]
fn test_assert_eq_succeeds_minified() {
    // Initialize the operation.
    let operation = |operands| AssertEq::<CurrentNetwork> { operands };
    // Initialize the opcode.
    let opcode = AssertEq::<CurrentNetwork>::opcode();

    // Prepare the key cache.
    let mut cache = Default::default();

    let mut rng = TestRng::default();

    use rand::distributions::{Alphanumeric, DistString};

    loop {
        let len_a = rng.gen_range(1..32);
        let len_b = rng.gen_range(1..32);

        let literal_a = Literal::String(console::types::StringType::new(&Alphanumeric.sample_string(&mut rng, len_a)));
        let literal_b = Literal::String(console::types::StringType::new(&Alphanumeric.sample_string(&mut rng, len_b)));

        check_assert(operation, opcode, &literal_a, &literal_b, &circuit::Mode::Public, &circuit::Mode::Public, &mut cache);
    }
}

ljedrz avatar Aug 30 '22 11:08 ljedrz

I might be onto something: when inspecting the Equal::is_equal impl for StringType, I noticed that string literals with lengths of 0 and anything other than 0 and ones differing by 32 or more crash the test; in other words:

literal_a.len() literal_b.len() result
0 0 pass
0 not 0 fail
1 - 31 1-31 pass
1 - 31 32 and more fail
32 - 63 32 - 63 pass
32 - 63 below 32 or 64 and more fail

and so on. As long as the number of underlying fields is the same, the test passes.

ljedrz avatar Aug 30 '22 14:08 ljedrz

Also, the related check doesn't actually compare the lengths of strings, but the underlying fields.

ljedrz avatar Aug 30 '22 14:08 ljedrz

@ljedrz Has this issue been resolved?

raychu86 avatar Jul 02 '24 17:07 raychu86

I can no longer reproduce the issue with https://github.com/AleoNet/snarkVM/issues/986#issuecomment-1227273374. However, the related PR states that it was only a temporary solution.

ljedrz avatar Jul 03 '24 08:07 ljedrz