From 715c9215d1aa15e9bbb6d9fa8e2f1869071d88e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Do=C5=A1li=C4=87?= Date: Fri, 6 Feb 2026 09:51:16 +0100 Subject: [PATCH 01/20] Add VM trace support for StateSyncTx tracing - Introduced `vm.Config` to multiple functions for customizable VM tracing. - Added tracer configuration flags (`vmtrace` and `vmtrace.jsonconfig`) to streamline monitoring during blockchain synchronization. - Updated GenesisContract and spanner interfaces for trace integration. - Integrated tracing hooks for StateSyncTx events. --- cmd/cli/main.go | 5 ++ consensus/bor/bor.go | 64 ++++++++++++++++++++++---- consensus/bor/bor_test.go | 2 +- consensus/bor/contract/client.go | 3 +- consensus/bor/genesis.go | 2 +- consensus/bor/heimdall/span/spanner.go | 4 +- consensus/bor/span.go | 2 +- consensus/bor/statefull/processor.go | 7 +-- docs/cli/example_config.toml | 12 +++-- docs/cli/server.md | 4 ++ eth/tracers/api.go | 2 +- internal/cli/server/config.go | 10 ++++ internal/cli/server/flags.go | 14 ++++++ 13 files changed, 106 insertions(+), 25 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 5863276d0b..aa47c53bfc 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -5,6 +5,11 @@ import ( "github.com/ethereum/go-ethereum/internal/cli" "github.com/ethereum/go-ethereum/params" + + // Force-load the tracer engines to trigger registration + _ "github.com/ethereum/go-ethereum/eth/tracers/js" + _ "github.com/ethereum/go-ethereum/eth/tracers/live" + _ "github.com/ethereum/go-ethereum/eth/tracers/native" ) func main() { diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index 9e0fc21560..3604cdec39 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -1089,14 +1089,40 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, var ( stateSyncData []*types.StateSyncData err error + vmCfg vm.Config ) + if bc, ok := chain.(*core.BlockChain); ok { + vmCfg = *bc.GetVMConfig() + } + if IsSprintStart(headerNumber, c.config.CalculateSprint(headerNumber)) { start := time.Now() cx := statefull.ChainContext{Chain: chain, Bor: c} + + // Start tracing StateSyncTx (if present in the block body) + var stateSyncTx *types.Transaction + if c.config.IsMadhugiri(header.Number) && len(body.Transactions) > 0 { + lastTx := body.Transactions[len(body.Transactions)-1] + if lastTx.Type() == types.StateSyncTxType { + stateSyncTx = lastTx + hasTracer := vmCfg.Tracer != nil + log.Info("[DEBUG] StateSyncTx found in Finalize", "block", headerNumber, "txHash", lastTx.Hash(), "hasTracer", hasTracer) + if hooks := vmCfg.Tracer; hooks != nil && hooks.OnTxStart != nil { + // todo(milando12): check how much overhead this adds, if it does we can initialize it earlier and pass down to ApplyMessage() + vmenv := vm.NewEVM( + core.NewEVMBlockContext(header, cx, &header.Coinbase), + wrappedState, c.chainConfig, vmCfg, + ) + log.Info("[DEBUG] StateSyncTx OnTxStart", "block", headerNumber, "txHash", stateSyncTx.Hash()) + hooks.OnTxStart(vmenv.GetVMContext(), stateSyncTx, statefull.SystemAddress) + } + } + } + // check and commit span if !c.config.IsRio(header.Number) { - if err := c.checkAndCommitSpan(wrappedState, header, cx); err != nil { + if err := c.checkAndCommitSpan(wrappedState, header, cx, vmCfg); err != nil { log.Error("Error while committing span", "error", err) return nil } @@ -1104,11 +1130,14 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, if c.HeimdallClient != nil { // commit states - stateSyncData, err = c.CommitStates(wrappedState, header, cx) + stateSyncData, err = c.CommitStates(wrappedState, header, cx, vmCfg) if err != nil { log.Error("Error while committing states", "error", err) return nil } + if len(stateSyncData) > 0 { + log.Info("[DEBUG] StateSyncTx CommitStates completed", "block", headerNumber, "eventCount", len(stateSyncData)) + } } // Get the underlying state for updating consensus time state := wrappedState.Inner() @@ -1135,7 +1164,7 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, return receipts } if lastTx.Type() == types.StateSyncTxType { - receipts = insertStateSyncTransactionAndCalculateReceipt(lastTx, header, body, wrappedState, receipts) + receipts = insertStateSyncTransactionAndCalculateReceipt(lastTx, header, body, wrappedState, receipts, vmCfg) } } } else { @@ -1146,7 +1175,7 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, return receipts } -func insertStateSyncTransactionAndCalculateReceipt(stateSyncTx *types.Transaction, header *types.Header, body *types.Body, state vm.StateDB, receipts []*types.Receipt) []*types.Receipt { +func insertStateSyncTransactionAndCalculateReceipt(stateSyncTx *types.Transaction, header *types.Header, body *types.Body, state vm.StateDB, receipts []*types.Receipt, vmConfig vm.Config) []*types.Receipt { allLogs := state.Logs() sort.SliceStable(allLogs, func(i, j int) bool { return allLogs[i].Index < allLogs[j].Index @@ -1179,6 +1208,13 @@ func insertStateSyncTransactionAndCalculateReceipt(stateSyncTx *types.Transactio } stateSyncReceipt.Bloom = types.CreateBloom(stateSyncReceipt) + + // End tracing for StateSyncTx + if hooks := vmConfig.Tracer; hooks != nil && hooks.OnTxEnd != nil { + log.Info("[DEBUG] StateSyncTx OnTxEnd", "block", header.Number, "txHash", stateSyncTx.Hash(), "logCount", len(stateSyncLogs), "status", stateSyncReceipt.Status) + hooks.OnTxEnd(stateSyncReceipt, nil) + } + receipts = append(receipts, stateSyncReceipt) return receipts @@ -1235,14 +1271,19 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ var ( stateSyncData []*types.StateSyncData err error + vmCfg vm.Config ) + if bc, ok := chain.(*core.BlockChain); ok { + vmCfg = *bc.GetVMConfig() + } + if IsSprintStart(headerNumber, c.config.CalculateSprint(headerNumber)) { cx := statefull.ChainContext{Chain: chain, Bor: c} // check and commit span if !c.config.IsRio(header.Number) { - if err = c.checkAndCommitSpan(state, header, cx); err != nil { + if err = c.checkAndCommitSpan(state, header, cx, vmCfg); err != nil { log.Error("Error while committing span", "error", err) return nil, nil, 0, err } @@ -1250,7 +1291,7 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ if c.HeimdallClient != nil { // commit states - stateSyncData, err = c.CommitStates(state, header, cx) + stateSyncData, err = c.CommitStates(state, header, cx, vmCfg) if err != nil { log.Error("Error while committing states", "error", err) return nil, nil, 0, err @@ -1276,7 +1317,7 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ StateSyncData: stateSyncData, }) body.Transactions = append(body.Transactions, stateSyncTx) - receipts = insertStateSyncTransactionAndCalculateReceipt(stateSyncTx, header, body, state, receipts) + receipts = insertStateSyncTransactionAndCalculateReceipt(stateSyncTx, header, body, state, receipts, vmCfg) } else { // set state sync bc := chain.(core.BorStateSyncer) @@ -1484,6 +1525,7 @@ func (c *Bor) checkAndCommitSpan( state vm.StateDB, header *types.Header, chain core.ChainContext, + vmCfg vm.Config, ) error { var ctx = context.Background() headerNumber := header.Number.Uint64() @@ -1500,7 +1542,7 @@ func (c *Bor) checkAndCommitSpan( tempState.IntermediateRoot(false) if c.needToCommitSpan(span, headerNumber) { - return c.FetchAndCommitSpan(ctx, span.Id+1, state, header, chain) + return c.FetchAndCommitSpan(ctx, span.Id+1, state, header, chain, vmCfg) } return nil @@ -1539,6 +1581,7 @@ func (c *Bor) FetchAndCommitSpan( state vm.StateDB, header *types.Header, chain core.ChainContext, + vmCfg vm.Config, ) error { var ( minSpan borTypes.Span @@ -1602,7 +1645,7 @@ func (c *Bor) FetchAndCommitSpan( ) } - return c.spanner.CommitSpan(ctx, minSpan, validators, producers, state, header, chain) + return c.spanner.CommitSpan(ctx, minSpan, validators, producers, state, header, chain, vmCfg) } // CommitStates commit states @@ -1610,6 +1653,7 @@ func (c *Bor) CommitStates( state vm.StateDB, header *types.Header, chain statefull.ChainContext, + vmCfg vm.Config, ) ([]*types.StateSyncData, error) { fetchStart := time.Now() number := header.Number.Uint64() @@ -1723,7 +1767,7 @@ func (c *Bor) CommitStates( // we expect that this call MUST emit an event, otherwise we wouldn't make a receipt // if the receiver address is not a contract then we'll skip the most of the execution and emitting an event as well // https://github.com/0xPolygon/genesis-contracts/blob/master/contracts/StateReceiver.sol#L27 - gasUsed, err = c.GenesisContractsClient.CommitState(eventRecord, state, header, chain) + gasUsed, err = c.GenesisContractsClient.CommitState(eventRecord, state, header, chain, vmCfg) if err != nil { return nil, err } diff --git a/consensus/bor/bor_test.go b/consensus/bor/bor_test.go index d3f6f5be27..5ae0fdca51 100644 --- a/consensus/bor/bor_test.go +++ b/consensus/bor/bor_test.go @@ -62,7 +62,7 @@ func (s *fakeSpanner) GetCurrentValidatorsByHash(ctx context.Context, headerHash func (s *fakeSpanner) GetCurrentValidatorsByBlockNrOrHash(ctx context.Context, _ rpc.BlockNumberOrHash, _ uint64) ([]*valset.Validator, error) { return s.vals, nil } -func (s *fakeSpanner) CommitSpan(ctx context.Context, _ borTypes.Span, _ []stakeTypes.MinimalVal, _ []stakeTypes.MinimalVal, _ vm.StateDB, _ *types.Header, _ core.ChainContext) error { +func (s *fakeSpanner) CommitSpan(ctx context.Context, _ borTypes.Span, _ []stakeTypes.MinimalVal, _ []stakeTypes.MinimalVal, _ vm.StateDB, _ *types.Header, _ core.ChainContext, _ vm.Config) error { if s.shouldFailCommit { return errors.New("span commit failed") } diff --git a/consensus/bor/contract/client.go b/consensus/bor/contract/client.go index 1ab8a9a754..c3f87f141c 100644 --- a/consensus/bor/contract/client.go +++ b/consensus/bor/contract/client.go @@ -74,6 +74,7 @@ func (gc *GenesisContractsClient) CommitState( state vm.StateDB, header *types.Header, chCtx statefull.ChainContext, + vmCfg vm.Config, ) (uint64, error) { eventRecord := event.BuildEventRecord() @@ -96,7 +97,7 @@ func (gc *GenesisContractsClient) CommitState( log.Info("→ committing new state", "eventRecord", event.ID) - gasUsed, err := statefull.ApplyMessage(context.Background(), msg, state, header, gc.chainConfig, chCtx) + gasUsed, err := statefull.ApplyMessage(context.Background(), msg, state, header, gc.chainConfig, chCtx, vmCfg) // Logging event log with time and individual gasUsed log.Info("→ committed new state", "eventRecord", event.String(gasUsed)) diff --git a/consensus/bor/genesis.go b/consensus/bor/genesis.go index 31c46ee2be..3d4757df2a 100644 --- a/consensus/bor/genesis.go +++ b/consensus/bor/genesis.go @@ -13,6 +13,6 @@ import ( //go:generate mockgen -destination=./genesis_contract_mock.go -package=bor . GenesisContract type GenesisContract interface { - CommitState(event *clerk.EventRecordWithTime, state vm.StateDB, header *types.Header, chCtx statefull.ChainContext) (uint64, error) + CommitState(event *clerk.EventRecordWithTime, state vm.StateDB, header *types.Header, chCtx statefull.ChainContext, vmCfg vm.Config) (uint64, error) LastStateId(state *state.StateDB, number uint64, hash common.Hash) (*big.Int, error) } diff --git a/consensus/bor/heimdall/span/spanner.go b/consensus/bor/heimdall/span/spanner.go index f63da09638..8569765b97 100644 --- a/consensus/bor/heimdall/span/spanner.go +++ b/consensus/bor/heimdall/span/spanner.go @@ -291,7 +291,7 @@ func (c *ChainSpanner) GetCurrentValidatorsByHash(ctx context.Context, headerHas const method = "commitSpan" -func (c *ChainSpanner) CommitSpan(ctx context.Context, minimalSpan borTypes.Span, validators, producers []stakeTypes.MinimalVal, state vm.StateDB, header *types.Header, chainContext core.ChainContext) error { +func (c *ChainSpanner) CommitSpan(ctx context.Context, minimalSpan borTypes.Span, validators, producers []stakeTypes.MinimalVal, state vm.StateDB, header *types.Header, chainContext core.ChainContext, vmCfg vm.Config) error { // get validators bytes validatorBytes, err := rlp.EncodeToBytes(validators) if err != nil { @@ -329,7 +329,7 @@ func (c *ChainSpanner) CommitSpan(ctx context.Context, minimalSpan borTypes.Span msg := statefull.GetSystemMessage(c.validatorContractAddress, data) // apply message - _, err = statefull.ApplyMessage(ctx, msg, state, header, c.chainConfig, chainContext) + _, err = statefull.ApplyMessage(ctx, msg, state, header, c.chainConfig, chainContext, vmCfg) return err } diff --git a/consensus/bor/span.go b/consensus/bor/span.go index 06f2673894..3680381c75 100644 --- a/consensus/bor/span.go +++ b/consensus/bor/span.go @@ -20,5 +20,5 @@ type Spanner interface { GetCurrentSpan(ctx context.Context, headerHash common.Hash, state *state.StateDB) (*borTypes.Span, error) GetCurrentValidatorsByHash(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) GetCurrentValidatorsByBlockNrOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, blockNumber uint64) ([]*valset.Validator, error) - CommitSpan(ctx context.Context, minimalSpan borTypes.Span, validators, producers []stakeTypes.MinimalVal, state vm.StateDB, header *types.Header, chainContext core.ChainContext) error + CommitSpan(ctx context.Context, minimalSpan borTypes.Span, validators, producers []stakeTypes.MinimalVal, state vm.StateDB, header *types.Header, chainContext core.ChainContext, vmCfg vm.Config) error } diff --git a/consensus/bor/statefull/processor.go b/consensus/bor/statefull/processor.go index ca84d9057a..e1fb708ded 100644 --- a/consensus/bor/statefull/processor.go +++ b/consensus/bor/statefull/processor.go @@ -17,7 +17,7 @@ import ( "github.com/ethereum/go-ethereum/params" ) -var systemAddress = common.HexToAddress("0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE") +var SystemAddress = common.HexToAddress("0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE") type ChainContext struct { Chain consensus.ChainHeaderReader @@ -70,7 +70,7 @@ func (m Callmsg) Data() []byte { return m.CallMsg.Data } func GetSystemMessage(toAddress common.Address, data []byte) Callmsg { return Callmsg{ ethereum.CallMsg{ - From: systemAddress, + From: SystemAddress, Gas: params.MaxTxGas, // should be more than enough for state-sync related syscalls GasPrice: big.NewInt(0), Value: big.NewInt(0), @@ -88,6 +88,7 @@ func ApplyMessage( header *types.Header, chainConfig *params.ChainConfig, chainContext core.ChainContext, + vmConfig vm.Config, ) (uint64, error) { initialGas := msg.Gas() @@ -96,7 +97,7 @@ func ApplyMessage( // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. - vmenv := vm.NewEVM(blockContext, state, chainConfig, vm.Config{}) + vmenv := vm.NewEVM(blockContext, state, chainConfig, vmConfig) // nolint : contextcheck // Apply the transaction to the current state (included in the env) diff --git a/docs/cli/example_config.toml b/docs/cli/example_config.toml index cf39142ccf..1118254c18 100644 --- a/docs/cli/example_config.toml +++ b/docs/cli/example_config.toml @@ -26,11 +26,13 @@ devfakeauthor = false # Run miner without validator set authorization "32000000" = "0x875500011e5eecc0c554f95d07b31cf59df4ca2505f4dbbfffa7d4e4da917c68" [log] - vmodule = "" # Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4) - json = false # Format logs with JSON - backtrace = "" # Request a stack trace at a specific logging statement (e.g. "block.go:271") - debug = true # Prepends log messages with call-site location (file and line number) - enable-block-tracking = false # Enables additional logging of information collected while tracking block lifecycle +vmodule = "" # Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4) +json = false # Format logs with JSON +backtrace = "" # Request a stack trace at a specific logging statement (e.g. "block.go:271") +debug = true # Prepends log messages with call-site location (file and line number) +enable-block-tracking = false # Enables additional logging of information collected while tracking block lifecycle +vmtrace = "supply" # Name of a tracer to record internal VM operations during blockchain synchronization (costly) +vmtrace.jsonconfig = '{"path": "output"}' # Tracer configuration (JSON) [p2p] maxpeers = 50 # Maximum number of network peers (network disabled if set to 0) diff --git a/docs/cli/server.md b/docs/cli/server.md index b10194153c..e422a9b29a 100644 --- a/docs/cli/server.md +++ b/docs/cli/server.md @@ -262,6 +262,10 @@ The ```bor server``` command runs the Bor client. - ```vmodule```: Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4) +- ```vmtrace```: Name of a tracer to record internal VM operations during blockchain synchronization (costly) + +- ```vmtrace.jsonconfig```: Tracer configuration (JSON) (default: {}) + ### P2P Options - ```bind```: Network binding address (default: 0.0.0.0) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 4a550f8610..5d37516660 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -723,7 +723,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config if includeStateSyncTx { callmsg := prepareCallMessage(*msg) statedb.SetTxContext(stateSyncHash, i) - if _, err := statefull.ApplyMessage(ctx, callmsg, statedb, block.Header(), api.backend.ChainConfig(), api.chainContext(ctx)); err != nil { + if _, err := statefull.ApplyMessage(ctx, callmsg, statedb, block.Header(), api.backend.ChainConfig(), api.chainContext(ctx), vm.Config{}); err != nil { log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", stateSyncHash, "err", err) // We intentionally don't return the error here: if we do, then the RPC server will not // return the roots. Most likely, the caller already knows that a certain transaction fails to diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go index cee9219214..55379cdd52 100644 --- a/internal/cli/server/config.go +++ b/internal/cli/server/config.go @@ -164,6 +164,12 @@ type Config struct { // HealthConfig has health check related settings Health *HealthConfig `hcl:"health,block" toml:"health,block"` + + // VMTrace Name of tracer which should record internal VM operations (costly) + VMTrace string `hcl:"vmtrace,optional" toml:"vmtrace,optional"` + + // VMTraceJsonConfig Tracer configuration (JSON) + VMTraceJsonConfig string `hcl:"vmtrace.jsonconfig,optional" toml:"vmtrace.jsonconfig,optional"` } type HistoryConfig struct { @@ -996,6 +1002,8 @@ func DefaultConfig() *Config { LogNoHistory: ethconfig.Defaults.LogNoHistory, StateHistory: params.FullImmutabilityThreshold, }, + VMTrace: "", + VMTraceJsonConfig: "{}", Health: &HealthConfig{ MaxGoRoutineThreshold: 0, WarnGoRoutineThreshold: 0, @@ -1545,6 +1553,8 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (* } n.EnableBlockTracking = c.Logging.EnableBlockTracking + n.VMTrace = c.VMTrace + n.VMTraceJsonConfig = c.VMTraceJsonConfig // Blind fork acceptance configs n.DisableBlindForkValidation = c.DisableBlindForkValidation diff --git a/internal/cli/server/flags.go b/internal/cli/server/flags.go index a7ae19265c..df3174d6d4 100644 --- a/internal/cli/server/flags.go +++ b/internal/cli/server/flags.go @@ -143,6 +143,20 @@ func (c *Command) Flags(config *Config) *flagset.Flagset { Default: c.cliConfig.Logging.Vmodule, Group: "Logging", }) + f.StringFlag(&flagset.StringFlag{ + Name: "vmtrace", + Usage: "Name of a tracer to record internal VM operations during blockchain synchronization (costly)", + Value: &c.cliConfig.VMTrace, + Default: c.cliConfig.VMTrace, + Group: "Logging", + }) + f.StringFlag(&flagset.StringFlag{ + Name: "vmtrace.jsonconfig", + Usage: "Tracer configuration (JSON)", + Value: &c.cliConfig.VMTraceJsonConfig, + Default: c.cliConfig.VMTraceJsonConfig, + Group: "Logging", + }) f.BoolFlag(&flagset.BoolFlag{ Name: "log.json", Usage: "Format logs with JSON", From 7b13a888fade81f2c2c9aec8e36e056608ab0aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Do=C5=A1li=C4=87?= Date: Fri, 6 Feb 2026 09:56:54 +0100 Subject: [PATCH 02/20] generate mocks --- consensus/bor/genesis_contract_mock.go | 8 ++++---- consensus/bor/span_mock.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/consensus/bor/genesis_contract_mock.go b/consensus/bor/genesis_contract_mock.go index 5b80826202..9268adf437 100644 --- a/consensus/bor/genesis_contract_mock.go +++ b/consensus/bor/genesis_contract_mock.go @@ -41,18 +41,18 @@ func (m *MockGenesisContract) EXPECT() *MockGenesisContractMockRecorder { } // CommitState mocks base method. -func (m *MockGenesisContract) CommitState(arg0 *clerk.EventRecordWithTime, arg1 vm.StateDB, arg2 *types.Header, arg3 statefull.ChainContext) (uint64, error) { +func (m *MockGenesisContract) CommitState(arg0 *clerk.EventRecordWithTime, arg1 vm.StateDB, arg2 *types.Header, arg3 statefull.ChainContext, arg4 vm.Config) (uint64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CommitState", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "CommitState", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(uint64) ret1, _ := ret[1].(error) return ret0, ret1 } // CommitState indicates an expected call of CommitState. -func (mr *MockGenesisContractMockRecorder) CommitState(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockGenesisContractMockRecorder) CommitState(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitState", reflect.TypeOf((*MockGenesisContract)(nil).CommitState), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitState", reflect.TypeOf((*MockGenesisContract)(nil).CommitState), arg0, arg1, arg2, arg3, arg4) } // LastStateId mocks base method. diff --git a/consensus/bor/span_mock.go b/consensus/bor/span_mock.go index 31e2319ad1..7c32e47c7f 100644 --- a/consensus/bor/span_mock.go +++ b/consensus/bor/span_mock.go @@ -44,17 +44,17 @@ func (m *MockSpanner) EXPECT() *MockSpannerMockRecorder { } // CommitSpan mocks base method. -func (m *MockSpanner) CommitSpan(arg0 context.Context, arg1 types.Span, arg2, arg3 []types0.MinimalVal, arg4 vm.StateDB, arg5 *types1.Header, arg6 core.ChainContext) error { +func (m *MockSpanner) CommitSpan(arg0 context.Context, arg1 types.Span, arg2, arg3 []types0.MinimalVal, arg4 vm.StateDB, arg5 *types1.Header, arg6 core.ChainContext, arg7 vm.Config) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CommitSpan", arg0, arg1, arg2, arg3, arg4, arg5, arg6) + ret := m.ctrl.Call(m, "CommitSpan", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) ret0, _ := ret[0].(error) return ret0 } // CommitSpan indicates an expected call of CommitSpan. -func (mr *MockSpannerMockRecorder) CommitSpan(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { +func (mr *MockSpannerMockRecorder) CommitSpan(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitSpan", reflect.TypeOf((*MockSpanner)(nil).CommitSpan), arg0, arg1, arg2, arg3, arg4, arg5, arg6) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitSpan", reflect.TypeOf((*MockSpanner)(nil).CommitSpan), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) } // GetCurrentSpan mocks base method. From 12e21b65d8931a4a930dd7867228e6c973ee3e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Do=C5=A1li=C4=87?= Date: Thu, 12 Feb 2026 20:14:00 +0100 Subject: [PATCH 03/20] Remove debug trace logs from StateSyncTx processing Remove the 4 [DEBUG] log lines that were added during development to diagnose StateSyncTx tracing issues. --- consensus/bor/bor.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index 3604cdec39..5992f1455e 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -1106,15 +1106,12 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, lastTx := body.Transactions[len(body.Transactions)-1] if lastTx.Type() == types.StateSyncTxType { stateSyncTx = lastTx - hasTracer := vmCfg.Tracer != nil - log.Info("[DEBUG] StateSyncTx found in Finalize", "block", headerNumber, "txHash", lastTx.Hash(), "hasTracer", hasTracer) if hooks := vmCfg.Tracer; hooks != nil && hooks.OnTxStart != nil { // todo(milando12): check how much overhead this adds, if it does we can initialize it earlier and pass down to ApplyMessage() vmenv := vm.NewEVM( core.NewEVMBlockContext(header, cx, &header.Coinbase), wrappedState, c.chainConfig, vmCfg, ) - log.Info("[DEBUG] StateSyncTx OnTxStart", "block", headerNumber, "txHash", stateSyncTx.Hash()) hooks.OnTxStart(vmenv.GetVMContext(), stateSyncTx, statefull.SystemAddress) } } @@ -1135,10 +1132,7 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, log.Error("Error while committing states", "error", err) return nil } - if len(stateSyncData) > 0 { - log.Info("[DEBUG] StateSyncTx CommitStates completed", "block", headerNumber, "eventCount", len(stateSyncData)) } - } // Get the underlying state for updating consensus time state := wrappedState.Inner() state.BorConsensusTime = time.Since(start) @@ -1211,7 +1205,6 @@ func insertStateSyncTransactionAndCalculateReceipt(stateSyncTx *types.Transactio // End tracing for StateSyncTx if hooks := vmConfig.Tracer; hooks != nil && hooks.OnTxEnd != nil { - log.Info("[DEBUG] StateSyncTx OnTxEnd", "block", header.Number, "txHash", stateSyncTx.Hash(), "logCount", len(stateSyncLogs), "status", stateSyncReceipt.Status) hooks.OnTxEnd(stateSyncReceipt, nil) } From 276a40489da72dd8c8b12deaecde176dbfa751de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Do=C5=A1li=C4=87?= Date: Thu, 12 Feb 2026 20:15:49 +0100 Subject: [PATCH 04/20] Propagate VM config through HeaderChain for tracer access When Finalize is called, the chain parameter can be a *core.HeaderChain (not just *core.BlockChain). Without this change, the tracer in vmCfg is nil during finalization, so StateSyncTx tracing hooks never fire. - Add vmConfig field and GetVMConfig() method to HeaderChain - Set hc.vmConfig in NewBlockChain after creating the HeaderChain - Add else-if branch in Finalize and FinalizeAndAssemble to extract VM config from HeaderChain --- consensus/bor/bor.go | 8 ++++++++ core/blockchain.go | 1 + core/headerchain.go | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index 5992f1455e..186dab6206 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -1094,6 +1094,10 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, if bc, ok := chain.(*core.BlockChain); ok { vmCfg = *bc.GetVMConfig() + } else if hc, ok := chain.(*core.HeaderChain); ok { + if cfg := hc.GetVMConfig(); cfg != nil { + vmCfg = *cfg + } } if IsSprintStart(headerNumber, c.config.CalculateSprint(headerNumber)) { @@ -1269,6 +1273,10 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ if bc, ok := chain.(*core.BlockChain); ok { vmCfg = *bc.GetVMConfig() + } else if hc, ok := chain.(*core.HeaderChain); ok { + if cfg := hc.GetVMConfig(); cfg != nil { + vmCfg = *cfg + } } if IsSprintStart(headerNumber, c.config.CalculateSprint(headerNumber)) { diff --git a/core/blockchain.go b/core/blockchain.go index 2863519b74..f51f7e2cba 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -464,6 +464,7 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine, if err != nil { return nil, err } + bc.hc.vmConfig = &bc.cfg.VmConfig bc.flushInterval.Store(int64(cfg.TrieTimeLimit)) bc.forker = NewForkChoice(bc, cfg.ShouldPreserve, cfg.Checker) diff --git a/core/headerchain.go b/core/headerchain.go index 08362e4daa..660b464716 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -71,6 +72,7 @@ type HeaderChain struct { procInterrupt func() bool engine consensus.Engine + vmConfig *vm.Config // VM configuration (propagated from BlockChain) stateSyncData []*types.StateSyncData // State sync data } @@ -106,6 +108,11 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c return hc, nil } +// GetVMConfig returns the VM config propagated from BlockChain, or nil if not set. +func (hc *HeaderChain) GetVMConfig() *vm.Config { + return hc.vmConfig +} + // GetBlockNumber retrieves the block number belonging to the given hash // from the cache or database func (hc *HeaderChain) GetBlockNumber(hash common.Hash) (uint64, bool) { From 2b43baf0f1276bece126767c2608792f39f5d27b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Do=C5=A1li=C4=87?= Date: Thu, 12 Feb 2026 20:17:39 +0100 Subject: [PATCH 05/20] Pass tracingStateDB to Finalize in state processor The Process function creates a tracingStateDB wrapper for tracing but was passing the unwrapped statedb to Finalize, causing the tracer to miss StateSyncTx state changes. --- core/state_processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/state_processor.go b/core/state_processor.go index 7bbbd9a245..f6e98306bc 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -155,7 +155,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // Finalize the block, applying any consensus engine specific extras (e.g. block rewards), apply // state sync event (if any), and append the receipt. receiptsCountBeforeFinalize := len(receipts) - receipts = p.chain.Engine().Finalize(p.chain, header, statedb, block.Body(), receipts) + receipts = p.chain.Engine().Finalize(p.chain, header, tracingStateDB, block.Body(), receipts) // apply state sync logs if p.chainConfig().Bor != nil && p.chainConfig().Bor.IsMadhugiri(block.Number()) { From afa51c2d0815d1e77d3cd6d9f96358f34eaf085d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Do=C5=A1li=C4=87?= Date: Mon, 23 Feb 2026 16:19:33 +0100 Subject: [PATCH 06/20] Add vm.Config parameter to failingGenesisContract's CommitState method Expand the `CommitState` method signature to include the `vm.Config` parameter, enabling proper VM tracing integration and configuration. --- consensus/bor/bor_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/bor/bor_test.go b/consensus/bor/bor_test.go index 5ae0fdca51..2260d90881 100644 --- a/consensus/bor/bor_test.go +++ b/consensus/bor/bor_test.go @@ -75,7 +75,7 @@ type failingHeimdallClient struct{} // failingGenesisContract simulates GenesisContract failures type failingGenesisContract struct{} -func (f *failingGenesisContract) CommitState(event *clerk.EventRecordWithTime, state vm.StateDB, header *types.Header, chCtx statefull.ChainContext) (uint64, error) { +func (f *failingGenesisContract) CommitState(event *clerk.EventRecordWithTime, state vm.StateDB, header *types.Header, chCtx statefull.ChainContext, vmCfg vm.Config) (uint64, error) { return 0, errors.New("commit state failed") } From ee7c1221e866abec2ac618a0b003472eedc9f75b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Do=C5=A1li=C4=87?= Date: Mon, 23 Feb 2026 16:21:17 +0100 Subject: [PATCH 07/20] Refactor StateSyncTx tracing logic to instantiate only if tracing is enabled --- consensus/bor/bor.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index 186dab6206..3267f4a563 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -1105,18 +1105,16 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, cx := statefull.ChainContext{Chain: chain, Bor: c} // Start tracing StateSyncTx (if present in the block body) - var stateSyncTx *types.Transaction - if c.config.IsMadhugiri(header.Number) && len(body.Transactions) > 0 { - lastTx := body.Transactions[len(body.Transactions)-1] - if lastTx.Type() == types.StateSyncTxType { - stateSyncTx = lastTx - if hooks := vmCfg.Tracer; hooks != nil && hooks.OnTxStart != nil { + if hooks := vmCfg.Tracer; hooks != nil && hooks.OnTxStart != nil { + if c.config.IsMadhugiri(header.Number) && len(body.Transactions) > 0 { + lastTx := body.Transactions[len(body.Transactions)-1] + if lastTx.Type() == types.StateSyncTxType { // todo(milando12): check how much overhead this adds, if it does we can initialize it earlier and pass down to ApplyMessage() vmenv := vm.NewEVM( core.NewEVMBlockContext(header, cx, &header.Coinbase), wrappedState, c.chainConfig, vmCfg, ) - hooks.OnTxStart(vmenv.GetVMContext(), stateSyncTx, statefull.SystemAddress) + hooks.OnTxStart(vmenv.GetVMContext(), lastTx, statefull.SystemAddress) } } } @@ -1136,7 +1134,7 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, log.Error("Error while committing states", "error", err) return nil } - } + } // Get the underlying state for updating consensus time state := wrappedState.Inner() state.BorConsensusTime = time.Since(start) From f1e065e123757cd589c2c7c24efd2d0559837229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Do=C5=A1li=C4=87?= Date: Mon, 23 Feb 2026 16:37:41 +0100 Subject: [PATCH 08/20] Remove unused comment in StateSyncTx tracing initialization logic EVM struct has a comment saying never reuse --- consensus/bor/bor.go | 1 - 1 file changed, 1 deletion(-) diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index 3267f4a563..f8c2167ff5 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -1109,7 +1109,6 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, if c.config.IsMadhugiri(header.Number) && len(body.Transactions) > 0 { lastTx := body.Transactions[len(body.Transactions)-1] if lastTx.Type() == types.StateSyncTxType { - // todo(milando12): check how much overhead this adds, if it does we can initialize it earlier and pass down to ApplyMessage() vmenv := vm.NewEVM( core.NewEVMBlockContext(header, cx, &header.Coinbase), wrappedState, c.chainConfig, vmCfg, From 9e48c5e5c8ee7727b1a2487764155e2f191803a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Do=C5=A1li=C4=87?= Date: Tue, 24 Feb 2026 14:26:50 +0100 Subject: [PATCH 09/20] trigger PR update From 8ddbaad2ace1d3517ffe96fd82d258019a80a981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Do=C5=A1li=C4=87?= Date: Tue, 24 Feb 2026 14:28:05 +0100 Subject: [PATCH 10/20] trigger PR update From 9ef5449ae0bc6a853ec52a0438fcd79aaaf9f221 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Fri, 13 Mar 2026 13:47:18 +0530 Subject: [PATCH 11/20] params: version bump to v2.7.0-beta --- params/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/params/version.go b/params/version.go index c1dac7824a..5b546184f3 100644 --- a/params/version.go +++ b/params/version.go @@ -23,10 +23,10 @@ import ( ) const ( - VersionMajor = 2 // Major version component of the current release - VersionMinor = 6 // Minor version component of the current release - VersionPatch = 0 // Patch version component of the current release - VersionMeta = "" // Version metadata to append to the version string + VersionMajor = 2 // Major version component of the current release + VersionMinor = 7 // Minor version component of the current release + VersionPatch = 0 // Patch version component of the current release + VersionMeta = "beta" // Version metadata to append to the version string ) var ( From c557c528322a371292f5ead93b5313f7b96a95e1 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Mon, 16 Mar 2026 18:52:14 +0530 Subject: [PATCH 12/20] added giugliano block for amoy (#2141) --- builder/files/genesis-amoy.json | 1 + internal/cli/server/chains/amoy.go | 1 + params/config.go | 1 + 3 files changed, 3 insertions(+) diff --git a/builder/files/genesis-amoy.json b/builder/files/genesis-amoy.json index 9e58eaa75c..14da93891a 100644 --- a/builder/files/genesis-amoy.json +++ b/builder/files/genesis-amoy.json @@ -28,6 +28,7 @@ "dandeliBlock": 31890000, "lisovoBlock": 33634700, "lisovoProBlock": 34062000, + "giuglianoBlock": 35573500, "skipValidatorByteCheck": [26160367, 26161087, 26171567, 26173743, 26175647], "stateSyncConfirmationDelay": { "0": 128 diff --git a/internal/cli/server/chains/amoy.go b/internal/cli/server/chains/amoy.go index 7db336f37f..9e70924aa6 100644 --- a/internal/cli/server/chains/amoy.go +++ b/internal/cli/server/chains/amoy.go @@ -41,6 +41,7 @@ var amoyTestnet = &Chain{ DandeliBlock: big.NewInt(31890000), LisovoBlock: big.NewInt(33634700), LisovoProBlock: big.NewInt(34062000), + GiuglianoBlock: big.NewInt(35573500), StateSyncConfirmationDelay: map[string]uint64{ "0": 128, }, diff --git a/params/config.go b/params/config.go index 9624c367af..a89120e2ee 100644 --- a/params/config.go +++ b/params/config.go @@ -345,6 +345,7 @@ var ( DandeliBlock: big.NewInt(31890000), LisovoBlock: big.NewInt(33634700), LisovoProBlock: big.NewInt(34062000), + GiuglianoBlock: big.NewInt(35573500), StateSyncConfirmationDelay: map[string]uint64{ "0": 128, }, From 9dbbecf57931d69e27d57f991362a10929137e58 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Mon, 16 Mar 2026 18:53:51 +0530 Subject: [PATCH 13/20] params: version bump to v2.7.0-beta2 --- params/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/params/version.go b/params/version.go index 5b546184f3..cd4cf256c1 100644 --- a/params/version.go +++ b/params/version.go @@ -23,10 +23,10 @@ import ( ) const ( - VersionMajor = 2 // Major version component of the current release - VersionMinor = 7 // Minor version component of the current release - VersionPatch = 0 // Patch version component of the current release - VersionMeta = "beta" // Version metadata to append to the version string + VersionMajor = 2 // Major version component of the current release + VersionMinor = 7 // Minor version component of the current release + VersionPatch = 0 // Patch version component of the current release + VersionMeta = "beta2" // Version metadata to append to the version string ) var ( From 2b949e9be7724d2d7c83c3fb548b57ff31cffdcd Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Thu, 19 Mar 2026 14:38:47 +0530 Subject: [PATCH 14/20] witness: disable filestore by default (#2144) --- docs/cli/default_config.toml | 2 +- docs/cli/server.md | 2 +- internal/cli/server/config.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/cli/default_config.toml b/docs/cli/default_config.toml index 534b88999d..d07bb5c1cf 100644 --- a/docs/cli/default_config.toml +++ b/docs/cli/default_config.toml @@ -235,7 +235,7 @@ devfakeauthor = false parallel-stateless-import = false parallel-stateless-import-workers = 0 witnessapi = false - filestore = true + filestore = false fastforwardthreshold = 6400 [pprof] diff --git a/docs/cli/server.md b/docs/cli/server.md index 93e1501d11..ba3538099b 100644 --- a/docs/cli/server.md +++ b/docs/cli/server.md @@ -118,7 +118,7 @@ The ```bor server``` command runs the Bor client. - ```witness.fastforwardthreshold```: Minimum necessary distance between local header and chain tip to trigger fast forward (default: 6400) -- ```witness.filestore```: Store witness blobs on the filesystem instead of the key-value database (default: true) +- ```witness.filestore```: Store witness blobs on the filesystem instead of the key-value database (default: false) - ```witness.parallelstatelessimport```: Enable parallel stateless block import (default: false) diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go index 745d35fd56..fcd4e31748 100644 --- a/internal/cli/server/config.go +++ b/internal/cli/server/config.go @@ -1041,7 +1041,7 @@ func DefaultConfig() *Config { EnableParallelStatelessImport: false, ParallelStatelessImportWorkers: 0, WitnessAPI: false, - FileStore: true, + FileStore: false, FastForwardThreshold: 6400, }, History: &HistoryConfig{ From 7d6a68d8b3c321b76c32aa5c76af884b6f006d13 Mon Sep 17 00:00:00 2001 From: Lucca Martins Date: Thu, 19 Mar 2026 07:48:27 -0300 Subject: [PATCH 15/20] eth/filters, rpc: add --rpc.rangelimit flag and fix filter config pass-through (#2149) * eth/filters, rpc: add `--rpc.rangelimit` flag and fix filter config pass-through (#2147) * Include range limit filter flag * update docs * apply more range checks * Including RPCLogQueryLimit * make docs --- cmd/geth/main.go | 1 + cmd/utils/flags.go | 13 +++- docs/cli/default_config.toml | 1 + docs/cli/server.md | 2 + eth/backend.go | 6 +- eth/ethconfig/config.go | 3 + eth/filters/api.go | 41 +++++++++++ eth/filters/bor_api.go | 7 ++ eth/filters/filter_system.go | 1 + eth/filters/filter_system_test.go | 117 ++++++++++++++++++++++++++++++ internal/cli/server/config.go | 4 + internal/cli/server/flags.go | 7 ++ 12 files changed, 201 insertions(+), 2 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index b79c37ac9f..8e417a7de8 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -197,6 +197,7 @@ var ( utils.RPCGlobalEVMTimeoutFlag, utils.RPCGlobalTxFeeCapFlag, utils.RPCGlobalLogQueryLimit, + utils.RPCGlobalRangeLimitFlag, utils.AllowUnprotectedTxs, utils.BatchRequestLimit, utils.BatchResponseMaxSize, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 4fc77ca3ff..86a28b93ff 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -663,6 +663,12 @@ var ( Value: ethconfig.Defaults.LogQueryLimit, Category: flags.APICategory, } + RPCGlobalRangeLimitFlag = &cli.Uint64Flag{ + Name: "rpc.rangelimit", + Usage: "Maximum block range allowed for eth_getLogs and bor_getLogs (0 = no limit)", + Value: ethconfig.Defaults.RPCBlockRangeLimit, + Category: flags.APICategory, + } RPCTxSyncDefaultTimeoutFlag = &cli.DurationFlag{ Name: "rpc.txsync.defaulttimeout", Usage: "Default timeout for eth_sendRawTransactionSync (e.g. 2s, 500ms)", @@ -1878,6 +1884,10 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } if ctx.IsSet(RPCGlobalLogQueryLimit.Name) { cfg.LogQueryLimit = ctx.Int(RPCGlobalLogQueryLimit.Name) + cfg.RPCLogQueryLimit = ctx.Int(RPCGlobalLogQueryLimit.Name) + } + if ctx.IsSet(RPCGlobalRangeLimitFlag.Name) { + cfg.RPCBlockRangeLimit = ctx.Uint64(RPCGlobalRangeLimitFlag.Name) } if ctx.IsSet(RPCTxSyncDefaultTimeoutFlag.Name) { cfg.TxSyncDefaultTimeout = ctx.Duration(RPCTxSyncDefaultTimeoutFlag.Name) @@ -2230,7 +2240,8 @@ func RegisterGraphQLService(stack *node.Node, backend ethapi.Backend, filterSyst func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconfig.Config) *filters.FilterSystem { filterSystem := filters.NewFilterSystem(backend, filters.Config{ LogCacheSize: ethcfg.FilterLogCacheSize, - LogQueryLimit: ethcfg.LogQueryLimit, + LogQueryLimit: ethcfg.RPCLogQueryLimit, + RangeLimit: ethcfg.RPCBlockRangeLimit, }) filterAPI := filters.NewFilterAPI(filterSystem, ethcfg.BorLogs) diff --git a/docs/cli/default_config.toml b/docs/cli/default_config.toml index d07bb5c1cf..45c7eff09d 100644 --- a/docs/cli/default_config.toml +++ b/docs/cli/default_config.toml @@ -112,6 +112,7 @@ devfakeauthor = false evmtimeout = "5s" txfeecap = 1.0 logquerylimit = 1000 + rangelimit = 0 allow-unprotected-txs = false enabledeprecatedpersonal = false "txsync.defaulttimeout" = "20s" diff --git a/docs/cli/server.md b/docs/cli/server.md index ba3538099b..2315381921 100644 --- a/docs/cli/server.md +++ b/docs/cli/server.md @@ -246,6 +246,8 @@ The ```bor server``` command runs the Bor client. - ```rpc.logquerylimit```: Maximum number of alternative addresses or topics allowed per search position in eth_getLogs filter criteria (0 = no cap) (default: 1000) +- ```rpc.rangelimit```: Maximum block range allowed for eth_getLogs and bor_getLogs (0 = no limit) (default: 0) + - ```rpc.txfeecap```: Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap) (default: 1) - ```rpc.txsync.defaulttimeout```: Default timeout for eth_sendRawTransactionSync (e.g. 2s, 500ms) (default: 20s) diff --git a/eth/backend.go b/eth/backend.go index 3705f72514..89e287c9d8 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -502,7 +502,11 @@ func (s *Ethereum) APIs() []rpc.API { apis = append(apis, s.engine.APIs(s.BlockChain())...) // BOR change starts - filterSystem := filters.NewFilterSystem(s.APIBackend, filters.Config{}) + filterSystem := filters.NewFilterSystem(s.APIBackend, filters.Config{ + LogCacheSize: s.config.FilterLogCacheSize, + LogQueryLimit: s.config.RPCLogQueryLimit, + RangeLimit: s.config.RPCBlockRangeLimit, + }) // set genesis to public filter api publicFilterAPI := filters.NewFilterAPI(filterSystem, s.config.BorLogs) // avoiding constructor changed by introducing new method to set genesis diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 4447437e60..6297d7ef44 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -231,6 +231,9 @@ type Config struct { // position in eth_getLogs filter criteria (0 = no cap) RPCLogQueryLimit int + // RPCBlockRangeLimit is the maximum block range allowed in eth_getLogs / bor_getLogs (0 = unlimited) + RPCBlockRangeLimit uint64 + // URL to connect to Heimdall node (comma-separated for failover: "url1,url2,url3") HeimdallURL string diff --git a/eth/filters/api.go b/eth/filters/api.go index 5bb0869e3b..3ca35276fa 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -45,6 +45,7 @@ var ( errExceedMaxTopics = errors.New("exceed max topics") errExceedLogQueryLimit = errors.New("exceed max addresses or topics per search position") errExceedMaxTxHashes = errors.New("exceed max number of transaction hashes allowed per transactionReceipts subscription") + errExceedBlockRangeLimit = errors.New("block range exceeds configured limit") ) const ( @@ -56,6 +57,38 @@ const ( maxTxHashes = 200 ) +// resolveBlockNumForRangeCheck converts a raw block number (which may be a negative +// sentinel for symbolic values like "latest", "earliest", etc.) to a concrete +// non-negative height suitable for range arithmetic. It is used only for the +// range-limit guard; the actual filter still receives the original sentinel value. +// - earliest (-5) → 0 +// - all other sentinels (latest, safe, finalized, pending) → head +// - concrete non-negative values → unchanged +func resolveBlockNumForRangeCheck(n int64, head uint64) uint64 { + if n >= 0 { + return uint64(n) + } + if n == rpc.EarliestBlockNumber.Int64() { + return 0 + } + return head // latest, safe, finalized, pending all treated as head +} + +// checkBlockRangeLimit returns errExceedBlockRangeLimit if the range [begin, end] +// exceeds the configured limit after resolving any symbolic block numbers. +// The check is a no-op when limit is 0. +func checkBlockRangeLimit(begin, end int64, head, limit uint64) error { + if limit == 0 { + return nil + } + effectiveBegin := resolveBlockNumForRangeCheck(begin, head) + effectiveEnd := resolveBlockNumForRangeCheck(end, head) + if effectiveEnd >= effectiveBegin && effectiveEnd-effectiveBegin > limit { + return errExceedBlockRangeLimit + } + return nil +} + // filter is a helper struct that holds meta information over the filter type // and associated subscription in the event system. type filter struct { @@ -490,6 +523,10 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type if begin >= 0 && begin < int64(api.events.backend.HistoryPruningCutoff()) { return nil, &history.PrunedHistoryError{} } + head := api.sys.backend.CurrentHeader().Number.Uint64() + if err := checkBlockRangeLimit(begin, end, head, api.sys.cfg.RangeLimit); err != nil { + return nil, err + } // Construct the range filter filter = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics) // Block bor filter @@ -571,6 +608,10 @@ func (api *FilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*types.Lo if f.crit.ToBlock != nil { end = f.crit.ToBlock.Int64() } + head := api.sys.backend.CurrentHeader().Number.Uint64() + if err := checkBlockRangeLimit(begin, end, head, api.sys.cfg.RangeLimit); err != nil { + return nil, err + } // Construct the range filter filter = api.sys.NewRangeFilter(begin, end, f.crit.Addresses, f.crit.Topics) diff --git a/eth/filters/bor_api.go b/eth/filters/bor_api.go index a691970b5d..fca26d99ad 100644 --- a/eth/filters/bor_api.go +++ b/eth/filters/bor_api.go @@ -39,6 +39,13 @@ func (api *FilterAPI) GetBorBlockLogs(ctx context.Context, crit FilterCriteria) if crit.ToBlock != nil { end = crit.ToBlock.Int64() } + if begin > 0 && end > 0 && begin > end { + return nil, errInvalidBlockRange + } + head := api.sys.backend.CurrentHeader().Number.Uint64() + if err := checkBlockRangeLimit(begin, end, head, api.sys.cfg.RangeLimit); err != nil { + return nil, err + } // Construct the range filter filter = NewBorBlockLogsRangeFilter(api.sys.backend, borConfig, begin, end, crit.Addresses, crit.Topics) } diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index 94d77b78bf..85360ec55f 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -44,6 +44,7 @@ type Config struct { LogCacheSize int // maximum number of cached blocks (default: 32) Timeout time.Duration // how long filters stay active (default: 5min) LogQueryLimit int // maximum number of addresses allowed in filter criteria (default: 1000) + RangeLimit uint64 // maximum block range for eth_getLogs / bor_getLogs (0 = unlimited) } func (cfg Config) withDefaults() Config { diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 9f444ceb17..07497c6805 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -996,3 +996,120 @@ func TestTransactionReceiptsSubscription(t *testing.T) { }) } } + +// TestRangeLimit verifies that RangeLimit is enforced for both GetLogs and GetBorBlockLogs. +func TestRangeLimit(t *testing.T) { + t.Parallel() + + const limit = 100 + + gspec := &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{}, + BaseFee: big.NewInt(params.InitialBaseFee), + } + + // Shared setup: small chain with limit=100. + db := rawdb.NewMemoryDatabase() + _, err := gspec.Commit(db, triedb.NewDatabase(db, nil)) + if err != nil { + t.Fatal(err) + } + chain, _ := core.GenerateChain(gspec.Config, gspec.ToBlock(), ethash.NewFaker(), db, 200, func(i int, gen *core.BlockGen) {}) + bc, err := core.NewBlockChain(db, gspec, ethash.NewFaker(), core.DefaultConfig().WithStateScheme(rawdb.HashScheme)) + if err != nil { + t.Fatal(err) + } + if _, err = bc.InsertChain(chain[:200], false); err != nil { + t.Fatal(err) + } + + backend, sys := newTestFilterSystem(db, Config{RangeLimit: limit}) + api := NewFilterAPI(sys, true) + api.SetChainConfig(params.BorUnittestChainConfig) + + backend.startFilterMaps(0, false, filtermaps.RangeTestParams) + defer backend.stopFilterMaps() + + // Numeric ranges exceeding the limit must be rejected. + overLimitCases := []FilterCriteria{ + {FromBlock: big.NewInt(0), ToBlock: big.NewInt(int64(limit) + 1)}, + {FromBlock: big.NewInt(0), ToBlock: big.NewInt(10000)}, + } + + for i, crit := range overLimitCases { + _, err := api.GetLogs(t.Context(), crit) + if !errors.Is(err, errExceedBlockRangeLimit) { + t.Errorf("GetLogs over-limit case %d: got %v, want errExceedBlockRangeLimit", i, err) + } + + _, err = api.GetBorBlockLogs(t.Context(), crit) + if !errors.Is(err, errExceedBlockRangeLimit) { + t.Errorf("GetBorBlockLogs over-limit case %d: got %v, want errExceedBlockRangeLimit", i, err) + } + } + + // Symbolic "latest" as toBlock must also be caught when the chain is longer than the limit. + // The chain has 200 blocks, limit is 100, so fromBlock=0 to toBlock=latest spans 200 > 100. + latestCrit := FilterCriteria{FromBlock: big.NewInt(0), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())} + if _, err := api.GetLogs(t.Context(), latestCrit); !errors.Is(err, errExceedBlockRangeLimit) { + t.Errorf("GetLogs with fromBlock=0 toBlock=latest on 200-block chain: got %v, want errExceedBlockRangeLimit", err) + } + if _, err := api.GetBorBlockLogs(t.Context(), latestCrit); !errors.Is(err, errExceedBlockRangeLimit) { + t.Errorf("GetBorBlockLogs with fromBlock=0 toBlock=latest on 200-block chain: got %v, want errExceedBlockRangeLimit", err) + } + + // GetFilterLogs (eth_getFilterLogs) must also respect the limit. + filterID, err := api.NewFilter(FilterCriteria{ + FromBlock: big.NewInt(0), + ToBlock: big.NewInt(int64(limit) + 1), + }) + if err != nil { + t.Fatal(err) + } + if _, err := api.GetFilterLogs(t.Context(), filterID); !errors.Is(err, errExceedBlockRangeLimit) { + t.Errorf("GetFilterLogs over-limit: got %v, want errExceedBlockRangeLimit", err) + } + + // A range exactly at the limit (end - begin == limit) must succeed. + atLimitCrit := FilterCriteria{FromBlock: big.NewInt(0), ToBlock: big.NewInt(int64(limit))} + if _, err := api.GetLogs(t.Context(), atLimitCrit); errors.Is(err, errExceedBlockRangeLimit) { + t.Error("GetLogs at exact limit should not return errExceedBlockRangeLimit") + } + if _, err := api.GetBorBlockLogs(t.Context(), atLimitCrit); errors.Is(err, errExceedBlockRangeLimit) { + t.Error("GetBorBlockLogs at exact limit should not return errExceedBlockRangeLimit") + } + + // With RangeLimit=0 (unlimited), large ranges must not trigger errExceedBlockRangeLimit. + db2 := rawdb.NewMemoryDatabase() + if _, err = gspec.Commit(db2, triedb.NewDatabase(db2, nil)); err != nil { + t.Fatal(err) + } + chain2, _ := core.GenerateChain(gspec.Config, gspec.ToBlock(), ethash.NewFaker(), db2, 200, func(i int, gen *core.BlockGen) {}) + bc2, err := core.NewBlockChain(db2, gspec, ethash.NewFaker(), core.DefaultConfig().WithStateScheme(rawdb.HashScheme)) + if err != nil { + t.Fatal(err) + } + if _, err = bc2.InsertChain(chain2[:200], false); err != nil { + t.Fatal(err) + } + + backend2, sys2 := newTestFilterSystem(db2, Config{RangeLimit: 0}) + api2 := NewFilterAPI(sys2, true) + api2.SetChainConfig(params.BorUnittestChainConfig) + + backend2.startFilterMaps(0, false, filtermaps.RangeTestParams) + defer backend2.stopFilterMaps() + + bigRangeCrit := FilterCriteria{FromBlock: big.NewInt(0), ToBlock: big.NewInt(int64(limit) * 10)} + if _, err := api2.GetLogs(t.Context(), bigRangeCrit); errors.Is(err, errExceedBlockRangeLimit) { + t.Error("GetLogs with RangeLimit=0 should not return errExceedBlockRangeLimit") + } + if _, err := api2.GetBorBlockLogs(t.Context(), bigRangeCrit); errors.Is(err, errExceedBlockRangeLimit) { + t.Error("GetBorBlockLogs with RangeLimit=0 should not return errExceedBlockRangeLimit") + } + + // Suppress unused variable warnings. + _ = bc + _ = bc2 +} diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go index fcd4e31748..496b3e5555 100644 --- a/internal/cli/server/config.go +++ b/internal/cli/server/config.go @@ -469,6 +469,9 @@ type JsonRPCConfig struct { // LogQueryLimit is the max number of addresses or topics allowed in filter criteria for eth_getLogs. LogQueryLimit int `hcl:"logquerylimit,optional" toml:"logquerylimit,optional"` + // RangeLimit is the maximum block range allowed for eth_getLogs and bor_getLogs (0 = no limit). + RangeLimit uint64 `hcl:"rangelimit,optional" toml:"rangelimit,optional"` + // Http has the json-rpc http related settings Http *APIConfig `hcl:"http,block" toml:"http,block"` @@ -1602,6 +1605,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (* n.TxSyncMaxTimeout = c.JsonRPC.TxSyncMaxTimeout n.RPCLogQueryLimit = c.JsonRPC.LogQueryLimit + n.RPCBlockRangeLimit = c.JsonRPC.RangeLimit // Choose the sync mode switch c.SyncMode { diff --git a/internal/cli/server/flags.go b/internal/cli/server/flags.go index 602bdb1c7f..78bbed01f4 100644 --- a/internal/cli/server/flags.go +++ b/internal/cli/server/flags.go @@ -722,6 +722,13 @@ func (c *Command) Flags(config *Config) *flagset.Flagset { Default: c.cliConfig.JsonRPC.LogQueryLimit, Group: "JsonRPC", }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "rpc.rangelimit", + Usage: "Maximum block range allowed for eth_getLogs and bor_getLogs (0 = no limit)", + Value: &c.cliConfig.JsonRPC.RangeLimit, + Default: c.cliConfig.JsonRPC.RangeLimit, + Group: "JsonRPC", + }) f.BoolFlag(&flagset.BoolFlag{ Name: "rpc.allow-unprotected-txs", Usage: "Allow for unprotected (non EIP155 signed) transactions to be submitted via RPC", From 155fe5e06c06aaf84c881794d1f19a3905ebda85 Mon Sep 17 00:00:00 2001 From: marcello33 Date: Mon, 16 Mar 2026 17:04:13 +0100 Subject: [PATCH 16/20] gomemlimit fix: additional changes for configs / fix gomemlimit --- internal/cli/server/config.go | 57 ++++++++++++++----- .../mainnet-v1/sentry/sentry/bor/config.toml | 11 ++-- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go index 496b3e5555..8253190bdd 100644 --- a/internal/cli/server/config.go +++ b/internal/cli/server/config.go @@ -4,11 +4,10 @@ import ( "crypto/ecdsa" "fmt" - // "math" + "math" "math/big" "os" "path/filepath" - "regexp" "runtime" "strconv" "strings" @@ -1511,11 +1510,11 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (* } // Apply configurable garbage collection settings with validation if c.Cache.GoMemLimit != "" { - if err := validateGoMemLimit(c.Cache.GoMemLimit); err != nil { + if bytes, err := parseGoMemLimit(c.Cache.GoMemLimit); err != nil { log.Warn("Invalid GOMEMLIMIT value, skipping", "value", c.Cache.GoMemLimit, "error", err) } else { - os.Setenv("GOMEMLIMIT", c.Cache.GoMemLimit) - log.Info("Set GOMEMLIMIT", "value", c.Cache.GoMemLimit) + prev := godebug.SetMemoryLimit(bytes) + log.Info("Set GOMEMLIMIT via runtime", "value", c.Cache.GoMemLimit, "bytes", bytes, "previous", prev) } } @@ -2137,21 +2136,49 @@ func MakePasswordListFromFile(path string) ([]string, error) { return lines, nil } -// validateGoMemLimit validates GOMEMLIMIT values -func validateGoMemLimit(value string) error { - // GOMEMLIMIT can be: - // - A number followed by optional unit (B, KB, MB, GB, TB, PB) - // - "off" to disable soft limit +// parseGoMemLimit parses a GOMEMLIMIT string (e.g. "100GB", "1024MB", "off") +// into bytes. Returns math.MaxInt64 for "off" (no limit). +func parseGoMemLimit(value string) (int64, error) { if value == "off" { - return nil + return math.MaxInt64, nil } - // Parse the value to validate format - if matched, _ := regexp.MatchString(`^[0-9]+(\.[0-9]+)?[KMGTPE]?[iB]?$`, value); !matched { - return fmt.Errorf("invalid GOMEMLIMIT format, expected number with optional unit (e.g., '8GB', '1024MB')") + // Split numeric prefix from unit suffix + i := 0 + for i < len(value) && (value[i] >= '0' && value[i] <= '9' || value[i] == '.') { + i++ } - return nil + if i == 0 { + return 0, fmt.Errorf("invalid GOMEMLIMIT: no numeric value in %q", value) + } + + numStr := value[:i] + unit := value[i:] + + num, err := strconv.ParseFloat(numStr, 64) + if err != nil { + return 0, fmt.Errorf("invalid GOMEMLIMIT number %q: %v", numStr, err) + } + + var multiplier float64 + + switch unit { + case "", "B": + multiplier = 1 + case "KB", "KiB": + multiplier = 1024 + case "MB", "MiB": + multiplier = 1024 * 1024 + case "GB", "GiB": + multiplier = 1024 * 1024 * 1024 + case "TB", "TiB": + multiplier = 1024 * 1024 * 1024 * 1024 + default: + return 0, fmt.Errorf("invalid GOMEMLIMIT unit %q (expected B, KB, MB, GB, TB)", unit) + } + + return int64(num * multiplier), nil } // sanitizeGoGC clamps GOGC values to reasonable bounds diff --git a/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml b/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml index e7ed595b75..2fc5bb4147 100644 --- a/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml +++ b/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml @@ -151,11 +151,12 @@ syncmode = "full" # [telemetry.influx.tags] [cache] - cache = 4096 - # gc = 25 - # snapshot = 10 - # database = 50 - # trie = 15 + cache = 49152 + gc = 5 + gomemlimit = "100GB" + snapshot = 25 + database = 45 + trie = 25 # noprefetch = false # preimages = false # txlookuplimit = 2350000 From e6bfe34dc3151d8cf3ef830dd00d2666d18a89ee Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Thu, 19 Mar 2026 17:32:40 +0530 Subject: [PATCH 17/20] update the config to initial state --- .../mainnet-v1/sentry/sentry/bor/config.toml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml b/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml index 2fc5bb4147..e7ed595b75 100644 --- a/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml +++ b/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml @@ -151,12 +151,11 @@ syncmode = "full" # [telemetry.influx.tags] [cache] - cache = 49152 - gc = 5 - gomemlimit = "100GB" - snapshot = 25 - database = 45 - trie = 25 + cache = 4096 + # gc = 25 + # snapshot = 10 + # database = 50 + # trie = 15 # noprefetch = false # preimages = false # txlookuplimit = 2350000 From 21922c7f5758a758912dddc416023f57a3875036 Mon Sep 17 00:00:00 2001 From: Manav Darji Date: Thu, 19 Mar 2026 18:00:06 +0530 Subject: [PATCH 18/20] core/vm: add kzg precompile back for madhugiri forks (#2140) * core/vm: add kzg precompile back for madhugiri forks * core/vm: remove kzg from prague * core/vm: update kzg tests to ensure activation and removal * core/vm: improve test * core/vm: typo Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * core/vm: improve test --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- core/vm/contracts.go | 3 ++ core/vm/contracts_test.go | 61 +++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 6f061fa784..8fab06ae9c 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -160,6 +160,7 @@ var PrecompiledContractsOsaka = PrecompiledContracts{ common.BytesToAddress([]byte{0x07}): &bn256ScalarMulIstanbul{}, common.BytesToAddress([]byte{0x08}): &bn256PairingIstanbul{}, common.BytesToAddress([]byte{0x09}): &blake2F{}, + // Note: Make sure to remove this (kzg) from bor if we ever enable Osaka as is. common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{}, common.BytesToAddress([]byte{0x0b}): &bls12381G1Add{}, common.BytesToAddress([]byte{0x0c}): &bls12381G1MultiExp{}, @@ -190,6 +191,7 @@ var PrecompiledContractsMadhugiri = PrecompiledContracts{ common.BytesToAddress([]byte{0x07}): &bn256ScalarMulIstanbul{}, common.BytesToAddress([]byte{0x08}): &bn256PairingIstanbul{}, common.BytesToAddress([]byte{0x09}): &blake2F{}, + common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{}, common.BytesToAddress([]byte{0x0b}): &bls12381G1Add{}, common.BytesToAddress([]byte{0x0c}): &bls12381G1MultiExp{}, common.BytesToAddress([]byte{0x0d}): &bls12381G2Add{}, @@ -211,6 +213,7 @@ var PrecompiledContractsMadhugiriPro = PrecompiledContracts{ common.BytesToAddress([]byte{0x07}): &bn256ScalarMulIstanbul{}, common.BytesToAddress([]byte{0x08}): &bn256PairingIstanbul{}, common.BytesToAddress([]byte{0x09}): &blake2F{}, + common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{}, common.BytesToAddress([]byte{0x0b}): &bls12381G1Add{}, common.BytesToAddress([]byte{0x0c}): &bls12381G1MultiExp{}, common.BytesToAddress([]byte{0x0d}): &bls12381G2Add{}, diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index d07fd314e2..ddb01122a0 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -589,37 +589,42 @@ func TestLisovoCLZOpcode(t *testing.T) { } // TestKZGPointEvaluationPrecompileRemoval verifies that the kzgPointEvaluation precompile -// is present before LisovoPro and removed starting with LisovoPro. +// is present from Madhugiri through Lisovo, and is not present before Madhugiri and starting +// with LisovoPro. func TestKZGPointEvaluationPrecompileRemoval(t *testing.T) { t.Parallel() kzgPointEvaluationAddr := common.BytesToAddress([]byte{0x0a}) - - // Test Lisovo: should have kzgPointEvaluation - lisovoRules := params.Rules{ - IsLisovo: true, - IsMadhugiriPro: true, - IsMadhugiri: true, - } - lisovoPrecompiles := ActivePrecompiledContracts(lisovoRules) - if _, exists := lisovoPrecompiles[kzgPointEvaluationAddr]; !exists { - t.Error("kzgPointEvaluation (0x0a) should exist in Lisovo precompiles") - } - - // Verify it's the correct type - if _, ok := lisovoPrecompiles[kzgPointEvaluationAddr].(*kzgPointEvaluation); !ok { - t.Error("precompile at 0x0a should be kzgPointEvaluation type in Lisovo") - } - - // Test LisovoPro: should not have kzgPointEvaluation - lisovoProRules := params.Rules{ - IsLisovoPro: true, - IsLisovo: true, - IsMadhugiriPro: true, - IsMadhugiri: true, - } - lisovoProPrecompiles := ActivePrecompiledContracts(lisovoProRules) - if _, exists := lisovoProPrecompiles[kzgPointEvaluationAddr]; exists { - t.Error("kzgPointEvaluation (0x0a) should not exist in LisovoPro precompiles") + kzgPointEvaluationPrecompile := &kzgPointEvaluation{} + + // We verify a few things in this test: + // - Madhugiri, MadhugiriPro, and Lisovo have the kzg precompile enabled + // - LisovoPro removes it, so it should not be enabled + // - Hard forks before Madhugiri (for example, Prague) should not have kzg enabled + type testCase struct { + name string + rules params.Rules + shouldHaveKzg bool + } + cases := []testCase{ + {name: "Cancun (Pre-Madhugiri)", rules: params.Rules{IsCancun: true}, shouldHaveKzg: false}, + {name: "Prague (Pre-Madhugiri)", rules: params.Rules{IsPrague: true}, shouldHaveKzg: false}, + {name: "Madhugiri", rules: params.Rules{IsMadhugiri: true}, shouldHaveKzg: true}, + {name: "MadhugiriPro", rules: params.Rules{IsMadhugiriPro: true}, shouldHaveKzg: true}, + {name: "Lisovo", rules: params.Rules{IsLisovo: true}, shouldHaveKzg: true}, + {name: "LisovoPro", rules: params.Rules{IsLisovoPro: true}, shouldHaveKzg: false}, + } + for _, tc := range cases { + precompiles := ActivePrecompiledContracts(tc.rules) + pc, exists := precompiles[kzgPointEvaluationAddr] + if tc.shouldHaveKzg && !exists { + t.Errorf("kzgPointEvaluation (0x0a) should exist in %v precompiles", tc.name) + } + if !tc.shouldHaveKzg && exists { + t.Errorf("kzgPointEvaluation (0x0a) should not exist in %v precompiles", tc.name) + } + if exists && pc.Name() != kzgPointEvaluationPrecompile.Name() { + t.Errorf("invalid precompile loaded instead of kzgPointEvaluation (0x0a). expected name: %s, got name: %s, test case: %s", kzgPointEvaluationPrecompile.Name(), pc.Name(), tc.name) + } } } From c2f2cee670b3560219fb64a17beb6be152595d84 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Thu, 19 Mar 2026 18:01:32 +0530 Subject: [PATCH 19/20] params: version bump to v2.7.0-beta3 --- params/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/params/version.go b/params/version.go index cd4cf256c1..106eca690c 100644 --- a/params/version.go +++ b/params/version.go @@ -26,7 +26,7 @@ const ( VersionMajor = 2 // Major version component of the current release VersionMinor = 7 // Minor version component of the current release VersionPatch = 0 // Patch version component of the current release - VersionMeta = "beta2" // Version metadata to append to the version string + VersionMeta = "beta3" // Version metadata to append to the version string ) var ( From cc1cc0d57122ea4d98939a336822d27c6e971e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Do=C5=A1li=C4=87?= Date: Thu, 26 Mar 2026 16:15:38 +0100 Subject: [PATCH 20/20] Remove deprecated VM tracing flags from server CLI --- internal/cli/server/flags.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/internal/cli/server/flags.go b/internal/cli/server/flags.go index fe365a6ffc..53c4b174e2 100644 --- a/internal/cli/server/flags.go +++ b/internal/cli/server/flags.go @@ -45,18 +45,6 @@ func (c *Command) Flags(config *Config) *flagset.Flagset { Value: &c.cliConfig.EnablePreimageRecording, Default: c.cliConfig.EnablePreimageRecording, }) - f.StringFlag(&flagset.StringFlag{ - Name: "vmtrace", - Usage: "Name of tracer which should observe internal VM operations (e.g. 'json')", - Value: &c.cliConfig.VMTrace, - Default: c.cliConfig.VMTrace, - }) - f.StringFlag(&flagset.StringFlag{ - Name: "vmtrace.jsonconfig", - Usage: "Tracer configuration (JSON)", - Value: &c.cliConfig.VMTraceJsonConfig, - Default: c.cliConfig.VMTraceJsonConfig, - }) f.StringFlag(&flagset.StringFlag{ Name: "datadir.ancient", Usage: "Data directory for ancient chain segments (default = inside chaindata)",