Skip to content

Commit

Permalink
test(oogx): refine outOfGasExceptionCallTest
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzogentile404 committed Dec 1, 2024
1 parent 53968fe commit 41a0c0b
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ private static boolean isMemoryExpansionFault(

private static boolean isOutOfGas(MessageFrame frame, OpCode opCode, GasProjector gp) {
final long required = gp.of(frame, opCode).upfrontGasCost();
System.out.println(
"opCode: "
+ opCode.name()
+ " ,required: "
+ required
+ " ,OOGX: "
+ (required > frame.getRemainingGas())
+ " ,remainingGas: "
+ frame.getRemainingGas());
return required > frame.getRemainingGas();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import net.consensys.linea.testing.BytecodeCompiler;
import net.consensys.linea.testing.BytecodeRunner;
import net.consensys.linea.testing.ToyAccount;
import net.consensys.linea.zktracer.module.constants.GlobalConstants;
import net.consensys.linea.zktracer.opcode.OpCode;
import net.consensys.linea.zktracer.opcode.OpCodeData;
import org.hyperledger.besu.datatypes.Address;
Expand All @@ -37,21 +38,21 @@

public class OutOfGasExceptionTest {

// TODO: add tests when address is warm. Use constants such as G_WARM_ACCESS etc, make clear the
// different types of costs
// TODO: add tests when address is warm for every opcode
@ParameterizedTest
@MethodSource("outOfGasExceptionSource")
void outOfGasExceptionColdTest(
OpCode opCode, int staticCost, int nPushes, boolean triggersOutOfGasExceptions) {
OpCode opCode, int opCodeStaticCost, int nPushes, short corneCase) {
BytecodeCompiler program = BytecodeCompiler.newProgram();
for (int i = 0; i < nPushes; i++) {
program.push(0);
}
program.op(opCode);
BytecodeRunner bytecodeRunner = BytecodeRunner.of(program.compile());
bytecodeRunner.run(
(long) 21000 + nPushes * 3L + staticCost - (triggersOutOfGasExceptions ? 1 : 0));
if (triggersOutOfGasExceptions) {
// TODO: check if this approach is general enough, maybe use a similar approach to the test
// below
bytecodeRunner.run((long) 21000 + nPushes * 3L + opCodeStaticCost + corneCase);
if (corneCase == -1) {
assertEquals(
OUT_OF_GAS_EXCEPTION,
bytecodeRunner.getHub().previousTraceSection().commonValues.tracedException());
Expand All @@ -66,31 +67,31 @@ static Stream<Arguments> outOfGasExceptionSource() {
List<Arguments> arguments = new ArrayList<>();
for (OpCodeData opCodeData : opCodeToOpCodeDataMap.values()) {
OpCode opCode = opCodeData.mnemonic();
int staticCost = opCodeData.stackSettings().staticGas().cost();
int delta = opCodeData.stackSettings().delta(); // number of items popped from the stack
int opCodeStaticCost = opCodeData.stackSettings().staticGas().cost();
int nPushes = opCodeData.stackSettings().delta(); // number of items popped from the stack
// TODO: some opCodes are excluded for now because they may need to be treated differently
if (staticCost > 0
if (opCodeStaticCost > 0
&& opCode != OpCode.MLOAD
&& opCode != OpCode.MSTORE8
&& opCode != OpCode.SELFDESTRUCT
&& opCode != OpCode.MSTORE) {
arguments.add(Arguments.of(opCode, staticCost, delta, true));
arguments.add(Arguments.of(opCode, staticCost, delta, false));
arguments.add(Arguments.of(opCode, opCodeStaticCost, nPushes, -1));
arguments.add(Arguments.of(opCode, opCodeStaticCost, nPushes, 0));
arguments.add(Arguments.of(opCode, opCodeStaticCost, nPushes, 1));
}
}
return arguments.stream();
}

@ParameterizedTest
@MethodSource("outOfGasExceptionCallSource")
void outOfGasExceptionCallTest(int value, boolean targetAddressExists, boolean isWarm) {
void outOfGasExceptionCallTest(
int value, boolean targetAddressExists, boolean isWarm, short cornerCase) {
BytecodeCompiler program = BytecodeCompiler.newProgram();
int nPushes = 0;

if (targetAddressExists && isWarm) {
// Note: this is a possible way to warm the address
program.push("ca11ee").op(OpCode.BALANCE);
nPushes += 1;
}

program
Expand All @@ -102,36 +103,72 @@ void outOfGasExceptionCallTest(int value, boolean targetAddressExists, boolean i
.push("ca11ee") // address
.push(1000) // gas
.op(OpCode.CALL);
nPushes += 7;

BytecodeRunner bytecodeRunner = BytecodeRunner.of(program.compile());

// TODO: the gas limits here are are surely not enough to run the program, but we want to ensure
// that the OOGX is triggered exactly when the CALL is executed
long gasLimit =
21000L
+ // base gas cost
(isWarm ? 3L + 2600L : 0) // BALANCE + PUSH
+ 7 * 3L // 7 PUSH
+ callGasCost(value, targetAddressExists, isWarm); // CALL

if (targetAddressExists) {
final ToyAccount calleeAccount =
ToyAccount.builder()
.balance(Wei.fromEth(1))
.nonce(10)
.address(Address.fromHexString("ca11ee"))
.build();
bytecodeRunner.run(21000L + nPushes * 3L, List.of(calleeAccount));
bytecodeRunner.run(gasLimit + cornerCase, List.of(calleeAccount));
} else {
bytecodeRunner.run(21000L + nPushes * 3L);
bytecodeRunner.run(gasLimit + cornerCase);
}

assertEquals(
OUT_OF_GAS_EXCEPTION,
bytecodeRunner.getHub().previousTraceSection().commonValues.tracedException());
if (cornerCase == -1) {
assertEquals(
OUT_OF_GAS_EXCEPTION,
bytecodeRunner.getHub().previousTraceSection().commonValues.tracedException());
} else {
assertNotEquals(
OUT_OF_GAS_EXCEPTION,
bytecodeRunner.getHub().previousTraceSection().commonValues.tracedException());
}
}

static Stream<Arguments> outOfGasExceptionCallSource() {
List<Arguments> arguments = new ArrayList<>();
for (int value : new int[] {0, 1}) {
arguments.add(Arguments.of(value, true, true));
arguments.add(Arguments.of(value, true, false));
arguments.add(Arguments.of(value, false, false));
for (short cornerCase : new short[] {-1, 0, 1}) {
arguments.add(Arguments.of(value, true, true, cornerCase));
arguments.add(Arguments.of(value, true, false, cornerCase));
arguments.add(Arguments.of(value, false, false, cornerCase));
}
}
return arguments.stream();
}

private long callGasCost(int value, boolean targetAddressExists, boolean isWarm) {
// TODO: check if this method is correct, general enough and can be simplified
if (value == 0) {
if (isWarm) {
return GlobalConstants.GAS_CONST_G_WARM_ACCESS;
} else {
return GlobalConstants.GAS_CONST_G_COLD_ACCOUNT_ACCESS;
}
} else {
if (isWarm) {
return GlobalConstants.GAS_CONST_G_WARM_ACCESS + GlobalConstants.GAS_CONST_G_CALL_VALUE;
} else {
if (targetAddressExists) {
return GlobalConstants.GAS_CONST_G_COLD_ACCOUNT_ACCESS
+ GlobalConstants.GAS_CONST_G_CALL_VALUE;
} else {
return GlobalConstants.GAS_CONST_G_NEW_ACCOUNT
+ GlobalConstants.GAS_CONST_G_COLD_ACCOUNT_ACCESS
+ GlobalConstants.GAS_CONST_G_CALL_VALUE;
}
}
}
}
}

0 comments on commit 41a0c0b

Please sign in to comment.