From 44af7e6b4527bdc9d094b72092c21955f9a3ba75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20B=C3=A9gassat?= <38285177+OlivierBBB@users.noreply.github.com> Date: Sat, 16 Nov 2024 14:24:27 +0700 Subject: [PATCH] `HUB` debugging continued (#1492) --- .../linea/zktracer/module/gas/Gas.java | 2 +- .../zktracer/module/hub/AccountSnapshot.java | 18 +- .../linea/zktracer/module/hub/Hub.java | 29 ++-- .../zktracer/module/hub/TransactionStack.java | 3 + .../linea/zktracer/module/hub/TxTrace.java | 2 +- .../module/hub/defer/DeferRegistry.java | 6 +- .../module/hub/defer/PostRollbackDefer.java | 2 +- .../module/hub/fragment/ContextFragment.java | 2 +- .../module/hub/fragment/StackFragment.java | 12 +- .../hub/fragment/common/CommonFragment.java | 22 +-- .../fragment/common/CommonFragmentValues.java | 80 +++++++-- .../module/hub/fragment/imc/ImcFragment.java | 30 +++- .../scenario/CallScenarioFragment.java | 13 +- .../scenario/CreateScenarioFragment.java | 4 +- .../module/hub/section/AccountSection.java | 28 ++-- .../module/hub/section/CreateSection.java | 62 ++++--- .../module/hub/section/KeccakSection.java | 2 +- .../module/hub/section/SloadSection.java | 2 +- .../module/hub/section/SstoreSection.java | 2 +- .../module/hub/section/TraceSection.java | 7 +- .../hub/section/TxFinalizationSection.java | 53 +++--- .../hub/section/TxInitializationSection.java | 12 +- .../hub/section/TxPreWarmingMacroSection.java | 10 +- ...SkippedSection.java => TxSkipSection.java} | 13 +- .../module/hub/section/call/CallSection.java | 86 +++++++--- .../PrecompileSubsection.java | 2 +- .../hub/section/copy/ExtCodeCopySection.java | 2 +- .../hub/section/halt/ReturnSection.java | 6 +- .../hub/section/halt/RevertSection.java | 2 +- .../hub/section/halt/SelfdestructSection.java | 2 +- .../module/hub/section/halt/StopSection.java | 2 +- .../module/hub/signals/Exceptions.java | 10 +- .../zktracer/module/mxp/MxpOperation.java | 2 +- .../zktracer/runtime/callstack/CallFrame.java | 14 +- .../zktracer/runtime/callstack/CallStack.java | 8 +- .../zktracer/runtime/stack/StackItem.java | 2 +- .../instructionprocessing/BalanceTests.java | 105 ++++++++++++ .../instructionprocessing/CallDataTests.java | 15 +- .../instructionprocessing/JumpTest.java | 16 ++ .../instructionprocessing/KeccakTests.java | 106 ++++++++++++ .../callTests/DoubleCall.java | 73 ++++++++ .../callTests/TrimmingTests.java | 63 +++++++ .../callTests/Utilities.java | 108 ++++++++++++ .../callTests/abort/BalanceAbortTests.java | 84 ++++++++++ .../abort/CallStackDepthAbortTests.java | 66 ++++++++ .../callTests/abort/MultiCallAbortTests.java | 73 ++++++++ .../callTests/eoa/EoaTests.java | 156 ++++++++++++++++++ .../callTests/eoa/gasStipendTests.java | 47 ++++++ .../prc/ecrecover/gasStipendTests.java | 88 ++++++++++ .../callTests/smc/Utilities.java | 65 ++++++++ .../monoOpCodeTargets/ImmediateInvalid.java | 57 +++++++ .../smc/monoOpCodeTargets/singleJumpDest.java | 103 ++++++++++++ .../smc/monoOpCodeTargets/singleStop.java | 62 +++++++ notes/emptyDeploymentsInTheRootTest_notes.md | 105 ------------ notes/failing_constraints_CODECOPY_tests.md | 80 --------- .../linea/testing/BytecodeRunner.java | 4 +- 56 files changed, 1628 insertions(+), 402 deletions(-) rename arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/{TxSkippedSection.java => TxSkipSection.java} (95%) create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/BalanceTests.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/KeccakTests.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/DoubleCall.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/TrimmingTests.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/Utilities.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/abort/BalanceAbortTests.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/abort/CallStackDepthAbortTests.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/abort/MultiCallAbortTests.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/eoa/EoaTests.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/eoa/gasStipendTests.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/prc/ecrecover/gasStipendTests.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/Utilities.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/monoOpCodeTargets/ImmediateInvalid.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/monoOpCodeTargets/singleJumpDest.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/monoOpCodeTargets/singleStop.java delete mode 100644 notes/emptyDeploymentsInTheRootTest_notes.md delete mode 100644 notes/failing_constraints_CODECOPY_tests.md diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/gas/Gas.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/gas/Gas.java index a24ad1675f..432469483f 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/gas/Gas.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/gas/Gas.java @@ -74,7 +74,7 @@ public void commit(List buffers) { public void resolvePostExecution( Hub hub, MessageFrame frame, Operation.OperationResult operationResult) { gasParameters.gasActual(BigInteger.valueOf(commonValues.gasActual)); - gasParameters.gasCost(BigInteger.valueOf(commonValues.gasCost())); + gasParameters.gasCost(BigInteger.valueOf(commonValues.gasCostToTrace())); gasParameters.xahoy(Exceptions.any(commonValues.exceptions)); gasParameters.oogx(commonValues.tracedException() == TracedException.OUT_OF_GAS_EXCEPTION); this.operations.add(new GasOperation(gasParameters, wcp)); diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/AccountSnapshot.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/AccountSnapshot.java index 871aa28886..47c1e1fe50 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/AccountSnapshot.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/AccountSnapshot.java @@ -67,6 +67,14 @@ public static AccountSnapshot canonical(Hub hub, Address address) { return canonicalSnapshot; } + public static AccountSnapshot canonical(Hub hub, WorldView world, Address address) { + return fromArguments( + world, + address, + hub.transients.conflation().deploymentInfo(), + isAddressWarm(hub.messageFrame(), address)); + } + public static AccountSnapshot canonical( Hub hub, WorldView world, Address address, boolean warmth) { return fromArguments(world, address, hub.transients.conflation().deploymentInfo(), warmth); @@ -234,13 +242,17 @@ public AccountSnapshot setWarmthTo(boolean newWarmth) { * @return {@code this} with nonce++ */ public AccountSnapshot raiseNonceByOne() { - nonce(nonce + 1); + this.nonce(nonce + 1); return this; } + public AccountSnapshot setDeploymentInfo(Hub hub) { + return this.setDeploymentInfo(hub.transients.conflation().deploymentInfo()); + } + public AccountSnapshot setDeploymentInfo(DeploymentInfo deploymentInfo) { - deploymentNumber(deploymentInfo.deploymentNumber(address)); - deploymentStatus(deploymentInfo.getDeploymentStatus(address)); + this.deploymentNumber(deploymentInfo.deploymentNumber(address)); + this.deploymentStatus(deploymentInfo.getDeploymentStatus(address)); return this; } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/Hub.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/Hub.java index 784558ebb0..9f10da9011 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/Hub.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/Hub.java @@ -502,7 +502,7 @@ public void traceStartTransaction(final WorldView world, final Transaction tx) { if (!transactionProcessingMetadata.requiresEvmExecution()) { state.setProcessingPhase(TX_SKIP); - new TxSkippedSection(this, world, transactionProcessingMetadata, transients); + new TxSkipSection(this, world, transactionProcessingMetadata, transients); } else { if (transactionProcessingMetadata.requiresPrewarming()) { state.setProcessingPhase(TX_WARM); @@ -634,7 +634,7 @@ public void traceContextEnter(MessageFrame frame) { final long callDataContextNumber = callStack.currentCallFrame().contextNumber(); - currentFrame().rememberGasNextBeforePausing(); + currentFrame().rememberGasNextBeforePausing(this); currentFrame().pauseCurrentFrame(); MemorySpan returnDataTargetInCaller = @@ -690,16 +690,17 @@ public void traceContextExit(MessageFrame frame) { coinbaseIsWarm, txStack.getAccumulativeGasUsedInBlockBeforeTxStart()); - if (state.getProcessingPhase() != TX_SKIP) { - state.setProcessingPhase(TX_FINL); - new TxFinalizationSection(this, frame.getWorldUpdater()); + if (state.getProcessingPhase() != TX_SKIP + && frame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { + this.state.setProcessingPhase(TX_FINL); + new TxFinalizationSection(this, frame.getWorldUpdater(), false); } } defers.resolveUponContextExit(this, this.currentFrame()); // TODO: verify me please @Olivier if (this.currentFrame().opCode() == REVERT || Exceptions.any(pch.exceptions())) { - defers.resolvePostRollback(this, frame, this.currentFrame()); + defers.resolveUponRollback(this, frame, this.currentFrame()); } if (frame.getDepth() > 0) { @@ -740,7 +741,7 @@ public void tracePostExecution(MessageFrame frame, Operation.OperationResult ope */ if (isExceptional()) { this.currentTraceSection() - .addFragments(ContextFragment.executionProvidesEmptyReturnData(this)); + .exceptionalContextFragment(ContextFragment.executionProvidesEmptyReturnData(this)); this.squashCurrentFrameOutputData(); this.squashParentFrameReturnData(); } @@ -750,6 +751,11 @@ public void tracePostExecution(MessageFrame frame, Operation.OperationResult ope if (!this.currentFrame().opCode().isCall() && !this.currentFrame().opCode().isCreate()) { this.unlatchStack(frame, currentSection); } + + if (frame.getDepth() == 0 && (isExceptional() || opCode() == REVERT)) { + this.state.setProcessingPhase(TX_FINL); + new TxFinalizationSection(this, frame.getWorldUpdater(), true); + } } /** @@ -771,6 +777,7 @@ private void compareLineaAndBesuGasCosts( currentSection.commonValues.gasCostExcluduingDeploymentCost(); if (operationResult.getHaltReason() != null) { + return; } @@ -801,11 +808,6 @@ public boolean isExceptional() { return !isUnexceptional(); } - public boolean raisesOogxOrIsUnexceptional() { - return currentTraceSection().commonValues.tracedException() == OUT_OF_GAS_EXCEPTION - || isUnexceptional(); - } - /** * If the current execution context is a deployment context the present method "exits" that * deployment in the sense that it updates the relevant deployment information. @@ -1101,7 +1103,8 @@ void traceOpcode(MessageFrame frame) { case CREATE -> new CreateSection(this); - case CALL -> new CallSection(this); + case CALL -> new CallSection(this, frame); + case INVALID -> new EarlyExceptionSection(this); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/TransactionStack.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/TransactionStack.java index 604c1177ac..ab14be2dff 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/TransactionStack.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/TransactionStack.java @@ -19,7 +19,9 @@ import java.util.List; import lombok.Getter; +import lombok.Setter; import net.consensys.linea.zktracer.container.StackedContainer; +import net.consensys.linea.zktracer.module.hub.section.TxInitializationSection; import net.consensys.linea.zktracer.module.hub.transients.Block; import net.consensys.linea.zktracer.types.TransactionProcessingMetadata; import org.hyperledger.besu.datatypes.Transaction; @@ -31,6 +33,7 @@ public class TransactionStack implements StackedContainer { new ArrayList<>(200); // TODO: write the allocated memory from .toml file private int currentAbsNumber; private int relativeTransactionNumber; + @Setter @Getter public TxInitializationSection initializationSection; public TransactionProcessingMetadata current() { return transactions.getLast(); diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/TxTrace.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/TxTrace.java index 8bd3e7fcfe..46e3273a11 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/TxTrace.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/TxTrace.java @@ -81,7 +81,6 @@ public boolean isEmpty() { * @param section the section to append */ public void add(TraceSection section) { - section.parentTrace(this); // Link the current section with the previous and next one final TraceSection previousSection = this.trace.isEmpty() ? null : this.trace.getLast(); if (previousSection != null) { @@ -112,6 +111,7 @@ public void commit(Trace hubTrace) { public int lineCount() { int lineCount = 0; for (TraceSection s : trace) { + if (s.exceptionalContextFragment != null) s.fragments().add(s.exceptionalContextFragment); lineCount += s.fragments().size(); } return lineCount; diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/defer/DeferRegistry.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/defer/DeferRegistry.java index b61f1378de..ec7379eea3 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/defer/DeferRegistry.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/defer/DeferRegistry.java @@ -169,14 +169,14 @@ public void resolvePostExecution(Hub hub, MessageFrame frame, Operation.Operatio * for the rollback. */ @Override - public void resolvePostRollback( + public void resolveUponRollback( final Hub hub, final MessageFrame messageFrame, CallFrame currentCallFrame) { Optional.ofNullable(hub.defers().rollbackDefers.get(currentCallFrame)) .ifPresent( defers -> { defers.forEach( - defer -> defer.resolvePostRollback(hub, messageFrame, currentCallFrame)); + defer -> defer.resolveUponRollback(hub, messageFrame, currentCallFrame)); defers.clear(); }); @@ -184,7 +184,7 @@ public void resolvePostRollback( final CallStack callStack = hub.callStack(); currentCallFrame.childFramesId().stream() .map(callStack::getById) - .forEach(childCallFrame -> resolvePostRollback(hub, messageFrame, childCallFrame)); + .forEach(childCallFrame -> resolveUponRollback(hub, messageFrame, childCallFrame)); } @Override diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/defer/PostRollbackDefer.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/defer/PostRollbackDefer.java index ee0d1e718c..a58a652a66 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/defer/PostRollbackDefer.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/defer/PostRollbackDefer.java @@ -25,5 +25,5 @@ public interface PostRollbackDefer { * @param messageFrame access point to world state & accrued state * @param callFrame reference to call frame whose actions are to be undone */ - public void resolvePostRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame); + public void resolveUponRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame); } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/ContextFragment.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/ContextFragment.java index 39f0486bac..aae719b9cc 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/ContextFragment.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/ContextFragment.java @@ -93,7 +93,7 @@ public static ContextFragment executionProvidesEmptyReturnData(final Hub hub) { public static ContextFragment executionProvidesEmptyReturnData(final Hub hub, int contextNumber) { CallStack callStack = hub.callStack(); - int parentId = callStack.getByContextNumber(contextNumber).callerId(); + int parentId = callStack.getByContextNumber(contextNumber).parentId(); return new ContextFragment( hub, callStack, Either.left(parentId), contextNumber, MemorySpan.empty(), true); } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/StackFragment.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/StackFragment.java index 0a3d39dcad..19bc88bfae 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/StackFragment.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/StackFragment.java @@ -76,11 +76,13 @@ private StackFragment( this.opCode = stack.getCurrentOpcodeData().mnemonic(); this.hashInfoFlag = switch (this.opCode) { - case SHA3 -> Exceptions.none(exceptions) && gp.messageSize() > 0; - case RETURN -> Exceptions.none(exceptions) && gp.messageSize() > 0 && isDeploying; - case CREATE2 -> Exceptions.none(exceptions) && aborts.none() && gp.messageSize() > 0; - default -> false; - }; + case SHA3 -> true; + case RETURN -> isDeploying; + case CREATE2 -> aborts.none(); + default -> false; + } + && Exceptions.none(exceptions) + && gp.messageSize() > 0; if (this.hashInfoFlag) { Bytes memorySegmentToHash; switch (this.opCode) { diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/common/CommonFragment.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/common/CommonFragment.java index a0ad080896..126ab7b9d1 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/common/CommonFragment.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/common/CommonFragment.java @@ -101,7 +101,7 @@ public Trace trace(Trace trace) { .contextNumber(isExec ? frame.contextNumber() : 0) .contextNumberNew(commonFragmentValues.contextNumberNew) .callerContextNumber( - commonFragmentValues.callStack.getById(frame.callerId()).contextNumber()) + commonFragmentValues.callStack.getById(frame.parentId()).contextNumber()) .contextWillRevert(frame.willRevert() && isExec) .contextGetsReverted(frame.getsReverted() && isExec) .contextSelfReverts(frame.selfReverts() && isExec) @@ -114,7 +114,7 @@ public Trace trace(Trace trace) { // peeking flags are traced in the respective fragments .gasExpected(Bytes.ofUnsignedLong(commonFragmentValues.gasExpected)) .gasActual(Bytes.ofUnsignedLong(commonFragmentValues.gasActual)) - .gasCost(gasCostToTrace()) + .gasCost(Bytes.ofUnsignedLong(commonFragmentValues.gasCostToTrace())) .gasNext( Bytes.ofUnsignedLong(isExec && isUnexceptional() ? commonFragmentValues.gasNext : 0)) .refundCounter(commonFragmentValues.gasRefund) @@ -125,24 +125,6 @@ public Trace trace(Trace trace) { .counterNsr((short) nonStackRowsCounter); } - private Bytes gasCostToTrace() { - - if (commonFragmentValues.hubProcessingPhase != TX_EXEC - || commonFragmentValues.tracedException() == TracedException.STACK_UNDERFLOW - || commonFragmentValues.tracedException() == TracedException.STACK_OVERFLOW - || commonFragmentValues.tracedException() == TracedException.RETURN_DATA_COPY_FAULT - || commonFragmentValues.tracedException() == TracedException.MEMORY_EXPANSION_EXCEPTION - || commonFragmentValues.tracedException() == TracedException.STATIC_FAULT - || commonFragmentValues.tracedException() == TracedException.INVALID_CODE_PREFIX - || commonFragmentValues.tracedException() == TracedException.MAX_CODE_SIZE_EXCEPTION) { - return Bytes.EMPTY; - } - - // TODO @Olivier: special care for CALL's and CREATE's - - return Bytes.ofUnsignedLong(commonFragmentValues.gasCost); - } - static long computeGasCost(Hub hub, WorldView world) { switch (hub.opCodeData().instructionFamily()) { diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/common/CommonFragmentValues.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/common/CommonFragmentValues.java index 09204c29c3..f335d77a18 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/common/CommonFragmentValues.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/common/CommonFragmentValues.java @@ -35,6 +35,7 @@ import net.consensys.linea.zktracer.module.hub.signals.TracedException; import net.consensys.linea.zktracer.opcode.InstructionFamily; import net.consensys.linea.zktracer.opcode.OpCode; +import net.consensys.linea.zktracer.opcode.gas.projector.GasProjection; import net.consensys.linea.zktracer.runtime.callstack.CallFrame; import net.consensys.linea.zktracer.runtime.callstack.CallStack; import net.consensys.linea.zktracer.types.TransactionProcessingMetadata; @@ -60,7 +61,7 @@ public class CommonFragmentValues { public final long gasExpected; public final long gasActual; @Getter final long gasCost; - final long gasNext; + @Getter @Setter long gasNext; @Getter final long gasCostExcluduingDeploymentCost; @Setter public long refundDelta = 0; // 0 is default Value, can be modified only by SSTORE section @Setter public long gasRefund; // Set at commit time @@ -94,7 +95,7 @@ public CommonFragmentValues(Hub hub) { this.gasExpected = isExec ? computeGasExpected() : 0; this.gasActual = isExec ? computeGasRemaining() : 0; this.gasCost = isExec ? computeGasCost() : 0; - this.gasNext = isExec ? computeGasNext() : 0; + this.gasNext = isExec ? computeGasNext(exceptions) : 0; this.gasCostExcluduingDeploymentCost = isExec ? computeGasCostExcludingDeploymentCost() : 0; final InstructionFamily instructionFamily = hub.opCode().getData().instructionFamily(); @@ -204,7 +205,7 @@ static int computePcNew(final Hub hub, final int pc, boolean stackException, boo "Instruction not covered " + opCode.getData().mnemonic() + " unable to compute pcNew."); } - private long computeGasRemaining() { + public long computeGasRemaining() { return hub.remainingGas(); } @@ -230,24 +231,47 @@ private long computeGasCostExcludingDeploymentCost() { return Hub.GAS_PROJECTOR.of(hub.messageFrame(), hub.opCode()).gasCostExcludingDeploymentCost(); } - public long computeGasNext() { - - if (hub.isExceptional()) { + /** + * Returns the value of the GAS_NEXT column. For CALL's and CREATE's it returns + * + *

remainingGas - upfrontGasCost
+ * + *

This initial computation has to be amended down the line to account for + * + *

- {@link GasProjection#stipend()} for aborted calls / EOA calls + * + *

- {@link GasProjection#gasPaidOutOfPocket()} when entering a CALL/CREATE + * + *

- precompile specific costs for PRC calls + * + *

The stipend is done through {@link CommonFragmentValues#collectChildStipend(Hub)}} + * + * @param exceptions + * @return + */ + public long computeGasNext(short exceptions) { + + if (Exceptions.any(exceptions)) { return 0; } final long gasAfterDeductingCost = computeGasRemaining() - computeGasCost(); + OpCode opCode = hub.opCode(); + GasProjection gasUtility = Hub.GAS_PROJECTOR.of(hub.messageFrame(), opCode); + return switch (hub.opCodeData().instructionFamily()) { - case KEC, COPY, STACK_RAM, STORAGE, LOG, HALT -> (hub.raisesOogxOrIsUnexceptional() - ? gasAfterDeductingCost - : 0); - case CREATE -> gasAfterDeductingCost - - Hub.GAS_PROJECTOR.of(hub.messageFrame(), hub.opCode()).gasPaidOutOfPocket(); - case CALL -> // TODO: this will not work because of 1. aborts with value transfers 2. EOA - // calls 3. precompile calls - gasAfterDeductingCost - - Hub.GAS_PROJECTOR.of(hub.messageFrame(), hub.opCode()).gasPaidOutOfPocket(); + case KEC, COPY, STACK_RAM, STORAGE, LOG, HALT -> gasAfterDeductingCost; + case CREATE -> gasAfterDeductingCost; + // TODO: this is only part of the story because of + // 1. nonempty init code CREATE's where gas is paid out of pocket + case CALL -> gasAfterDeductingCost; + // TODO: this is only part of the story because of + // 1. aborts with value transfers (immediately reapStipend) + // 2. EOA calls with value transfer (immediately reapStipend) + // 3. SMC calls: gas paid out of pocket + // 4. PRC calls: gas paid out of pocket + special PRC cost + returned gas + default -> // ADD, MUL, MOD, EXT, WCP, BIN, SHF, CONTEXT, ACCOUNT, TRANSACTION, BATCH, JUMP, // MACHINE_STATE, PUSH_POP, DUP, SWAP, INVALID // TODO: this may not work for EXP, EXTCODEHASH, EXTCODESIZE, BALANCE as they require extra @@ -255,4 +279,30 @@ public long computeGasNext() { gasAfterDeductingCost; }; } + + public void payGasPaidOutOfPocket(Hub hub) { + this.gasNext -= Hub.GAS_PROJECTOR.of(hub.messageFrame(), hub.opCode()).gasPaidOutOfPocket(); + } + + public void collectChildStipend(Hub hub) { + this.gasNext += Hub.GAS_PROJECTOR.of(hub.messageFrame(), hub.opCode()).stipend(); + } + + public long gasCostToTrace() { + + if (hubProcessingPhase != TX_EXEC + || tracedException() == TracedException.STACK_UNDERFLOW + || tracedException() == TracedException.STACK_OVERFLOW + || tracedException() == TracedException.RETURN_DATA_COPY_FAULT + || tracedException() == TracedException.MEMORY_EXPANSION_EXCEPTION + || tracedException() == TracedException.STATIC_FAULT + || tracedException() == TracedException.INVALID_CODE_PREFIX + || tracedException() == TracedException.MAX_CODE_SIZE_EXCEPTION) { + return 0; + } + + // TODO @Olivier: special care for CALL's and CREATE's + + return gasCost; + } } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/imc/ImcFragment.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/imc/ImcFragment.java index 75672ad69b..b98e7a9a9a 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/imc/ImcFragment.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/imc/ImcFragment.java @@ -20,18 +20,20 @@ import net.consensys.linea.zktracer.module.hub.Hub; import net.consensys.linea.zktracer.module.hub.Trace; +import net.consensys.linea.zktracer.module.hub.defer.ContextReEntryDefer; import net.consensys.linea.zktracer.module.hub.fragment.TraceFragment; import net.consensys.linea.zktracer.module.hub.fragment.TraceSubFragment; import net.consensys.linea.zktracer.module.hub.fragment.imc.exp.ExpCall; import net.consensys.linea.zktracer.module.hub.fragment.imc.mmu.MmuCall; import net.consensys.linea.zktracer.module.hub.fragment.imc.oob.OobCall; +import net.consensys.linea.zktracer.runtime.callstack.CallFrame; import net.consensys.linea.zktracer.types.TransactionProcessingMetadata; /** * IMCFragments embed data required for Inter-Module Communication, i.e. data that are required to * correctly trigger other modules from the Hub. */ -public class ImcFragment implements TraceFragment { +public class ImcFragment implements TraceFragment, ContextReEntryDefer { /** the list of modules to trigger withing this fragment. */ private final List moduleCalls = new ArrayList<>(5); @@ -43,6 +45,8 @@ public class ImcFragment implements TraceFragment { private boolean mmuIsSet = false; private boolean stpIsSet = false; + private CallFrame childFrame = null; + private ImcFragment(final Hub hub) { this.hub = hub; } @@ -136,6 +140,30 @@ public Trace trace(Trace trace) { subFragment.trace(trace, hub.state.stamps()); } + if (childFrame != null) { + trace.pMiscCcrsStamp(childFrame.revertStamp()).pMiscCcsrFlag(childFrame.selfReverts()); + } + return trace; } + + // TODO: The most natural thing would be to implement resolveAtContextEntry instead. + @Override + public void resolveAtContextReEntry(Hub hub, CallFrame frame) { + childFrame = hub.callStack().getById(frame.childFramesId().getLast()); + } + + /** + * The IMC fragment (or MISCELLANEOUS fragment in the specification) requires, for CALL and CREATE + * instructions, to record the following data + * + *

- whether the child context will or won't self-revert (i.e. CHILD_CONTEXT_SELF_REVERTS ≡ + * CCSR) + * + *

- if it does, at what point in time (i.e. CHILD_CONTEXT_REVERT_STAMP ≡ CCRS) + * + *

In order to capture this information we will schedule IMC fragments for context-re-entry. + * + * @param hub + */ } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/scenario/CallScenarioFragment.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/scenario/CallScenarioFragment.java index a15c1a40a6..8400819dff 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/scenario/CallScenarioFragment.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/scenario/CallScenarioFragment.java @@ -17,8 +17,6 @@ import static com.google.common.base.Preconditions.*; import static net.consensys.linea.zktracer.module.hub.fragment.scenario.CallScenarioFragment.CallScenario.*; -import java.util.List; - import lombok.Getter; import lombok.Setter; import net.consensys.linea.zktracer.module.hub.Trace; @@ -32,10 +30,6 @@ public CallScenarioFragment() { scenario = UNDEFINED; } - public CallScenarioFragment(final CallScenario callScenario) { - scenario = callScenario; - } - public enum CallScenario { UNDEFINED, CALL_EXCEPTION, @@ -62,14 +56,15 @@ public boolean isPrecompileScenario() { || this == CALL_PRC_SUCCESS_WONT_REVERT; } + public boolean isAbortingScenario() { + return this == CALL_ABORT_WILL_REVERT || this == CALL_ABORT_WONT_REVERT; + } + public boolean noLongerUndefined() { return this != UNDEFINED && this != CALL_PRC_UNDEFINED && this != CALL_SMC_UNDEFINED; } } - private static final List illegalTracingScenario = - List.of(UNDEFINED, CALL_SMC_UNDEFINED, CALL_PRC_UNDEFINED); - public Trace trace(Trace trace) { checkArgument(scenario.noLongerUndefined(), "Final Scenario hasn't been set"); return trace diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/scenario/CreateScenarioFragment.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/scenario/CreateScenarioFragment.java index 434a68d9f1..3a249c755a 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/scenario/CreateScenarioFragment.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/fragment/scenario/CreateScenarioFragment.java @@ -41,8 +41,8 @@ public CreateScenarioFragment() { this.scenario = CreateScenario.UNDEFINED; } - public CreateScenarioFragment(CreateScenario scenario) { - this.scenario = scenario; + public boolean isAbortedCreate() { + return scenario == CreateScenario.CREATE_ABORT; } @Override diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/AccountSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/AccountSection.java index c25a1e3f70..9665ed1cdc 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/AccountSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/AccountSection.java @@ -37,6 +37,8 @@ public class AccountSection extends TraceSection implements PostRollbackDefer { Bytes rawTargetAddress; Address targetAddress; + AccountSnapshot accountSnapshotBefore; + AccountSnapshot accountSnapshotAfter; public AccountSection(Hub hub) { super(hub, maxNumberOfRows(hub)); @@ -70,11 +72,11 @@ public AccountSection(Hub hub) { default -> throw new RuntimeException("Not an ACCOUNT instruction"); }; - final AccountSnapshot accountSnapshotBefore = AccountSnapshot.canonical(hub, targetAddress); - final AccountSnapshot accountSnapshotAfter = accountSnapshotBefore.deepCopy(); + accountSnapshotBefore = AccountSnapshot.canonical(hub, targetAddress); + accountSnapshotAfter = accountSnapshotBefore.deepCopy(); if (Exceptions.none(exceptions)) { - accountSnapshotAfter.turnOnWarmth(); // TODO: use canonical instead at postExecDefers ? + accountSnapshotAfter.turnOnWarmth(); } final DomSubStampsSubFragment doingDomSubStamps = @@ -94,25 +96,15 @@ public AccountSection(Hub hub) { this.addFragment(doingAccountFragment); } - public void resolvePostRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { + public void resolveUponRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { + final AccountSnapshot preRollBackAccountSnapshot = + accountSnapshotAfter.deepCopy().setDeploymentInfo(hub); final AccountSnapshot postRollBackAccountSnapshot = - AccountSnapshot.canonical(hub, targetAddress); - + accountSnapshotBefore.deepCopy().setDeploymentInfo(hub); final DomSubStampsSubFragment undoingDomSubStamps = DomSubStampsSubFragment.revertWithCurrentDomSubStamps( - this.hubStamp(), hub.currentFrame().revertStamp(), 0); - - final AccountSnapshot preRollBackAccountSnapshot = - postRollBackAccountSnapshot.deepCopy().turnOnWarmth(); - - // sanity check - final int deploymentNumberAtRollback = - hub.transients().conflation().deploymentInfo().deploymentNumber(targetAddress); - final boolean deploymentStatusAtRollback = - hub.transients().conflation().deploymentInfo().getDeploymentStatus(targetAddress); - checkArgument(deploymentNumberAtRollback == postRollBackAccountSnapshot.deploymentNumber()); - checkArgument(deploymentStatusAtRollback == postRollBackAccountSnapshot.deploymentStatus()); + this.hubStamp(), hub.currentFrame().revertStamp(), 1); this.addFragment( hub.factories() diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/CreateSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/CreateSection.java index 685d6f5a24..e2af4e48b0 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/CreateSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/CreateSection.java @@ -33,10 +33,7 @@ import net.consensys.linea.zktracer.module.hub.AccountSnapshot; import net.consensys.linea.zktracer.module.hub.Hub; -import net.consensys.linea.zktracer.module.hub.defer.ContextReEntryDefer; -import net.consensys.linea.zktracer.module.hub.defer.ImmediateContextEntryDefer; -import net.consensys.linea.zktracer.module.hub.defer.PostRollbackDefer; -import net.consensys.linea.zktracer.module.hub.defer.PostTransactionDefer; +import net.consensys.linea.zktracer.module.hub.defer.*; import net.consensys.linea.zktracer.module.hub.fragment.ContextFragment; import net.consensys.linea.zktracer.module.hub.fragment.DomSubStampsSubFragment; import net.consensys.linea.zktracer.module.hub.fragment.account.AccountFragment; @@ -60,10 +57,12 @@ import org.hyperledger.besu.evm.account.AccountState; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.internal.Words; +import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.worldstate.WorldView; public class CreateSection extends TraceSection - implements ImmediateContextEntryDefer, + implements PostOpcodeDefer, + ImmediateContextEntryDefer, PostRollbackDefer, ContextReEntryDefer, PostTransactionDefer { @@ -159,7 +158,8 @@ public CreateSection(Hub hub) { if (aborts.any()) { scenarioFragment.setScenario(CREATE_ABORT); - this.finishAbortCreate(hub); + this.finishAbort(hub); + hub.defers().scheduleForPostExecution(this); return; } @@ -195,30 +195,33 @@ public CreateSection(Hub hub) { new ShakiraDataOperation(hub.stamp(), create2InitCode); hub.shakiraData().call(shakiraDataOperation); - triggerHashInfo(shakiraDataOperation.result()); + writeHashInfoResult(shakiraDataOperation.result()); } value = failedCreate ? Wei.ZERO : Wei.of(UInt256.fromBytes(messageFrame.getStackItem(0))); - if (failedCreate || emptyInitCode) { + if (failedCreate) { finalContextFragment = ContextFragment.nonExecutionProvidesEmptyReturnData(hub); + scenarioFragment.setScenario(CREATE_FAILURE_CONDITION_WONT_REVERT); + hub.failureConditionForCreates = true; + return; + } - if (failedCreate) { - scenarioFragment.setScenario(CREATE_FAILURE_CONDITION_WONT_REVERT); - hub.failureConditionForCreates = true; - return; - } - - // this "if" is redundant and could be removed - // --- please don't, for now at least - if (emptyInitCode) { - scenarioFragment.setScenario(CREATE_EMPTY_INIT_CODE_WONT_REVERT); - hub.transients().conflation().deploymentInfo().newDeploymentSansExecutionAt(createeAddress); - return; - } + if (emptyInitCode) { + finalContextFragment = ContextFragment.nonExecutionProvidesEmptyReturnData(hub); + scenarioFragment.setScenario(CREATE_EMPTY_INIT_CODE_WONT_REVERT); + hub.transients().conflation().deploymentInfo().newDeploymentSansExecutionAt(createeAddress); + return; } // Finally, non-exceptional, non-aborting, non-failing, non-emptyInitCode create + //////////////////////////////////////////////////////////////////////////////// + + // we capture revert information about the child context: CCSR and CCRS + hub.defers().scheduleForContextReEntry(imcFragment, hub.currentFrame()); + + // The current execution context pays (63/64)ths of it current gas to the child context + commonValues.payGasPaidOutOfPocket(hub); hub.defers() .scheduleForContextReEntry(this, callFrame); // To get the success bit of the CREATE(2) @@ -307,7 +310,7 @@ public void resolveAtContextReEntry(Hub hub, CallFrame frame) { } @Override - public void resolvePostRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { + public void resolveUponRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { scenarioFragment.setScenario(switchToRevert(scenarioFragment.getScenario())); final AccountFragment.AccountFragmentFactory accountFragmentFactory = @@ -350,7 +353,7 @@ private static short maxNumberOfLines(final short exceptions, final AbortingCond return 11; // Note: could be lower for unreverted successful CREATE(s) } - private void finishAbortCreate(final Hub hub) { + private void finishAbort(final Hub hub) { final AccountFragment.AccountFragmentFactory accountFragmentFactory = hub.factories().accountFragment(); final AccountFragment creatorAccountFragment = @@ -375,4 +378,17 @@ private static CreateScenarioFragment.CreateScenario switchToRevert( default -> throw new IllegalArgumentException("unexpected Create scenario"); }; } + + public boolean isAbortedCreate() { + return scenarioFragment.isAbortedCreate(); + } + + // we unlatched the stack after a CREATE if and only if we don't "contextEnter" the CREATE. + // "failure condition CREATE's" do enter the CREATE context. + @Override + public void resolvePostExecution( + Hub hub, MessageFrame frame, Operation.OperationResult operationResult) { + checkState(isAbortedCreate()); + hub.unlatchStack(frame, this); + } } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/KeccakSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/KeccakSection.java index 258713dcf2..fe311be853 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/KeccakSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/KeccakSection.java @@ -68,7 +68,7 @@ public void resolvePostExecution( final Bytes32 hashResult = Bytes32.leftPad(frame.getStackItem(0)); // retroactively set HASH_INFO_FLAG and HASH_INFO_KECCAK_HI, HASH_INFO_KECCAK_LO - this.triggerHashInfo(hashResult); + this.writeHashInfoResult(hashResult); if (triggerMmu) { final ShakiraDataOperation shakiraDataOperation = diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/SloadSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/SloadSection.java index 07ab744dd7..7f0b5f8fa5 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/SloadSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/SloadSection.java @@ -104,7 +104,7 @@ private StorageFragment doingSload(Hub hub) { } @Override - public void resolvePostRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { + public void resolveUponRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { if (!this.undoingRequired()) { return; diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/SstoreSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/SstoreSection.java index 5b9c1ad479..93642ab7b0 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/SstoreSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/SstoreSection.java @@ -129,7 +129,7 @@ private StorageFragment doingSstore(Hub hub) { } @Override - public void resolvePostRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { + public void resolveUponRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { final DomSubStampsSubFragment undoingDomSubStamps = DomSubStampsSubFragment.revertWithCurrentDomSubStamps( this.hubStamp(), hub.callStack().currentCallFrame().revertStamp(), 0); diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TraceSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TraceSection.java index fd59a8276d..93d5803566 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TraceSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TraceSection.java @@ -31,7 +31,7 @@ import net.consensys.linea.zktracer.module.hub.Hub; import net.consensys.linea.zktracer.module.hub.HubProcessingPhase; import net.consensys.linea.zktracer.module.hub.Trace; -import net.consensys.linea.zktracer.module.hub.TxTrace; +import net.consensys.linea.zktracer.module.hub.fragment.ContextFragment; import net.consensys.linea.zktracer.module.hub.fragment.StackFragment; import net.consensys.linea.zktracer.module.hub.fragment.TraceFragment; import net.consensys.linea.zktracer.module.hub.fragment.common.CommonFragment; @@ -48,11 +48,11 @@ public class TraceSection { private final Hub hub; public final CommonFragmentValues commonValues; @Getter List fragments; - @Getter @Setter private TxTrace parentTrace; /* A link to the previous section */ @Setter public TraceSection previousSection = null; /* A link to the next section */ @Setter public TraceSection nextSection = null; + @Setter public ContextFragment exceptionalContextFragment = null; /** Default creator specifying the max number of rows the section can contain. */ public TraceSection(final Hub hub, final short maxNumberOfLines) { @@ -190,10 +190,9 @@ private List makeStackFragments(final Hub hub, CallFrame currentF return stackFragments; } - public void triggerHashInfo(Bytes hash) { + public void writeHashInfoResult(Bytes hash) { for (TraceFragment fragment : this.fragments()) { if (fragment instanceof StackFragment) { - ((StackFragment) fragment).hashInfoFlag = true; ((StackFragment) fragment).hash = hash; } } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxFinalizationSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxFinalizationSection.java index 56116364ef..013abad503 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxFinalizationSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxFinalizationSection.java @@ -40,8 +40,9 @@ public class TxFinalizationSection extends TraceSection implements PostTransacti private @Setter AccountSnapshot recipientSnapshotAfterTxFinalization; private @Setter AccountSnapshot coinbaseSnapshotAfterFinalization; - public TxFinalizationSection(Hub hub, WorldView world) { + public TxFinalizationSection(Hub hub, WorldView world, boolean exceptionOrRevert) { super(hub, (short) 4); + txMetadata = hub.txStack().current(); final Address senderAddress = txMetadata.getSender(); @@ -49,21 +50,15 @@ public TxFinalizationSection(Hub hub, WorldView world) { final Address coinbaseAddress = txMetadata.getCoinbase(); // recipient - senderSnapshotBeforeFinalization = AccountSnapshot.canonical(hub, senderAddress); - recipientSnapshotBeforeFinalization = AccountSnapshot.canonical(hub, recipientAddress); - coinbaseSnapshotBeforeTxFinalization = AccountSnapshot.canonical(hub, coinbaseAddress); - - // TODO: re-enable checks - // checkArgument( - // senderSnapshotBeforeFinalization.isWarm(), - // "The sender account ought to be warm during TX_FINL"); - // checkArgument( - // recipientSnapshotBeforeFinalization.isWarm(), - // "The recipient account ought to be warm during TX_FINL"); - checkArgument( - txMetadata.isCoinbaseWarmAtTransactionEnd() - == coinbaseSnapshotBeforeTxFinalization.isWarm(), - "isCoinbaseWarmAtTransactiondEnd prediction is wrong"); + senderSnapshotBeforeFinalization = + exceptionOrRevert + ? hub.txStack().getInitializationSection().getSenderAfterPayingForTransaction() + : AccountSnapshot.canonical(hub, world, senderAddress); + recipientSnapshotBeforeFinalization = + exceptionOrRevert + ? hub.txStack().getInitializationSection().getRecipientAfterValueTransfer() + : AccountSnapshot.canonical(hub, world, recipientAddress); + coinbaseSnapshotBeforeTxFinalization = AccountSnapshot.canonical(hub, world, coinbaseAddress); hub.defers().scheduleForPostTransaction(this); } @@ -75,39 +70,39 @@ public void resolvePostTransaction( final boolean coinbaseWarmth = txMetadata.isCoinbaseWarmAtTransactionEnd(); final Address senderAddress = senderSnapshotBeforeFinalization.address(); - senderSnapshotAfterTxFinalization = AccountSnapshot.canonical(hub, senderAddress); + senderSnapshotAfterTxFinalization = AccountSnapshot.canonical(hub, world, senderAddress); senderSnapshotAfterTxFinalization.turnOnWarmth(); // purely constraints based final Address recipientAddress = recipientSnapshotBeforeFinalization.address(); - recipientSnapshotAfterTxFinalization = AccountSnapshot.canonical(hub, recipientAddress); + recipientSnapshotAfterTxFinalization = AccountSnapshot.canonical(hub, world, recipientAddress); recipientSnapshotAfterTxFinalization.turnOnWarmth(); // purely constraints based final Address coinbaseAddress = coinbaseSnapshotBeforeTxFinalization.address(); - coinbaseSnapshotAfterFinalization = AccountSnapshot.canonical(hub, coinbaseAddress); + coinbaseSnapshotAfterFinalization = AccountSnapshot.canonical(hub, world, coinbaseAddress); coinbaseSnapshotAfterFinalization.setWarmthTo(coinbaseWarmth); // purely constraints based DeploymentInfo deploymentInfo = hub.transients().conflation().deploymentInfo(); checkArgument(isSuccessful == txMetadata.statusCode()); // TODO: do we switch off the deployment status at the end of a deployment ? - checkArgument( - !deploymentInfo.getDeploymentStatus(senderAddress), - "The sender may not be under deployment"); - checkArgument( - !deploymentInfo.getDeploymentStatus(recipientAddress), - "The recipient may not be under deployment"); + // checkArgument( + // !deploymentInfo.getDeploymentStatus(senderAddress), + // "The sender may not be under deployment"); + // checkArgument( + // !deploymentInfo.getDeploymentStatus(recipientAddress), + // "The recipient may not be under deployment"); checkArgument( !deploymentInfo.getDeploymentStatus(coinbaseAddress), "The coinbase may not be under deployment"); if (isSuccessful) { - successfulFinalization(hub); + successFinalization(hub); } else { - unsuccessfulFinalization(hub); + failureFinalization(hub); } } - private void successfulFinalization(Hub hub) { + private void successFinalization(Hub hub) { if (!senderIsCoinbase()) { @@ -159,7 +154,7 @@ private void successfulFinalization(Hub hub) { this.addFragments(senderAccountFragment, coinbaseAccountFragment, currentTransactionFragment); } - private void unsuccessfulFinalization(Hub hub) { + private void failureFinalization(Hub hub) { if (noAddressCollisions()) { AccountFragment senderAccountFragment = diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxInitializationSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxInitializationSection.java index e5b09c4951..98f2f50793 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxInitializationSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxInitializationSection.java @@ -18,6 +18,7 @@ import static net.consensys.linea.zktracer.module.hub.HubProcessingPhase.TX_EXEC; import com.google.common.base.Preconditions; +import lombok.Getter; import net.consensys.linea.zktracer.module.hub.AccountSnapshot; import net.consensys.linea.zktracer.module.hub.Hub; import net.consensys.linea.zktracer.module.hub.fragment.ContextFragment; @@ -35,9 +36,15 @@ import org.hyperledger.besu.evm.worldstate.WorldView; public class TxInitializationSection extends TraceSection { + + @Getter private final AccountSnapshot senderAfterPayingForTransaction; + @Getter private final AccountSnapshot recipientAfterValueTransfer; + public TxInitializationSection(Hub hub, WorldView world) { super(hub, (short) 5); + hub.txStack().setInitializationSection(this); + final TransactionProcessingMetadata tx = hub.txStack().current(); final boolean isDeployment = tx.isDeployment(); final Address recipientAddress = tx.getEffectiveRecipient(); @@ -59,8 +66,7 @@ public TxInitializationSection(Hub hub, WorldView world) { final Wei valueAndGasCost = transactionGasPrice.multiply(tx.getBesuTransaction().getGasLimit()).add(value); - final AccountSnapshot senderAfterPayingForTransaction = - senderBeforePayingForTransaction.deepCopy(); + senderAfterPayingForTransaction = senderBeforePayingForTransaction.deepCopy(); senderAfterPayingForTransaction .decrementBalanceBy(valueAndGasCost) .turnOnWarmth() @@ -94,7 +100,7 @@ public TxInitializationSection(Hub hub, WorldView world) { final Bytecode initCode = new Bytecode(tx.getBesuTransaction().getInit().orElse(Bytes.EMPTY)); - final AccountSnapshot recipientAfterValueTransfer = recipientBeforeValueTransfer.deepCopy(); + recipientAfterValueTransfer = recipientBeforeValueTransfer.deepCopy(); if (isDeployment) { Preconditions.checkState( !recipientBeforeValueTransfer.deploymentStatus() diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxPreWarmingMacroSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxPreWarmingMacroSection.java index bf3a04293c..bc2be6fc6c 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxPreWarmingMacroSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxPreWarmingMacroSection.java @@ -80,7 +80,13 @@ public TxPreWarmingMacroSection(WorldView world, Hub hub) { final DomSubStampsSubFragment domSubStampsSubFragment = new DomSubStampsSubFragment( - DomSubStampsSubFragment.DomSubType.STANDARD, hub.stamp(), 0, 0, 0, 0, 0); + DomSubStampsSubFragment.DomSubType.STANDARD, + hub.stamp() + 1, + 0, + 0, + 0, + 0, + 0); new TxPrewarmingSection( hub, @@ -117,7 +123,7 @@ public TxPreWarmingMacroSection(WorldView world, Hub hub) { value, seenKeys.computeIfAbsent(address, x -> new HashSet<>()).contains(key), true, - DomSubStampsSubFragment.standardDomSubStamps(hub.stamp(), 0), + DomSubStampsSubFragment.standardDomSubStamps(hub.stamp() + 1, 0), hub.state.firstAndLastStorageSlotOccurrences.size(), PRE_WARMING); diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxSkippedSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxSkipSection.java similarity index 95% rename from arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxSkippedSection.java rename to arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxSkipSection.java index 56891c3acc..72b784d918 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxSkippedSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxSkipSection.java @@ -35,14 +35,14 @@ * later, through a {@link PostTransactionDefer}, to generate the trace chunks required for the * proving of a pure transaction. */ -public class TxSkippedSection extends TraceSection implements PostTransactionDefer { +public class TxSkipSection extends TraceSection implements PostTransactionDefer { final TransactionProcessingMetadata txMetadata; final AccountSnapshot senderAccountSnapshotBefore; final AccountSnapshot recipientAccountSnapshotBefore; final AccountSnapshot coinbaseAccountSnapshotBefore; - public TxSkippedSection( + public TxSkipSection( Hub hub, WorldView world, TransactionProcessingMetadata transactionProcessingMetadata, @@ -124,27 +124,30 @@ public void resolvePostTransaction(Hub hub, WorldView state, Transaction tx, boo final AccountFragment senderAccountFragment = hub.factories() .accountFragment() - .make( + .makeWithTrm( senderAccountSnapshotBefore, senderAccountSnapshotAfter, + senderAddress, DomSubStampsSubFragment.standardDomSubStamps(hub.stamp(), 0)); // recipient account fragment final AccountFragment recipientAccountFragment = hub.factories() .accountFragment() - .make( + .makeWithTrm( recipientAccountSnapshotBefore, recipientAccountSnapshotAfter, + recipientAddress, DomSubStampsSubFragment.standardDomSubStamps(hub.stamp(), 1)); // coinbase account fragment final AccountFragment coinbaseAccountFragment = hub.factories() .accountFragment() - .make( + .makeWithTrm( coinbaseAccountSnapshotBefore, coinbaseAccountSnapshotAfter, + coinbaseAddress, DomSubStampsSubFragment.standardDomSubStamps(hub.stamp(), 2)); // transaction fragment diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/call/CallSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/call/CallSection.java index 1ac0988f63..d34af30743 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/call/CallSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/call/CallSection.java @@ -88,6 +88,10 @@ public class CallSection extends TraceSection // row i+0 private final CallScenarioFragment scenarioFragment = new CallScenarioFragment(); + public boolean isAbortingScenario() { + return scenarioFragment.getScenario().isAbortingScenario(); + } + // last row @Setter private ContextFragment finalContextFragment; @@ -121,7 +125,7 @@ public class CallSection extends TraceSection @Getter private MemorySpan callProvidedReturnDataTargetSpan; - public CallSection(Hub hub) { + public CallSection(Hub hub, MessageFrame frame) { super(hub, maxNumberOfLines(hub)); final short exceptions = hub.pch().exceptions(); @@ -199,7 +203,6 @@ public CallSection(Hub hub) { if (aborts) { this.abortingCall(hub); - hub.defers().scheduleForPostExecution(this); return; } @@ -232,6 +235,8 @@ public CallSection(Hub hub) { } if (scenarioFragment.getScenario() == CALL_SMC_UNDEFINED) { + this.commonValues.payGasPaidOutOfPocket(hub); + hub.defers().scheduleForContextReEntry(firstImcFragment, currentFrame); finalContextFragment = ContextFragment.initializeNewExecutionContext(hub); final boolean isSelfCall = callerAddress.equals(calleeAddress); selfCallWithNonzeroValueTransfer = isSelfCall && !value.isZero(); @@ -240,6 +245,7 @@ public CallSection(Hub hub) { } if (scenarioFragment.getScenario() == CALL_EOA_SUCCESS_WONT_REVERT) { + this.commonValues.collectChildStipend(hub); finalContextFragment = ContextFragment.nonExecutionProvidesEmptyReturnData(hub); } } @@ -280,21 +286,44 @@ private void oogXCall(Hub hub) { private void abortingCall(Hub hub) { scenarioFragment.setScenario(CALL_ABORT_WONT_REVERT); + postOpcodeCallerSnapshot = preOpcodeCallerSnapshot.deepCopy(); + postOpcodeCalleeSnapshot = preOpcodeCalleeSnapshot.deepCopy().turnOnWarmth(); + final Factories factories = hub.factories(); + final AccountFragment readingCallerAccount = + factories + .accountFragment() + .make( + preOpcodeCallerSnapshot, + postOpcodeCallerSnapshot, + DomSubStampsSubFragment.standardDomSubStamps(this.hubStamp(), 0)); + + final AccountFragment readingCalleeAccountAndWarmth = + factories + .accountFragment() + .makeWithTrm( + preOpcodeCalleeSnapshot, + postOpcodeCalleeSnapshot, + rawCalleeAddress, + DomSubStampsSubFragment.standardDomSubStamps(this.hubStamp(), 1)); finalContextFragment = ContextFragment.nonExecutionProvidesEmptyReturnData(hub); + this.addFragments(readingCallerAccount, readingCalleeAccountAndWarmth); + hub.defers().scheduleForPostExecution(this); + // we immediately reap the call stipend + commonValues.collectChildStipend(hub); } @Override public void resolvePostExecution( Hub hub, MessageFrame frame, Operation.OperationResult operationResult) { - checkArgument(scenarioFragment.getScenario() == CALL_ABORT_WONT_REVERT); - postOpcodeCallerSnapshot = canonical(hub, preOpcodeCallerSnapshot.address()); - postOpcodeCalleeSnapshot = canonical(hub, preOpcodeCalleeSnapshot.address()); + // we unlatched the stack after a CALL if and only if we don't "contextEnter" the CALL. + hub.unlatchStack(frame, this); } @Override public void resolveUponContextEntry(Hub hub) { - postOpcodeCallerSnapshot = canonical(hub, preOpcodeCallerSnapshot.address()); - postOpcodeCalleeSnapshot = canonical(hub, preOpcodeCalleeSnapshot.address()); + postOpcodeCallerSnapshot = preOpcodeCallerSnapshot.deepCopy().decrementBalanceBy(value); + postOpcodeCalleeSnapshot = + preOpcodeCalleeSnapshot.deepCopy().incrementBalanceBy(value).turnOnWarmth(); switch (scenarioFragment.getScenario()) { case CALL_SMC_UNDEFINED -> { @@ -303,8 +332,8 @@ public void resolveUponContextEntry(Hub hub) { // is decremented by the value transferred. This becomes the initial state // of the callee, which is then credited by that value. This can happen // only for the SMC case. - postOpcodeCallerSnapshot.decrementBalanceBy(value); - preOpcodeCalleeSnapshot.decrementBalanceBy(value); + preOpcodeCalleeSnapshot = postOpcodeCallerSnapshot; + postOpcodeCalleeSnapshot = preOpcodeCallerSnapshot; } final Factories factories = hub.factories(); @@ -348,14 +377,6 @@ public void resolveUponContextExit(Hub hub, CallFrame frame) { childContextExitCallerSnapshot = canonical(hub, preOpcodeCallerSnapshot.address()); childContextExitCalleeSnapshot = canonical(hub, preOpcodeCalleeSnapshot.address()); - - // TODO: what follows assumes that the caller's stack has been updated - // to contain the success bit of the call at traceContextReEntry. - // See issue #872. - // TODO: when does the callFrame update its output data? - // TODO: when does the callFrame update to the parent callFrame ? - finalContextFragment.returnDataContextNumber(hub.currentFrame().contextNumber()); - finalContextFragment.returnDataSegment(hub.currentFrame().outputDataSpan()); } @Override @@ -385,11 +406,14 @@ public void resolveAtContextReEntry(Hub hub, CallFrame frame) { } case CALL_SMC_UNDEFINED -> { + + // CALL_SMC_SUCCESS_XXX case if (successBit) { scenarioFragment.setScenario(CALL_SMC_SUCCESS_WONT_REVERT); return; } + // CALL_SMC_FAILURE_XXX case scenarioFragment.setScenario(CALL_SMC_FAILURE_WONT_REVERT); if (selfCallWithNonzeroValueTransfer) { @@ -397,14 +421,18 @@ public void resolveAtContextReEntry(Hub hub, CallFrame frame) { reEntryCalleeSnapshot.decrementBalanceBy(value); } + int childId = hub.currentFrame().childFramesId().getLast(); + CallFrame childFrame = hub.callStack().getById(childId); + int childContextRevertStamp = childFrame.revertStamp(); + final AccountFragment postReEntryCallerAccountFragment = hub.factories() .accountFragment() .make( childContextExitCallerSnapshot, reEntryCallerSnapshot, - DomSubStampsSubFragment.revertWithCurrentDomSubStamps( - this.hubStamp(), this.revertStamp(), 2)); + DomSubStampsSubFragment.revertsWithChildDomSubStamps( + this.hubStamp(), childContextRevertStamp, 2)); final AccountFragment postReEntryCalleeAccountFragment = hub.factories() @@ -412,8 +440,8 @@ public void resolveAtContextReEntry(Hub hub, CallFrame frame) { .make( childContextExitCalleeSnapshot, reEntryCalleeSnapshot, - DomSubStampsSubFragment.revertWithCurrentDomSubStamps( - this.hubStamp(), this.revertStamp(), 3)); + DomSubStampsSubFragment.revertsWithChildDomSubStamps( + this.hubStamp(), childContextRevertStamp, 3)); this.addFragments(postReEntryCallerAccountFragment, postReEntryCalleeAccountFragment); } @@ -423,7 +451,7 @@ public void resolveAtContextReEntry(Hub hub, CallFrame frame) { } @Override - public void resolvePostRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { + public void resolveUponRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { final Factories factory = hub.factories(); postRollbackCalleeSnapshot = canonical(hub, preOpcodeCalleeSnapshot.address()); postRollbackCallerSnapshot = canonical(hub, preOpcodeCallerSnapshot.address()); @@ -433,7 +461,7 @@ public void resolvePostRollback(Hub hub, MessageFrame messageFrame, CallFrame ca final CallScenarioFragment.CallScenario callScenario = scenarioFragment.getScenario(); switch (callScenario) { - case CALL_ABORT_WONT_REVERT -> completeAbortWillRevert(factory); + case CALL_ABORT_WONT_REVERT -> completeAbortWillRevert(hub, factory); case CALL_EOA_SUCCESS_WONT_REVERT -> completeEoaSuccessWillRevert(factory); case CALL_SMC_FAILURE_WONT_REVERT -> completeSmcFailureWillRevert(factory); case CALL_SMC_SUCCESS_WONT_REVERT, @@ -467,16 +495,20 @@ public void resolvePostTransaction( this.addFragment(finalContextFragment); } - private void completeAbortWillRevert(Factories factory) { + private void completeAbortWillRevert(Hub hub, Factories factory) { scenarioFragment.setScenario(CALL_ABORT_WILL_REVERT); + AccountSnapshot preRollBackCalleeSnapshot = + postOpcodeCalleeSnapshot.deepCopy().setDeploymentInfo(hub); + AccountSnapshot postRollBackCalleeSnapshot = + preOpcodeCalleeSnapshot.deepCopy().setDeploymentInfo(hub); final AccountFragment undoingCalleeAccountFragment = factory .accountFragment() .make( - postOpcodeCalleeSnapshot, - postRollbackCalleeSnapshot, + preRollBackCalleeSnapshot, + postRollBackCalleeSnapshot, DomSubStampsSubFragment.revertWithCurrentDomSubStamps( - this.hubStamp(), this.revertStamp(), 0)); + this.hubStamp(), this.revertStamp(), 2)); this.addFragment(undoingCalleeAccountFragment); } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/call/precompileSubsection/PrecompileSubsection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/call/precompileSubsection/PrecompileSubsection.java index b8c71472a9..ce04a893d8 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/call/precompileSubsection/PrecompileSubsection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/call/precompileSubsection/PrecompileSubsection.java @@ -178,7 +178,7 @@ public void sanityCheck() { } @Override - public void resolvePostRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { + public void resolveUponRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { // only successful PRC calls should enter here checkArgument(precompileScenarioFragment.scenario() == PRC_SUCCESS_WONT_REVERT); diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/copy/ExtCodeCopySection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/copy/ExtCodeCopySection.java index 1b6e251896..227e050cc9 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/copy/ExtCodeCopySection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/copy/ExtCodeCopySection.java @@ -134,7 +134,7 @@ public ExtCodeCopySection(Hub hub) { } @Override - public void resolvePostRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { + public void resolveUponRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { final AccountSnapshot accountPostRollback = AccountSnapshot.canonical(hub, address); diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/ReturnSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/ReturnSection.java index 3c832bf370..2366180f53 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/ReturnSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/ReturnSection.java @@ -177,7 +177,7 @@ public ReturnSection(Hub hub) { final ContextFragment updateCallerReturnData = ContextFragment.executionProvidesReturnData( hub, - hub.callStack().getById(callFrame.callerId()).contextNumber(), + hub.callStack().getById(callFrame.parentId()).contextNumber(), callFrame.contextNumber()); this.addFragment(updateCallerReturnData); @@ -236,7 +236,7 @@ public ReturnSection(Hub hub) { MmuCall.returnFromDeployment(hub); secondImcFragment.callMmu(nonemptyDeploymentMmuCall); - triggerHashInfo(nonemptyDeploymentMmuCall.hashResult()); + writeHashInfoResult(nonemptyDeploymentMmuCall.hashResult()); if (hub.messageFrame().getDepth() == 0) { this.addDeploymentAccountFragmentIfRoot(hub, mxpCall); @@ -278,7 +278,7 @@ public void resolveAtContextReEntry(Hub hub, CallFrame frame) { } @Override - public void resolvePostRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { + public void resolveUponRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { checkArgument(returnFromDeployment); returnScenarioFragment.setScenario( diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/RevertSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/RevertSection.java index d160bff285..6535c9c6a5 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/RevertSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/RevertSection.java @@ -75,7 +75,7 @@ public RevertSection(Hub hub) { final ContextFragment updateCallerReturnData = ContextFragment.executionProvidesReturnData( hub, - hub.callStack().getById(callFrame.callerId()).contextNumber(), + hub.callStack().getById(callFrame.parentId()).contextNumber(), callFrame.contextNumber()); this.addFragments(currentContext, updateCallerReturnData); diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/SelfdestructSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/SelfdestructSection.java index 18becd5952..6b3ac62a8b 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/SelfdestructSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/SelfdestructSection.java @@ -202,7 +202,7 @@ public SelfdestructSection(Hub hub) { } @Override - public void resolvePostRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { + public void resolveUponRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { // Undo the modifications we applied to selfdestructorFirstAccountFragment and // recipientFirstAccountFragment final AccountFragment selfDestroyerUndoingAccountFragment = diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/StopSection.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/StopSection.java index 531891139b..ecd048eab6 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/StopSection.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/section/halt/StopSection.java @@ -110,7 +110,7 @@ public void deploymentStopSection(Hub hub) { * @param callFrame reference to call frame whose actions are to be undone */ @Override - public void resolvePostRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { + public void resolveUponRollback(Hub hub, MessageFrame messageFrame, CallFrame callFrame) { if (!this.deploymentStatus) { return; diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/signals/Exceptions.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/signals/Exceptions.java index 6b0d34fb37..336318ec69 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/signals/Exceptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/signals/Exceptions.java @@ -231,6 +231,10 @@ private static boolean isCodeSizeOverflow(MessageFrame frame) { return codeSize > MAX_CODE_SIZE; } + public static boolean isOogxOrUnexceptional(short exceptions) { + return Exceptions.none(exceptions) || Exceptions.outOfGasException(exceptions); + } + /** * Return the first exception that may have happened in the current frame. Although multiple * exceptions may be triggered, the one minimizing the quantity of trace lines is generated. @@ -241,15 +245,15 @@ public static short fromFrame(final Hub hub, final MessageFrame frame) { final OpCode opCode = hub.opCode(); final OpCodeData opCodeData = hub.currentFrame().opCodeData(); - if (isInvalidOpcode(opCode)) { - return INVALID_OPCODE; - } if (isStackUnderflow(frame, opCodeData)) { return STACK_UNDERFLOW; } if (isStackOverflow(frame, opCodeData)) { return STACK_OVERFLOW; } + if (isInvalidOpcode(opCode)) { + return INVALID_OPCODE; + } if (isStaticFault(frame, opCodeData)) { return STATIC_FAULT; } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/mxp/MxpOperation.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/mxp/MxpOperation.java index 8259ab770c..c9dc4227f9 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/mxp/MxpOperation.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/mxp/MxpOperation.java @@ -241,7 +241,7 @@ protected void setNoOperation() { private void setMtntop() { final boolean mxpx = mxpCall.isMxpx(); mxpCall.setMayTriggerNontrivialMmuOperation( - typeMxp == MxpType.TYPE_4 && !mxpx && mxpCall.getSize1().loBigInt().signum() != 0); + typeMxp == MxpType.TYPE_4 && !mxpx && mxpCall.getSize1().loBigInt().signum() > 0); } /** set max offsets 1 and 2. */ diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/callstack/CallFrame.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/callstack/CallFrame.java index 6b32e78872..0172959463 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/callstack/CallFrame.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/callstack/CallFrame.java @@ -62,7 +62,7 @@ public boolean isMessageCall() { } /** the ID of this {@link CallFrame} parent in the {@link CallStack}. */ - @Getter private int callerId; + @Getter private int parentId; /** all the {@link CallFrame} that have been called by this frame. */ @Getter private final List childFramesId = new ArrayList<>(); @@ -118,8 +118,8 @@ public void unpauseCurrentFrame() { executionPaused = false; } - public void rememberGasNextBeforePausing() { - lastValidGasNext = frame.getRemainingGas(); + public void rememberGasNextBeforePausing(Hub hub) { + lastValidGasNext = hub.state.current().txTrace().currentSection().commonValues.gasNext(); } /** the ether amount given to this frame. */ @@ -192,7 +192,7 @@ public static void updateParentContextReturnData( type = CallFrameType.EMPTY; contextNumber = 0; accountAddress = Address.ZERO; - callerId = -1; + parentId = -1; callDataInfo = new CallDataInfo(Bytes.EMPTY, 0, 0, 0); returnDataTargetInCaller = MemorySpan.empty(); depth = 0; @@ -217,7 +217,7 @@ public static void updateParentContextReturnData( * @param byteCode byteCode that executes in the present context * @param callerAddress either account address of the caller/creator context * @param callDataContextNumber CN of the RAM segment wherein the call data lives - * @param callerId ID of the caller frame in the {@link CallStack} + * @param parentId ID of the caller frame in the {@link CallStack} * @param callData {@link Bytes} containing this frame's call data * @param callDataOffset offset of call data in the caller's RAM (if applicable) * @param callDataSize size (in bytes) of the call data @@ -237,7 +237,7 @@ public static void updateParentContextReturnData( Bytecode byteCode, Address callerAddress, long callDataContextNumber, - int callerId, + int parentId, Bytes callData, long callDataOffset, long callDataSize, @@ -255,7 +255,7 @@ public static void updateParentContextReturnData( this.byteCodeDeploymentNumber = byteCodeDeploymentNumber; this.code = byteCode; this.callerAddress = callerAddress; - this.callerId = callerId; + this.parentId = parentId; this.callDataInfo = new CallDataInfo(callData, callDataOffset, callDataSize, callDataContextNumber); this.outputDataSpan = MemorySpan.empty(); diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/callstack/CallStack.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/callstack/CallStack.java index f0e32c59ee..9b0dcede16 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/callstack/CallStack.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/callstack/CallStack.java @@ -146,8 +146,8 @@ public int futureId() { * @return the parent {@link CallFrame} of the current frame */ public CallFrame parent() { - if (this.currentCallFrame().callerId() != -1) { - return this.callFrames.get(this.currentCallFrame().callerId()); + if (this.currentCallFrame().parentId() != -1) { + return this.callFrames.get(this.currentCallFrame().parentId()); } else { return CallFrame.EMPTY; } @@ -233,7 +233,7 @@ public void enter( public void exit() { this.depth -= 1; Preconditions.checkState(this.depth >= 0); - this.currentId = this.currentCallFrame().callerId(); + this.currentId = this.currentCallFrame().parentId(); } /** @@ -300,7 +300,7 @@ public CallFrame getParentCallFrameById(int id) { return CallFrame.EMPTY; } - return this.getById(this.callFrames.get(id).callerId()); + return this.getById(this.callFrames.get(id).parentId()); } /** diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/stack/StackItem.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/stack/StackItem.java index d65917fd08..432d587b4d 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/stack/StackItem.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/stack/StackItem.java @@ -30,7 +30,7 @@ */ @Accessors(fluent = true) public final class StackItem { - private static final Bytes MARKER = Bytes.fromHexString("0xDEADBEEF"); + private static final Bytes MARKER = Bytes.fromHexString("0x1337deadbeef"); /** * The relative height of the element with regard to the stack height just before executing the diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/BalanceTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/BalanceTests.java new file mode 100644 index 0000000000..08eaa425df --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/BalanceTests.java @@ -0,0 +1,105 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing; + +import java.util.List; + +import net.consensys.linea.testing.*; +import net.consensys.linea.zktracer.opcode.OpCode; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.crypto.KeyPair; +import org.hyperledger.besu.crypto.SECP256K1; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.TransactionType; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.junit.jupiter.api.Test; + +/** + * the purpose of these tests is to track balance updates for the sender, the coinbase and, in case + * of a reverted transaction, the recipient. + */ +public class BalanceTests { + + @Test + void unrevertedValueTransfer() { + + ToyExecutionEnvironmentV2.builder() + .accounts(accounts) + .transaction(stopTransaction) + .transactionProcessingResultValidator(TransactionProcessingResultValidator.EMPTY_VALIDATOR) + .build() + .run(); + } + + @Test + void revertedValueTransferTest() { + + ToyExecutionEnvironmentV2.builder() + .accounts(accounts) + .transaction(revertTransaction) + .transactionProcessingResultValidator(TransactionProcessingResultValidator.EMPTY_VALIDATOR) + .build() + .run(); + } + + KeyPair keyPair = new SECP256K1().generateKeyPair(); + Address senderAddress = Address.extract(Hash.hash(keyPair.getPublicKey().getEncodedBytes())); + ToyAccount senderAccount = + ToyAccount.builder().balance(Wei.of(2_000_001L)).nonce(23).address(senderAddress).build(); + + ToyAccount stopAccount = + ToyAccount.builder() + .balance(Wei.of(500_000L)) + .nonce(23) + .code(Bytes.of(0)) // bytecode = STOP + .address(Address.fromHexString("0xadd7e55")) + .build(); + + Bytes revertByteCode = BytecodeCompiler.newProgram().push(0).push(0).op(OpCode.REVERT).compile(); + + ToyAccount revertAccount = + ToyAccount.builder() + .balance(Wei.of(500_000L)) + .nonce(37) + .code(revertByteCode) // bytecode = two push 0's and a REVERT + .address(Address.fromHexString("badadd7e55bad")) + .build(); + + List accounts = List.of(senderAccount, stopAccount, revertAccount); + + Transaction stopTransaction = + ToyTransaction.builder() + .sender(senderAccount) + .to(stopAccount) + .transactionType(TransactionType.FRONTIER) + .value(Wei.of(1_000_000L)) + .keyPair(keyPair) + .gasLimit(125_000L) + .gasPrice(Wei.of(8)) // total 1 million wei in gas + .build(); + + Transaction revertTransaction = + ToyTransaction.builder() + .sender(senderAccount) + .to(revertAccount) + .transactionType(TransactionType.FRONTIER) + .value(Wei.of(1_000_000L)) + .keyPair(keyPair) + .gasLimit(125_000L) + .gasPrice(Wei.of(8)) // total 1 million wei in gas + .build(); +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/CallDataTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/CallDataTests.java index a664495cc4..461dcc66f8 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/CallDataTests.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/CallDataTests.java @@ -34,6 +34,13 @@ * sense. They aren't for message calls. */ public class CallDataTests { + // @Test + // void transactionCallDataForMessageCallTest() { + // } + + // @Test + // void transactionCallDataForDeploymentTest() { + // } @Test void nonAlignedCallDataInCallTest() { @@ -47,6 +54,10 @@ void nonAlignedCallDataInCallTest() { .run(); } + // @Test + // void callDataInCreateTest() { + // } + private final Bytes callData32 = Bytes.fromHexString("abcdef01234567890000deadbeef0000aa0f517e002024aa9876543210fedcba"); @@ -70,11 +81,11 @@ void nonAlignedCallDataInCallTest() { final Bytes callerCode = BytecodeCompiler.newProgram() .push(callData32) - .push(1) + .push(2) .op(OpCode.MSTORE) .push(44) // r@c, shorter than the return data .push(19) // r@o, deliberately overlaps with call data - .push(32) // cds + .push(35) // cds .push(1) // cdo .push("ca11da7ac0de") // address .op(OpCode.GAS) // gas diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/JumpTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/JumpTest.java index 829a8ac062..97f6aaabf1 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/JumpTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/JumpTest.java @@ -20,6 +20,7 @@ import net.consensys.linea.testing.BytecodeRunner; import net.consensys.linea.zktracer.opcode.OpCode; import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -65,4 +66,19 @@ In order to run a specific test case (for example, the first one) use the follow .filter(arguments -> "jumpOntoValidJumpDestination".equals(arguments.get()[0])); */ } + + @Test + void simplestJumpiTest() { + final Bytes bytecode = + BytecodeCompiler.newProgram() + .push(1) // pc = 0, 1 + .push(8) // pc = 2, 3 + .op(OpCode.JUMPI) // pc = 4 + .op(OpCode.INVALID) // pc = 5 + .op(OpCode.JUMPDEST) // pc = 6, 6 ≡ true JUMPDEST + .push(OpCode.JUMPDEST.byteValue()) // pc = 7, 8, 8 ≡ false JUMPDEST + .compile(); + System.out.println(bytecode.toHexString()); + BytecodeRunner.of(bytecode).run(); + } } diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/KeccakTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/KeccakTests.java new file mode 100644 index 0000000000..4b8ccd14b8 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/KeccakTests.java @@ -0,0 +1,106 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing; + +import net.consensys.linea.testing.BytecodeCompiler; +import net.consensys.linea.testing.BytecodeRunner; +import net.consensys.linea.zktracer.opcode.OpCode; +import org.junit.jupiter.api.Test; + +public class KeccakTests { + @Test + void singleEmptyKeccak() { + BytecodeRunner.of( + BytecodeCompiler.newProgram() + .push(0) // size + .push(0) // offset + .op(OpCode.SHA3) + .op(OpCode.POP) + .compile()) + .run(); + } + + /** computing KEC("ee ee ... ee"), aligned on 1st byte */ + @Test + void singleWordKeccakNonAligned() { + BytecodeRunner.of( + BytecodeCompiler.newProgram() + .push("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") + .push(1) + .op(OpCode.MSTORE) + .push(32) // size + .push(1) // offset + .op(OpCode.SHA3) + .op(OpCode.POP) + .compile()) + .run(); + } + + /** computing KEC("ee ee ... ee"), aligned on 0th byte */ + @Test + void singleWordKeccakAligned() { + BytecodeRunner.of( + BytecodeCompiler.newProgram() + .push("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") + .push(0) + .op(OpCode.MSTORE) + .push(32) // size + .push(0) // offset + .op(OpCode.SHA3) + .op(OpCode.POP) + .compile()) + .run(); + } + + /** computing KEC("ee") */ + @Test + void singleByteKeccak() { + BytecodeRunner.of( + BytecodeCompiler.newProgram() + .push("ee") + .push(1) + .op(OpCode.MSTORE8) + .push(1) // size + .push(1) // offset + .op(OpCode.SHA3) + .op(OpCode.POP) + .compile()) + .run(); + } + + @Test + void testSeveralKeccaks() { + BytecodeRunner.of( + BytecodeCompiler.newProgram() + .push(0) + .push(0) + .op(OpCode.SHA3) + .op(OpCode.POP) + .push(64) + .push(13) + .op(OpCode.SHA3) + .op(OpCode.POP) + .push(11) + .push(75) + .op(OpCode.SHA3) + .op(OpCode.POP) + .push(32) + .push(32) + .op(OpCode.SHA3) + .op(OpCode.POP) + .compile()) + .run(); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/DoubleCall.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/DoubleCall.java new file mode 100644 index 0000000000..9c78596adc --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/DoubleCall.java @@ -0,0 +1,73 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests; + +import static net.consensys.linea.zktracer.instructionprocessing.callTests.Utilities.*; +import static net.consensys.linea.zktracer.opcode.OpCode.*; + +import net.consensys.linea.testing.BytecodeCompiler; +import net.consensys.linea.testing.BytecodeRunner; +import org.hyperledger.besu.datatypes.Address; +import org.junit.jupiter.api.Test; + +public class DoubleCall { + + /** Same address */ + @Test + void doubleCallToSameAddressWontRevert() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress), 1, 0, 0, 0, 0); + + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress), 2, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(); + } + + @Test + void doubleCallToSameAddressWillRevert() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress), 1, 0, 0, 0, 0); + + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress), 2, 0, 0, 0, 0); + + program.op(REVERT); + + BytecodeRunner.of(program.compile()).run(); + } + + /** Different address */ + @Test + void doubleCallTodifferentAddressesWontRevert() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress), 1, 0, 0, 0, 0); + + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress2), 2, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(); + } + + @Test + void doubleCallTodifferentAddressesWillRevert() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress), 1, 0, 0, 0, 0); + + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress2), 2, 0, 0, 0, 0); + + program.push(13).push(71); // the stack already contains two items but why not ... + program.op(REVERT); + + BytecodeRunner.of(program.compile()).run(); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/TrimmingTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/TrimmingTests.java new file mode 100644 index 0000000000..f33cfb72c7 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/TrimmingTests.java @@ -0,0 +1,63 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests; + +import static net.consensys.linea.zktracer.instructionprocessing.callTests.Utilities.eoaAddress; +import static net.consensys.linea.zktracer.instructionprocessing.callTests.Utilities.untrimmedEoaAddress; +import static net.consensys.linea.zktracer.opcode.OpCode.*; + +import net.consensys.linea.testing.BytecodeCompiler; +import net.consensys.linea.testing.BytecodeRunner; +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; + +public class TrimmingTests { + + @Test + void targetTrimming() { + + Bytes bytecode = + BytecodeCompiler.newProgram() + .push(1) + .push(2) + .push(3) + .push(4) + .push(0) // value + .push(untrimmedEoaAddress) // address + .push(0) // gas + .op(CALL) + .op(POP) + .push(untrimmedEoaAddress) // address + .op(BALANCE) + .push(eoaAddress) // address + .op(BALANCE) + .push(1) + .push(2) + .push(3) + .push(4) + .push(0) // value + .push(eoaAddress) // address + .push(0) // gas + .op(CALL) + .op(POP) + .push(untrimmedEoaAddress) // address + .op(BALANCE) + .push(eoaAddress) // address + .op(EXTCODEHASH) + .compile(); + + BytecodeRunner.of(bytecode).run(); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/Utilities.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/Utilities.java new file mode 100644 index 0000000000..e14877e80c --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/Utilities.java @@ -0,0 +1,108 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests; + +import static com.google.common.base.Preconditions.checkArgument; +import static net.consensys.linea.zktracer.opcode.OpCode.*; + +import net.consensys.linea.testing.BytecodeCompiler; +import net.consensys.linea.zktracer.opcode.OpCode; +import org.hyperledger.besu.datatypes.Address; + +public class Utilities { + + public static final String fullEoaAddress = "000000000000000000000000abcdef0123456789"; + public static final String toTrim12 = "aaaaaaaaaaaaaaaaaaaaaaaa"; + public static final String untrimmedEoaAddress = toTrim12 + fullEoaAddress; + public static final String eoaAddress = "c0ffeef00d"; + public static final String eoaAddress2 = "badbeef"; + + public static void fullGasCall( + BytecodeCompiler program, + OpCode callOpcode, + Address to, + int value, + int cdo, + int cds, + int rao, + int rac) { + program.push(rac).push(rao).push(cds).push(cdo); + if (callOpcode.callCanTransferValue()) { + program.push(value); + } + program.push(to).op(GAS).op(callOpcode).op(POP); + } + + public static void simpleCall( + BytecodeCompiler program, + OpCode callOpcode, + int gas, + Address to, + int value, + int cdo, + int cds, + int rao, + int rac) { + program.push(rac).push(rao).push(cds).push(cdo); + if (callOpcode.callCanTransferValue()) { + program.push(value); + } + program.push(to).push(gas).op(callOpcode); + } + + public static void appendInsufficientBalanceCall( + BytecodeCompiler program, + OpCode callOpcode, + int gas, + Address to, + int cdo, + int cds, + int rao, + int rac) { + checkArgument(callOpcode.callCanTransferValue()); + program + .push(rac) + .push(rao) + .push(cds) + .push(cdo) + .op(BALANCE) + .push(1) + .op(ADD) // puts balance + 1 on the stack + .push(to) + .push(gas) + .op(callOpcode); + } + + /** + * Pushing, in order: h, v, r, s; values produce a valid signature. + * + * @param program + */ + public static void validEcrecoverData(BytecodeCompiler program) { + program + .push("279d94621558f755796898fc4bd36b6d407cae77537865afe523b79c74cc680b") + .push(0) + .op(MSTORE) + .push("1b") + .push(32) + .op(MSTORE) + .push("c2ff96feed8749a5ad1c0714f950b5ac939d8acedbedcbc2949614ab8af06312") + .push(64) + .op(MSTORE) + .push("1feecd50adc6273fdd5d11c6da18c8cfe14e2787f5a90af7c7c1328e7d0a2c42") + .push(96) + .op(MSTORE); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/abort/BalanceAbortTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/abort/BalanceAbortTests.java new file mode 100644 index 0000000000..a602be5a64 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/abort/BalanceAbortTests.java @@ -0,0 +1,84 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests.abort; + +import static net.consensys.linea.zktracer.instructionprocessing.callTests.Utilities.appendInsufficientBalanceCall; +import static net.consensys.linea.zktracer.opcode.OpCode.*; +import static net.consensys.linea.zktracer.opcode.OpCode.CALL; + +import net.consensys.linea.testing.BytecodeCompiler; +import net.consensys.linea.testing.BytecodeRunner; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.datatypes.Address; +import org.junit.jupiter.api.Test; + +/** + * The arithmetization has a two aborting scenarios for CALL's + * + *

- scn/CALL_ABORT_WILL_REVERT + * + *

- scn/CALL_ABORT_WONT_REVERT The main point being: (unexceptional) aborted CALL's warm + * up the target account. + */ +public class BalanceAbortTests { + + final String eoaAddress = "abcdef0123456789"; + + @Test + void insufficientBalanceAbortWarmsUpTarget() { + + Bytes bytecode = + BytecodeCompiler.newProgram() + .push(1) + .push(2) + .push(3) + .push(4) + .op(SELFBALANCE) + .push(1) + .op(ADD) // our balance + 1 + .push(eoaAddress) // address + .push(0) // gas + .op(CALL) // CALL_ABORT_WONT_REVERT + .op(POP) + .push(eoaAddress) // address + .op(EXTCODESIZE) // discounted pricing + .compile(); + + BytecodeRunner.of(bytecode).run(); + } + + /** scenario/CALL_ABORT_WILL_REVERT; reverts the warmth; */ + @Test + void insufficientBalanceAbortWillRevert() { + + BytecodeCompiler program = BytecodeCompiler.newProgram(); + appendInsufficientBalanceCall( + program, CALL, 1000, Address.fromHexString(eoaAddress), 0, 0, 0, 0); + program.push(6).push(7).op(REVERT); + Bytes bytecode = program.compile(); + BytecodeRunner.of(bytecode).run(); + } + + /** scenario/CALL_ABORT_WONT_REVERT */ + @Test + void insufficientBalanceAbortWontRevert() { + + BytecodeCompiler program = BytecodeCompiler.newProgram(); + appendInsufficientBalanceCall( + program, CALL, 1000, Address.fromHexString(eoaAddress), 0, 0, 0, 0); + Bytes bytecode = program.compile(); + BytecodeRunner.of(bytecode).run(); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/abort/CallStackDepthAbortTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/abort/CallStackDepthAbortTests.java new file mode 100644 index 0000000000..ad369914c0 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/abort/CallStackDepthAbortTests.java @@ -0,0 +1,66 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests.abort; + +import static net.consensys.linea.zktracer.opcode.OpCode.*; + +import net.consensys.linea.testing.BytecodeCompiler; +import net.consensys.linea.testing.BytecodeRunner; +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; + +/** + * Attempt to trigger the maximum call stack depth abort. We put everything to 0 to avoid memory + * expansion costs. We will want to revert so we transfer value to see the effect of reverting. + */ +public class CallStackDepthAbortTests { + @Test + void attemptAtCallStackDepthAbortWillRevert() { + + Bytes bytecode = + BytecodeCompiler.newProgram() + .push(0) + .push(0) + .push(0) + .push(0) // + .push(5) // value + .op(ADDRESS) // current address + .op(GAS) // providing all available gas + .op(CALL) // self-call + .push(6) + .push(7) + .op(REVERT) + .compile(); + + BytecodeRunner.of(bytecode).run(); + } + + @Test + void attemptAtCallStackDepthAbortWontRevert() { + + Bytes bytecode = + BytecodeCompiler.newProgram() + .push(0) + .push(0) + .push(0) + .push(0) + .op(ADDRESS) + .op(GAS) // providing as much gas as possible + .op(STATICCALL) // self-call + .compile(); + + BytecodeRunner.of(bytecode).run(); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/abort/MultiCallAbortTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/abort/MultiCallAbortTests.java new file mode 100644 index 0000000000..351c3aaf0a --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/abort/MultiCallAbortTests.java @@ -0,0 +1,73 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests.abort; + +import static net.consensys.linea.zktracer.instructionprocessing.callTests.Utilities.*; +import static net.consensys.linea.zktracer.opcode.OpCode.*; + +import net.consensys.linea.testing.BytecodeCompiler; +import net.consensys.linea.testing.BytecodeRunner; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.datatypes.Address; +import org.junit.jupiter.api.Test; + +/** CALL/ABORT's are revert sensitive. We test this with two CALL's. */ +public class MultiCallAbortTests { + + @Test + void normalCallThenAbortedCallToEoaThenRevert() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress), 1, 0, 0, 0, 0); + appendInsufficientBalanceCall( + program, CALL, 1000, Address.fromHexString(eoaAddress), 0, 0, 0, 0); + program.push(6).push(7).op(REVERT); + Bytes bytecode = program.compile(); + BytecodeRunner.of(bytecode).run(); + } + + @Test + void abortedCallNormalCallToEoaThenRevert() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + appendInsufficientBalanceCall( + program, CALL, 1000, Address.fromHexString(eoaAddress), 0, 0, 0, 0); + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress), 1, 0, 0, 0, 0); + program.push(6).push(7).op(REVERT); + Bytes bytecode = program.compile(); + BytecodeRunner.of(bytecode).run(); + } + + @Test + void balanceThenAbortedCallToEoaThenRevert() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + program.push(eoaAddress).op(BALANCE).op(POP); + appendInsufficientBalanceCall( + program, CALL, 1000, Address.fromHexString(eoaAddress), 0, 0, 0, 0); + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress), 0, 0, 0, 0, 0); + program.push(6).push(7).op(REVERT); + Bytes bytecode = program.compile(); + BytecodeRunner.of(bytecode).run(); + } + + @Test + void abortedCallThenBalanceToEoaThenRevert() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + appendInsufficientBalanceCall( + program, CALL, 1000, Address.fromHexString(eoaAddress), 0, 0, 0, 0); + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress), 0, 0, 0, 0, 0); + program.push(6).push(7).op(REVERT); + Bytes bytecode = program.compile(); + BytecodeRunner.of(bytecode).run(); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/eoa/EoaTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/eoa/EoaTests.java new file mode 100644 index 0000000000..4d438d7d2b --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/eoa/EoaTests.java @@ -0,0 +1,156 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests.eoa; + +import static net.consensys.linea.zktracer.opcode.OpCode.*; + +import net.consensys.linea.testing.BytecodeCompiler; +import net.consensys.linea.testing.BytecodeRunner; +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; + +/** + * In the arithmetization there are the following EOA specific scenarios: + * + *

- scn/CALL_EOA_SUCCESS_WILL_REVERT + * + *

- scn/CALL_EOA_SUCCESS_WONT_REVERT + */ +public class EoaTests { + + final String eoaAddress = "abcdef0123456789"; + + @Test + void transfersValueWillRevertTest() { + + Bytes bytecode = + BytecodeCompiler.newProgram() + .push(1) + .push(2) + .push(3) + .push(4) + .push(5) // value + .push(eoaAddress) // address + .push(0) // gas + .op(CALL) + .op(POP) + .push(6) + .push(7) + .op(REVERT) + .compile(); + + BytecodeRunner.of(bytecode).run(); + } + + @Test + void transfersValueWontRevertTest() { + + Bytes bytecode = + BytecodeCompiler.newProgram() + .push(1) + .push(2) + .push(3) + .push(4) + .push(5) // value + .push(eoaAddress) // address + .push(0) // gas + .op(CALL) + .op(POP) + .compile(); + + BytecodeRunner.of(bytecode).run(); + } + + @Test + void transfersAllValueWillRevertTest() { + + Bytes program = + BytecodeCompiler.newProgram() + .push(1) + .push(2) + .push(3) + .push(4) + .op(SELFBALANCE) // all our balance + .push(eoaAddress) // address + .push(0) // gas + .op(CALL) + .op(POP) + .push(6) + .push(7) + .op(REVERT) + .compile(); + + BytecodeRunner.of(program).run(); + } + + @Test + void transfersAllValueWontRevertTest() { + + Bytes bytecode = + BytecodeCompiler.newProgram() + .push(1) + .push(2) + .push(3) + .push(4) + .op(SELFBALANCE) // all our balance + .push(eoaAddress) // address + .push(0) // gas + .op(CALL) + .compile(); + + BytecodeRunner.of(bytecode).run(); + } + + @Test + void transfersNoValueWillRevertTest() { + + Bytes bytecode = + BytecodeCompiler.newProgram() + .push(1) + .push(2) + .push(3) + .push(4) + .push(0) // value + .push(eoaAddress) // address + .push(0) // gas + .op(CALL) + .op(POP) + .push(6) + .push(7) + .op(REVERT) + .compile(); + + BytecodeRunner.of(bytecode).run(); + } + + @Test + void transfersNoValueWontRevertTest() { + + Bytes bytecode = + BytecodeCompiler.newProgram() + .push(1) + .push(2) + .push(3) + .push(4) + .push(0) // value + .push(eoaAddress) // address + .push(0) // gas + .op(CALL) + .op(POP) + .compile(); + + BytecodeRunner.of(bytecode).run(); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/eoa/gasStipendTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/eoa/gasStipendTests.java new file mode 100644 index 0000000000..e693d765dc --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/eoa/gasStipendTests.java @@ -0,0 +1,47 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests.eoa; + +import static net.consensys.linea.zktracer.instructionprocessing.callTests.Utilities.*; +import static net.consensys.linea.zktracer.opcode.OpCode.*; + +import net.consensys.linea.testing.BytecodeCompiler; +import net.consensys.linea.testing.BytecodeRunner; +import org.hyperledger.besu.datatypes.Address; +import org.junit.jupiter.api.Test; + +/** + * Transferring nonzero value provides a gas stipend to the callee. This stipend will immediately be + * restituted to the caller in case of an EOA call. + */ +public class gasStipendTests { + + @Test + void zeroValueEoaCallTest() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress), 0, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(); + } + + // The caller should recover and extra G_stipend = 2300 gas. + @Test + void nonzeroValueEoaCallTest() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + simpleCall(program, CALL, 0, Address.fromHexString(eoaAddress), 1, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/prc/ecrecover/gasStipendTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/prc/ecrecover/gasStipendTests.java new file mode 100644 index 0000000000..0acfcef5ef --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/prc/ecrecover/gasStipendTests.java @@ -0,0 +1,88 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests.prc.ecrecover; + +import static net.consensys.linea.zktracer.instructionprocessing.callTests.Utilities.*; +import static net.consensys.linea.zktracer.opcode.OpCode.CALL; + +import net.consensys.linea.testing.BytecodeCompiler; +import net.consensys.linea.testing.BytecodeRunner; +import org.hyperledger.besu.datatypes.Address; +import org.junit.jupiter.api.Test; + +/** + * For these tests to work as expected, the transaction should start out with sufficient gas. At + * least 21k plus gas to cover the PUSH's and the (warm) CALL with potentially value transfer and + * potentially account creation costs. Also there should be enough gas in the end for the 63/64 + * business not diminish the gas we provide the callee. + * + *

Something like 60k gas should cover all costs (21k transaction costs + 9k for potential value + * transfer + 25k if value transfer leads to a precompile starting to exist in the state etc ... + + * 3k for the callee + opcode costs on the order of 130 or so) + */ +public class gasStipendTests { + + // sufficient gas for PRC execution + @Test + void zeroValueEcrecoverCallTest() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + validEcrecoverData(program); + simpleCall( + program, CALL, 3000, Address.fromHexString(Address.ECREC.toHexString()), 0, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(); + } + + @Test + void nonzeroValueEcrecoverCallTest() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + validEcrecoverData(program); + simpleCall( + program, CALL, 3000, Address.fromHexString(Address.ECREC.toHexString()), 1, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(); + } + + @Test + void nonzeroValueStipendCompletesGasEcrecoverCallTest() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + validEcrecoverData(program); + simpleCall( + program, CALL, 700, Address.fromHexString(Address.ECREC.toHexString()), 1, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(); + } + + // insufficient gas for PRC execution + @Test + void nonzeroValueShortOnGasEcrecoverCallTest() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + validEcrecoverData(program); + simpleCall( + program, CALL, 2999, Address.fromHexString(Address.ECREC.toHexString()), 1, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(); + } + + @Test + void nonzeroValueStipendFallsShortOfCompletingGasEcrecoverCallTest() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + validEcrecoverData(program); + simpleCall( + program, CALL, 699, Address.fromHexString(Address.ECREC.toHexString()), 1, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/Utilities.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/Utilities.java new file mode 100644 index 0000000000..e0ef573629 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/Utilities.java @@ -0,0 +1,65 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests.smc; + +import java.util.List; + +import net.consensys.linea.testing.ToyAccount; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.crypto.KeyPair; +import org.hyperledger.besu.crypto.SECP256K1; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; + +public class Utilities { + + public static KeyPair keyPair = new SECP256K1().generateKeyPair(); + public static Address userAddress = + Address.extract(Hash.hash(keyPair.getPublicKey().getEncodedBytes())); + public static ToyAccount userAccount = + ToyAccount.builder().balance(Wei.fromEth(10)).nonce(99).address(userAddress).build(); + + public static ToyAccount accountWhoseByteCodeIsASingleStop = + ToyAccount.builder() + .balance(Wei.fromEth(1)) + .nonce(13) + .address(Address.fromHexString("c0de00")) + .code(Bytes.fromHexString("00")) + .build(); + + public static ToyAccount accountWhoseByteCodeIsASingleJumpDest = + ToyAccount.builder() + .balance(Wei.fromEth(1)) + .nonce(19) + .address(Address.fromHexString("c0de5b")) + .code(Bytes.fromHexString("5b")) + .build(); + + public static ToyAccount accountWhoseByteCodeIsASingleInvalid = + ToyAccount.builder() + .balance(Wei.fromEth(1)) + .nonce(13) + .address(Address.fromHexString("c0defe")) + .code(Bytes.fromHexString("fe")) + .build(); + + public static List accounts = + List.of( + userAccount, + accountWhoseByteCodeIsASingleStop, + accountWhoseByteCodeIsASingleJumpDest, + accountWhoseByteCodeIsASingleInvalid); +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/monoOpCodeTargets/ImmediateInvalid.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/monoOpCodeTargets/ImmediateInvalid.java new file mode 100644 index 0000000000..47c04d3dba --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/monoOpCodeTargets/ImmediateInvalid.java @@ -0,0 +1,57 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests.smc.monoOpCodeTargets; + +import static net.consensys.linea.zktracer.instructionprocessing.callTests.Utilities.simpleCall; +import static net.consensys.linea.zktracer.instructionprocessing.callTests.smc.Utilities.*; +import static net.consensys.linea.zktracer.opcode.OpCode.CALL; +import static net.consensys.linea.zktracer.opcode.OpCode.REVERT; + +import net.consensys.linea.testing.BytecodeCompiler; +import net.consensys.linea.testing.BytecodeRunner; +import org.junit.jupiter.api.Test; + +public class ImmediateInvalid { + + @Test + void zeroValueTransferToInvalid() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + + simpleCall(program, CALL, 0, accountWhoseByteCodeIsASingleInvalid.getAddress(), 0, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(accounts); + } + + @Test + void nonZeroValueTransferToInvalidContract() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + + simpleCall(program, CALL, 0, accountWhoseByteCodeIsASingleInvalid.getAddress(), 1, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(accounts); + } + + @Test + void nonZeroValueTransferToInvalidContractRevertingTransaction() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + + simpleCall(program, CALL, 0, accountWhoseByteCodeIsASingleInvalid.getAddress(), 1, 0, 0, 0, 0); + + // we use the 1 on the stack after this successful CALL as the revert message size + program.push(0).op(REVERT); + + BytecodeRunner.of(program.compile()).run(accounts); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/monoOpCodeTargets/singleJumpDest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/monoOpCodeTargets/singleJumpDest.java new file mode 100644 index 0000000000..7190a9f6d4 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/monoOpCodeTargets/singleJumpDest.java @@ -0,0 +1,103 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests.smc.monoOpCodeTargets; + +import static net.consensys.linea.zktracer.instructionprocessing.callTests.Utilities.simpleCall; +import static net.consensys.linea.zktracer.instructionprocessing.callTests.smc.Utilities.*; +import static net.consensys.linea.zktracer.opcode.OpCode.CALL; +import static net.consensys.linea.zktracer.opcode.OpCode.REVERT; + +import net.consensys.linea.testing.BytecodeCompiler; +import net.consensys.linea.testing.BytecodeRunner; +import org.junit.jupiter.api.Test; + +/** + * Second-simplest case where we enter a smart contract. The called smart contract executes a single + * JUMPDEST opcode (which is costs gas). + */ +public class singleJumpDest { + + /** This test should trigger the scenario/CALL_TO_SMC_SUCCESS_WONT_REVERT scenario. */ + @Test + void zeroValueTransferToJumpDestContract() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + + simpleCall( + program, CALL, 10, accountWhoseByteCodeIsASingleJumpDest.getAddress(), 0, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(accounts); + } + + /** This test should trigger the scenario/CALL_TO_SMC_SUCCESS_WONT_REVERT scenario. */ + @Test + void nonZeroValueTransferToJumpDestContract() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + + simpleCall( + program, CALL, 10, accountWhoseByteCodeIsASingleJumpDest.getAddress(), 1, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(accounts); + } + + /** This test should trigger the scenario/CALL_TO_SMC_SUCCESS_WILL_REVERT scenario. */ + @Test + void nonZeroValueTransferToJumpDestContractRevertingTransaction() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + + simpleCall( + program, CALL, 10, accountWhoseByteCodeIsASingleJumpDest.getAddress(), 1, 0, 0, 0, 0); + + // we use the 1 on the stack after this successful CALL as the revert message size + program.push(0).op(REVERT); + + BytecodeRunner.of(program.compile()).run(accounts); + } + + // CALL reverts because of OOGX + /////////////////////////////// + + /** This test should trigger the scenario/CALL_TO_SMC_FAILURE_WONT_REVERT scenario. */ + @Test + void zeroValueTransferToJumpDestContractOogx() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + + simpleCall(program, CALL, 0, accountWhoseByteCodeIsASingleJumpDest.getAddress(), 0, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(accounts); + } + + /** This test should trigger the scenario/CALL_TO_SMC_FAILURE_WONT_REVERT scenario. */ + @Test + void nonZeroValueTransferToJumpDestContractOogx() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + + simpleCall(program, CALL, 0, accountWhoseByteCodeIsASingleJumpDest.getAddress(), 1, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(accounts); + } + + /** This test should trigger the scenario/CALL_TO_SMC_FAILURE_WILL_REVERT scenario. */ + @Test + void nonZeroValueTransferToJumpDestContractOogxAndRevertingTransaction() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + + simpleCall(program, CALL, 0, accountWhoseByteCodeIsASingleJumpDest.getAddress(), 1, 0, 0, 0, 0); + + // we use the 1 on the stack after this successful CALL as the revert message size + program.push(0).op(REVERT); + + BytecodeRunner.of(program.compile()).run(accounts); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/monoOpCodeTargets/singleStop.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/monoOpCodeTargets/singleStop.java new file mode 100644 index 0000000000..cdf44ac4f8 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/instructionprocessing/callTests/smc/monoOpCodeTargets/singleStop.java @@ -0,0 +1,62 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.instructionprocessing.callTests.smc.monoOpCodeTargets; + +import static net.consensys.linea.zktracer.instructionprocessing.callTests.Utilities.simpleCall; +import static net.consensys.linea.zktracer.instructionprocessing.callTests.smc.Utilities.*; +import static net.consensys.linea.zktracer.opcode.OpCode.*; + +import net.consensys.linea.testing.*; +import org.junit.jupiter.api.Test; + +/** + * Simplest case where we enter a smart contract. The called smart contract executes a single STOP + * opcode (which is free of charge). No value is transferred. + */ +public class singleStop { + + /** This test should trigger the scenario/CALL_TO_SMC_SUCCESS_WONT_REVERT scenario. */ + @Test + void zeroValueTransferToContractThatStops() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + + simpleCall(program, CALL, 0, accountWhoseByteCodeIsASingleStop.getAddress(), 0, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(accounts); + } + + /** This test should trigger the scenario/CALL_TO_SMC_SUCCESS_WONT_REVERT scenario. */ + @Test + void nonZeroValueTransferToContractThatStops() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + + simpleCall(program, CALL, 0, accountWhoseByteCodeIsASingleStop.getAddress(), 1, 0, 0, 0, 0); + + BytecodeRunner.of(program.compile()).run(accounts); + } + + /** This test should trigger the scenario/CALL_TO_SMC_SUCCESS_WILL_REVERT scenario. */ + @Test + void nonZeroValueTransferToContractThatStopsRevertingTransaction() { + BytecodeCompiler program = BytecodeCompiler.newProgram(); + + simpleCall(program, CALL, 0, accountWhoseByteCodeIsASingleStop.getAddress(), 1, 0, 0, 0, 0); + + // we use the 1 on the stack after this successful CALL as the revert message size + program.push(0).op(REVERT); + + BytecodeRunner.of(program.compile()).run(accounts); + } +} diff --git a/notes/emptyDeploymentsInTheRootTest_notes.md b/notes/emptyDeploymentsInTheRootTest_notes.md deleted file mode 100644 index 132b0828cd..0000000000 --- a/notes/emptyDeploymentsInTheRootTest_notes.md +++ /dev/null @@ -1,105 +0,0 @@ -deploymentTransactionLeadsToEmptyDeploymentTest -=============================================== - -hub.tx-finalization---success-setting-sender-account-row - - -deploymentTransactionLeadsToNonemptyDeploymentTest -================================================== - -hub.return-instruction---first-account-row-for-nonempty-deployments -hub.return-instruction---justifying-the-ICPX -hub.return-instruction---setting-the-second-MMU-instruction -hub.tx-finalization---success-setting-sender-account-row - - -deploymentTransactionEmptyReverts -================================= - -hub.tx-finalization---failure-setting-recipient-account-row -hub.tx-finalization---failure-setting-sender-account-row - - -deploymentTransactionNonemptyReverts -==================================== - -hub-into-gas -hub.tx-finalization---failure-setting-recipient-account-row -hub.tx-finalization---failure-setting-sender-account-row - - -createDeploysEmptyByteCode -========================== - -hub-into-romlex -hub.account---the-ROMLEX-lookup-requires-nonzero-code-size -hub.create-instruction---createe-balance-operation -hub.create-instruction---createe-code-operation -hub.create-instruction---createe-nonce-operation -hub.create-instruction---creator-balance-update -hub.create-instruction---setting-GAS_NEXT -hub.tx-finalization---success-setting-sender-account-row - - -createDeploysNonemptyByteCode -============================= - -hub-into-romlex -hub.account---the-ROMLEX-lookup-requires-nonzero-code-size -hub.create-instruction---createe-balance-operation -hub.create-instruction---createe-code-operation -hub.create-instruction---createe-nonce-operation -hub.create-instruction---creator-balance-update -hub.create-instruction---setting-GAS_NEXT -hub.return-instruction---justifying-the-ICPX -hub.tx-finalization---success-setting-sender-account-row - - -createRevertsWithNonemptyReturnData -=================================== - -hub-into-gas -hub-into-romlex -hub.account---the-ROMLEX-lookup-requires-nonzero-code-size -hub.account-instruction---foreign-address-opcode---doing-account-row -hub.account-instruction---foreign-address-opcode---setting-peeking-flags -hub.account-instruction---foreign-address-opcode---undoing-account-row -hub.create-instruction---createe-balance-operation -hub.create-instruction---createe-code-operation -hub.create-instruction---createe-nonce-operation -hub.create-instruction---creator-balance-update -hub.create-instruction---final-context-row-for-deployment-failures-that-will-revert -hub.create-instruction---setting-GAS_NEXT -hub.create-instruction---setting-the-CREATE-scenario---not-rebuffed-nonempty-init-code -hub.create-instruction---undoing-createe-account-operations-for-deployment-failures -hub.create-instruction---undoing-creator-account-operations-for-deployment-failures -hub.generalities---context-number-generalities -hub.tx-finalization---failure-setting-recipient-account-row -hub.tx-finalization---failure-setting-sender-account-row - - -createRevertsWithEmptyReturnData -================================ - -hub-into-romlex -hub.account---the-ROMLEX-lookup-requires-nonzero-code-size -hub.create-instruction---createe-balance-operation -hub.create-instruction---createe-code-operation -hub.create-instruction---createe-nonce-operation -hub.create-instruction---creator-balance-update -hub.create-instruction---final-context-row-for-deployment-failures-that-wont-revert -hub.create-instruction---setting-GAS_NEXT -hub.create-instruction---setting-the-CREATE-scenario---not-rebuffed-nonempty-init-code -hub.create-instruction---undoing-createe-account-operations-for-deployment-failures -hub.create-instruction---undoing-creator-account-operations-for-deployment-failures -hub.tx-finalization---success-setting-sender-account-row - - -TO INVESTIGATE NEXT -=================== - -hub.create-instruction---setting-the-CREATE-scenario -hub.generalities---context-number-generalities - -RESOLVED -======== diff --git a/notes/failing_constraints_CODECOPY_tests.md b/notes/failing_constraints_CODECOPY_tests.md deleted file mode 100644 index adca2b346b..0000000000 --- a/notes/failing_constraints_CODECOPY_tests.md +++ /dev/null @@ -1,80 +0,0 @@ -testCreateContractFromInitCodeThatDeploysItself -=============================================== - -hub.copy-instruction---CODECOPY---debug-consistency-constraints -hub.create-instruction---createe-account-row-first-appearance -hub.create-instruction---createe-balance-operation -hub.create-instruction---createe-code-operation -hub.create-instruction---createe-nonce-operation -hub.create-instruction---creator-balance-update -hub.create-instruction---final-context-row-for-deployment-successes-that-wont-revert -hub.create-instruction---setting-GAS_NEXT -hub.create-instruction---setting-the-MMU-instruction -hub.return-instruction---justifying-the-ICPX -hub.return-instruction---setting-MMU-data-first-call -hub.tx-finalization---success-setting-sender-account-row - -testCreateContractFromInitCodeSimple -==================================== - -hub.copy-instruction---CODECOPY---debug-consistency-constraints -hub.create-instruction---createe-account-row-first-appearance -hub.create-instruction---createe-balance-operation -hub.create-instruction---createe-code-operation -hub.create-instruction---createe-nonce-operation -hub.create-instruction---creator-balance-update -hub.create-instruction---final-context-row-for-deployment-successes-that-wont-revert -hub.create-instruction---setting-GAS_NEXT -hub.create-instruction---setting-the-MMU-instruction -hub.tx-finalization---success-setting-sender-account-row - -testDeploymentTransactionDeploysOwnInitCodeThroughCodeCopy -========================================================== - -hub.return-instruction---first-account-row-for-nonempty-deployments -hub.return-instruction---justifying-the-ICPX -hub.return-instruction---setting-NSR -hub.return-instruction---setting-peeking-flags -hub.return-instruction---setting-the-callers-new-return-data-nonempty-deployments -hub.return-instruction---setting-the-second-MMU-instruction -hub.tx-finalization---success-setting-sender-account-row - -testCreateContractFromInitCodeWithMload -======================================= - -hub.copy-instruction---CODECOPY---debug-consistency-constraints -hub.create-instruction---createe-account-row-first-appearance -hub.create-instruction---createe-balance-operation -hub.create-instruction---createe-code-operation -hub.create-instruction---createe-nonce-operation -hub.create-instruction---creator-balance-update -hub.create-instruction---final-context-row-for-deployment-successes-that-wont-revert -hub.create-instruction---setting-GAS_NEXT -hub.create-instruction---setting-the-MMU-instruction -hub.tx-finalization---success-setting-sender-account-row - - -all failing constraints: -======================== - -hub.copy-instruction---CODECOPY---debug-consistency-constraints -hub.create-instruction---createe-account-row-first-appearance -hub.create-instruction---createe-balance-operation -hub.create-instruction---createe-code-operation -hub.create-instruction---createe-nonce-operation -hub.create-instruction---creator-balance-update -hub.create-instruction---final-context-row-for-deployment-successes-that-wont-revert -hub.create-instruction---setting-GAS_NEXT -hub.create-instruction---setting-the-MMU-instruction -hub.return-instruction---first-account-row-for-nonempty-deployments -hub.return-instruction---justifying-the-ICPX -hub.return-instruction---setting-NSR -hub.return-instruction---setting-peeking-flags -hub.return-instruction---setting-the-callers-new-return-data-nonempty-deployments -hub.return-instruction---setting-the-second-MMU-instruction -hub.tx-finalization---success-setting-sender-account-row - -RESOLVED: -========= - -hub.return-instruction---setting-MMU-data-first-call diff --git a/testing/src/main/java/net/consensys/linea/testing/BytecodeRunner.java b/testing/src/main/java/net/consensys/linea/testing/BytecodeRunner.java index e75f648a0f..1ebe9ff86a 100644 --- a/testing/src/main/java/net/consensys/linea/testing/BytecodeRunner.java +++ b/testing/src/main/java/net/consensys/linea/testing/BytecodeRunner.java @@ -41,7 +41,7 @@ */ @Accessors(fluent = true) public final class BytecodeRunner { - public static final long DEFAULT_GAS_LIMIT = 25_000_000L; + public static final long DEFAULT_GAS_LIMIT = 61_000_000L; private final Bytes byteCode; ToyExecutionEnvironmentV2 toyExecutionEnvironmentV2; @@ -107,7 +107,7 @@ public void run(Wei senderBalance, Long gasLimit, List additionalAcc ToyTransaction.builder() .sender(senderAccount) .to(receiverAccount) - .value(Wei.ONE) + .value(Wei.of(69)) .keyPair(keyPair) .gasLimit(selectedGasLimit) .gasPrice(Wei.of(8))