Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: MockProver cell override #352

Merged

Conversation

teddav
Copy link

@teddav teddav commented Jun 6, 2024

This PR adds the ability to modify cells in the MockProver and act like a "malicious prover".
This is related to this issue: zcash#280

I added a test to show how this can be used. I can add more doc/tests if needed.

This is a first shot at it, I can try to improve it based on comments

@adria0
Copy link
Member

adria0 commented Jun 10, 2024

While at first glance it's ok, and soundness is something that needs to be tested, but I'm not sure finding where this could be beneficial if not is a trivial example:

  • If the developer of the chip / state machine wants to do the test, I think that is just easier to output modified witness

  • If you are using an external chip / state machine, it's tricky to guess which column/row you have to poke, and breaks a little bit the encapsulation principle, and, well, tests needs to be part of the component not, something external.

  • If using some kind of cell manager (cell allocator, chiquito, halo2lib, powdr), no (direct) way to know where is the original column / row is.

Do you have some specific use case where there methods are usefull?

Let's me say that testing bad witness values are a real pain and needs to be adressed somehow, but probably needs some methodology first and then the according code changes to allow its usage.

Edit: I just seen your nice halo2 impl https://dev.to/teddav/tornado-cash-with-halo2-62b ! Have you tried to apply your PR to test it?


region.assign_advice(|| "a0", config.a, 0, || Value::known(Fp::from(3)))?;
region.assign_advice(|| "a1", config.a, 1, || Value::known(Fp::from(9)))?;
region.assign_advice(|| "a2", config.a, 2, || Value::known(Fp::from(10)))
Copy link
Member

Choose a reason for hiding this comment

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

I would suggest having a circuit that assigns a correct witness, make sure the mock prover can verify it, and then start manipulating the advices / instances to check that it now gives errors when verifying with the mock prover.
This flow better shows how a negative test would go: start from a correct assignment that passes, make a hypothesis about a witness that should not pass, and make sure it doesn't.

@ed255
Copy link
Member

ed255 commented Jun 11, 2024

I want to share my thoughts on @adria0's comment

While at first glance it's ok, and soundness is something that needs to be tested, but I'm not sure finding where this could be beneficial if not is a trivial example:

  • If the developer of the chip / state machine wants to do the test, I think that is just easier to output modified witness

I don't think that's easy, as it needs custom boilerplate in the chip / state machine / gadget just to be able to override values. It can be cumbersome to propagate this override data from the test to the actual assignment code. For example, take a look at this circuit from the zkevm-circuits that performs overrides:

Here's the test itself:
https://github.com/privacy-scaling-explorations/zkevm-circuits/blob/fe3a814558fa09951fa40adf07faffe024b47283/zkevm-circuits/src/state_circuit/test.rs#L409-L411
It requires extending the circuit with a field to store the overrides:
https://github.com/privacy-scaling-explorations/zkevm-circuits/blob/fe3a814558fa09951fa40adf07faffe024b47283/zkevm-circuits/src/state_circuit.rs#L466
It requires extra code in the synthesize to assign the overrides:
https://github.com/privacy-scaling-explorations/zkevm-circuits/blob/fe3a814558fa09951fa40adf07faffe024b47283/zkevm-circuits/src/state_circuit.rs#L595-L607

Having these two extra methods in the MockProver allows following the same pattern for negative testing with less boilerplate on the circuit. And the extra methods for the MockProver are very simple and lightweight, so I think it leads to a more clear negative testing.

  • If you are using an external chip / state machine, it's tricky to guess which column/row you have to poke, and breaks a little bit the encapsulation principle, and, well, tests needs to be part of the component not, something external.

I agree that it can be tricky to guess which column/row to poke, but I don't think this invalidates the MockProver cell override methods. Each chip / component can have negative testing, and they can use the MockProver cell override methods. I think negative testing works much better with unit tests of circuits that contain a single component; so in that case you would not do a negative test of a big circuit by poking an internal component that is defined in a different module.

  • If using some kind of cell manager (cell allocator, chiquito, halo2lib, powdr), no (direct) way to know where is the original column / row is.

