Skip to content

Conversation

@letypequividelespoubelles
Copy link
Contributor

@letypequividelespoubelles letypequividelespoubelles commented Dec 18, 2025

This PR implements issue(s) #

Checklist

  • I wrote new tests for my new core changes.
  • I have successfully ran tests, style checker and build against my new changes locally.
  • I have informed the team of any breaking changes if there are any.

Note

Reworks the OOB module to self-contained zkasm-style operations (setInputs/setOutputs + traceOob), removes exo-calls, updates hub/IMC, CREATE flow, precompile OOBs and tests, and standardizes traceHub APIs; also bumps test timeout.

  • Core/OOB architecture:
    • Replace exo-based OOB with zkasm-style OobCall ops (setInputs/setOutputs + traceOob); remove OobExoCall and OobOperation.
    • Update Hub to use new Oob() and ImcFragment to call hub.oob().call(f, hub).
  • Trace API cleanup:
    • Rename trace(...) to traceHub(...) across fragments; adjust all callers.
  • CREATE flow:
    • Consolidate CreateOobCall (remove London/Shanghai variants) and use XCreateOobCall on max code size; simplify section logic.
  • OOB opcodes:
    • Inline logic for CALL, CDL, RDC, JUMP(I), SSTORE, XCALL, XCREATE, Deployment using EWord types and internal comparisons.
  • Precompile OOBs:
    • Refactor BLAKE, MODEXP (incl. computeExponentLog), EC (ECADD/ECMUL/ECPAIRING/ECRECOVER), BLS (G1/G2 add/msm/pairing/check/map), SHA/RIP/ID to compute costs/flags internally and trace via inst(...).
  • Tests/Utils:
    • Update tests to new APIs and helpers; import computeExponentLog from MODEXP OOB; adapt OOB accessors.
  • Build:
    • Increase JUnit default timeout to 30 minutes in reference tests.

Written by Cursor Bugbot for commit 47c8c2e. This will update automatically on new commits. Configure here.

@letypequividelespoubelles letypequividelespoubelles added Arithmetization Arithmetization team is in charge or involved in this task field agnosticity labels Dec 18, 2025
@letypequividelespoubelles letypequividelespoubelles linked an issue Dec 18, 2025 that may be closed by this pull request
6 tasks
@letypequividelespoubelles letypequividelespoubelles changed the title feat: zkasm OOB [ZkTracer] feat: zkasm OOB Dec 18, 2025
Signed-off-by: F Bojarski <[email protected]>
Signed-off-by: F Bojarski <[email protected]>
Signed-off-by: F Bojarski <[email protected]>
Signed-off-by: F Bojarski <[email protected]>
Signed-off-by: F Bojarski <[email protected]>
Signed-off-by: F Bojarski <[email protected]>
Signed-off-by: F Bojarski <[email protected]>
Signed-off-by: F Bojarski <[email protected]>
Signed-off-by: F Bojarski <[email protected]>
This puts through some minor tweaks for running the tracer tests.
The calcuation for determining when a JUMP is out-of-bounds was
comparing an EWord (pcNew) egainst a Bytes instance (codeSize) which was
failing (presumably comparing these two types does something else).

