Use the statedb database to get Tries

The old approach of getting state tries involved instantiating a
new state database and using it to instantiate a trie. What we didn't
realize was that state database needed to have Close() called on it,
which we didn't offer a way for plugins to do, resulting in memory
leaking.

This approach reuses the primary trie database associated with the
blockchain object, albeit a bit indirectly. This will allow access
to recent tries that are stored in memory, where previously only
tries that had been committed to disk were accessible.
This commit is contained in:
Austin Roberts 2023-12-06 16:04:56 -06:00
parent 703c3b6b74
commit bc14d12fd7
4 changed files with 14 additions and 87 deletions

View File

@ -358,8 +358,7 @@ func geth(ctx *cli.Context) error {
} }
stack, backend := makeFullNode(ctx) stack, backend := makeFullNode(ctx)
trieCfg := plugethCaptureTrieConfig(ctx, stack, backend) wrapperBackend := backendwrapper.NewBackend(backend)
wrapperBackend := backendwrapper.NewBackend(backend, trieCfg)
pluginsInitializeNode(stack, wrapperBackend) pluginsInitializeNode(stack, wrapperBackend)
if ok, err := plugins.RunSubcommand(ctx); ok { if ok, err := plugins.RunSubcommand(ctx); ok {

View File

@ -1,25 +1,15 @@
package main package main
import ( import (
"github.com/ethereum/go-ethereum/cmd/utils"
gcore "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
"github.com/ethereum/go-ethereum/plugins" "github.com/ethereum/go-ethereum/plugins"
"github.com/ethereum/go-ethereum/plugins/wrappers" "github.com/ethereum/go-ethereum/plugins/wrappers"
"github.com/openrelayxyz/plugeth-utils/core" "github.com/openrelayxyz/plugeth-utils/core"
"github.com/openrelayxyz/plugeth-utils/restricted" "github.com/openrelayxyz/plugeth-utils/restricted"
"github.com/urfave/cli/v2"
) )
func apiTranslate(apis []core.API) []rpc.API { func apiTranslate(apis []core.API) []rpc.API {
@ -132,69 +122,3 @@ func pluginBlockChain() {
} }
BlockChain(plugins.DefaultPluginLoader) BlockChain(plugins.DefaultPluginLoader)
} }
func plugethCaptureTrieConfig(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) *trie.Config {
ethCfg := new(ethconfig.Config)
if ctx.IsSet(utils.CacheFlag.Name) || ctx.IsSet(utils.CacheTrieFlag.Name) {
ethCfg.TrieCleanCache = ctx.Int(utils.CacheFlag.Name) * ctx.Int(utils.CacheTrieFlag.Name) / 100
}
if ctx.IsSet(utils.CacheNoPrefetchFlag.Name) {
ethCfg.NoPrefetch = ctx.Bool(utils.CacheNoPrefetchFlag.Name)
}
if ctx.IsSet(utils.CacheFlag.Name) || ctx.IsSet(utils.CacheGCFlag.Name) {
ethCfg.TrieDirtyCache = ctx.Int(utils.CacheFlag.Name) * ctx.Int(utils.CacheGCFlag.Name) / 100
}
if ctx.IsSet(utils.GCModeFlag.Name) {
ethCfg.NoPruning = ctx.String(utils.GCModeFlag.Name) == "archive"
}
if ctx.IsSet(utils.CacheFlag.Name) || ctx.IsSet(utils.CacheSnapshotFlag.Name) {
ethCfg.SnapshotCache = ctx.Int(utils.CacheFlag.Name) * ctx.Int(utils.CacheSnapshotFlag.Name) / 100
}
ethCfg.Preimages = ctx.Bool(utils.CachePreimagesFlag.Name)
if ethCfg.NoPruning && !ethCfg.Preimages {
ethCfg.Preimages = true
log.Info("Enabling recording of key preimages since archive mode is used")
}
if ctx.IsSet(utils.StateHistoryFlag.Name) {
ethCfg.StateHistory = ctx.Uint64(utils.StateHistoryFlag.Name)
}
chaindb := backend.ChainDb()
scheme, err := rawdb.ParseStateScheme(ctx.String(utils.StateSchemeFlag.Name), chaindb)
if err != nil {
utils.Fatalf("%v", err)
}
ethCfg.StateScheme = scheme
cacheCfg := &gcore.CacheConfig{
TrieCleanLimit: ethCfg.TrieCleanCache,
TrieCleanNoPrefetch: ethCfg.NoPrefetch,
TrieDirtyLimit: ethCfg.TrieDirtyCache,
TrieDirtyDisabled: ethCfg.NoPruning,
TrieTimeLimit: ethconfig.Defaults.TrieTimeout,
SnapshotLimit: ethCfg.SnapshotCache,
Preimages: ethCfg.Preimages,
StateHistory: ethCfg.StateHistory,
StateScheme: ethCfg.StateScheme,
}
config := &trie.Config{Preimages: cacheCfg.Preimages}
if cacheCfg.StateScheme == rawdb.HashScheme {
config.HashDB = &hashdb.Config{
CleanCacheSize: cacheCfg.TrieCleanLimit * 1024 * 1024,
}
}
if cacheCfg.StateScheme == rawdb.PathScheme {
config.PathDB = &pathdb.Config{
StateHistory: cacheCfg.StateHistory,
CleanCacheSize: cacheCfg.TrieCleanLimit * 1024 * 1024,
DirtyCacheSize: cacheCfg.TrieDirtyLimit * 1024 * 1024,
}
}
return config
}

View File

@ -13,12 +13,10 @@ import (
gcore "github.com/ethereum/go-ethereum/core" gcore "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
gparams "github.com/ethereum/go-ethereum/params" gparams "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
@ -29,6 +27,7 @@ import (
type Backend struct { type Backend struct {
b ethapi.Backend b ethapi.Backend
db state.Database
newTxsFeed event.Feed newTxsFeed event.Feed
newTxsOnce sync.Once newTxsOnce sync.Once
chainFeed event.Feed chainFeed event.Feed
@ -44,14 +43,17 @@ type Backend struct {
removedLogsFeed event.Feed removedLogsFeed event.Feed
removedLogsOnce sync.Once removedLogsOnce sync.Once
chainConfig *params.ChainConfig chainConfig *params.ChainConfig
trieConfig *trie.Config
} }
func NewBackend(b ethapi.Backend, tc *trie.Config) *Backend { func NewBackend(b ethapi.Backend) *Backend {
state, _, err := b.StateAndHeaderByNumber(context.Background(), 0)
if err != nil {
panic(err.Error())
}
return &Backend{ return &Backend{
b: b, b: b,
trieConfig: tc} db: state.Database(),
}
} }
func (b *Backend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { func (b *Backend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
@ -492,7 +494,7 @@ func CloneChainConfig(cf *gparams.ChainConfig) *params.ChainConfig {
} }
func (b *Backend) GetTrie(h core.Hash) (core.Trie, error) { func (b *Backend) GetTrie(h core.Hash) (core.Trie, error) {
tr, err := trie.NewStateTrie(trie.TrieID(common.Hash(h)), trie.NewDatabase(b.b.ChainDb(), b.trieConfig)) tr, err := b.db.OpenTrie(common.Hash(h))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -508,7 +510,7 @@ func (b *Backend) GetAccountTrie(stateRoot core.Hash, account core.Address) (cor
if err != nil { if err != nil {
return nil, err return nil, err
} }
acTr, err := trie.NewStateTrie(trie.StorageTrieID(common.Hash(stateRoot), crypto.Keccak256Hash(account[:]), common.Hash(act.Root)), trie.NewDatabase(b.b.ChainDb(), b.trieConfig)) acTr, err := b.db.OpenStorageTrie(common.Hash(stateRoot), common.Address(account), common.Hash(act.Root))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -40,7 +40,9 @@ func (t *WrappedTrie) Hash() core.Hash {
func (t *WrappedTrie) NodeIterator(startKey []byte) core.NodeIterator { func (t *WrappedTrie) NodeIterator(startKey []byte) core.NodeIterator {
itr, err := t.t.NodeIterator(startKey) itr, err := t.t.NodeIterator(startKey)
if err != nil {
log.Error("Error returned from geth side NodeIterator", "err", err) log.Error("Error returned from geth side NodeIterator", "err", err)
}
return &WrappedNodeIterator{itr} return &WrappedNodeIterator{itr}
} }