diff --git a/core/token_gas.go b/core/token_gas.go index 88d2cb96d..8206f29e6 100644 --- a/core/token_gas.go +++ b/core/token_gas.go @@ -89,6 +89,12 @@ func GetAltTokenBalanceByEVM(evm *vm.EVM, tokenAddress, userAddress common.Addre data := append(methodID, paddedAddress...) // Create a message call context sender := vm.AccountRef(userAddress) + + if evm.Config.Tracer != nil && evm.Config.Tracer.OnSystemCallStartV2 != nil && evm.Config.Tracer.OnSystemCallEnd != nil { + evm.Config.Tracer.OnSystemCallStartV2(evm.GetVMContext()) + defer evm.Config.Tracer.OnSystemCallEnd() + } + // Execute the call (using StaticCall since we're only reading state) ret, _, err := evm.StaticCall(sender, tokenAddress, data, maxGas) if err != nil { @@ -111,7 +117,6 @@ func transferAltTokenByEVM(evm *vm.EVM, tokenAddress, from, to common.Address, a if amount == nil || amount.Sign() <= 0 { return fmt.Errorf("invalid transfer amount") } - var fromBalanceBefore *big.Int var err error if userBalanceBefore != nil { @@ -137,6 +142,12 @@ func transferAltTokenByEVM(evm *vm.EVM, tokenAddress, from, to common.Address, a data := append(methodID, append(paddedAddress, paddedAmount...)...) // Create a message call context sender := vm.AccountRef(from) + + if evm.Config.Tracer != nil && evm.Config.Tracer.OnSystemCallStartV2 != nil && evm.Config.Tracer.OnSystemCallEnd != nil { + evm.Config.Tracer.OnSystemCallStartV2(evm.GetVMContext()) + defer evm.Config.Tracer.OnSystemCallEnd() + } + // Execute the call ret, _, err := evm.Call(sender, tokenAddress, data, maxGas, big.NewInt(0)) if err != nil { diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 9c393c4b2..ec568db91 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -501,6 +501,7 @@ func (l *StructLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, l.statesAffected[*to] = struct{}{} } } + func (l *StructLogger) OnSystemCallStart(env *tracing.VMContext) { l.skip = true } diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index f67b9c4f9..d7fb3f9f4 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -119,6 +119,7 @@ type callTracer struct { depth int interrupt atomic.Bool // Atomic flag to signal execution interruption reason error // Textual reason for the interruption + skip bool } type callTracerConfig struct { @@ -135,11 +136,13 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *param } return &tracers.Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: t.OnTxStart, - OnTxEnd: t.OnTxEnd, - OnEnter: t.OnEnter, - OnExit: t.OnExit, - OnLog: t.OnLog, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnLog: t.OnLog, + OnSystemCallStartV2: t.OnSystemCall, + OnSystemCallEnd: t.OnSystemCallEnd, }, GetResult: t.GetResult, Stop: t.Stop, @@ -158,6 +161,9 @@ func newCallTracerObject(ctx *tracers.Context, cfg json.RawMessage) (*callTracer // OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *callTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + if t.skip { + return + } t.depth = depth if t.config.OnlyTopCall && depth > 0 { return @@ -185,6 +191,9 @@ func (t *callTracer) OnEnter(depth int, typ byte, from common.Address, to common // OnExit is called when EVM exits a scope, even if the scope didn't // execute any code. func (t *callTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + if t.skip { + return + } if depth == 0 { t.captureEnd(output, gasUsed, err, reverted) return @@ -218,10 +227,16 @@ func (t *callTracer) captureEnd(output []byte, gasUsed uint64, err error, revert } func (t *callTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { + if t.skip { + return + } t.gasLimit = tx.Gas() } func (t *callTracer) OnTxEnd(receipt *types.Receipt, err error) { + if t.skip { + return + } // Error happened during tx validation. if err != nil { return @@ -235,7 +250,18 @@ func (t *callTracer) OnTxEnd(receipt *types.Receipt, err error) { } } +func (t *callTracer) OnSystemCall(env *tracing.VMContext) { + t.skip = true +} + +func (t *callTracer) OnSystemCallEnd() { + t.skip = false +} + func (t *callTracer) OnLog(log *types.Log) { + if t.skip { + return + } // Only logs need to be captured via opcode processing if !t.config.WithLog { return diff --git a/rollup/tracing/mux.go b/rollup/tracing/mux.go index d91e51236..f0db9c283 100644 --- a/rollup/tracing/mux.go +++ b/rollup/tracing/mux.go @@ -22,18 +22,20 @@ func NewMuxTracer(structLogger *logger.StructLogger, subTracers ...tracers.Trace } return &tracers.Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: t.OnTxStart, - OnTxEnd: t.OnTxEnd, - OnEnter: t.OnEnter, - OnExit: t.OnExit, - OnOpcode: t.OnOpcode, - OnFault: t.OnFault, - OnGasChange: t.OnGasChange, - OnBalanceChange: t.OnBalanceChange, - OnNonceChange: t.OnNonceChange, - OnCodeChange: t.OnCodeChange, - OnStorageChange: t.OnStorageChange, - OnLog: t.OnLog, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnOpcode: t.OnOpcode, + OnFault: t.OnFault, + OnGasChange: t.OnGasChange, + OnBalanceChange: t.OnBalanceChange, + OnNonceChange: t.OnNonceChange, + OnCodeChange: t.OnCodeChange, + OnStorageChange: t.OnStorageChange, + OnLog: t.OnLog, + OnSystemCallStartV2: t.OnSystemCallStart, + OnSystemCallEnd: t.OnSystemCallEnd, }, Stop: t.Stop, } @@ -141,6 +143,24 @@ func (t *muxTracer) OnLog(log *types.Log) { } } +func (t *muxTracer) OnSystemCallStart(env *tracing.VMContext) { + for _, t := range t.subTracers { + if t.OnSystemCallStartV2 != nil { + t.OnSystemCallStartV2(env) + } + } + t.structLogger.OnSystemCallStart(env) +} + +func (t *muxTracer) OnSystemCallEnd() { + for _, t := range t.subTracers { + if t.OnSystemCallEnd != nil { + t.OnSystemCallEnd() + } + } + t.structLogger.OnSystemCallEnd() +} + // Stop terminates execution of the tracer at the first opportune moment. func (t *muxTracer) Stop(err error) { for _, t := range t.subTracers { diff --git a/rollup/tracing/tracing.go b/rollup/tracing/tracing.go index f0667cfb6..b598be639 100644 --- a/rollup/tracing/tracing.go +++ b/rollup/tracing/tracing.go @@ -375,6 +375,34 @@ func (env *TraceEnv) getTxResult(statedb *state.StateDB, index int, block *types env.Codes[codeHash] = codeInfo } } + + // For AltFeeTx, manually collect token contract bytecode + // since direct storage slot operations don't trigger EVM execution + if tx.Type() == types.AltFeeTxType && tx.FeeTokenID() != 0 { + tokenInfo, err := fees.GetTokenInfo(statedb, tx.FeeTokenID()) + if err == nil && tokenInfo.TokenAddress != (common.Address{}) { + + collectBytecode := func(addr common.Address) { + code := statedb.GetCode(addr) + keccakCodeHash := statedb.GetKeccakCodeHash(addr) + poseidonCodeHash := statedb.GetPoseidonCodeHash(addr) + codeSize := statedb.GetCodeSize(addr) + + if poseidonCodeHash != (common.Hash{}) { + if _, exists := env.Codes[poseidonCodeHash]; !exists { + env.Codes[poseidonCodeHash] = logger.CodeInfo{ + CodeSize: codeSize, + KeccakCodeHash: keccakCodeHash, + PoseidonCodeHash: poseidonCodeHash, + Code: code, + } + } + } + } + collectBytecode(tokenInfo.TokenAddress) + collectBytecode(rcfg.L2TokenRegistryAddress) + } + } env.cMu.Unlock() // merge required proof data