Therefore, converting codeSize to be stored in an EWord instance
resolves the issue.
These values were not being capped at 0x20 due to the use of
Bytes.compareTo instead of e.g. EWord.compareTo.
Again, use of `Bytes.compareTo()` was causing incorrect success flag
settings.
@DavePearce DavePearce force-pushed the 1943-zktracer-zkasm-oob branch from 818e67e to a1b3c11 Compare December 19, 2025 00:42
This changes a number of fields from having type Bytes into type EWord.
Whilst in some cases this may seem odd, since the given value may be
consider of "arbitrary size" we know in practice it should not overflow.
This fixes an issue identified by cursor where the remainder was being
computed instead of the division.
public void setInputs(Hub hub, MessageFrame frame) {
setOffset(EWord.of(frame.getStackItem(1)));
setSize(EWord.of(frame.getStackItem(2)));
setRds(Bytes.ofUnsignedLong(frame.getReturnData().size()));
Copy link

Choose a reason for hiding this comment

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

Bug: Missing overflow check in RETURNDATACOPY bounds validation

The refactored setOutputs() method in ReturnDataCopyOobCall computes sum = offset.add(size) using 256-bit modular arithmetic and checks sum.compareTo(rds) > 0. The old code first checked if offset.hi() or size.hi() were non-zero (indicating values >= 2^128) and would immediately fail the bounds check. With the new logic, if both offset and size have large values causing their sum to overflow and wrap around to a small value, the comparison with rds could incorrectly return false. For example, if offset = 2^255 and size = 2^255, their sum wraps to 0, making rdcx incorrectly false when it should be true.

Fix in Cursor Fix in Web

// row i + 3
final OobExoCall creatorNonceAbortCall = callToLT(wcp, creatorNonce, EIP2681_MAX_NONCE_BYTES);
exoCalls.add(creatorNonceAbortCall);
final boolean creatorNonceAbort = !bytesToBoolean(creatorNonceAbortCall.result());
Copy link

Choose a reason for hiding this comment

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

Bug: Inconsistent trace method call for CreateOobCall

The traceOob method in CreateOobCall uses .validateRow() while all other OobCall implementations use .fillAndValidateRow(). This inconsistency suggests a potential oversight during refactoring. If fillAndValidateRow() is responsible for setting default values on unfilled trace columns, using only validateRow() could result in missing data in the trace output.

Fix in Cursor Fix in Web

This adds provisional support for the p256verify precompile contract to
the zkasm oob implementation.
public void setInputs(Hub hub, MessageFrame frame) {
setOffset(EWord.of(frame.getStackItem(1)));
setSize(EWord.of(frame.getStackItem(2)));
setRds(Bytes.ofUnsignedLong(frame.getReturnData().size()));
Copy link

Choose a reason for hiding this comment

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

Bug: Missing overflow check in RETURNDATACOPY bounds validation

The refactored setOutputs() method no longer checks for range-out-of-bounds (rdcRoob) when offset.hi() or size.hi() is non-zero. The original implementation explicitly detected this case because: (1) values with non-zero high parts are >= 2^128, which guarantees an out-of-bounds condition for any realistic return data size, and (2) the 256-bit addition of such large values can overflow and wrap around to a small value. The new code offset.add(size).compareTo(rds) > 0 fails when the sum overflows to a value smaller than rds, incorrectly setting rdcx = false when it should be true. This could cause RETURNDATACOPY operations to incorrectly pass bounds checks with specially crafted offset/size values.

Fix in Cursor Fix in Web

.data9(creatorNonce);
.data9(creatorNonce)
.data10(Bytes.ofUnsignedLong(codeSize))
.validateRow();
Copy link

Choose a reason for hiding this comment

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

Bug: Inconsistent trace method used in CreateOobCall

The traceOob() method calls .validateRow() while all other OobCall implementations consistently use .fillAndValidateRow(). This inconsistency could cause issues if validateRow() doesn't fill in default values for unset columns while fillAndValidateRow() does. All other trace methods in the OOB module uniformly use fillAndValidateRow(), suggesting this is likely an oversight during the refactoring.

Fix in Cursor Fix in Web

Another comparison of EWord against Bytes.
return CT_MAX_RDC;
public void setOutputs() {
final EWord sum = offset.add(size);
setRdcx(sum.compareTo(rds) > 0);
Copy link

Choose a reason for hiding this comment

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

Bug: Missing overflow check in RETURNDATACOPY OOB validation

The refactored setOutputs() method computes offset.add(size) and compares the result to rds, but it's missing the original overflow check. The previous implementation first checked if offset.hi() or size.hi() were non-zero (indicating values >= 2^128), which would immediately set rdcx = true. Since EWord extends BaseUInt256Value and uses modular 256-bit arithmetic, offset.add(size) wraps on overflow. If both values are very large and their sum wraps to a small value, the comparison could incorrectly return false when it should return true. For example, if offset = 2^256 - 1 and size = 2, the sum wraps to 1, which would compare as less than a typical rds value, incorrectly allowing an out-of-bounds operation.

Fix in Cursor Fix in Web

return CT_MAX_RDC;
public void setOutputs() {
final EWord sum = offset.add(size);
setRdcx(sum.compareTo(rds) > 0);
Copy link

Choose a reason for hiding this comment

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

Bug: Addition overflow in RETURNDATACOPY bounds check

The setOutputs() method computes offset.add(size) without first checking if either value's high 128 bits are non-zero. Since EWord extends BaseUInt256Value with modular 256-bit arithmetic, if both offset and size are large (e.g., both >= 2^255), their sum can overflow and wrap to a small value. For example, 2^255 + 2^255 = 0 mod 2^256. This would cause sum.compareTo(rds) > 0 to incorrectly return false when it should be true, failing to detect an out-of-bounds condition. The original code prevented this by explicitly checking isZero(concat(offset.hi(), size.hi())) first and treating non-zero high parts as immediate out-of-bounds.

Fix in Cursor Fix in Web

return CT_MAX_RDC;
public void setOutputs() {
final EWord sum = offset.add(size);
setRdcx(sum.compareTo(rds) > 0);
Copy link

Choose a reason for hiding this comment

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

Bug: Missing overflow check in RETURNDATACOPY bounds validation

The refactored setOutputs() method computes offset.add(size) and compares the result to rds. However, EWord uses modular arithmetic (mod 2^256), so if offset + size overflows, the result wraps around to a small value. The original implementation first checked if offset.hi() or size.hi() were non-zero (indicating values >= 2^128) and immediately flagged as out-of-bounds before performing the addition. Without this check, extreme values like offset = 2^256 - 1 and size = 2 would wrap to sum = 1, incorrectly passing the bounds check when rds > 1. This could cause the zkTracer to produce incorrect traces that don't match actual EVM semantics for RETURNDATACOPY.

Fix in Cursor Fix in Web

Copy link
Contributor

@DavePearce DavePearce left a comment

Choose a reason for hiding this comment

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

LGTM

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

Labels

Arithmetization Arithmetization team is in charge or involved in this task field agnosticity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ZkTracer] zkasm OOB

4 participants