Skip to content

Commit

Permalink
feat: check counters overflow on eth_estimateGas (#1795)
Browse files Browse the repository at this point in the history
  • Loading branch information
IvanBelyakoff authored Feb 26, 2025
1 parent b9306f9 commit c66ff5b
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 3 deletions.
29 changes: 29 additions & 0 deletions core/vm/zk_counters.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,35 @@ func (c Counters) UsedAsMap() map[string]int {
}
}

func (c Counters) OverflownAsString() string {
var res string
if c[SHA].remaining < 0 {
res += fmt.Sprintf("[%s: %v]", CounterKeyNames[SHA], c[SHA].remaining)
}
if c[A].remaining < 0 {
res += fmt.Sprintf("[%s: %v]", CounterKeyNames[A], c[A].remaining)
}
if c[B].remaining < 0 {
res += fmt.Sprintf("[%s: %v]", CounterKeyNames[B], c[B].remaining)
}
if c[K].remaining < 0 {
res += fmt.Sprintf("[%s: %v]", CounterKeyNames[K], c[K].remaining)
}
if c[M].remaining < 0 {
res += fmt.Sprintf("[%s: %v]", CounterKeyNames[M], c[M].remaining)
}
if c[P].remaining < 0 {
res += fmt.Sprintf("[%s: %v]", CounterKeyNames[P], c[P].remaining)
}
if c[S].remaining < 0 {
res += fmt.Sprintf("[%s: %v]", CounterKeyNames[S], c[S].remaining)
}
if c[D].remaining < 0 {
res += fmt.Sprintf("[%s: %v]", CounterKeyNames[D], c[D].remaining)
}
return res
}

func (c *Counters) GetArithmetics() *Counter {
return (*c)[A]
}
Expand Down
11 changes: 10 additions & 1 deletion turbo/jsonrpc/eth_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,12 @@ func (api *APIImpl) EstimateGas(ctx context.Context, argsOrNil *ethapi2.CallArgs
}
header := block.HeaderNoCopy()

caller, err := transactions.NewReusableCaller(engine, stateReader, nil, header, args, api.GasCap, latestNumOrHash, dbtx, api._blockReader, chainConfig, api.evmCallTimeout, api.VirtualCountersSmtReduction, false)
caller, err := transactions.NewReusableCaller(engine, stateReader, nil, header, args, api.GasCap, latestNumOrHash, dbtx, api._blockReader, chainConfig, api.evmCallTimeout, api.VirtualCountersSmtReduction, true)
if err != nil {
return 0, err
}

countersChecked := false
// Create a helper to check if a gas allowance results in an executable transaction
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
result, err := caller.DoCallWithNewGas(ctx, gas)
Expand All @@ -267,6 +268,14 @@ func (api *APIImpl) EstimateGas(ctx context.Context, argsOrNil *ethapi2.CallArgs
// Bail out
return true, nil, err
}

if !countersChecked {
if overflow, err := caller.CheckCountersOverflow(result); overflow {
return true, nil, err
}
countersChecked = true
}

return result.Failed(), result, nil
}

Expand Down
11 changes: 10 additions & 1 deletion turbo/jsonrpc/zkevm_counters.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,22 @@ func (zkapi *ZkEvmAPIImpl) EstimateCounters(ctx context.Context, rpcTx *zkevmRPC
return nil, err
}

if err = txCounters.ProcessTx(ibs, execResult.ReturnData); err != nil {
return nil, err
}

batchCounters.UpdateExecutionAndProcessingCountersCache(txCounters)

collected, err := batchCounters.CombineCollectors(l1InfoIndex != 0)
overflow, err := batchCounters.CheckForOverflow(l1InfoIndex != 0)
if err != nil {
return nil, err
}

collected := batchCounters.CombineCollectorsNoChanges()
if oocError == nil && overflow {
oocError = fmt.Errorf("%s", collected.OverflownAsString())
}

res, err := populateCounters(&collected, execResult, tx.GetGas(), oocError)
if err != nil {
return nil, err
Expand Down
33 changes: 32 additions & 1 deletion turbo/transactions/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ type ReusableCaller struct {
callTimeout time.Duration
message *types.Message
batchCounters *vm.BatchCounterCollector
txCounters *vm.TransactionCounter
}

func (r *ReusableCaller) DoCallWithNewGas(
Expand Down Expand Up @@ -248,10 +249,11 @@ func NewReusableCaller(
)

var batchCounters *vm.BatchCounterCollector
var txCounters *vm.TransactionCounter
var counterCollector *vm.CounterCollector
if useCounters {
batchCounters = vm.NewBatchCounterCollector(smtDepth, uint16(forkId), VirtualCountersSmtReduction, false, nil)
txCounters := vm.NewTransactionCounter(transaction, smtDepth, uint16(forkId), VirtualCountersSmtReduction, false)
txCounters = vm.NewTransactionCounter(transaction, smtDepth, uint16(forkId), VirtualCountersSmtReduction, false)

_, err = batchCounters.AddNewTransactionCounters(txCounters)
if err != nil {
Expand All @@ -273,5 +275,34 @@ func NewReusableCaller(
stateReader: stateReader,
message: &msg,
batchCounters: batchCounters,
txCounters: txCounters,
}, nil
}

func (r *ReusableCaller) CheckCountersOverflow(result *core.ExecutionResult) (bool, error) {
if r.batchCounters == nil || r.txCounters == nil {
return false, nil
}

if err := r.txCounters.ProcessTx(r.intraBlockState, result.ReturnData); err != nil {
return false, err
}

r.batchCounters.UpdateExecutionAndProcessingCountersCache(r.txCounters)

overflow, err := r.batchCounters.CheckForOverflow(false)
if err != nil {
log.Info("CheckForOverflow failed", "err", err)
return false, err
}

if overflow {
collected, err := r.batchCounters.CombineCollectors(false)
if err != nil {
return false, err
}
return true, fmt.Errorf("counters overflow: %s", collected.OverflownAsString())
}

return false, nil
}

0 comments on commit c66ff5b

Please sign in to comment.