Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

copy-fix-terminate: enforce secure termination of the copy circuit #1568

Conversation

naure
Copy link
Contributor

@naure naure commented Aug 17, 2023

Description

This PR fixes two soundness issues in the Copy circuit.

  1. There are a number of checks at the end of an event when is_last=1. However, there are no guarantees that this last row exists. The last event may not be checked fully.

  2. The constraints may query cells of the "last two rows", whose content is not constrained. For example, booleans are not necessarily booleans there.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Content

This is a backport from Scroll, see more details here: scroll-tech#649. Quoting:

The new constraint constrain_must_terminate guarantees that:

  • a "last row" is eventually reached so that final checks happen,
  • and every row of an event can safely query rotations 1 and 2.

Now to witness generation. There must be two additional rows that are enabled so their content is verified, but not used because they touch disabled rows. See UNUSED_ROWS.

Note that there was already a sanity check that no constraints query reserved rows with the error active on an unusable row - missing selector?. Rows can be reserved for blinding, or beyond max_rows, or simply to avoid wrap around back to the first row. This check was satisfied by adding two disabled rows (see assign_padding_row(…)). However, this alone was not sound.

How Has This Been Tested?

cargo test --release -p zkevm-circuits -p zkevm-circuits  -- copy

@github-actions github-actions bot added crate-bus-mapping Issues related to the bus-mapping workspace member crate-zkevm-circuits Issues related to the zkevm-circuits workspace member crate-gadgets Issues related to the gadgets workspace member labels Aug 17, 2023
@ChihChengLiang ChihChengLiang self-requested a review August 18, 2023 17:48
@miha-stopar miha-stopar self-requested a review September 4, 2023 10:58
Copy link
Collaborator

@ChihChengLiang ChihChengLiang left a comment

Choose a reason for hiding this comment

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

Hi @naure, Thanks for the PR, and sorry for the late review. After looking into it, I think this fix does not work. Please check my comments.

Comment on lines +498 to +499
meta.query_fixed(q_enable, Rotation(2)),
1.expr(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a no-op constraint. q_enable is a fixed column determined at setup time, so there is no point constraining a fixed value equal to a constant.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this would make sense if q_enabled could be 0 in some of the non-padding rows, but this indeed probably shouldn't happen at all as q_enabled is a fixed column and thus determined in advance as CC pointed out - q_enabled just needs to be 1 everywhere I think (at least in some older versions of Halo2, the lookups required to have a fixed column I think, so we need to have at least one fixed column).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It reads like this: if we are processing an event, then we are not about to hit the end of the circuit.

At the end of the circuit, q_enable switches to 0, which is how we detect the end. The rows just before that end are forced to be padding (is_event=0). This forces the last event to be fully verified by then.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, if the number of rows with q_enabled = true is constant (which it is), then I think this is all good.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hi @naure, what you say makes sense to me now, but I have a nitpick for readability.
It looks like if we unroll this constraint, it will be

is_event * (q_enable - 1)

Which is the same as (q_enable - 1) * is_event. The latter way fits the Selector/Fixed column first convention.

Can we rewrite it to something like this for readability?

 cb.condition(meta.query_fixed(q_enable, Rotation(2)) - 1, |cb| {
        cb.require_equal(
            "The final step must be padding",
            is_event,
            0.expr(),

// The tag is then copied to the next step by "rows[0].tag == rows[2].tag". Eventually,
// q_enable=0. By that point the tag must have switched to Padding, which is only possible with
// is_last=1. This guarantees that all the final conditions are checked.
let is_event = tag.value(Rotation::cur())(meta) - tag.constant_expr::<F>(CopyDataType::Padding);
Copy link
Collaborator

Choose a reason for hiding this comment

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

tag.constant_expr::<F>(CopyDataType::Padding) part doesn't make sense to me.
Why are we binary decomposing a known value into bits, then binary composing back to constant, instead of using the known value directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed the constant should happen to equal Padding as u64. However, in this context the tag is not a number but a T: AsBits<N>, so this maintain the abstraction by mirroring the value() method just above.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Makes sense now.

zkevm-circuits/src/copy_circuit.rs Show resolved Hide resolved
Copy link
Collaborator

@miha-stopar miha-stopar left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Collaborator

@ChihChengLiang ChihChengLiang left a comment

Choose a reason for hiding this comment

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

LGTM with a nitpick. Feel free to skip it.
Thanks for the fix and explaining the rationale to us.

Comment on lines +498 to +499
meta.query_fixed(q_enable, Rotation(2)),
1.expr(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hi @naure, what you say makes sense to me now, but I have a nitpick for readability.
It looks like if we unroll this constraint, it will be

is_event * (q_enable - 1)

Which is the same as (q_enable - 1) * is_event. The latter way fits the Selector/Fixed column first convention.

Can we rewrite it to something like this for readability?

 cb.condition(meta.query_fixed(q_enable, Rotation(2)) - 1, |cb| {
        cb.require_equal(
            "The final step must be padding",
            is_event,
            0.expr(),

zkevm-circuits/src/copy_circuit.rs Show resolved Hide resolved
// The tag is then copied to the next step by "rows[0].tag == rows[2].tag". Eventually,
// q_enable=0. By that point the tag must have switched to Padding, which is only possible with
// is_last=1. This guarantees that all the final conditions are checked.
let is_event = tag.value(Rotation::cur())(meta) - tag.constant_expr::<F>(CopyDataType::Padding);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Makes sense now.

@ChihChengLiang ChihChengLiang added this pull request to the merge queue Sep 12, 2023
Merged via the queue into privacy-scaling-explorations:main with commit 610acc2 Sep 12, 2023
11 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
crate-bus-mapping Issues related to the bus-mapping workspace member crate-gadgets Issues related to the gadgets workspace member crate-zkevm-circuits Issues related to the zkevm-circuits workspace member
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants