-
Notifications
You must be signed in to change notification settings - Fork 20.1k
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
core/tracing: v1.1 #30441
base: master
Are you sure you want to change the base?
core/tracing: v1.1 #30441
Conversation
NonceReadHook = func(addr common.Address, nonce uint64) | ||
|
||
// CodeReadHook is called when EVM reads the code of an account. | ||
CodeReadHook = func(addr common.Address, code []byte) |
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.
Open question: should we add codeHash here to be consistent with OnCodeChange
?
Ah seems like the journal has a crasher:
|
|
||
### New methods | ||
|
||
- `OnReorg(reverted []*types.Block)`: This hook is called when a reorg is detected. The `reverted` slice contains the blocks that are no longer part of the canonical chain. |
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.
Here types block is very very heavy. You should at most pass headers and allow chain access to pull the blocks on demand (chain access in someconstructor, ha)
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.
On second thought what is the issue? it is a slice so passed by reference and the memory can be freed as soon as OnReorg
processing is done.
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.
Ugh, this is annoying. So reorg in the blockchain at some point in the past used to collect blocks. Turned out that sometimes it became insanely heavy and we've switched so it operates on headers. I guess later someone refactored it back to operate on blocks again. This is an issue when you do setHead or any similar operation; of even if finality fails for a while and you have blocks reorging back and forth. It's very very bad to pull all the block in from disk IMO.
CC @holiman @rjl493456442 ?
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 agree. I don't particularly recall switching from headers to blocks....
@@ -194,6 +221,30 @@ type Hooks struct { | |||
OnCodeChange CodeChangeHook | |||
OnStorageChange StorageChangeHook | |||
OnLog LogHook | |||
// State reads | |||
OnBalanceRead BalanceReadHook | |||
OnNonceRead NonceReadHook |
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.
Question from triage: how exactly is OnNonceRead used?
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 don't see any need for these: OnBalanceRead
etc. It adds non-generic handlers for certain opcodes, but a more generic solution already exists, using the per-opcode step function.
Here's the old prestate tracer js:
// step is invoked for every opcode that the VM executes.
step: function(log, db) {
// Add the current account if we just started tracing
if (this.prestate === null){
this.prestate = {};
// Balance will potentially be wrong here, since this will include the value
// sent along with the message. We fix that in 'result()'.
this.lookupAccount(log.contract.getAddress(), db);
}
// Whenever new state is accessed, add it to the prestate
switch (log.op.toString()) {
case "EXTCODECOPY": case "EXTCODESIZE": case "EXTCODEHASH": case "BALANCE":
this.lookupAccount(toAddress(log.stack.peek(0).toString(16)), db);
break;
case "CREATE":
var from = log.contract.getAddress();
this.lookupAccount(toContract(from, db.getNonce(from)), db);
The existing way to it is arguably slower, since it's on the hot-path and invoked on every opcode. We could mitigate that, if e.g. tracers declare a whitelist of ops that they are interested in (e.g. optionally expose a method which spits out a list).
The existing way is perhaps a bit clunky, in that it's up to the tracer to make sense of the stack arguments, but otoh the stack arguments are not something that is changed frequently, since it's consensus-critical, and can only be changed in hardforks.
It's also a bit clunky to see the poststate: for op X
, you see the stack prior to the execution of X
. In order to see the stack after , you need to check on the next op too. Which might be difficult, especially if we have whitelisted X
only -- but we could improve this too, e.g. by using a returnvalue saying hey I want to be notified about the next op too
.
All in all, I think we should iterate on the existing generic solution, and not litter the code with these hooks.
@@ -134,6 +136,9 @@ type ( | |||
// GenesisBlockHook is called when the genesis block is being processed. | |||
GenesisBlockHook = func(genesis *types.Block, alloc types.GenesisAlloc) | |||
|
|||
// ReorgHook is called when a segment of the chain is reverted. | |||
ReorgHook = func(reverted []*types.Block) |
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 consensus was to drop Reorg hook for now because it's not clear what the best API would be. We can shit the rest and then iterate on this one with whoever wants to use it before comitting.
Hi @holiman, We're really excited about the live tracing feature and see immense value in it, especially for our specific use case. Currently, we fetch blocks from nodes (clients) in a polling fashion and re-execute them using a customized EVM that performs more detailed tracing. By utilizing live tracing directly on the node (client), we can significantly boost both performance and correctness, and it would allow us to completely remove the re-execution and re-processing logic from our pipeline. This is why having more explicit hooks, like OnBalanceRead and others, is crucial for us. These hooks would allow us to optimize our tracing workflow, making it more efficient and accurate, which is why we strongly favor this approach and would love to keep it in place. Here are some of the key benefits we see in favor of keeping the more explicit hooks: Accurate State Tracking Separation of Concerns State Consistency Performance Optimization Future-Proofing: Transition from Full Archive to Full Node Dalibor from Tenderly crew! |
For For |
Note, if we can get something like this to work, then I'm a lot more open to having all sorts of hooks: #30569
I don't like the deep integration, but if it's possible via a separate layer then "let's go wild" imo
|
@fjl regarding the backwards-compatibility I have for now added a |
I dropped OnReorg and merged in changes from master. |
Implements #30356