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/cmd/keeper/go.mod b/cmd/keeper/go.mod index 768125cbd1..1da16dd486 100644 --- a/cmd/keeper/go.mod +++ b/cmd/keeper/go.mod @@ -52,6 +52,7 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/time v0.12.0 // indirect + golang.org/x/sys v0.40.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index 9a9c4a2cab..518f26bbd9 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -1170,6 +1170,19 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header, w return nil } +// extractVMConfig retrieves the vm.Config from the chain reader, if available. +func extractVMConfig(chain consensus.ChainHeaderReader) vm.Config { + if bc, ok := chain.(*core.BlockChain); ok { + return *bc.GetVMConfig() + } + if hc, ok := chain.(*core.HeaderChain); ok { + if cfg := hc.GetVMConfig(); cfg != nil { + return *cfg + } + } + return vm.Config{} +} + // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given. func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, wrappedState vm.StateDB, body *types.Body, receipts []*types.Receipt) []*types.Receipt { @@ -1186,12 +1199,29 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, err error ) + vmCfg := extractVMConfig(chain) + 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) + 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 { + vmenv := vm.NewEVM( + core.NewEVMBlockContext(header, cx, &header.Coinbase), + wrappedState, c.chainConfig, vmCfg, + ) + hooks.OnTxStart(vmenv.GetVMContext(), lastTx, statefull.GetSystemAddress()) + } + } + } + // 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 } @@ -1199,7 +1229,7 @@ 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 @@ -1230,7 +1260,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 { @@ -1241,7 +1271,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 @@ -1274,6 +1304,12 @@ func insertStateSyncTransactionAndCalculateReceipt(stateSyncTx *types.Transactio } stateSyncReceipt.Bloom = types.CreateBloom(stateSyncReceipt) + + // End tracing for StateSyncTx + if hooks := vmConfig.Tracer; hooks != nil && hooks.OnTxEnd != nil { + hooks.OnTxEnd(stateSyncReceipt, nil) + } + receipts = append(receipts, stateSyncReceipt) return receipts @@ -1332,12 +1368,14 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ err error ) + vmCfg := extractVMConfig(chain) + 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 } @@ -1345,7 +1383,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 @@ -1371,7 +1409,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) @@ -1573,6 +1611,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() @@ -1589,7 +1628,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 @@ -1628,6 +1667,7 @@ func (c *Bor) FetchAndCommitSpan( state vm.StateDB, header *types.Header, chain core.ChainContext, + vmCfg vm.Config, ) error { var ( minSpan borTypes.Span @@ -1691,7 +1731,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 @@ -1699,6 +1739,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() @@ -1812,7 +1853,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 6caf7bf280..751ca477b5 100644 --- a/consensus/bor/bor_test.go +++ b/consensus/bor/bor_test.go @@ -67,7 +67,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") } @@ -80,7 +80,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") } @@ -2102,7 +2102,7 @@ func TestInsertStateSyncTransactionAndCalculateReceipt(t *testing.T) { }, } - receipts := insertStateSyncTransactionAndCalculateReceipt(stateSyncTx, header, body, mockState, existingReceipts) + receipts := insertStateSyncTransactionAndCalculateReceipt(stateSyncTx, header, body, mockState, existingReceipts, vm.Config{}) require.Len(t, receipts, 2) ssReceipt := receipts[1] @@ -2673,7 +2673,7 @@ func TestFetchAndCommitSpan_WithHeimdallClient(t *testing.T) { h := &types.Header{Number: big.NewInt(64), ParentHash: genesis.Hash()} - err := b.FetchAndCommitSpan(context.Background(), 1, statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + err := b.FetchAndCommitSpan(context.Background(), 1, statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.NoError(t, err) } @@ -2707,7 +2707,7 @@ func TestFetchAndCommitSpan_ChainIDMismatch(t *testing.T) { h := &types.Header{Number: big.NewInt(64), ParentHash: genesis.Hash()} - err := b.FetchAndCommitSpan(context.Background(), 1, statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + err := b.FetchAndCommitSpan(context.Background(), 1, statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.Error(t, err) require.Contains(t, err.Error(), "doesn't match") } @@ -2726,7 +2726,7 @@ func TestFetchAndCommitSpan_NilResponse(t *testing.T) { h := &types.Header{Number: big.NewInt(64), ParentHash: genesis.Hash()} - err := b.FetchAndCommitSpan(context.Background(), 1, statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + err := b.FetchAndCommitSpan(context.Background(), 1, statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.Error(t, err) } @@ -2771,7 +2771,7 @@ func TestCommitStates_WithOverrideSkip(t *testing.T) { h := &types.Header{Number: big.NewInt(16), ParentHash: genesis.Hash(), Time: genesis.Time + 32} // CommitStates with override that sets records to 0 should skip - result, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + result, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.NoError(t, err) require.Empty(t, result) } @@ -2803,7 +2803,7 @@ func TestCommitStates_WithIndore(t *testing.T) { h := &types.Header{Number: big.NewInt(16), ParentHash: genesis.Hash(), Time: genesis.Time + 32} - result, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + result, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.NoError(t, err) require.Empty(t, result) // no events } @@ -2846,7 +2846,7 @@ func TestCommitStates_WithEvents(t *testing.T) { h := &types.Header{Number: big.NewInt(16), ParentHash: genesis.Hash(), Time: uint64(now.Unix())} - result, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + result, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.NoError(t, err) require.Len(t, result, 1) require.Equal(t, uint64(1), result[0].ID) @@ -3222,7 +3222,7 @@ type mockGenesisContractForCommitStatesIndore struct { gasUsed uint64 } -func (m *mockGenesisContractForCommitStatesIndore) CommitState(event *clerk.EventRecordWithTime, state vm.StateDB, header *types.Header, chCtx statefull.ChainContext) (uint64, error) { +func (m *mockGenesisContractForCommitStatesIndore) CommitState(event *clerk.EventRecordWithTime, state vm.StateDB, header *types.Header, chCtx statefull.ChainContext, vmCfg vm.Config) (uint64, error) { return m.gasUsed, nil } @@ -3282,7 +3282,7 @@ func TestCommitStates_WithIndore_EventProcessing(t *testing.T) { Time: uint64(now.Unix()), } - result, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + result, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.NoError(t, err) require.Len(t, result, 2) // both events should be processed } @@ -3336,7 +3336,7 @@ func TestCommitStates_NonIndore(t *testing.T) { Time: uint64(time.Now().Unix()), } - result, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + result, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.NoError(t, err) require.Len(t, result, 1) } @@ -3861,7 +3861,7 @@ func TestCommitStates_WithOverrideStateSyncRecords(t *testing.T) { GasLimit: genesis.GasLimit, } - data, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + data, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.NoError(t, err) // With OverrideStateSyncRecords truncating to 0, should get empty data require.Empty(t, data) @@ -4121,7 +4121,7 @@ func TestCommitStates_WithOverrideStateSyncRecordsInRange(t *testing.T) { GasLimit: genesis.GasLimit, } - data, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + data, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.NoError(t, err) require.Empty(t, data) // truncated to 0 by range override } @@ -4160,7 +4160,7 @@ func TestCommitStates_StateSyncEventsError(t *testing.T) { GasLimit: genesis.GasLimit, } - data, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + data, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.NoError(t, err) // error is logged but returns empty data require.Empty(t, data) } @@ -4210,7 +4210,7 @@ func TestCommitStates_EventIdLessThanLastStateId(t *testing.T) { GasLimit: genesis.GasLimit, } - data, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + data, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.NoError(t, err) // Event ID=3 should be skipped (3 <= 5), event ID=6 should be processed require.Len(t, data, 1) @@ -4259,7 +4259,7 @@ func TestCommitStates_EventValidationError(t *testing.T) { GasLimit: genesis.GasLimit, } - data, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}) + data, err := b.CommitStates(statedb, h, statefull.ChainContext{Chain: chain.HeaderChain(), Bor: b}, vm.Config{}) require.NoError(t, err) // validation error is logged but returned data should be empty require.Empty(t, data) } 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/genesis_contract_mock.go b/consensus/bor/genesis_contract_mock.go index a95129d61a..965676148c 100644 --- a/consensus/bor/genesis_contract_mock.go +++ b/consensus/bor/genesis_contract_mock.go @@ -42,18 +42,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/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/span_mock.go b/consensus/bor/span_mock.go index f9d0f3f316..6417b590d2 100644 --- a/consensus/bor/span_mock.go +++ b/consensus/bor/span_mock.go @@ -45,17 +45,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. diff --git a/consensus/bor/statefull/processor.go b/consensus/bor/statefull/processor.go index 2e714d0e56..98eb7c48ae 100644 --- a/consensus/bor/statefull/processor.go +++ b/consensus/bor/statefull/processor.go @@ -19,6 +19,10 @@ import ( var systemAddress = common.HexToAddress("0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE") +func GetSystemAddress() common.Address { + return systemAddress +} + type ChainContext struct { Chain consensus.ChainHeaderReader Bor consensus.Engine @@ -88,6 +92,7 @@ func ApplyMessage( header *types.Header, chainConfig *params.ChainConfig, chainContext core.ChainContext, + vmConfig vm.Config, ) (uint64, error) { initialGas := msg.Gas() @@ -96,7 +101,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/core/blockchain.go b/core/blockchain.go index 39d65ba7d3..080a74e252 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -490,6 +490,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) { diff --git a/core/state_processor.go b/core/state_processor.go index 4c6bd9d181..ffb6ab3dbb 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -154,7 +154,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()) { diff --git a/docs/cli/example_config.toml b/docs/cli/example_config.toml index 33b6249046..05680c467a 100644 --- a/docs/cli/example_config.toml +++ b/docs/cli/example_config.toml @@ -31,11 +31,13 @@ enable-private-tx = true # Enable private transaction relay bp-rpc-endpoints = [ ] # Comma separated rpc endpoints of all block producers [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 93e1501d11..2de960b4e6 100644 --- a/docs/cli/server.md +++ b/docs/cli/server.md @@ -280,6 +280,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 b0f15b6e5c..a2e2d55c1e 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -745,7 +745,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/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 7d6325646e..1811c3809b 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -142,6 +142,9 @@ func TestSupplyRewards(t *testing.T) { ) expected := supplyInfo{ + Issuance: &supplyInfoIssuance{ + Reward: (*hexutil.Big)(ethash.ConstantinopleBlockReward.ToBig()), + }, Number: 1, Hash: common.HexToHash("0xcbb08370505be503dafedc4e96d139ea27aba3cbc580148568b8a307b3f51052"), ParentHash: common.HexToHash("0xadeda0a83e337b6c073e3f0e9a17531a04009b397a9588c093b628f21b8bc5a3"), @@ -251,6 +254,9 @@ func TestSupplyEip1559Burn(t *testing.T) { head = chain.CurrentBlock() burn = new(big.Int).Mul(big.NewInt(21000), head.BaseFee) expected = supplyInfo{ + Issuance: &supplyInfoIssuance{ + Reward: (*hexutil.Big)(ethash.ConstantinopleBlockReward.ToBig()), + }, Burn: &supplyInfoBurn{ EIP1559: (*hexutil.Big)(burn), }, diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go index 745d35fd56..fa689db360 100644 --- a/internal/cli/server/config.go +++ b/internal/cli/server/config.go @@ -173,6 +173,12 @@ type Config struct { // Relay has transaction relay related settings Relay *RelayConfig `hcl:"relay,block" toml:"relay,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 { @@ -1050,6 +1056,8 @@ func DefaultConfig() *Config { LogNoHistory: ethconfig.Defaults.LogNoHistory, StateHistory: params.FullImmutabilityThreshold, }, + VMTrace: "", + VMTraceJsonConfig: "{}", Health: &HealthConfig{ MaxGoRoutineThreshold: 0, WarnGoRoutineThreshold: 0, @@ -1678,6 +1686,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 602bdb1c7f..288e8f5401 100644 --- a/internal/cli/server/flags.go +++ b/internal/cli/server/flags.go @@ -155,6 +155,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", diff --git a/tests/bor/bor_test.go b/tests/bor/bor_test.go index a7eac3e37f..85114579b7 100644 --- a/tests/bor/bor_test.go +++ b/tests/bor/bor_test.go @@ -2928,7 +2928,7 @@ func getMockedSpannerWithSpanRotation(t *testing.T, validator1, validator2 commo } spanner.EXPECT().GetCurrentSpan(gomock.Any(), gomock.Any(), gomock.Any()).Return(span1Mock, nil).AnyTimes() - spanner.EXPECT().CommitSpan(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + spanner.EXPECT().CommitSpan(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() return spanner } diff --git a/tests/bor/helper.go b/tests/bor/helper.go index 81df9ab851..3eae031097 100644 --- a/tests/bor/helper.go +++ b/tests/bor/helper.go @@ -495,7 +495,7 @@ func getMockedSpanner(t *testing.T, validators []*valset.Validator) *bor.MockSpa spanner.EXPECT().GetCurrentValidatorsByHash(gomock.Any(), gomock.Any(), gomock.Any()).Return(validators, nil).AnyTimes() spanner.EXPECT().GetCurrentValidatorsByBlockNrOrHash(gomock.Any(), gomock.Any(), gomock.Any()).Return(validators, nil).AnyTimes() spanner.EXPECT().GetCurrentSpan(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockSpan, nil).AnyTimes() - spanner.EXPECT().CommitSpan(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + spanner.EXPECT().CommitSpan(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() return spanner }