Skip to content
This repository was archived by the owner on Jun 9, 2024. It is now read-only.

Commit a41940c

Browse files
author
Devon Bear
authored
feat(rpc): implement debug api (#1340)
NOTE: endpoints that require tracing "bad blocks" are currently not supported (debug_traceBadBlock, debug_intermediateRoots, debug_standardTraceBadBlockToFile) ## Summary by CodeRabbit - **New Features** - Enhanced tracing capabilities with new backend methods for state retrieval at specific blocks and transactions. - Expanded API services for "web3" and "debug" namespaces to improve user interactions and debugging processes. - **Documentation** - Updated public interface documentation to reflect newly added methods and functionalities.
1 parent 58177db commit a41940c

File tree

7 files changed

+181
-24
lines changed

7 files changed

+181
-24
lines changed

eth/core/chain.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,6 @@ func (bc *blockchain) PreparePlugins(ctx context.Context) {
156156
}
157157
}
158158

159-
// ChainConfig returns the Ethereum chain config of the chain.
160-
func (bc *blockchain) Config() *params.ChainConfig {
161-
return bc.config
162-
}
163-
164159
// loadLastState loads the last known chain state from the database. This method
165160
// assumes that the chain manager mutex is held.
166161
func (bc *blockchain) loadLastState(number uint64) error {

eth/core/chain_resources.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,38 @@
2121
package core
2222

2323
import (
24+
"context"
2425
"errors"
26+
"fmt"
2527

2628
"github.com/berachain/polaris/eth/core/state"
2729

2830
"github.com/ethereum/go-ethereum/common"
31+
gethcore "github.com/ethereum/go-ethereum/core"
32+
ethtypes "github.com/ethereum/go-ethereum/core/types"
2933
"github.com/ethereum/go-ethereum/core/vm"
34+
"github.com/ethereum/go-ethereum/eth/tracers"
3035
"github.com/ethereum/go-ethereum/params"
3136
)
3237

3338
// ChainResources is the interface that defines functions for code paths within the chain to
3439
// acquire resources to use in execution such as StateDBss and EVMss.
3540
type ChainResources interface {
41+
// state snapshots
3642
StateAtBlockNumber(uint64) (state.StateDB, error)
3743
StateAt(root common.Hash) (state.StateDB, error)
44+
45+
// state for tracing
46+
StateAtBlock(
47+
_ context.Context, block *ethtypes.Block, _ uint64,
48+
_ state.StateDB, _ bool, _ bool,
49+
) (state.StateDB, tracers.StateReleaseFunc, error)
50+
StateAtTransaction(
51+
ctx context.Context, block *ethtypes.Block,
52+
txIndex int, reexec uint64,
53+
) (*gethcore.Message, vm.BlockContext, state.StateDB, tracers.StateReleaseFunc, error)
54+
55+
// vm/chain config
3856
GetVMConfig() *vm.Config
3957
Config() *params.ChainConfig
4058
}
@@ -76,7 +94,103 @@ func (bc *blockchain) HasBlockAndState(hash common.Hash, number uint64) bool {
7694
return true
7795
}
7896

97+
// StateAtBlock retrieves the state database associated with a certain block.
98+
// If no state is locally available for the given block, a number of blocks
99+
// are attempted to be reexecuted to generate the desired state. The optional
100+
// base layer statedb can be provided which is regarded as the statedb of the
101+
// parent block.
102+
//
103+
// An additional release function will be returned if the requested state is
104+
// available. Release is expected to be invoked when the returned state is no
105+
// longer needed. Its purpose is to prevent resource leaking. Though it can be
106+
// noop in some cases.
107+
//
108+
// Parameters:
109+
// - block: The block for which we want the state(state = block.Root)
110+
// - reexec: The maximum number of blocks to reprocess trying to obtain the desired state
111+
// - base: If the caller is tracing multiple blocks, the caller can provide the parent
112+
// state continuously from the callsite.
113+
// - readOnly: If true, then the live 'blockchain' state database is used. No mutation should
114+
// be made from caller, e.g. perform Commit or other 'save-to-disk' changes.
115+
// Otherwise, the trash generated by caller may be persisted permanently.
116+
// - preferDisk: This arg can be used by the caller to signal that even though the 'base' is
117+
// provided, it would be preferable to start from a fresh state, if we have it
118+
// on disk.
119+
func (bc *blockchain) StateAtBlock(
120+
_ context.Context, block *ethtypes.Block, _ uint64,
121+
_ state.StateDB, _ bool, _ bool,
122+
) (state.StateDB, tracers.StateReleaseFunc, error) {
123+
// Check if the requested state is available in the live chain.
124+
statedb, err := bc.StateAtBlockNumber(block.Number().Uint64())
125+
if err != nil {
126+
// If there is an error, it means the state is not available.
127+
// TODO: Historic state is not supported in path-based scheme.
128+
// Fully archive node in pbss will be implemented by relying
129+
// on state history, but needs more work on top.
130+
return nil, nil, errors.Join(
131+
err, errors.New("historical state not available in path scheme yet"),
132+
)
133+
}
134+
135+
// If there is no error, return the state, a no-op function, and no error.
136+
return statedb, func() {}, nil
137+
}
138+
139+
// StateAtTransaction returns the execution environment of a certain transaction.
140+
func (bc *blockchain) StateAtTransaction(
141+
ctx context.Context, block *ethtypes.Block,
142+
txIndex int, reexec uint64,
143+
) (*gethcore.Message, vm.BlockContext, state.StateDB, tracers.StateReleaseFunc, error) {
144+
// Short circuit if it's genesis block.
145+
if block.NumberU64() == 0 {
146+
return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis")
147+
}
148+
// Create the parent state database
149+
parent := bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
150+
if parent == nil {
151+
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
152+
}
153+
// Lookup the statedb of parent block from the live database,
154+
// otherwise regenerate it on the flight.
155+
statedb, release, err := bc.StateAtBlock(ctx, parent, reexec, nil, true, false)
156+
if err != nil {
157+
return nil, vm.BlockContext{}, nil, nil, err
158+
}
159+
if txIndex == 0 && len(block.Transactions()) == 0 {
160+
return nil, vm.BlockContext{}, statedb, release, nil
161+
}
162+
// Recompute transactions up to the target index.
163+
signer := ethtypes.MakeSigner(bc.Config(), block.Number(), block.Time())
164+
for idx, tx := range block.Transactions() {
165+
// Assemble the transaction call message and return if the requested offset
166+
msg, _ := gethcore.TransactionToMessage(tx, signer, block.BaseFee())
167+
txContext := gethcore.NewEVMTxContext(msg)
168+
context := gethcore.NewEVMBlockContext(block.Header(), bc, nil)
169+
if idx == txIndex {
170+
return msg, context, statedb, release, nil
171+
}
172+
// Not yet the searched for transaction, execute on top of the current state
173+
vmenv := vm.NewEVM(context, txContext, statedb, bc.Config(), vm.Config{})
174+
statedb.SetTxContext(tx.Hash(), idx)
175+
if _, err = gethcore.ApplyMessage(vmenv,
176+
msg, new(gethcore.GasPool).AddGas(tx.Gas())); err != nil {
177+
return nil, vm.BlockContext{}, nil, nil,
178+
fmt.Errorf("transaction %s failed: %w", tx.Hash().Hex(), err)
179+
}
180+
// Ensure any modifications are committed to the state
181+
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
182+
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
183+
}
184+
return nil, vm.BlockContext{}, nil, nil,
185+
fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
186+
}
187+
79188
// GetVMConfig returns the vm.Config for the current chain.
80189
func (bc *blockchain) GetVMConfig() *vm.Config {
81190
return bc.vmConfig
82191
}
192+
193+
// Config returns the Ethereum chain config from the host chain.
194+
func (bc *blockchain) Config() *params.ChainConfig {
195+
return bc.config
196+
}

eth/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ require (
4646
github.com/deckarep/golang-set/v2 v2.3.1 // indirect
4747
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
4848
github.com/deepmap/oapi-codegen v1.13.4 // indirect
49+
github.com/dlclark/regexp2 v1.7.0 // indirect
50+
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 // indirect
4951
github.com/ethereum/c-kzg-4844 v0.4.0 // indirect
5052
github.com/fjl/memsize v0.0.1 // indirect
5153
github.com/fsnotify/fsnotify v1.7.0 // indirect
@@ -59,6 +61,7 @@ require (
5961
github.com/go-playground/locales v0.14.1 // indirect
6062
github.com/go-playground/universal-translator v0.18.1 // indirect
6163
github.com/go-playground/validator/v10 v10.15.1 // indirect
64+
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
6265
github.com/go-stack/stack v1.8.1 // indirect
6366
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
6467
github.com/goccy/go-json v0.10.2 // indirect

0 commit comments

Comments
 (0)