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

Fix to handle successful run with Uint64 overflow for multiple opcodes #1317

Conversation

silathdiir
Copy link
Contributor

Description

Fix successful run cases with Uint64 overflow for multiple opcodes.

  1. Add WordByteRangeGadget to constrain if Word is within the specified byte range.

  2. Add WordByteCapGadget to constrain if Word is within the specified byte range (implemented by WordByteRangeGadget) and less than a maximum cap (used to replace a WordByteRangeGadget and LtGadget).

  3. Fix bus-mapping and zkevm-circuits to handle overflow cases. And add unit-tests for these cases.

TODO: will try to handle memory offset overflow with zero length in another PR (try to rebase for this local PR scroll-tech#393) and related issue #1301.

Rationale

Reference detailed code in go-etherum as:

. BLOCKHASH
. CALLDATALOAD
. CALLDATACOPY
. CODECOPY
. EXTCODECOPY
. JUMPI

Issue Link

Close #1276

Type of change

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

How Has This Been Tested?

Add unit-test cases for Uint64 overflow values.

@github-actions github-actions bot added crate-bus-mapping Issues related to the bus-mapping workspace member crate-eth-types Issues related to the eth-types workspace member crate-zkevm-circuits Issues related to the zkevm-circuits workspace member labels Mar 13, 2023
@silathdiir silathdiir changed the title Bug: handle successful run with Uint64 overflow for multiple opcodes Fix to handle successful run with Uint64 overflow for multiple opcodes Mar 13, 2023
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.

First pass review, so far I've reviewed:

  • common_gadget
  • circuits: jumpi, extcodecopy, codecopy, error_invalid_jump

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.

Review completed: In general everything looks correct! My only notes are about a redundant constraint, and a few IsZero gadget that seem to be unused. I think they should be used: the Lt check is only correct if the value uses the specified number of bytes, and that's what the IsZero gadget inside of WordByteRangeGadget can check. So probably the logic should be something like:

            let src_addr = select::expr(
                code_offset.overflow(), 
                code_size.expr(), 
                select::expr(
                   code_offset.lt_cap(),
                   code_offset.valid_value(),
                   code_size.expr()),
            );

@silathdiir
Copy link
Contributor Author

Review completed ...

Thanks for review. I will try to resolve merge conflicts and review comments. Thanks.

@adria0 adria0 self-requested a review April 21, 2023 06:56
@adria0
Copy link
Member

adria0 commented Apr 22, 2023

LGTM

Amazing work @silathdiir

Also I see that this PR also activates EXTCODECOPY and there's a bunch of tests that are not passing, but nice to address them in another PRs.

e.g. cargo run --release -- --inspect "extcodehashEmpty_d1(storage)_g0_v0" dumps

error: constraint not satisfied

  Cell layout in region 'Execution step':
    | Offset |EVM_q_step|
    +--------+----------+
    |   345  |    x0    | <--{ Gate 'CALL_OP' applied here

  Constraint 'sum(addends_lo) == sum_lo + carry_lo ⋅ 2^128':
    (S9 * x0) * (( * ) * ((0 + 0 +  * 1 +  * 0x100 +  * 0x10000 +  * 0x1000000 +  * 0x100000000 +  * 0x10000000000 +  * 0x1000000000000 +  * 0x100000000000000 +  * 0x10000000000000000 +  * 0x1000000000000000000 +  * 0x100000000000000000000 +  * 0x10000000000000000000000 +  * 0x1000000000000000000000000 +  * 0x100000000000000000000000000 +  * 0x10000000000000000000000000000 +  * 0x1000000000000000000000000000000 + 0 +  * 1 +  * 0x100 +  * 0x10000 +  * 0x1000000 +  * 0x100000000 +  * 0x10000000000 +  * 0x1000000000000 +  * 0x100000000000000 +  * 0x10000000000000000 +  * 0x1000000000000000000 +  * 0x100000000000000000000 +  * 0x10000000000000000000000 +  * 0x1000000000000000000000000 +  * 0x100000000000000000000000000 +  * 0x10000000000000000000000000000 +  * 0x1000000000000000000000000000000 - (0 +  * 1 +  * 0x100 +  * 0x10000 +  * 0x1000000 +  * 0x100000000 +  * 0x10000000000 +  * 0x1000000000000 +  * 0x100000000000000 +  * 0x10000000000000000 +  * 0x1000000000000000000 +  * 0x100000000000000000000 +  * 0x10000000000000000000000 +  * 0x1000000000000000000000000 +  * 0x100000000000000000000000000 +  * 0x10000000000000000000000000000 +  * 0x1000000000000000000000000000000 +  * 0x100000000000000000000000000000000)) * )) = 0

  Assigned cell values:
    x0 = 1

error: constraint not satisfied

  Cell layout in region 'Execution step':
    | Offset |EVM_q_step|
    +--------+----------+
    |   345  |    x0    | <--{ Gate 'CALL_OP' applied here

  Constraint 'sum(addends_hi) + carry_lo == sum_hi':
    (S9 * x0) * (( * ) * ((0 + 0 +  * 1 +  * 0x100 +  * 0x10000 +  * 0x1000000 +  * 0x100000000 +  * 0x10000000000 +  * 0x1000000000000 +  * 0x100000000000000 +  * 0x10000000000000000 +  * 0x1000000000000000000 +  * 0x100000000000000000000 +  * 0x10000000000000000000000 +  * 0x1000000000000000000000000 +  * 0x100000000000000000000000000 +  * 0x10000000000000000000000000000 +  * 0x1000000000000000000000000000000 + 0 +  * 1 +  * 0x100 +  * 0x10000 +  * 0x1000000 +  * 0x100000000 +  * 0x10000000000 +  * 0x1000000000000 +  * 0x100000000000000 +  * 0x10000000000000000 +  * 0x1000000000000000000 +  * 0x100000000000000000000 +  * 0x10000000000000000000000 +  * 0x1000000000000000000000000 +  * 0x100000000000000000000000000 +  * 0x10000000000000000000000000000 +  * 0x1000000000000000000000000000000 +  - (0 +  * 1 +  * 0x100 +  * 0x10000 +  * 0x1000000 +  * 0x100000000 +  * 0x10000000000 +  * 0x1000000000000 +  * 0x100000000000000 +  * 0x10000000000000000 +  * 0x1000000000000000000 +  * 0x100000000000000000000 +  * 0x10000000000000000000000 +  * 0x1000000000000000000000000 +  * 0x100000000000000000000000000 +  * 0x10000000000000000000000000000 +  * 0x1000000000000000000000000000000)) * )) = 0

  Assigned cell values:
    x0 = 1

error: constraint not satisfied

  Cell layout in region 'Execution step':
    | Offset |EVM_q_step|
    +--------+----------+
    |   345  |    x0    | <--{ Gate 'CALL_OP' applied here

  Constraint 'sum(addends_lo) == sum_lo + carry_lo ⋅ 2^128':
    (S9 * x0) * (( * ) * ((0 + 0 +  * 1 +  * 0x100 +  * 0x10000 +  * 0x1000000 +  * 0x100000000 +  * 0x10000000000 +  * 0x1000000000000 +  * 0x100000000000000 +  * 0x10000000000000000 +  * 0x1000000000000000000 +  * 0x100000000000000000000 +  * 0x10000000000000000000000 +  * 0x1000000000000000000000000 +  * 0x100000000000000000000000000 +  * 0x10000000000000000000000000000 +  * 0x1000000000000000000000000000000 + 0 +  * 1 +  * 0x100 +  * 0x10000 +  * 0x1000000 +  * 0x100000000 +  * 0x10000000000 +  * 0x1000000000000 +  * 0x100000000000000 +  * 0x10000000000000000 +  * 0x1000000000000000000 +  * 0x100000000000000000000 +  * 0x10000000000000000000000 +  * 0x1000000000000000000000000 +  * 0x100000000000000000000000000 +  * 0x10000000000000000000000000000 +  * 0x1000000000000000000000000000000 - (0 +  * 1 +  * 0x100 +  * 0x10000 +  * 0x1000000 +  * 0x100000000 +  * 0x10000000000 +  * 0x1000000000000 +  * 0x100000000000000 +  * 0x10000000000000000 +  * 0x1000000000000000000 +  * 0x100000000000000000000 +  * 0x10000000000000000000000 +  * 0x1000000000000000000000000 +  * 0x100000000000000000000000000 +  * 0x10000000000000000000000000000 +  * 0x1000000000000000000000000000000 +  * 0x100000000000000000000000000000000)) * )) = 0

  Assigned cell values:
    x0 = 1

error: constraint not satisfied

  Cell layout in region 'Execution step':
    | Offset |EVM_q_step|
    +--------+----------+
    |   345  |    x0    | <--{ Gate 'CALL_OP' applied here

  Constraint 'State transition (delta) constraint of rw_counter':
    (S9 * x0) * (( * ) * ( * )) = 0

  Assigned cell values:
    x0 = 1

error: constraint not satisfied

  Cell layout in region 'Execution step':
    | Offset |EVM_q_step|
    +--------+----------+
    |   345  |    x0    | <--{ Gate 'CALL_OP' applied here

  Constraint 'State transition (delta) constraint of reversible_write_counter':
    (S9 * x0) * (( * ) * ( * )) = 0

  Assigned cell values:
    x0 = 1

error: lookup input does not exist in table
  (L0) ∉ ("A13" * C2 + "A12" * C2 + "A11" * C2 + "A10" * C2 + "A9" * C2 + "field_tag" * C2 + "address" * C2 + "id" * C2 + "tag" * C2 + "is_write" * C2 + "rw_counter")

  Lookup 'Rw' inputs:
    L0 = x0
    ^

    | Cell layout in region 'Execution step':
    |   | Offset |EVM_lookup_rw_0|
    |   +--------+---------------+
    |   |   348  |       x0      | <--{ Lookup 'Rw' inputs queried here
    |
    | Assigned cell values:
    |   x0 = 0x27f6a29ea0deafc1f53194885721c68d2d039966c07f5a0db4f23e49b662f264

error: lookup input does not exist in table
  (L0) ∉ ("A13" * C2 + "A12" * C2 + "A11" * C2 + "A10" * C2 + "A9" * C2 + "field_tag" * C2 + "address" * C2 + "id" * C2 + "tag" * C2 + "is_write" * C2 + "rw_counter")

  Lookup 'Rw' inputs:
    L0 = x0
    ^

    | Cell layout in region 'Execution step':
    |   | Offset |EVM_lookup_rw_2|
    |   +--------+---------------+
    |   |   348  |       x0      | <--{ Lookup 'Rw' inputs queried here
    |
    | Assigned cell values:
    |   x0 = 0x2c972172974c7a6810556b70c357a2743815b4c4fd8442fdd8eaf8bf5b8dad64

from_bytes::expr(&self.original.cells[..VALID_BYTES])
}

pub(crate) fn within_range(&self) -> Expression<F> {
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick: maybe is more readable if we name not_overflow() instead valid_range(), overflow/not_overflow is more dualistic over the same thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Rename within_range to not_overflow in commit 11f8eb3.

for (i, byte) in calldata_bytes.iter_mut().enumerate() {
if call.is_root {
// Fetch from tx call data.
if src_addr.checked_add(i as u64).unwrap() < tx.call_data_length as u64 {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe better just adding instead using checked_add with an unwrap later?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replace check_add and unwrap with just adding in commit 594553d.

}
} else {
// Fetch from memory.
if src_addr.checked_add(i as u64).unwrap()
Copy link
Member

Choose a reason for hiding this comment

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

same

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replace check_add and unwrap with just adding in commit 594553d.

@silathdiir
Copy link
Contributor Author

silathdiir commented Apr 27, 2023

Review completed: In general everything looks correct! My only notes are about a redundant constraint, and a few IsZero gadget that seem to be unused. I think they should be used: the Lt check is only correct if the value uses the specified number of bytes, and that's what the IsZero gadget inside of WordByteRangeGadget can check. So probably the logic should be something like:

            let src_addr = select::expr(
                code_offset.overflow(), 
                code_size.expr(), 
                select::expr(
                   code_offset.lt_cap(),
                   code_offset.valid_value(),
                   code_size.expr()),
            );

Seems that lt_cap contains the logic of within_range as with_range && offset < cap in WordByteCapGadget::construct. Could we use lt_cap to check directly?

            let src_addr = select::expr(
                code_offset.lt_cap(),
                code_offset.valid_value(),
                code_size.expr(),
            );

@silathdiir
Copy link
Contributor Author

LGTM

Amazing work @silathdiir

Also I see that this PR also activates EXTCODECOPY and there's a bunch of tests that are not passing, but nice to address them in another PRs.

e.g. cargo run --release -- --inspect "extcodehashEmpty_d1(storage)_g0_v0"

It may be caused by memory_offset + memory_length should not be Uint64 overflow. I will check if it could be fixed by local PR scroll-tech#393. Thanks.

@silathdiir silathdiir requested a review from ed255 April 27, 2023 09:03
@lispc lispc enabled auto-merge April 28, 2023 06:37
auto-merge was automatically disabled April 28, 2023 11:19

Head branch was pushed to by a user without write access

@lispc lispc added this pull request to the merge queue Apr 28, 2023
Merged via the queue into privacy-scaling-explorations:main with commit 22dd263 Apr 28, 2023
@silathdiir silathdiir deleted the bug/some-successful-cases-with-overflow-input-params branch April 29, 2023 06:33
silathdiir added a commit to scroll-tech/zkevm-circuits that referenced this pull request May 2, 2023
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-eth-types Issues related to the eth-types workspace member crate-zkevm-circuits Issues related to the zkevm-circuits workspace member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bug: second stack pop value offset (source offset) of CALLDATACOPY could be overflow and success to run
4 participants