You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TraceTransaction currently executes every transaction in a finalized block on a cache miss, even when the requested transaction appears near the beginning.
Workflow
This PR caches successful tracing progress from the beginning of each block (a prefix). Later requests reconstruct the state at the cached point and execute only the remaining transactions through the requested target (the missing suffix). Only one request extends a block at a time, while different blocks can be traced concurrently.
lookup(block, target)
├─ target cached → return, even if an extension is active
├─ extension active
│ ├─ completed → retry lookup
│ └─ caller cancelled → return cancellation error
└─ target missing → acquire lease, reconstruct state, execute missing suffix
├─ success → combine prefix + suffix, publish, wake waiters
└─ failure or panic → preserve record, release lease, wake waiters
❌ Patch coverage is 83.33333% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.46%. Comparing base (5132f3f) to head (b5bbbac).
traceBlockTransactionWithVM unconditionally calls plan.ResumeState guarded by if plan != nil, but later transactions = transactions[plan.Start:plan.End] and tracecache.OffsetExecutionError(err, plan.Start) and traceTransactionsWithState(..., plan.Start) are called without checking plan != nil first for the pending-block path where plan remains nil (isPending branch skips plan assignment). This will panic with a nil pointer dereference when tracing a pending block, since plan is only set if !isPending.
func (h*Handler) traceBlockTransactionWithVM(block*core.Block, plan*tracecache.Range) (
*tracecache.BlockTrace, http.Header, *jsonrpc.Error,
) {
httpHeader:=defaultExecutionHeader()
transactions:=block.Transactionsifplan!=nil {
transactions=transactions[plan.Start:plan.End]
}
state, closer, err:=h.bcReader.StateAtBlockHash(block.ParentHash)
iferr!=nil {
returnnil, httpHeader, rpccore.ErrBlockNotFound
}
deferh.callAndLogErr(closer, "Failed to close state in traceBlockTransactions")
var (
headState core.StateReaderheadStateCloser blockchain.StateCloser
)
headState, headStateCloser, err=h.bcReader.HeadState()
iferr!=nil {
returnnil, httpHeader, jsonrpc.Err(jsonrpc.InternalError, err.Error())
}
deferh.callAndLogErr(headStateCloser, "Failed to close head state in traceBlockTransactions")
ifplan!=nil {
state, err=plan.ResumeState(state, headState, block.Number)
iferr!=nil {
returnnil, httpHeader, jsonrpc.Err(jsonrpc.InternalError, err.Error())
}
}
varclasses []core.ClassDefinitionpaidFeesOnL1:= []*felt.Felt{}
for_, transaction:=rangetransactions {
switchtx:=transaction.(type) {
case*core.DeclareTransaction:
class, stateErr:=headState.Class(tx.ClassHash)
ifstateErr!=nil {
returnnil, httpHeader, jsonrpc.Err(jsonrpc.InternalError, stateErr.Error())
}
classes=append(classes, class.Class)
case*core.L1HandlerTransaction:
// TODO (granza): use real L1 message fee.paidFeesOnL1=append(paidFeesOnL1, &felt.One)
}
}
blockHashToBeRevealed, err:=h.getRevealedBlockHash(block.Number)
iferr!=nil {
returnnil, httpHeader, rpccore.ErrInternal.CloneWithData(err)
}
header:=block.HeaderblockInfo:= vm.BlockInfo{
Header: header,
BlockHashToBeRevealed: blockHashToBeRevealed,
}
executionResult, err:=h.vm.Trace(transactions, classes, paidFeesOnL1,
&blockInfo, state, vm.TraceOptions{})
ifplan!=nil {
err=tracecache.OffsetExecutionError(err, plan.Start)
}
In traceFinalisedBlock, plan is only computed inside the else branch (non-feeder path). When fetchFromFeederGW is true, plan stays nil, but the code after the if/else calls plan.Combine(traces) only inside the else block, so that part is fine, but traceBlockWithVM unconditionally dereferences plan.Start/plan.End — this function is only called from the else branch so it should be fine. However, verify that plan cannot be nil in traceBlockWithVM since it directly does transactions[plan.Start:plan.End] without a nil check, differing from v8/v10 pending-block handling; if any future caller passes nil plan this will panic. Confirm all call sites always supply a non-nil plan.
// traceBlockWithVM traces a block using the local VM.func (h*Handler) traceBlockWithVM(
header*core.Header,
transactions []core.Transaction,
plan*tracecache.Range,
) (*tracecache.BlockTrace, http.Header, *jsonrpc.Error) {
transactions=transactions[plan.Start:plan.End]
traceBlockWithVM in rpc/v10 does transactions[plan.Start:plan.End] without checking plan for nil, unlike v8's pending-block path which passes a nil plan. If traceBlockWithVM is ever invoked with a nil plan (e.g. future refactor or missed initialization), this will panic. Confirm plan is always non-nil for all call sites in this file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
User description
Summary
TraceTransaction currently executes every transaction in a finalized block on a cache miss, even when the requested transaction appears near the beginning.
Workflow
This PR caches successful tracing progress from the beginning of each block (a prefix). Later requests reconstruct the state at the cached point and execute only the remaining transactions through the requested target (the missing suffix). Only one request extends a block at a time, while different blocks can be traced concurrently.
lookup(block, target)
├─ target cached → return, even if an extension is active
├─ extension active
│ ├─ completed → retry lookup
│ └─ caller cancelled → return cancellation error
└─ target missing → acquire lease, reconstruct state, execute missing suffix
├─ success → combine prefix + suffix, publish, wake waiters
└─ failure or panic → preserve record, release lease, wake waiters
Benchmarks
Cold
starknet_traceTransactionat 100 VUsBroad corpus sample size: 1,000
Default size: 200
Interleaved
traceTransaction->traceBlockTransactionsSample size: 200
PR Type
Enhancement, Tests
Description
Introduce progressive trace caching in RPC to reuse successful tracing prefixes and execute only the missing transaction suffixes.
Implement state resumption and execution error offsetting for partial block tracing.
Integrate targeted subset trace resolutions in RPC versions 8, 9, and 10 trace handlers.
Add comprehensive test suites for caching behaviors, wait cancellations, and error offsets.
File Walkthrough
shared_trace_test.go
Update shared trace tests and add progressive tracing checksrpc/shared_trace_test.go
extensions and initial reads.
TestProgressiveTraceEachVersionExtendsto test progressive tracingbehaviors across RPC versions 8, 9, and 10.
progressive_trace_test.go
Add test suite for progressive trace caching in v10rpc/v10/progressive_trace_test.go
error offset retentions.
reads replay interactions.
trace_test.go
Fix mock trace initialization in v10 trace testsrpc/v10/trace_test.go
StateDiffto avoid nil pointer issuesduring tests.
trace.go
Integrate progressive trace caching in v10 handlersrpc/v10/trace.go
traceTransactionsWithStateto offset VM execution errors usingerrorIndexOffset.traceFinalisedBlockto accept atargettransaction andconditionally skip previously cached trace prefixes.
tracecache.PlanRangeto combine pre-cached trace prefixeswith new execution results.
trace.go
Integrate progressive trace caching in v8 handlersrpc/v8/trace.go
traceBlockTransactionsto leverage progressive trace cache andtarget-specific resolutions.
tracecache.Rangedetails anderrorIndexOffsetto supporttargeted state resumptions and combined execution outputs.
trace.go
Integrate progressive trace caching in v9 handlersrpc/v9/trace.go
traceFinalisedBlockto match partial traces against specifictargetqueries.plan.ResumeStatefor targeted state recovery and combine cachedoutcomes with VM suffix runs.
errorIndexOffsetacross trace execution paths to properly adjustpotential transaction errors.