I agree, and again I don't think this invalidates this PR. Negative testing requires assigning invalid witnesses, no matter if you're using a cell allocator or not. A component that uses a cell allocator can just collect metadata during assignment that a test can use to know which cells to override, and doing the override is easier with the MockProver support than having a custom assignment code for overrides.

Do you have some specific use case where there methods are usefull?

Just to give examples, one would be the negative testing of the state circuit in zkevm-circuits that I mentioned before.

Another example would be this negative test case for the shuffle API:

shuffled.index_mut(0).swap(0, 1);

Currently the circuit keeps two list of values (original and shuffled) and the negative test changes the shuffled list. This follows the pattern of the override. In this case the change would be minimal, but I just wanted to show that it's following the same pattern. A real circuit may not have as inputs the original and shuffled list; maybe it has the original list and builds the shuffle by sorting the original; in that case doing an overwrite without MockProver support would be harder.

Overall I agree that having the MockProver cell override doesn't give you the final solution for negative testing in all cases, but I think it's a very useful (necessary even) component to do negative testing!

@adria0
Copy link
Member

adria0 commented Jun 11, 2024

Overall I agree that having the MockProver cell override doesn't give you the final solution for negative testing in all cases, but I think it's a very useful (necessary even) component to do negative testing!

This is my main point, I'm not "against" this PR, but I think that does not solve the problem that needs to be solved, that is lack of methodology for negative tests.

IMO we have to check how it works in a real case, in the theoretical side I agree that could be usefull. So, it needs a little of extra checking, I would say.

@ed255
Copy link
Member

ed255 commented Jun 11, 2024

Overall I agree that having the MockProver cell override doesn't give you the final solution for negative testing in all cases, but I think it's a very useful (necessary even) component to do negative testing!

This is my main point, I'm not "against" this PR, but I think that does not solve the problem that needs to be solved, that is lack of methodology for negative tests.

That's fair. I agree that this PR doesn't resolve the universal methodology for negative tests but I think:

  • It makes progress towards better negative testing
  • It solves negative testing for particular cases (see the state circuit from zkevm-circuits for example)

IMO we have to check how it works in a real case, in the theoretical side I agree that could be usefull. So, it needs a little of extra checking, I would say.

To me, this PR is already useful for real cases. For that reason, and also because I think this is a useful component for more complex negative test cases, I'm in favor of merging this new MockProver feature as is (maybe extending / changing the tests if necessary, even changing the API if necessary). I think it's not fair to put the burden of solving negative testing for all cases on this PR, because then it's very hard to make progress.

Although I agree that with this PR merged, the issue of "universal methodology for negative testing" is not resolved.

@teddav
Copy link
Author

teddav commented Jun 12, 2024

@adria0 I completely agree that this is not a final solution, that's what i was saying in my first comment. As @ed255 is saying, this is a way to get started on this topic and introduce tools for negative testing. I'd be happy to have a discussion (on discord?) about how we want to address it and what the next step could be.
Based on @ed255 suggestion, i'm going to add some better tests and I agree that this could be merged as it is and improved after

Copy link
Member

@ed255 ed255 left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks for working on this feature!

// and now we try to trick the verifier
// we assign on row 0 `Fp - 3`, which is also a square root of 9
// but will overflow the prime field
let sqrt_9 = Fp::zero() - Fp::from(3);
Copy link
Member

Choose a reason for hiding this comment

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

Nice one!

@ed255
Copy link
Member

ed255 commented Jun 17, 2024

@adria0 the coverage github task failed. Maybe it doesn't work with PRs using forked repositories?

@adria0
Copy link
Member

adria0 commented Jun 17, 2024

@adria0 the coverage github task failed. Maybe it doesn't work with PRs using forked repositories?

I did not know this, it seems that now needs an CODECOV_TOKEN in the forked repo, some time ago this was not mandatory.

@teddav, please take a look to https://docs.codecov.com/docs/adding-the-codecov-token

@adria0 adria0 self-requested a review June 18, 2024 08:09
@davidnevadoc davidnevadoc self-requested a review June 18, 2024 08:32
@teddav
Copy link
Author

teddav commented Jun 18, 2024

@teddav teddav requested a review from adria0 June 18, 2024 10:47
@adria0 adria0 merged commit 1a6468e into privacy-scaling-explorations:main Jun 18, 2024
14 checks passed
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.

4 participants