-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HUB
debugging continued
#1492
HUB
debugging continued
#1492
Conversation
added a new "canonical" method for AccountSnapshot's
The WorldUpdater of the frame gets discarded in case of an exceptional halt. Besu does this BEFORE it traceContextExit. This fix does creates the TxFinalizationSection before the wiping of the WorldUpdater so as to extract the "unupdated" balances. Note. We will have to apply a similar fix to other operations that make snapshots at the boundaries of context changes: CALL's, CREATE's, RETURN's and SELFDESTRUCT's to name a few, and more generally those opcodes that are REVERT sensitive.
In the TraceSection() constructor we were using the Hub's raisesOogxOrIsUnexceptional method, which queries the currentTraceSection. However, and at that point, this trace section isn't the current one. And its 'exceptions' object is that of the previous opcode.
In the prewarming section we create account and storage fragments using the current hub.stamp(). However it is only later that we create the TraceSection. It is this step that raises the HUB_STAMP. We therefore pre-increment the HUB_STAMP when filling the DOM/SUB stamps.
There was a bug in that we may want to write the result of a KECCAK even if we don't set the hashInfoFlag. This occurs for unexceptional KEC's and for unexceptional, unaborted CREATE2's, both with nonzero size parameter.
GAS_NEXT now returns remainingGas - upfrontGasCost this works for most opCodes. CALL's and CREATE's have specialized methods to amend GAS_NEXT depending on the scenario.
Amending GAS_NEXT is more involved for precompiles.
…n the relevant sections
We talk about child context's and child frames a lot. Also child frames are created also for deployments where "caller" isn't the right term for the parent context.
This is required for both CALL's and CREATE's that actually "enter" a child context with non empty bytecode and nonempty initialization code respectively. We need to write both CCSR: CHILD_CONTEXT_SELF_REVERTS CCRS: CHILD_CONTEXT_REVERT_STAMP This information only becomes avaiable once the child frame's relevant fields were discovered.
This applies whenever we call a SMC with nonempty bytecode.
this fragment is initialized only for the TraceSection's arising from exceptional opCodes. It is appended to its section at the moment of line counting.
@@ -74,7 +74,7 @@ public void commit(List<MappedByteBuffer> 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())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't always compute the gas cost (e.g. if an exception occurs that has greater priority, e.g. SUX
/SOX
/STATICX
/MXPX
/... but we always fill the GAS_COST column. We fill it with some value which we call gasCostToTrace
.
hub.transients.conflation().deploymentInfo(), | ||
isAddressWarm(hub.messageFrame(), address)); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At times the trace method we are using provides us with a WorldView
. When that is the case it is often more accurate to use this world
rather than the maybe deprecated hub.messageFrame().getWorldUpdater()
that we use in the other canonical
method. This method is then preferred.
This issue arose e.g. in traceEndTransaction
and was the reason for why some accounts appeared with invalid balances.
@@ -634,7 +634,7 @@ public void traceContextEnter(MessageFrame frame) { | |||
|
|||
final long callDataContextNumber = callStack.currentCallFrame().contextNumber(); | |||
|
|||
currentFrame().rememberGasNextBeforePausing(); | |||
currentFrame().rememberGasNextBeforePausing(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to get the gasNext
from the hub
, likely from the commonValuesFragment
of the currentTraceSection
;
} | ||
} | ||
|
||
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()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renaming to be consistent with the other methods.
if (state.getProcessingPhase() != TX_SKIP | ||
&& frame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { | ||
this.state.setProcessingPhase(TX_FINL); | ||
new TxFinalizationSection(this, frame.getWorldUpdater(), false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are now 2 places that can trigger the creation of a TxFinalizationSection
:
traceContextExit
tracePostExecution
the reason being: the root context behaves differently in Besu. But looking at Besu I don't remember why that is. And maybe we want to change that and only trace it during traceContextExit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean we don't traceContextExit the root context in case of exception ?
recipientSnapshotAfterTxFinalization.turnOnWarmth(); // purely constraints based | ||
|
||
final Address coinbaseAddress = coinbaseSnapshotBeforeTxFinalization.address(); | ||
coinbaseSnapshotAfterFinalization = AccountSnapshot.canonical(hub, coinbaseAddress); | ||
coinbaseSnapshotAfterFinalization = AccountSnapshot.canonical(hub, world, coinbaseAddress); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the correct world to make snapshots. This world is provided to us in traceContextExit
(in the unexceptional case.)
@@ -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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have to pre-emptively raise the hub.stamp()
as this only happens later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes true, we raise h only when creating the section ! :) nice
DomSubStampsSubFragment.standardDomSubStamps(hub.stamp(), 0)); | ||
|
||
// recipient account fragment | ||
final AccountFragment recipientAccountFragment = | ||
hub.factories() | ||
.accountFragment() | ||
.make( | ||
.makeWithTrm( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is required by the arithmetization, specifically: in the permuted domain the first appearance of an account should be with some trimming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a issue for this, did you link it ?
edit: I did
postOpcodeCallerSnapshot.decrementBalanceBy(value); | ||
preOpcodeCalleeSnapshot.decrementBalanceBy(value); | ||
preOpcodeCalleeSnapshot = postOpcodeCallerSnapshot; | ||
postOpcodeCalleeSnapshot = preOpcodeCallerSnapshot; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switcheroo
scenarioFragment.setScenario(CALL_SMC_FAILURE_WONT_REVERT); | ||
|
||
if (selfCallWithNonzeroValueTransfer) { | ||
childContextExitCallerSnapshot.decrementBalanceBy(value); | ||
reEntryCalleeSnapshot.decrementBalanceBy(value); | ||
} | ||
|
||
int childId = hub.currentFrame().childFramesId().getLast(); | ||
CallFrame childFrame = hub.callStack().getById(childId); | ||
int childContextRevertStamp = childFrame.revertStamp(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note. This revertStamp is that of a "self-induced" revert. Not the actual revert stamp of the child context. The child call / deployment may be successful (in which case we don't enter here) and be reverted later. Here we only care about children that are failures.
We (or I) don't really understand when Besu's MessageFrame is EXCEPTIONAL_HALT, in particular as it relates to - invalidCodePrefixException - maxCodeSizeException for RETURN instructions in a deployment context
@@ -753,7 +753,7 @@ public void tracePostExecution(MessageFrame frame, Operation.OperationResult ope | |||
} | |||
|
|||
if (frame.getDepth() == 0 | |||
&& (frame.getState() == MessageFrame.State.EXCEPTIONAL_HALT || opCode() == REVERT)) { | |||
&& (isExceptional() || opCode() == REVERT)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unclear exactly when Besu's MessageFrame
enters the EXCEPTIONAL_HALT
state. This is particularly true wrt deployment related exceptions (invalidCodePrefixException
and maxCodeSizeException
) for whatever reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, some minor comments
public AccountSnapshot setDeploymentInfo(Hub hub) { | ||
return this.setDeploymentInfo(hub.transients.conflation().deploymentInfo()); | ||
} | ||
|
||
public AccountSnapshot setDeploymentInfo(DeploymentInfo deploymentInfo) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not having a @Setter
?
if (state.getProcessingPhase() != TX_SKIP | ||
&& frame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { | ||
this.state.setProcessingPhase(TX_FINL); | ||
new TxFinalizationSection(this, frame.getWorldUpdater(), false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean we don't traceContextExit the root context in case of exception ?
@@ -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)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's strange that we don't traceContextExit in case of exception in the root context ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I probably phrased that wrong. We certainly don't traceContextReEnter
as there is nothing to re-enter. TBH I'm not sure it makes sense to have two entry points for TxFinalizationSection
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to have only one entry point, yes ...
case CREATE2 -> Exceptions.none(exceptions) && aborts.none() && gp.messageSize() > 0; | ||
default -> false; | ||
}; | ||
Exceptions.none(exceptions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure it's an optim, we'll add two more operation (Exceptions.none
and gp.messageSize > 0
) for every stack row ... and it doesn't make the code much easier to read
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I exchanged positions so that these checks are done after the switch
. I believe that Java is smart enough to ignore conditions in a chain of &&
's if a previous condition was false.
// 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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't we get the gasNext from besu (ie from the next section, in seal()
method ?
@@ -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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes true, we raise h only when creating the section ! :) nice
DomSubStampsSubFragment.standardDomSubStamps(hub.stamp(), 0)); | ||
|
||
// recipient account fragment | ||
final AccountFragment recipientAccountFragment = | ||
hub.factories() | ||
.accountFragment() | ||
.make( | ||
.makeWithTrm( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a issue for this, did you link it ?
edit: I did
@@ -107,7 +107,7 @@ public void run(Wei senderBalance, Long gasLimit, List<ToyAccount> additionalAcc | |||
ToyTransaction.builder() | |||
.sender(senderAccount) | |||
.to(receiverAccount) | |||
.value(Wei.ONE) | |||
.value(Wei.of(69)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly to disambiguate with other 1's in the trace 🙂.
@@ -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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could use the constant from GlabalConsta 'LINEA_BLOCK_GAS_LIMIT`or whatever
No sure. I don't think this would work at the boundaries of execution contexts for instance. |
No description provided.