-
Notifications
You must be signed in to change notification settings - Fork 43
HUB
consistency arguments
#1746
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
Conversation
This fix enforces that the sender be warm in all cases during TX_FINL
Previously we were writing account/CODE_FRAGMENT_INDEX only if requiresRomLex. This is wrong and breaks account consistency constraints.
fixes issue where SELFDESTRUCT's would have old and new AccountSnapshot's be empty on the account wiping account-row.
fixes various acp_ related bugs
26c3912 addresses Bug in |
The point being: our MemoryRange object now should always remember the relevant context number. MemoryRange.EMPTY, however, doesn't remember it. This is problematic when encountering a context row after the context row which updated the return data after an exception.
@@ -733,8 +733,6 @@ public void tracePostExecution(MessageFrame frame, Operation.OperationResult ope | |||
if (isExceptional()) { | |||
this.currentTraceSection() | |||
.exceptionalContextFragment(ContextFragment.executionProvidesEmptyReturnData(this)); | |||
this.squashCurrentFrameOutputData(); | |||
this.squashParentFrameReturnData(); | |||
} |
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.
squashParentFrameReturnData
replaces the value of MemorySpan returnData
provided to the parent frame in exceptionalContextFragment
, which is
MemorySpan(currentFrame.contextnumber())
which has a nonzero context number, with
MemorySpan.EMPTY
which has a context number of 0
. This was a mistake dating back to the time when we had both a returnDataContextNumber
and a returnDataSpan
rather than the current system of having both united in a MemorySpan
object.
@@ -1100,7 +1098,7 @@ public void squashCurrentFrameOutputData() { | |||
} | |||
|
|||
public void squashParentFrameReturnData() { | |||
callStack.parentCallFrame().outputDataRange(MemoryRange.EMPTY); | |||
callStack.parentCallFrame().returnDataRange(MemoryRange.EMPTY); |
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.
Typo
newState.address(), newState.deploymentNumber(), newState.deploymentStatus()); | ||
} catch (RuntimeException e) { | ||
codeFragmentIndex = 0; | ||
} |
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.
CFI's are required on all account-rows, but they are looked up only on those where requiresRomlex = true
.
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 still don't understand the need for this change : in case requiresRomlex
is false, the previous method gives 0 and shouldn't raise an exception
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.
in case requiresRomlex is false, the previous method gives 0 and shouldn't raise an exception
I may have mixed things up then. I vaguely remember getting an exception at some point, but I may be misremembering.
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.
The reason, as discussed earlier, seems to be that when we request the CFI of an account with either empty code or whose byte code does not get loaded into the ROM it may throw an exception, which is what I remember. I may change it to
if (cfiRequired) {
// old way
} else {
// above way with error handling
}
to make sure we don't have "required CFI requests" that don't find a match in the ROM_LEX map.
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 now check that if cfiRequired = true
that getting the CFI doesn't throw a run time exception.
...ion/src/main/java/net/consensys/linea/zktracer/module/hub/section/TxFinalizationSection.java
Outdated
Show resolved
Hide resolved
@@ -123,7 +129,7 @@ private void setSnapshots(Hub hub, WorldView world) { | |||
senderGasRefundNew = | |||
senderIsCoinbase(hub) | |||
? coinbaseGasRefund.deepCopy() | |||
: AccountSnapshot.canonical(hub, world, senderAddress); | |||
: AccountSnapshot.canonical(hub, world, senderAddress).turnOnWarmth(); |
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.
The comment on coinbase address warmth at transaction end, in particular in case of address collisions, explains why we do this.
calleeThird = calleeSecondNew.deepCopy().setDeploymentNumber(hub); | ||
calleeThirdNew = calleeFirst.deepCopy().setDeploymentNumber(hub); | ||
} | ||
|
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.
See Consensys/linea-specification#83 for an explanation.
@@ -203,6 +203,7 @@ public void rememberGasNextBeforePausing(Hub hub) { | |||
this.parentId = parentId; | |||
this.callDataRange = callDataRange; | |||
this.returnAtRange = returnAtRange; | |||
this.outputDataRange = new MemoryRange(contextNumber); |
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.
The outputDataRange
, if not overwritten nontrivially at traceContextExit
, should at least remember its own context number, as that will always be correct and provides its parent frame at least with the correct returnDataContextNumber
by means of returnDataRange.contextNumber()
.
/** Get to fucking work GitHub */ | ||
public void squashReturnData() { | ||
returnDataRange(MemoryRange.EMPTY); | ||
} |
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.
A relic of past frustration when GitHub runners were down for a few days 😅
Bytes val1 = getStack(frame, 0); | ||
Bytes val2 = getStack(frame, depth); | ||
Bytes topValue = getStack(frame, 0); | ||
Bytes botValue = getStack(frame, depth); |
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.
Simple mix up. Was invisible before stack consistency constraints.
destructedAccountsSnapshot.add( | ||
AccountSnapshot.fromAddress( | ||
address, true, hub.deploymentNumberOf(address), hub.deploymentStatusOf(address))); | ||
destructedAccountsSnapshot.add(AccountSnapshot.canonical(hub, 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.
Using canonical
gives us access to the correct code, nonce, etc ...
Bytecode.EMPTY, | ||
deploymentInfo.deploymentNumber(address), | ||
deploymentStatus); | ||
this.nonce(0).balance(Wei.ZERO).code(Bytecode.EMPTY).setDeploymentInfo(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.
The wipe
method must be invoked after the SELFDESTRUCT
'ing account's deployment information underwent a freshDeployment
.
defers.resolveAtEndTransaction(this, world, tx, isSuccessful); | ||
defers.resolveAfterTransactionFinalization(this, world); |
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.
The only items that are in the resolveAfterTransactionFinalization
defer registry are the "account wiping rows" of successful SELFDESTRUCT
's.
coinbaseWarmthAtTransactionEnd = | ||
isExceptional() || opCode() == REVERT | ||
? txStack.current().coinbaseWarmthAfterTxInit(this) | ||
: frame.isAddressWarm(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.
If the root context reverts Besu's frame will revert
and reset account warmths. This will clash with conventions where e.g. the sender is warm at transaction end in the ZKEVM, regardless of circumstances. This isn't an issue for the sender but it may be an issue for the coinbase in case the coinbase is either the sender or the recipient.
defer.resolveAtEndTransaction(hub, world, tx, isSuccessful); | ||
} | ||
postTransactionDefers.clear(); | ||
endTransactionDefers.clear(); | ||
} |
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.
Nothing happens here, it's just the renaming of postTransactionDefers
to endTransactionDefers
.
for (AfterTransactionFinalizationDefer defer : afterTransactionFinalizationDefers) { | ||
defer.resolveAfterTransactionFinalization(hub, worldView); | ||
} | ||
afterTransactionFinalizationDefers.clear(); |
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.
Wiping this registry is important 🙂.
.pAccountDeploymentStatus(oldState.deploymentStatus()) | ||
.pAccountDeploymentNumberNew(newState.deploymentNumber()) | ||
.pAccountDeploymentStatusNew(newState.deploymentStatus()) | ||
.pAccountDeploymentNumberInfty(deploymentNumberInfinity) |
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.
Just reordering.
|
.deploymentStatus(true) | ||
.code(recipientValueReceptionNew.code()); | ||
} | ||
|
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 deployment transactions get converted to either
- deployed byte code
- no deployed byte code
and this requires a transition from the state of the account where there is initialization code (recipientValueReceptionNew.code()
) to one where there isn't. We thus grab the initialization code to fill the "pre-state" with it.
@@ -112,7 +112,7 @@ public void resolveAtEndTransaction( | |||
// may have to be modified in case of address collision | |||
senderNew = canonical(hub, world, sender.address(), isPrecompile(sender.address())); | |||
recipientNew = canonical(hub, world, recipient.address(), isPrecompile(recipient.address())); | |||
coinbaseNew = canonical(hub, world, coinbase.address(), isPrecompile(recipient.address())); | |||
coinbaseNew = canonical(hub, world, coinbase.address(), isPrecompile(coinbase.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.
typo
calleeSecond = reEntryCalleeSnapshot.deepCopy().setDeploymentInfo(hub); | ||
calleeSecondNew = calleeFirst.deepCopy().setDeploymentInfo(hub); | ||
calleeSecond = reEntryCalleeSnapshot.deepCopy().setDeploymentNumber(hub); | ||
calleeSecondNew = calleeFirst.deepCopy().setDeploymentNumber(hub); |
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.
The deployment number may have evolved, but it is the deployment status we are walking back.
calleeSecond = calleeFirstNew.deepCopy().setDeploymentInfo(hub); | ||
calleeSecondNew = calleeFirst.deepCopy().setDeploymentInfo(hub); | ||
calleeSecond = calleeFirstNew.deepCopy().setDeploymentNumber(hub); | ||
calleeSecondNew = calleeFirst.deepCopy().setDeploymentNumber(hub); |
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.
The deployment number may have evolved, but it is the deployment status we are walking back.
|
||
this.addFragment(finalUnexceptionalContextFragment); | ||
accountWipingNew.wipe(hub.transients().conflation().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.
This is the raison d'être of the AfterTransactionFinalization
defer registry. It is so that we don't raise the deployment number of SELFDESTRUCT
'ing accounts prior to executing the TX_FINL
phase. This messes things up in case the COINBASE
is among the SELFDESTRUCT
'ing accounts.
@@ -126,7 +125,7 @@ public void commit(List<MappedByteBuffer> buffers) { | |||
traceStackSettings(op, trace); | |||
traceBillingSettings(op, trace); | |||
trace | |||
.opcode(Bytes.ofUnsignedInt(i)) | |||
.opcode(UnsignedByte.of(i)) |
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.
@DavePearce I've had to correct this a couple of times here and there. Just letting you know, not sure if it's a corset change that's at the source of this change in signature.
destructedAccountsSnapshot.add( | ||
AccountSnapshot.fromAddress( | ||
address, true, hub.deploymentNumberOf(address), hub.deploymentStatusOf(address))); | ||
destructedAccountsSnapshot.add(AccountSnapshot.canonical(hub, world, 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.
This was a very stupid oversight / temporary solution to a problem that only became noticeable with account consistency on. The fromAddress
assigns empty code, zero balance, zero nonce etc. to the AccountSnapshot
it is creating. Unsurprisingly this broke account consistency constraints.
|| coinbaseIsPrecompile | ||
|| coinbaseIsSender | ||
|| coinbaseIsRecipient; | ||
} |
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 method logs whether the COINBASE
address will be warm at the end of TX_INIT
. This is required in case of a REVERT
'ing transaction with an address collision. It will be made obsolete by EIP-3651.
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.
accountWipingNew.wipe(hub.transients().conflation().deploymentInfo());
returns a new AccountSnapshot
that is not used.
As discussed on slack, this is not the case. |
No description provided.