Skip to content

test(msm): add negative/adversarial tests for MSM constraint satisfaction#392

Merged
Bisht13 merged 7 commits intoworldfnd:mainfrom
PranavPipariya:pranavpipariya/add-msm-negative-tests
Apr 20, 2026
Merged

test(msm): add negative/adversarial tests for MSM constraint satisfaction#392
Bisht13 merged 7 commits intoworldfnd:mainfrom
PranavPipariya:pranavpipariya/add-msm-negative-tests

Conversation

@PranavPipariya
Copy link
Copy Markdown
Contributor

Summary

Adds the negative/adversarial MSM constraint-satisfaction tests proposed in #348.

This PR is test-only and does not change MSM implementation behavior.

Closes #348.

Included tests

  • wrong output coordinate
  • off-curve input point
  • wrong scalar/output pairing
  • s2 = 0 forgery attempt
  • flipped neg1 sign bit

Validation

  • cargo +nightly-2026-03-04 fmt --all --check
  • cargo +nightly-2026-03-04 clippy --all-targets --all-features --verbose
  • cargo +nightly-2026-03-04 test -p provekit-bench --test msm_witness_solving
  • cargo +nightly-2026-03-04 test --no-fail-fast --all-features --verbose --lib --tests --bins

@ocdbytes ocdbytes self-requested a review April 1, 2026 00:39
Copy link
Copy Markdown
Collaborator

@ocdbytes ocdbytes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PranavPipariya

Can you also add two more test cases here :

  • out_y limb corruption (same as written for out_x).
  • scalar corruption (replace the scalar but keep output computed from the original scalar). this is to check end to end scalar output binding.

After this will give one more review for the code.

@PranavPipariya
Copy link
Copy Markdown
Contributor Author

@ocdbytes

Have added both the cases and re-ran the validation commands.

New tests:

  • out_y limb corruption
  • scalar corruption while keeping the original output

Copy link
Copy Markdown
Collaborator

@ocdbytes ocdbytes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add a test for

  • out_inf corruption
  • neg2 flip
  • multi point MSM (this will catch any error in the process multi point path)

NIT : for tests which dont require much changes and are mostly duplicated logic like out_x and out_y or neg_1 and neg_2. We can have a single test and add it as a case rather than having two different tests with mostly same logic.

Comment on lines +485 to +495
let curve = Secp256r1;
let gx = curve.generator().0;
let gy = curve.generator().1;
let scalar: [u64; 4] = [7, 0, 0, 0];
let (ex, ey) = ec_scalar_mul(
&gx,
&gy,
&scalar,
&curve.curve_a(),
&curve.field_modulus_p(),
);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ig we can de duplicate this among the tests

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consolidated the duplicated out_x/out_y and neg1/neg2 cases into case-driven tests that share the setup. Please take a look at 73f41e68.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic :

let curve = Secp256r1;
    let gx = curve.generator().0;
    let gy = curve.generator().1;
    let scalar: [u64; 4] = [7, 0, 0, 0];
    let (ex, ey) = ec_scalar_mul(
        &gx,
        &gy,
        &scalar,
        &curve.curve_a(),
        &curve.field_modulus_p(),
    );

appears too many times which is just a repeating code. Ig this can be delegated to a single function

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you're totally right. should've been more DRY in the first place. have extracted the repeated single-point generator setup into a shared helper and updated the affected tests to use it. please take a look.

Comment on lines +141 to +147
fn different_field_element(value: FieldElement) -> FieldElement {
if value == FieldElement::zero() {
FieldElement::from(1u64)
} else {
FieldElement::zero()
}
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use a better approach here rather than toggling between 0 and 1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced different_field_element; the general corruption path now uses corrupt_field_element, and the neg1/neg2 case uses an actual boolean flip. Please take a look.

@PranavPipariya
Copy link
Copy Markdown
Contributor Author

Made the updates:

  • consolidated the duplicated out_x/out_y and neg1/neg2 cases into case-driven tests
  • added coverage for out_inf, neg2, and the multi-point MSM negative path
  • updated the neg1/neg2 case to use an actual boolean flip

Also re-ran the validation commands locally.

@ocdbytes
Copy link
Copy Markdown
Collaborator

ocdbytes commented Apr 9, 2026

@PranavPipariya Please address the above comments regarding the code and then will give a final review.

Copy link
Copy Markdown
Collaborator

@ocdbytes ocdbytes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some NITS.
Other than this things look good to me !!

/// Two-point MSM: corrupting the output must be rejected, exercising the
/// multi-point accumulation and output-constraining path.
#[test]
fn test_two_point_msm_rejects_wrong_output() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this new test it just copy of the test build_single_point_msm_fixture. We can de duplicate this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. have extracted the shared setup into build_default_two_point_msm_fixture. Both test_two_point_msm and test_two_point_msm_rejects_wrong_output use it now. please take a look.

Comment on lines 517 to 521
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre existing test should also be using the the function for params

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. the pre-existing positive tests now use secp256r1_generator_params (or secp256r1_generator where curve params aren't needed).

Comment on lines 754 to 758
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these print statements these were left in previous review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the print statements

Comment on lines +556 to +557
let case = secp256r1_generator_case([7, 0, 0, 0]);
let fixture = build_generator_single_point_fixture(case);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT :

The pattern here of calling secp256r1_generator_case and build_generator_single_point_fixture looks redundant use something like this :

 fn generator_fixture(scalar: [u64; 4]) -> SinglePointMsmFixture {
      build_generator_single_point_fixture(secp256r1_generator_case(scalar))
  } 

so the tests which require their own case internally can call otherwise

  fn test_single_point_rejects_wrong_output_inf() {
      let fixture = generator_fixture([7, 0, 0, 0]);            // ← one call
......
  }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added generator_fixture as mentioned. simple tests now use the one-liner. test_single_point_rejects_off_curve_input and test_single_point_rejects_wrong_scalar_output_pairing keep the two-step form because they read case fields before building the fixture.

Comment thread tooling/provekit-bench/tests/msm_witness_solving.rs
@ocdbytes
Copy link
Copy Markdown
Collaborator

@PranavPipariya fix the lint and then we can merge the PR

@ocdbytes ocdbytes requested a review from Bisht13 April 16, 2026 07:00
@Bisht13 Bisht13 merged commit 5749786 into worldfnd:main Apr 20, 2026
7 of 8 checks passed
@Bisht13
Copy link
Copy Markdown
Collaborator

Bisht13 commented Apr 20, 2026

Thanks for the contribution @PranavPipariya!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test: Add negative/adversarial tests for MSM constraint satisfaction

3 participants