-
Notifications
You must be signed in to change notification settings - Fork 200
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
Bug: ExecutionResources specs mismatch (v7 and v8) #2497
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2497 +/- ##
==========================================
+ Coverage 73.49% 73.53% +0.04%
==========================================
Files 136 136
Lines 16657 16650 -7
==========================================
+ Hits 12242 12244 +2
+ Misses 3547 3540 -7
+ Partials 868 866 -2 ☔ View full report in Codecov by Sentry. |
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 actually have to have
L1Gas: l1Gas,
L1DataGas: l1DataGas,
L2Gas: l2Gas,
in v8
and ComputationResources
in v7
. I described this issue in #2430
|
||
ComputationResources | ||
*ComputationResources |
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 should be this one be a pointer?
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.
Because, in v8, we don't want to have ComputationResources
fields inside ExecutionResources
. So, i needed to set it to its zero-value so that it doesn't get marshalled after, which means:
1- It creates a whole ComputationResources
instance (14x uint64) just to finally get ignored when marshalled. So, I thought it was not the most efficient.
2- ComputationResources
contains the field Step
that doesn't have omitempty
. So, without the pointer to the whole struct, it would still end up getting marshalled. I could've added omitempty
on the Step
field but as I thought about the efficiency issue that I mentioned in 1-, I thought might be better putting the ComputationResources
as a pointer.
L1Gas *uint64 `json:"l1_gas,omitempty"` | ||
L1DataGas *uint64 `json:"l1_data_gas,omitempty"` | ||
L2Gas *uint64 `json:"l2_gas,omitempty"` |
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.
Are all of this optional?
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.
Mmh the thing is we reuse the TransactionTrace
from the VM pkg for all rpc versions. So, we don't want those fields for v6 and v7, but we want them for v8. That's why, i had to put them as optional, and make sure they are set in v8
rpc/v8/trace.go
Outdated
if fnInvocation.ExecutionResources != nil { | ||
// Still return them if they are nil | ||
if fnInvocation.ExecutionResources.L1Gas == nil { | ||
fnInvocation.ExecutionResources.L1Gas = utils.Ptr(uint64(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.
Use new
to create an object and get a reference. Eg:
x := new(uint64)
for len(stack) > 0 { | ||
// Pop a FunctionInvocation from the stack | ||
n := len(stack) - 1 | ||
fnInvocation := stack[n] | ||
stack = stack[:n] |
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.
Re-slicing can be very expensive. I think in this case is better just to move the pointer to the left
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 i thought about that but its slicing to remove the last element on the slice, so it should be O(1)
rpc/v8/trace.go
Outdated
pushFnInvocationIfNotNil := func(fn *vm.FunctionInvocation) { | ||
if fn != nil { | ||
stack = append(stack, fn) | ||
} | ||
} | ||
|
||
pushFnInvocationIfNotNil(trace.FeeTransferInvocation) | ||
pushFnInvocationIfNotNil(trace.ValidateInvocation) |
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 think adding a function to avoid writing an if
three times might be a bit of an over stretch. I think it might be a bit more readable if just remove the function definition and write the ifs.
Also, I believe it is not performant friendly and there is little optimizations that compiler can due to the existence of a closure in the definition
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.
Oh okay good to know!
Done!
// Clean trace's inner execution resources to return only the expected fields | ||
func cleanTraceInnerExecutionResources(trace *vm.TransactionTrace) { | ||
// Stack to process FunctionInvocations iteratively (dfs) | ||
stack := []*vm.FunctionInvocation{} |
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 know this variable is going to have at maximum size 3. Create the array with default capacity of 3 (using make
).
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.
Actually, it might have more than 3 because when doing dfs, i push the inner/nested FunctionInvocation
(to clean the nested ExecutionResources)
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 was reading the comment on the PR and this PR is affecting the very first thing we wanted to avoid. We are changing structs across diferent packages to match different specs (or am I missing something).
It's ok to have a vX and a vY structs that are 99% equal duplicated. The whole goal of the refactor was to make it simple to support at the cost of duplicated code.
Indeed, the main issue seems to be that the To solve that bug/issue, we could duplicate the |
Superceded by #2563 |
No refactor after all but bug solving.
DRAFT: I realized I also need to update
simulateTransaction
whereExecutionResources
is also used.Created another PR with a different approach as agreed.
Context
Problem
In this PR, instead of refactoring, I actually corrected the returned trace according to the specs, more specifically, the
ExecutionResources
struct in a trace:ExecutionResources
at the trace root level should contain:ComputationResources + DataAvailability
--> no matter the type of tx, the TRANSACTION_TRACE'sExecutionResources
field always lead to ExecutionResourcesExecutionResources
in trace's inner calls should contain: ComputationResourcesExecutionResources
at the trace root level should contain:L1Gas, L1DataGas, L2Gas
--> no matter the type of tx, the TRANSACTION_TRACE'sExecutionResources
field always lead to ExecutionResourcesExecutionResources
in trace's inner calls should contain: L1Gas, L2GasSolution
To adjust the
ExecutionResources
field correctly, instead of re-creating anExecutionResources
type for trace root-level as well as for inner calls for v7 and v8 (which would also force me to re-create the wholeTransactionTrace
type just to refer to the custom v7 and v8ExecutionResources
inner and root-level types) and then marshal for specific pkgs, I kept the originalvm.TransactionTrace
, transform some fields into pointers + addedomitempty
on those fields. To make it work, I added some cleaning functions for traces executed and retrieved by the VM to clean theExecutionResources
.Btw for those cleaning fcts, I used an iterative approach because even though recursion gives the same time & space complexities and looks a bit easier to understand imo, the iterative approach allows to avoid any stack overflow (even though probably the inner calls of traces will not go deep enough as to create a stack overflow, but just in case). That being said, appending to my custom slice
stack
might add a bit of overhead for memory allocation compared to recursion. A good compromise would have been using tail recursion but it seems like Go's compiler doesn't do tail call opti unfortunately :(