Skip to content

Commit

Permalink
HUB debugging continued --- CALL's (#1508)
Browse files Browse the repository at this point in the history
  • Loading branch information
OlivierBBB authored Nov 23, 2024
1 parent 3e3d33b commit 0f870fa
Show file tree
Hide file tree
Showing 54 changed files with 2,345 additions and 665 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,15 @@
import net.consensys.linea.zktracer.module.shf.Shf;
import net.consensys.linea.zktracer.module.stp.Stp;
import net.consensys.linea.zktracer.module.tables.bin.BinRt;
import net.consensys.linea.zktracer.module.tables.instructionDecoder.InstructionDecoder;
import net.consensys.linea.zktracer.module.tables.instructionDecoder.*;
import net.consensys.linea.zktracer.module.tables.shf.ShfRt;
import net.consensys.linea.zktracer.module.trm.Trm;
import net.consensys.linea.zktracer.module.txndata.TxnData;
import net.consensys.linea.zktracer.module.wcp.Wcp;
import net.consensys.linea.zktracer.opcode.OpCode;
import net.consensys.linea.zktracer.opcode.OpCodeData;
import net.consensys.linea.zktracer.opcode.gas.projector.GasProjector;
import net.consensys.linea.zktracer.runtime.callstack.CallDataInfo;
import net.consensys.linea.zktracer.runtime.callstack.CallFrame;
import net.consensys.linea.zktracer.runtime.callstack.CallFrameType;
import net.consensys.linea.zktracer.runtime.callstack.CallStack;
Expand All @@ -121,7 +122,6 @@
import org.hyperledger.besu.datatypes.Wei;
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.log.Log;
import org.hyperledger.besu.evm.log.LogTopic;
import org.hyperledger.besu.evm.operation.Operation;
Expand Down Expand Up @@ -606,41 +606,29 @@ public void traceContextEnter(MessageFrame frame) {
// internal transaction (CALL) or internal deployment (CREATE)
if (frame.getDepth() > 0) {
final OpCode currentOpCode = callStack.currentCallFrame().opCode();
final boolean isDeployment = frame.getType() == CONTRACT_CREATION;

checkState(currentOpCode.isCall() || currentOpCode.isCreate());
checkState(
currentTraceSection() instanceof CallSection
|| currentTraceSection() instanceof CreateSection);
final boolean isDeployment = frame.getType() == CONTRACT_CREATION;
checkState(currentTraceSection() instanceof CreateSection == isDeployment);

final CallFrameType frameType =
frame.isStatic() ? CallFrameType.STATIC : CallFrameType.STANDARD;

final long callDataOffset =
isDeployment
? 0
: Words.clampedToLong(
callStack
.currentCallFrame()
.frame()
.getStackItem(currentOpCode.callMayNotTransferValue() ? 2 : 3));

final long callDataSize =
final CallDataInfo callDataInfo =
isDeployment
? 0
: Words.clampedToLong(
callStack
.currentCallFrame()
.frame()
.getStackItem(currentOpCode.callMayNotTransferValue() ? 3 : 4));

final long callDataContextNumber = callStack.currentCallFrame().contextNumber();
? CallDataInfo.empty()
: ((CallSection) currentTraceSection()).getCallDataInfo();

currentFrame().rememberGasNextBeforePausing(this);
currentFrame().pauseCurrentFrame();

MemorySpan returnDataTargetInCaller =
(currentTraceSection() instanceof CallSection)
? ((CallSection) currentTraceSection()).getCallProvidedReturnDataTargetSpan()
: MemorySpan.empty();
isDeployment
? MemorySpan.empty()
: ((CallSection) currentTraceSection()).getReturnAtMemorySpan();

callStack.enter(
frameType,
Expand All @@ -654,10 +642,7 @@ public void traceContextEnter(MessageFrame frame) {
this.deploymentNumberOf(frame.getContractAddress()),
new Bytecode(frame.getCode().getBytes()),
frame.getSenderAddress(),
frame.getInputData(),
callDataOffset,
callDataSize,
callDataContextNumber,
callDataInfo,
returnDataTargetInCaller);

this.currentFrame().initializeFrame(frame);
Expand Down Expand Up @@ -724,6 +709,16 @@ public void tracePreExecution(final MessageFrame frame) {
this.processStateExec(frame);
}

/**
* A comment on {@link #unlatchStack(MessageFrame, TraceSection)}: Any instruction that writes
* onto the stack gets immediately unlatched if it raises an exception. If unexceptional it also
* gets immediately unlatched, except CALL's and CREATE's. The value written on the stack
* (<b>successBit</b> or <b>successBit ∙ [child address]</b> respectively) is only written after
* the child context has been executed.
*
* <p><b>Question:</b> Does this work well with CALL's to EOA's ? to PRC's ? trivial deployments
* (i.e. empty initialization code) ?
*/
public void tracePostExecution(MessageFrame frame, Operation.OperationResult operationResult) {
checkArgument(
this.state().processingPhase == TX_EXEC,
Expand All @@ -748,7 +743,7 @@ public void tracePostExecution(MessageFrame frame, Operation.OperationResult ope

defers.resolvePostExecution(this, frame, operationResult);

if (!this.currentFrame().opCode().isCall() && !this.currentFrame().opCode().isCreate()) {
if (isExceptional() || !opCode().isCallOrCreate()) {
this.unlatchStack(frame, currentSection);
}

Expand Down Expand Up @@ -1062,7 +1057,7 @@ void traceOpcode(MessageFrame frame) {
case RETURN -> new ReturnSection(this);
case REVERT -> new RevertSection(this);
case STOP -> new StopSection(this);
case SELFDESTRUCT -> new SelfdestructSection(this);
case SELFDESTRUCT -> new SelfdestructSection(this, frame);
}
}

Expand Down Expand Up @@ -1101,7 +1096,7 @@ void traceOpcode(MessageFrame frame) {

case JUMP -> new JumpSection(this);

case CREATE -> new CreateSection(this);
case CREATE -> new CreateSection(this, frame);

case CALL -> new CallSection(this, frame);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@

import net.consensys.linea.zktracer.module.hub.Hub;

public interface ImmediateContextEntryDefer {
public interface ContextEntryDefer {
void resolveUponContextEntry(Hub hub);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
*/
public class DeferRegistry
implements PostOpcodeDefer,
ImmediateContextEntryDefer,
ContextEntryDefer,
ContextExitDefer,
PostRollbackDefer,
PostTransactionDefer,
Expand All @@ -41,7 +41,7 @@ public class DeferRegistry
private final List<PostOpcodeDefer> postOpcodeDefers = new ArrayList<>();

/** A list of actions deferred to the immediate entry into a child or parent context */
private final List<ImmediateContextEntryDefer> immediateContextEntryDefers = new ArrayList<>();
private final List<ContextEntryDefer> contextEntryDefers = new ArrayList<>();

/** A list of actions deferred to the end of a given context */
private final Map<Integer, List<ContextExitDefer>> contextExitDefers = new HashMap<>();
Expand All @@ -63,8 +63,8 @@ public class DeferRegistry
private final Map<CallFrame, List<PostRollbackDefer>> rollbackDefers = new HashMap<>();

/** Schedule an action to be executed after the completion of the current opcode. */
public void scheduleForImmediateContextEntry(ImmediateContextEntryDefer defer) {
immediateContextEntryDefers.add(defer);
public void scheduleForContextEntry(ContextEntryDefer defer) {
contextEntryDefers.add(defer);
}

/** Schedule an action to be executed after the completion of the current opcode. */
Expand Down Expand Up @@ -189,10 +189,10 @@ public void resolveUponRollback(

@Override
public void resolveUponContextEntry(Hub hub) {
for (ImmediateContextEntryDefer defer : immediateContextEntryDefers) {
for (ContextEntryDefer defer : contextEntryDefers) {
defer.resolveUponContextEntry(hub);
}
immediateContextEntryDefers.clear();
contextEntryDefers.clear();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,15 @@ public StpCall(Hub hub, long memoryExpansionGas) {
private void stpCallForCalls(Hub hub) {
final MessageFrame frame = hub.messageFrame();

final boolean callCanTransferValue = opCode.callCanTransferValue();
final boolean callHasValueArgument = opCode.callHasValueArgument();
final Address to = Words.toAddress(frame.getStackItem(1));
final Account toAccount = frame.getWorldUpdater().get(to);
this.gas = EWord.of(frame.getStackItem(0));
this.value = (callCanTransferValue) ? EWord.of(frame.getStackItem(2)) : ZERO;
this.value = (callHasValueArgument) ? EWord.of(frame.getStackItem(2)) : ZERO;
this.exists =
switch (hub.opCode()) {
case CALL, STATICCALL -> toAccount != null
? !toAccount.isEmpty()
: false; // the address that matters here is that of the callee
case CALLCODE,
DELEGATECALL -> true; // the address that matters here is that of the caller --- who
// always exists
// TODO: @Olivier or @François:
// replace this with the same logic above with the
// current account (frame.getRecipientAddress() ?)
// also add arg check verifying existence == true
// in that case.
case CALL -> toAccount != null ? !toAccount.isEmpty() : false;
case CALLCODE, DELEGATECALL, STATICCALL -> false;
default -> throw new IllegalArgumentException(
"STP module triggered for a non CALL-type instruction");
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public enum CallScenario {
CALL_ABORT_WILL_REVERT,
CALL_ABORT_WONT_REVERT,
// Externally owned account call scenarios
CALL_EOA_UNDEFINED,
CALL_EOA_SUCCESS_WILL_REVERT,
CALL_EOA_SUCCESS_WONT_REVERT,
// Smart contract call scenarios:
Expand All @@ -50,18 +51,44 @@ public enum CallScenario {
CALL_PRC_SUCCESS_WILL_REVERT,
CALL_PRC_SUCCESS_WONT_REVERT;

public boolean isPrecompileScenario() {
public boolean isIndefinitePrcCallScenario() {
return this == CALL_PRC_UNDEFINED || isPrcCallScenario();
}

public boolean isPrcCallScenario() {
return this == CALL_PRC_FAILURE
|| this == CALL_PRC_SUCCESS_WILL_REVERT
|| this == CALL_PRC_SUCCESS_WONT_REVERT;
}

public boolean isIndefiniteSmcCallScenario() {
return this == CALL_SMC_UNDEFINED || isSmcCallScenario();
}

public boolean isSmcCallScenario() {
return this == CALL_SMC_FAILURE_WILL_REVERT
|| this == CALL_SMC_FAILURE_WONT_REVERT
|| this == CALL_SMC_SUCCESS_WILL_REVERT
|| this == CALL_SMC_SUCCESS_WONT_REVERT;
}

public boolean isIndefiniteEoaCallScenario() {
return this == CALL_EOA_UNDEFINED || isEoaCallScenario();
}

public boolean isEoaCallScenario() {
return this == CALL_EOA_SUCCESS_WILL_REVERT || this == CALL_EOA_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;
return this != UNDEFINED
&& this != CALL_PRC_UNDEFINED
&& this != CALL_SMC_UNDEFINED
&& this != CALL_EOA_UNDEFINED;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,15 @@

public class CreateSection extends TraceSection
implements PostOpcodeDefer,
ImmediateContextEntryDefer,
ContextEntryDefer,
PostRollbackDefer,
ContextReEntryDefer,
PostTransactionDefer {

private Address creatorAddress;
private Address createeAddress;
final ImcFragment imcFragment;

// Just before create
private AccountSnapshot preOpcodeCreatorSnapshot;
private AccountSnapshot preOpcodeCreateeSnapshot;
Expand All @@ -91,7 +95,7 @@ public class CreateSection extends TraceSection

// TODO: according to our preliminary conclusion in issue #866
// CREATE's that raise a failure condition _do spawn a child context_.
public CreateSection(Hub hub) {
public CreateSection(Hub hub, MessageFrame frame) {
super(hub, maxNumberOfLines(hub.pch().exceptions(), hub.pch().abortingConditions()));
final short exceptions = hub.pch().exceptions();

Expand All @@ -106,7 +110,7 @@ public CreateSection(Hub hub) {
this.addFragment(currentContextFragment);

// row: i + 2
final ImcFragment imcFragment = ImcFragment.empty(hub);
imcFragment = ImcFragment.empty(hub);
this.addFragment(imcFragment);

// STATICX case
Expand Down Expand Up @@ -148,12 +152,11 @@ public CreateSection(Hub hub) {
checkArgument(oobCall.isAbortingCondition() == aborts.any());

final CallFrame callFrame = hub.currentFrame();
final MessageFrame messageFrame = hub.messageFrame();

final Address creatorAddress = callFrame.accountAddress();
creatorAddress = frame.getRecipientAddress();
preOpcodeCreatorSnapshot = AccountSnapshot.canonical(hub, creatorAddress);

final Address createeAddress = getDeploymentAddress(messageFrame);
createeAddress = getDeploymentAddress(frame);
preOpcodeCreateeSnapshot = AccountSnapshot.canonical(hub, createeAddress);

if (aborts.any()) {
Expand All @@ -165,14 +168,14 @@ public CreateSection(Hub hub) {

// The CREATE(2) is now unexceptional and unaborted
checkArgument(aborts.none());
hub.defers().scheduleForImmediateContextEntry(this); // when we add the two account fragments
hub.defers().scheduleForContextEntry(this); // when we add the two account fragments
hub.defers().scheduleForPostRollback(this, hub.currentFrame()); // in case of Rollback
hub.defers().scheduleForPostTransaction(this); // when we add the last context row

rlpAddrSubFragment = RlpAddrSubFragment.makeFragment(hub, createeAddress);

final Optional<Account> deploymentAccount =
Optional.ofNullable(messageFrame.getWorldUpdater().get(createeAddress));
Optional.ofNullable(frame.getWorldUpdater().get(createeAddress));
final boolean createdAddressHasNonZeroNonce =
deploymentAccount.map(a -> a.getNonce() != 0).orElse(false);
final boolean createdAddressHasNonEmptyCode =
Expand All @@ -181,12 +184,12 @@ public CreateSection(Hub hub) {
final boolean failedCreate = createdAddressHasNonZeroNonce || createdAddressHasNonEmptyCode;
final boolean emptyInitCode = hub.transients().op().initCodeSegment().isEmpty();

final long offset = Words.clampedToLong(messageFrame.getStackItem(1));
final long size = Words.clampedToLong(messageFrame.getStackItem(2));
final long offset = Words.clampedToLong(frame.getStackItem(1));
final long size = Words.clampedToLong(frame.getStackItem(2));

// Trigger MMU & SHAKIRA to hash the (non-empty) InitCode of CREATE2 - even for failed CREATE2
if (hub.opCode() == CREATE2 && !emptyInitCode) {
final Bytes create2InitCode = messageFrame.shadowReadMemory(offset, size);
final Bytes create2InitCode = frame.shadowReadMemory(offset, size);

final MmuCall mmuCall = MmuCall.create2(hub, create2InitCode, failedCreate);
imcFragment.callMmu(mmuCall);
Expand All @@ -198,7 +201,7 @@ public CreateSection(Hub hub) {
writeHashInfoResult(shakiraDataOperation.result());
}

value = failedCreate ? Wei.ZERO : Wei.of(UInt256.fromBytes(messageFrame.getStackItem(0)));
value = failedCreate ? Wei.ZERO : Wei.of(UInt256.fromBytes(frame.getStackItem(0)));

if (failedCreate) {
finalContextFragment = ContextFragment.nonExecutionProvidesEmptyReturnData(hub);
Expand Down Expand Up @@ -226,11 +229,11 @@ public CreateSection(Hub hub) {
.scheduleForContextReEntry(this, callFrame); // To get the success bit of the CREATE(2)

requiresRomLex = true;
hub.romLex().callRomLex(messageFrame);
hub.romLex().callRomLex(frame);
hub.transients()
.conflation()
.deploymentInfo()
.newDeploymentWithExecutionAt(createeAddress, messageFrame.shadowReadMemory(offset, size));
.newDeploymentWithExecutionAt(createeAddress, frame.shadowReadMemory(offset, size));

// Note: the case CREATE2 has been set before, we need to do it even in the failure case
if (hub.opCode() == CREATE) {
Expand Down
Loading

0 comments on commit 0f870fa

Please sign in to comment.